diff --git a/.github/workflows/async-auto-label.yml b/.github/workflows/async-auto-label.yml new file mode 100644 index 000000000..b0a17c7fa --- /dev/null +++ b/.github/workflows/async-auto-label.yml @@ -0,0 +1,17 @@ +--- +name: Apply requested async label + +'on': + issue_comment: + types: + - created + - edited + +permissions: {} + +jobs: + apply_async_labels: + name: Apply requested async label + uses: alisw/ali-bot/.github/workflows/async-auto-label.yml@master + permissions: + pull-requests: write # to update labels diff --git a/.github/workflows/async-list-label.yml b/.github/workflows/async-list-label.yml new file mode 100644 index 000000000..b53ec6e6e --- /dev/null +++ b/.github/workflows/async-list-label.yml @@ -0,0 +1,19 @@ +--- +name: Collect and print async labels + +'on': + pull_request_target: + types: + - opened + - reopened + branches: + - master + +permissions: {} + +jobs: + list_async_labels: + name: Collect and print async labels + uses: alisw/ali-bot/.github/workflows/async-list-label.yml@master + permissions: + pull-requests: write # to update labels diff --git a/.github/workflows/clean-ci.yml b/.github/workflows/clean-ci.yml new file mode 100644 index 000000000..982db579b --- /dev/null +++ b/.github/workflows/clean-ci.yml @@ -0,0 +1,35 @@ +--- +name: Clean PR builds + +'on': + workflow_dispatch: + inputs: + pr: + description: PR number in this repo to be cleaned + type: string # can't use number here + required: true + + # Warning: GitHub limits the total number of inputs to 10, so a maximum of + # 9 checks is allowed here! + # Warning: the check_* keys are magic and must consist of the string + # "check_" followed by the applicable check name exactly. The + # "description" field is only the human-readable label for the input. + 'check_build/O2DPG/O2fst/o2': + description: build/O2DPG/O2fst/o2 + type: boolean + default: true + +permissions: {} + +jobs: + clean: + name: Clean PR checks + uses: alisw/ali-bot/.github/workflows/clean-pr-checks.yml@master + with: + owner: ${{ github.event.repository.owner.login }} + repo: ${{ github.event.repository.name }} + pr: ${{ github.event.inputs.pr }} + checks: ${{ toJSON(github.event.inputs) }} + permissions: + pull-requests: read # to get last commit for pr (octokit/graphql-action) + statuses: write # for set-github-status diff --git a/.github/workflows/sim-tests.yml b/.github/workflows/sim-tests.yml new file mode 100644 index 000000000..60c05358f --- /dev/null +++ b/.github/workflows/sim-tests.yml @@ -0,0 +1,192 @@ +--- +name: Simulation tests + +'on': + pull_request: {} + workflow_dispatch: + inputs: + tag: + description: 'O2PDPSuite tag to test against (default: newest daily)' + type: string + required: false + +permissions: {} + +concurrency: + group: sim-tests-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + sim-tests: + name: Simulation tests against CVMFS + runs-on: [self-hosted, cvmfs] + timeout-minutes: 180 + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + # The changed-file logic diffs against the merge base, so the full + # history is needed, not a shallow clone. + fetch-depth: 0 + + - name: Resolve the diff base + id: base + if: github.event_name == 'pull_request' + env: + BASE_SHA: ${{ github.event.pull_request.base.sha }} + HEAD_SHA: ${{ github.event.pull_request.head.sha }} + run: | + set -eu + git rev-parse --verify "$BASE_SHA^{commit}" >/dev/null || { + echo "::error title=Cannot resolve diff base::pull request base commit $BASE_SHA does not resolve in this checkout" + exit 1 + } + git rev-parse --verify "$HEAD_SHA^{commit}" >/dev/null || { + echo "::error title=Cannot resolve diff head::pull request head commit $HEAD_SHA does not resolve in this checkout" + exit 1 + } + merge_base=$(git merge-base "$BASE_SHA" "$HEAD_SHA") || { + echo "::error title=Cannot compute diff base::git merge-base failed" + exit 1 + } + [ -n "$merge_base" ] || { + echo "::error title=Cannot compute diff base::merge base is empty" + exit 1 + } + echo "sha=$merge_base" >> "$GITHUB_OUTPUT" + + - name: Skip when not relevant or opted into the source build + id: gate + if: github.event_name == 'pull_request' + env: + BASE_SHA: ${{ steps.base.outputs.sha }} + HEAD_SHA: ${{ github.event.pull_request.head.sha }} + run: | + set -eu + changed=$(git diff --name-only "$BASE_SHA" "$HEAD_SHA") + # Opting in means *editing* the existing sentinel, so only count it + # when it is Modified. A pull request that Adds it — which is what the + # pull request introducing the sentinel does — would otherwise trip its + # own opt-out and pass without testing anything. + opted=$(git diff --name-only --diff-filter=M "$BASE_SHA" "$HEAD_SHA") + if grep -qx 'test/needs-o2-dev' <<< "$opted" ; then + echo "::notice title=Skipped::this pull request touches test/needs-o2-dev, so it is tested by build/O2DPG/sim/o2dev against O2 dev instead" + echo "skip=true" >> "$GITHUB_OUTPUT" + elif ! grep -qE '^(DATA/|MC/|test/|RelVal/)' <<< "$changed" ; then + echo "::notice title=Skipped::no changed file matches DATA/, MC/, test/ or RelVal/" + echo "skip=true" >> "$GITHUB_OUTPUT" + else + echo "skip=false" >> "$GITHUB_OUTPUT" + fi + + - name: Check the CVMFS environment + if: steps.gate.outputs.skip != 'true' + run: | + set -eu + test -d /cvmfs/alice.cern.ch || { + echo "::error title=CVMFS unavailable::/cvmfs/alice.cern.ch is not mounted on this runner" + exit 1 + } + test -x /cvmfs/alice.cern.ch/bin/alienv || { + echo "::error title=CVMFS unavailable::/cvmfs/alice.cern.ch/bin/alienv is missing" + exit 1 + } + + - name: Check the AliEn token + if: steps.gate.outputs.skip != 'true' + env: + JALIEN_TOKEN_CERT: /run/alien-ci/tokencert.pem + JALIEN_TOKEN_KEY: /run/alien-ci/tokenkey.pem + run: | + set -eu + # DPL's CCDB backend refuses to talk to alice-ccdb.cern.ch without a + # token and aborts the whole device, so check up front rather than + # letting it surface as a confusing task crash deep in a workflow. + for f in "$JALIEN_TOKEN_CERT" "$JALIEN_TOKEN_KEY" ; do + test -r "$f" || { + echo "::error title=No AliEn token::$f is missing or unreadable. On the runner host: systemctl start alien-ci-token.service" + exit 1 + } + done + if ! openssl x509 -in "$JALIEN_TOKEN_CERT" -noout -checkend 3600 >/dev/null 2>&1 ; then + echo "::error title=AliEn token expiring::the token expires within the hour. On the runner host: systemctl start alien-ci-token.service" + exit 1 + fi + echo "AliEn token valid until $(openssl x509 -in "$JALIEN_TOKEN_CERT" -noout -enddate | cut -d= -f2)" + + - name: Resolve the O2PDPSuite tag + id: tag + if: steps.gate.outputs.skip != 'true' + env: + REQUESTED_TAG: ${{ inputs.tag }} + PR_BODY: ${{ github.event.pull_request.body }} + run: | + set -eu + # shellcheck source=test/ci/resolve_tag.sh + . test/ci/resolve_tag.sh + moduledir=/cvmfs/alice.cern.ch/el9-x86_64/Modules/modulefiles/O2PDPSuite + requested=$REQUESTED_TAG + if [ -z "$requested" ]; then + # A PR can pin the release with a line "sim-tests-tag: ". + requested=$(printf '%s\n' "$PR_BODY" | + sed -n 's/^[[:space:]]*sim-tests-tag:[[:space:]]*//p' | head -n 1 | + tr -d '[:space:]') + fi + tag=$(resolve_o2pdpsuite_tag "$moduledir" "$requested") || { + echo "::error title=No usable O2PDPSuite release::see the message above" + exit 1 + } + echo "Testing against O2PDPSuite::$tag" + echo "tag=$tag" >> "$GITHUB_OUTPUT" + + - name: Run the O2DPG tests + if: steps.gate.outputs.skip != 'true' + env: + O2PDPSUITE_TAG: ${{ steps.tag.outputs.tag }} + O2DPG_TEST_HASH_BASE: ${{ steps.base.outputs.sha }} + O2DPG_TEST_HASH_HEAD: ${{ github.event.pull_request.head.sha }} + JOBS: 8 + # o2dpg_sim_workflow.py calls JAlien(['whoami']) purely to fill the + # AOD's --created-by field, and only when JALIEN_USER is unset. Setting + # it keeps the tests from needing a GRID credential at all, which + # matters here: anything readable by this account is readable by the + # fork-pull-request code that runs as it. + JALIEN_USER: alien-ci + # DPL's CCDB backend needs a GRID token for alice-ccdb.cern.ch. These + # are *paths* to a short-lived token minted from a service certificate + # by a root-owned timer on the runner host; the certificate itself is + # never readable by this account. Verified that paths work — the same + # variables also accept PEM content, which is what ali-bot passes. + JALIEN_TOKEN_CERT: /run/alien-ci/tokencert.pem + JALIEN_TOKEN_KEY: /run/alien-ci/tokenkey.pem + run: | + set -eu + # Everything after "-c" is joined into one string and re-evaluated + # by the CVMFS alienv via "bash -c \"$*\"". Quoting here is applied + # once then discarded, so it is safe for the runner's workspace path, + # but a path with a space or "$" would break or double-evaluate. + /cvmfs/alice.cern.ch/bin/alienv setenv "O2PDPSuite/$O2PDPSUITE_TAG" -c \ + env O2DPG_ROOT="$PWD" O2DPG_MC_CONFIG_ROOT="$PWD" \ + O2DPG_TEST_REPO_DIR="$PWD" \ + O2DPG_TEST_HASH_BASE="$O2DPG_TEST_HASH_BASE" \ + O2DPG_TEST_HASH_HEAD="$O2DPG_TEST_HASH_HEAD" \ + JOBS="$JOBS" \ + bash test/run_tests.sh + + - name: Upload logs + if: always() + uses: actions/upload-artifact@v4 + with: + name: o2dpg-test-logs + path: | + o2dpg_tests/**/*.log + o2dpg_tests/**/*serverlog* + o2dpg_tests/**/*workerlog* + o2dpg_tests/**/*mergerlog* + if-no-files-found: ignore + retention-days: 14 + + - name: Prune test artifacts + if: always() + run: find o2dpg_tests -type f ! -name '*.log' ! -name '*serverlog*' ! -name '*workerlog*' ! -name '*mergerlog*' -delete || true diff --git a/.github/workflows/syntax-checks.yml b/.github/workflows/syntax-checks.yml new file mode 100644 index 000000000..73b888523 --- /dev/null +++ b/.github/workflows/syntax-checks.yml @@ -0,0 +1,176 @@ +--- +name: Validate syntax + +'on': + - push + - pull_request + +permissions: {} + +jobs: + json-syntax: + name: JSON + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Validate syntax for JSON files + run: | + error=0 + readarray -d '' json_files < \ + <(find . \( -path ./.git -or -path ./DATA/testing/private \) -prune -false -or -type f -name '*.json' -print0) + for jsonf in "${json_files[@]}"; do + echo "::debug::Checking $jsonf..." + if ! errmsg=$(jq . "$jsonf" 2>&1 >/dev/null); then + error=1 + echo "Invalid JSON syntax found in $jsonf:" >&2 + printf '::error file=%s,title=%s::%s\n' "$jsonf" 'Invalid JSON syntax' "$errmsg" + fi + done + exit "$error" + + bash-syntax: + name: Bash + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Validate syntax with bash -n + run: | + error=0 + readarray -d '' files < \ + <(find . -path ./.git -prune -false -or -type f -name '*.sh' -print0) + for bashf in "${files[@]}"; do + echo "::debug::Checking $bashf..." + if ! errmsg=$(bash -n "$bashf" 2>&1 >/dev/null); then + error=1 + echo "Invalid Bash syntax found in $bashf:" >&2 + printf '::error file=%s,title=%s::%s\n' "$bashf" 'Invalid syntax' "$errmsg" + fi + done + exit "$error" + + shellcheck: + name: Shellcheck + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Run Shellcheck to find errors + run: | + error=0 + find . -path ./.git -prune -false -or -type f -name '*.sh' -print0 | + xargs -0 shellcheck -xf json1 -S error -s bash > errors.json || error=$? + # Produce code annotations in GitHub's format. + jq -r '.comments[] | "Error found in \(.file) line \(.line):\n::error file=\(.file),line=\(.line),endLine=\(.endLine),col=\(.column),endColumn=\(.endColumn)::\(.message)"' errors.json + exit "$error" + + config-check: + name: Config-check + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Run .ini configs check + run: | + error=0 + readarray -d '' files < \ + <(find . -path ./.git -prune -false -or -type f -name '*.ini' -print0) + for cfg in "${files[@]}"; do + if grep -q "O2DPG_ROOT" "$cfg"; then + error=1 + echo "Deprecated O2DPG_ROOT detected in $cfg, replace with O2DPG_MC_CONFIG_ROOT" >&2 + fi + done + exit "$error" + + - name: Run .C configs check + run: | + error=0 + readarray -d '' files < \ + <(find . -path ./.git -prune -false -or -type f -name '*.C' -print0) + for cfg in "${files[@]}"; do + if grep -q "\$O2DPG_ROOT" "$cfg" || grep -q "\${O2DPG_ROOT}" "$cfg"; then + error=1 + echo "Deprecated O2DPG_ROOT detected in $cfg, replace with O2DPG_MC_CONFIG_ROOT" >&2 + fi + done + exit "$error" + + runner-tests: + name: Workflow-runner unit tests + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install prerequisites + run: pip install pytest psutil + + - name: Run the o2dpg_runner test suite + working-directory: MC/workflow_runner + run: pytest o2dpg_runner/tests -q + + filegraph-tests: + name: File-IO-graph unit tests + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install prerequisites + run: pip install psutil + + - name: Run the FileIOGraph test suite + run: python3 -m unittest discover -s UTILS/FileIOGraph/tests -t UTILS/FileIOGraph/tests + + bash-harnesses: + name: Test-harness unit tests + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Run the offline test harnesses + run: | + error=0 + for t in test/tests/*.sh ; do + echo "::group::$t" + bash "$t" || error=1 + echo "::endgroup::" + done + exit "$error" + + pylint: + name: Pylint + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install prerequisites + run: | + sudo apt update -y + sudo apt install -y pylint + + - name: Run Pylint to find errors + run: | + error=0 + find . -path ./.git -prune -false -or -type f -name '*.py' -print0 | + # "import-errors" are shown for valid modules like ROOT, so ignore them. + xargs -0 pylint -E -f json --disable import-error > errors.json || error=$? + # Produce code annotations in GitHub's format. + jq -r '.[] | "Error found in \(.path) line \(.line):\n::error file=\(.path),line=\(.line),endLine=\(.endLine),col=\(.column),endColumn=\(.endColumn),title=Pylint \(.type) \(.symbol)::\(.message)"' errors.json + exit "$error" diff --git a/.gitignore b/.gitignore index 27281eba7..8271e2f98 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ .vscode *.pyc o2dpg_tests/** +__pycache__/ diff --git a/CODEOWNERS b/CODEOWNERS index f697dcaf8..20e4e4eee 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -1,5 +1,5 @@ -* @davidrohr @martenole @shahor02 @chiarazampolli @sawenzel @benedikt-voelkel -/DATA/production @davidrohr @martenole @shahor02 @chiarazampolli +* @davidrohr @shahor02 @chiarazampolli @sawenzel +/DATA/production @davidrohr @shahor02 @chiarazampolli /DATA/testing/detectors/CPV /DATA/testing/detectors/EMC @@ -8,14 +8,25 @@ /DATA/testing/detectors/FV0 /DATA/testing/detectors/HMP /DATA/testing/detectors/ITS -/DATA/testing/detectors/MCH @aphecethce +/DATA/testing/detectors/MCH /DATA/testing/detectors/MFT -/DATA/testing/detectors/MID @aphecethce +/DATA/testing/detectors/MID /DATA/testing/detectors/PHS /DATA/testing/detectors/TOF @noferini @chiarazampolli /DATA/testing/detectors/TPC @wiechula -/DATA/testing/detectors/TRD @martenole +/DATA/testing/detectors/TRD /DATA/testing/detectors/ZDC -/MC @sawenzel @chiarazampolli @gconesab @benedikt-voelkel -/RelVal @sawenzel @chiarazampolli @gconesab @benedikt-voelkel +/MC @sawenzel @jackal1-66 +/GRID @sawenzel + +# PWG experts +/MC/*/ALICE3 @njacazio +/MC/*/PWGHF @AliceO2Group/reviewers-pwg-hf +/MC/*/PWGLF @AliceO2Group/reviewers-pwg-lf +/MC/*/PWGEM @AliceO2Group/reviewers-pwg-em +/MC/*/PWGGAJE @AliceO2Group/reviewers-pwg-je +/MC/*/PWGCF @AliceO2Group/reviewers-pwg-cf +/MC/*/PWGDQ @AliceO2Group/reviewers-pwg-dq +/MC/*/PWGUD @AliceO2Group/reviewers-pwg-ud +/RelVal @sawenzel @chiarazampolli diff --git a/DATA/README.md b/DATA/README.md index 6b3bffbb7..8b3afcb9e 100644 --- a/DATA/README.md +++ b/DATA/README.md @@ -43,6 +43,7 @@ Another abstraction layer above the *workflows* are **topology descriptions**. T ``` - Workflows should aggregate their workflow parts in the `$WORKFLOW` variable, and use `add_W` to add a workflow, and `add_QC_from_consul` to add a QC workflow with a JSON file from consul. - Calibration workflows must not pollute the production CCDB in the following case (`if [[ $RUNTYPE == "SYNTHETIC" || "${GEN_TOPO_DEPLOYMENT_TYPE:-}" == "ALICE_STAGING" ]]; then` (bash script)), in this case please e.g. upload to `ccdb-test.cern.ch`. +- If a workflow uses an `o2-dpl-raw-proxy` to receive data from DataDistribution, that device must have the `--inject-missing-data` comand line option. # Configuring and selecting workflow in AliECS: There are 3 ways foreseen to configure the *full topology* in AliECS: (currently only the manual XML option exists) @@ -91,6 +92,7 @@ commit=xxxx|path=xxxx file=topologies.desc topology=demo-full-topology parameter Calibration workflows can be different when they use an aggregator. In that case, there is processing running on each EPN, and the output is sent to an aggregator node. Communication happens via the `o2-dpl-raw-proxy` and the `o2-dpl-output-proxy`. To set up such a workflow, a couple of points must be followed: - There are 2 different shell scripts, one for the "reco" part running on each EPN, and one for the calibration aggregator "calib" part on the calibration node. There may be more than one aggregator in the topology, in that case it is one *reco* script and multiple *calib* scripts. +- The o2-dpl-raw-proxy on a calib aggrgator workflow MUST NOT use the `--inject-missing-data` command line argument! (This is only for input proxies receiving data from DataDistribution!) - The *reco* script must contain an `o2-dpl-output-proxy` to send the output and each calib script must contain an `o2-dpl-raw-proxy` for the input. - Each of the input "raw" proxies must be assigned a unique name via the `--proxy-name [NAME]` option. Note that the *reco* script also contains an input raw proxy, with the default name `readout-proxy`. - The channel-name of each input proxy must match the proxy name. The *calib* input proxies' channels must use `method=bind`. The output proxies must use `method=connect` and the channel name must match the name of the input proxy they are connecting to. @@ -192,7 +194,7 @@ Resolving deltas: 100% (64/64), done. [drohr@epn245 ~]$ cd O2DPG/DATA/testing/ [drohr@epn245 testing]$ mkdir -p private/drohr [drohr@epn245 testing]$ ls examples/ -example-workflow.sh workflows.desc +example-workflow.sh workflows.desc [drohr@epn245 testing]$ cp examples/* private/drohr/ [drohr@epn245 testing]$ vi private/drohr/workflows.desc [drohr@epn245 testing]$ mv private/drohr/example-workflow.sh private/drohr/my-workflow.sh @@ -225,7 +227,7 @@ export WORKFLOW_PARAMETERS= # Additiona export RECO_NUM_NODES_OVERRIDE=0 # Override the number of EPN compute nodes to use (default is specified in description library file) export NHBPERTF=256 # Number of HBF per TF -/opt/alisw/el8/GenTopo/bin/gen_topo.sh > $HOME/gen_topo_output.xml +/opt/alisw/el10/GenTopo/bin/gen_topo.sh > $HOME/gen_topo_output.xml [drohr@epn245 test]$ ./run.sh Loading ODC/0.36-1 Loading requirement: BASE/1.0 GCC-Toolchain/v10.2.0-alice2-3 fmt/7.1.0-10 FairLogger/v1.9.1-7 zlib/v1.2.8-8 OpenSSL/v1.0.2o-9 libpng/v1.6.34-9 sqlite/v3.15.0-2 libffi/v3.2.1-2 FreeType/v2.10.1-8 Python/v3.6.10-12 Python-modules/1.0-16 boost/v1.75.0-13 ZeroMQ/v4.3.3-6 ofi/v1.7.1-8 asio/v1.19.1-2 asiofi/v0.5.1-2 DDS/3.5.16-5 FairMQ/v1.4.40-4 @@ -275,7 +277,7 @@ export WORKFLOW_PARAMETERS=EVENT_DISPLAY,CTF,GPU # Additiona export RECO_NUM_NODES_OVERRIDE=0 # Override the number of EPN compute nodes to use (default is specified in description library file) export NHBPERTF=256 # Number of HBF per TF -/opt/alisw/el8/GenTopo/bin/gen_topo.sh > $HOME/gen_topo_output.xml +/opt/alisw/el10/GenTopo/bin/gen_topo.sh > $HOME/gen_topo_output.xml [drohr@epn245 test]$ ./run.sh Loading ODC/0.36-1 Loading requirement: BASE/1.0 GCC-Toolchain/v10.2.0-alice2-3 fmt/7.1.0-10 FairLogger/v1.9.1-7 zlib/v1.2.8-8 OpenSSL/v1.0.2o-9 libpng/v1.6.34-9 sqlite/v3.15.0-2 libffi/v3.2.1-2 FreeType/v2.10.1-8 Python/v3.6.10-12 Python-modules/1.0-16 boost/v1.75.0-13 ZeroMQ/v4.3.3-6 ofi/v1.7.1-8 asio/v1.19.1-2 asiofi/v0.5.1-2 DDS/3.5.16-5 FairMQ/v1.4.40-4 diff --git a/DATA/common/README.md b/DATA/common/README.md index b93622c0a..702565f69 100644 --- a/DATA/common/README.md +++ b/DATA/common/README.md @@ -1,51 +1 @@ -The `setenv-sh` script sets the following environment options -* `NTIMEFRAMES`: Number of time frames to process. -* `TFDELAY`: Delay in seconds between publishing time frames (1 / rate). -* `NGPUS`: Number of GPUs to use, data distributed round-robin. -* `GPUTYPE`: GPU Tracking backend to use, can be CPU / CUDA / HIP / OCL / OCL2. -* `SHMSIZE`: Size of the global shared memory segment. -* `DDSHMSIZE`: Size of shared memory unmanaged region for DataDistribution Input. -* `GPUMEMSIZE`: Size of allocated GPU memory (if GPUTYPE != CPU) -* `HOSTMEMSIZE`: Size of allocated host memory for GPU reconstruction (0 = default). - * For `GPUTYPE = CPU`: TPC Tracking scratch memory size. (Default 0 -> dynamic allocation.) - * Otherwise : Size of page-locked host memory for GPU processing. (Defauls 0 -> 1 GB.) -* `CREATECTFDICT`: Create CTF dictionary. -* `SAVECTF`: Save the CTF to a root file. - * 0: Read `ctf_dictionary.root` as input. - * 1: Create `ctf_dictionary.root`. Note that this was already done automatically if the raw data was simulated with `full_system_test.sh`. -* `SYNCMODE`: Run only reconstruction steps of the synchronous reconstruction. - * Note that there is no `ASYNCMODE` but instead the `CTFINPUT` option already enforces asynchronous processing. -* `NUMAGPUIDS`: NUMAID-aware GPU id selection. Needed for the full EPN configuration with 8 GPUs, 2 NUMA domains, 4 GPUs per domain. - In this configuration, 2 instances of `dpl-workflow.sh` must run in parallel. - To be used in combination with `NUMAID` to select the id per workflow. - `start_tmux.sh` will set up these variables automatically. -* `NUMAID`: SHM segment id to use for shipping data as well as set of GPUs to use (use `0` / `1` for 2 NUMA domains, 0 = GPUS `0` to `NGPUS - 1`, 1 = GPUS `NGPUS` to `2 * NGPUS - 1`) -* 0: Runs all reconstruction steps, of sync and of async reconstruction, using raw data input. -* 1: Runs only the steps of synchronous reconstruction, using raw data input. -* `EXTINPUT`: Receive input from raw FMQ channel instead of running o2-raw-file-reader. - * 0: `dpl-workflow.sh` can run as standalone benchmark, and will read the input itself. - * 1: To be used in combination with either `datadistribution.sh` or `raw-reader.sh` or with another DataDistribution instance. -* `CTFINPUT`: Read input from CTF ROOT file. This option is incompatible to EXTINPUT=1. The CTF ROOT file can be stored via SAVECTF=1. -* `NHBPERTF`: Time frame length (in HBF) -* `GLOBALDPLOPT`: Global DPL workflow options appended to o2-dpl-run. -* `EPNPIPELINES`: Set default EPN pipeline multiplicities. - Normally the workflow will start 1 dpl device per processor. - For some of the CPU parts, this is insufficient to keep step with the GPU processing rate, e.g. one ITS-TPC matcher on the CPU is slower than the TPC tracking on multiple GPUs. - This option adds some multiplicies for CPU processes using DPL's pipeline feature. - The settings were tuned for EPN processing with 4 GPUs (i.e. the default multiplicities are per NUMA domain). - The multiplicities are scaled with the `NGPUS` setting, i.e. with 1 GPU only 1/4th are applied. - You can pass an option different to 1, and than it will be applied as factor on top of the multiplicities. - It is auto-selected by `start-tmux.sh`. -* `SEVERITY`: Log verbosity (e.g. info or error, default: info) -* `INFOLOGGER_SEVERITY`: Min severity for messages sent to Infologger. (default: `$SEVERITY`) -* `SHMTHROW`: Throw exception when running out of SHM memory. - It is suggested to leave this enabled (default) on tests on the laptop to get an actual error when it runs out of memory. - This is disabled in `start_tmux.sh`, to avoid breaking the processing while there is a chance that another process might free memory and we can continue. -* `NORATELOG`: Disable FairMQ Rate Logging. -* `INRAWCHANNAME`: FairMQ channel name used by the raw proxy, must match the name used by DataDistribution. -* `WORKFLOWMODE`: run (run the workflow (default)), print (print the command to stdout), dds (create partial DDS topology) -* `FILEWORKDIR`: directory for all input / output files. E.g. grp / geometry / dictionaries etc. are read from here, and dictionaries / ctf / etc. are written to there. - Some files have more fine grained control via other environment variables (e.g. to store the CTF to somewhere else). Such variables are initialized to `$FILEWORKDIR` by default but can be overridden. -* `EPNSYNCMODE`: Specify that this is a workflow running on the EPN for synchronous processing, e.g. logging goes to InfoLogger, DPL metrics to to the AliECS monitoring, etc. -* `BEAMTYPE`: Beam type, must be PbPb, pp, pPb, cosmic, technical. -* `IS_SIMULATED_DATA` : 1 for MC data, 0 for RAW data. +For a reference to available env-variables, please check https://github.com/AliceO2Group/AliceO2/blob/dev/prodtests/full-system-test/documentation/env-variables.md diff --git a/DATA/common/gen_topo_helper_functions.sh b/DATA/common/gen_topo_helper_functions.sh index 6a3e84149..c868411b2 100755 --- a/DATA/common/gen_topo_helper_functions.sh +++ b/DATA/common/gen_topo_helper_functions.sh @@ -9,9 +9,19 @@ has_detector() [[ $WORKFLOW_DETECTORS =~ (^|,)"$1"(,|$) ]] } +has_detector_from_global_reader_clusters() +{ + [[ $WORKFLOW_DETECTORS_USE_GLOBAL_READER_CLUSTERS =~ (^|,)"$1"(,|$) ]] +} + +has_detector_from_global_reader_tracks() +{ + [[ $WORKFLOW_DETECTORS_USE_GLOBAL_READER_TRACKS =~ (^|,)"$1"(,|$) ]] +} + has_detector_from_global_reader() { - [[ $WORKFLOW_DETECTORS_USE_GLOBAL_READER =~ (^|,)"$1"(,|$) ]] + has_detector_from_global_reader_tracks $1 || has_detector_from_global_reader_clusters $1 } has_detector_calib() @@ -39,6 +49,21 @@ has_detector_matching() [[ $WORKFLOW_DETECTORS_MATCHING =~ (^|,)"ALL"(,|$) ]] || [[ $WORKFLOW_DETECTORS_MATCHING =~ (^|,)"$1"(,|$) ]] } +has_detector_gpu() +{ + has_detector $1 && [[ $WORKFLOW_DETECTORS_GPU =~ (^|,)"$1"(,|$) ]] +} + +has_secvtx_source() +{ + [[ $SVERTEXING_SOURCES =~ (^|,)"ALL"(,|$) ]] || [[ $SVERTEXING_SOURCES =~ (^|,)"$1"(,|$) ]] +} + +has_detector_in_secvtx_sources() +{ + [[ $SVERTEXING_SOURCES =~ (^|,)"ALL"(,|$) ]] || [[ $SVERTEXING_SOURCES =~ (^|,|-)"$1"(-|,|$) ]] +} + has_detector_qc() { has_detector $1 && [[ $WORKFLOW_DETECTORS_QC =~ (^|,)"$1"(,|$) ]] @@ -94,37 +119,42 @@ _check_multiple() has_detectors() { - _check_multiple has_detector $@ + _check_multiple has_detector "$@" } has_detectors_qc() { - _check_multiple has_detector_qc $@ + _check_multiple has_detector_qc "$@" } has_detectors_calib() { - _check_multiple has_detector_calib $@ + _check_multiple has_detector_calib "$@" } has_detectors_reco() { - _check_multiple has_detector_reco $@ + _check_multiple has_detector_reco "$@" } has_detectors_ctf() { - _check_multiple has_detector_ctf $@ + _check_multiple has_detector_ctf "$@" } has_detectors_flp_processing() { - _check_multiple has_detector_flp_processing $@ + _check_multiple has_detector_flp_processing "$@" +} + +has_detectors_gpu() +{ + _check_multiple has_detector_gpu "$@" } workflow_has_parameters() { - _check_multiple workflow_has_parameter $@ + _check_multiple workflow_has_parameter "$@" } add_comma_separated() @@ -237,18 +267,22 @@ add_W() # Add binarry to workflow command USAGE: add_W [BINARY] [COMMAND_LINE_OP WORKFLOW+=$WFADD } -if [[ "${GEN_TOPO_DEPLOYMENT_TYPE:-}" == "ALICE_STAGING" ]]; then - GEN_TOPO_QC_CONSUL_SERVER=ali-staging.cern.ch -else - GEN_TOPO_QC_CONSUL_SERVER=alio2-cr1-hv-con01.cern.ch +if [[ ${EPNSYNCMODE:-0} == 1 ]]; then + if [[ "${GEN_TOPO_DEPLOYMENT_TYPE:-}" == "ALICE_STAGING" ]]; then + GEN_TOPO_QC_CONSUL_SERVER=ali-staging.cern.ch + else + GEN_TOPO_QC_CONSUL_SERVER=alio2-cr1-hv-con01.cern.ch + fi + GEN_TOPO_QC_APRICOT_SERVER=`curl -s "http://${GEN_TOPO_QC_CONSUL_SERVER}:8500/v1/kv/o2/runtime/aliecs/vars/apricot_endpoint?raw"` fi add_QC_from_consul() { + [[ ${EPNSYNCMODE:-0} == 1 ]] || { echo "Error fetching QC JSON $1: consul server only set for EPNSYNCMODE == 1 " 1>&2 && exit 1; } if [[ ! -z ${GEN_TOPO_QC_JSON_FILE:-} ]]; then curl -s -o $GEN_TOPO_QC_JSON_FILE "http://${GEN_TOPO_QC_CONSUL_SERVER}:8500/v1/kv${1}?raw" if [[ $? != 0 ]]; then - echo "Error fetching QC JSON $1" + echo "Error fetching QC JSON $1 (1)" 1>&2 exit 1 fi QC_CONFIG_ARG="json://${GEN_TOPO_QC_JSON_FILE}" @@ -258,4 +292,24 @@ add_QC_from_consul() add_W o2-qc "--config $QC_CONFIG_ARG $2" } +add_QC_from_apricot() +{ + [[ ${EPNSYNCMODE:-0} == 1 ]] || { echo "Error fetching QC JSON $1: apricot server only set for EPNSYNCMODE == 1 " 1>&2 && exit 1; } + if [[ ! -z ${GEN_TOPO_QC_JSON_FILE:-} ]]; then + if [[ ${1} =~ "?" ]]; then + curl -s -o $GEN_TOPO_QC_JSON_FILE "${GEN_TOPO_QC_APRICOT_SERVER}/${1}\&process=true" + else + curl -s -o $GEN_TOPO_QC_JSON_FILE "${GEN_TOPO_QC_APRICOT_SERVER}/${1}?process=true" + fi + if [[ $? != 0 ]]; then + echo "Error fetching QC JSON $1 (2)" 1>&2 + exit 1 + fi + QC_CONFIG_ARG="json://${GEN_TOPO_QC_JSON_FILE}" + else + QC_CONFIG_ARG="apricot://${GEN_TOPO_QC_APRICOT_SERVER}$1" + fi + add_W o2-qc "--config $QC_CONFIG_ARG $2" +} + fi # functions.sh sourced diff --git a/DATA/common/getCommonArgs.sh b/DATA/common/getCommonArgs.sh index dd4b5d551..bcecea974 100755 --- a/DATA/common/getCommonArgs.sh +++ b/DATA/common/getCommonArgs.sh @@ -10,18 +10,25 @@ if [[ -z $SEVERITY || -z $NUMAID || -z $SHMSIZE || -z $FILEWORKDIR || -z $EPNSYN fi ARGS_ALL="--session ${OVERRIDE_SESSION:-default} --severity $SEVERITY --shm-segment-id ${O2JOBSHMID:-$NUMAID} --shm-segment-size $SHMSIZE ${ARGS_ALL_EXTRA:-} --early-forward-policy noraw" -ARGS_ALL_CONFIG="NameConf.mDirGeom=$FILEWORKDIR;NameConf.mDirGRP=$FILEWORKDIRRUN;keyval.input_dir=$FILEWORKDIR;keyval.output_dir=/dev/null;${ALL_EXTRA_CONFIG:-}" +ARGS_ALL_CONFIG="keyval.input_dir=$FILEWORKDIR;" if [[ $EPNSYNCMODE == 1 ]]; then ARGS_ALL+=" --infologger-severity $INFOLOGGER_SEVERITY" - ARGS_ALL+=" --monitoring-backend influxdb-unix:///tmp/telegraf.sock --resources-monitoring 15" - ARGS_ALL_CONFIG+="NameConf.mCCDBServer=$GEN_TOPO_EPN_CCDB_SERVER;" + ARGS_ALL+=" --monitoring-backend influxdb-unix:///tmp/telegraf.sock --resources-monitoring ${GEN_TOPO_RESOURCE_MONITORING:-15} ${GEN_TOPO_RESOURCE_MONITORING:+--dpl-stats-min-online-publishing-interval $GEN_TOPO_RESOURCE_MONITORING}" + ARGS_ALL_CONFIG+="keyval.output_dir=/dev/null;NameConf.mCCDBServer=$GEN_TOPO_EPN_CCDB_SERVER;" export DPL_CONDITION_BACKEND=$GEN_TOPO_EPN_CCDB_SERVER [[ -z ${O2_DPL_DEPLOYMENT_MODE:-} ]] && O2_DPL_DEPLOYMENT_MODE=OnlineECS elif [[ "${ENABLE_METRICS:-}" != "1" ]]; then ARGS_ALL+=" --monitoring-backend no-op://" fi +ARGS_ALL_CONFIG+="${ALL_EXTRA_CONFIG:-}" + [[ $SHMTHROW == 0 ]] && ARGS_ALL+=" --bad-alloc-max-attempts 60 --bad-alloc-attempt-interval 1000" [[ ! -z ${SHM_MANAGER_SHMID:-} && ${GEN_TOPO_CALIB_WORKFLOW:-} != 1 ]] && ARGS_ALL+=" --no-cleanup --shm-no-cleanup on --shmid $SHM_MANAGER_SHMID" [[ $NORATELOG == 1 ]] && ARGS_ALL+=" --fairmq-rate-logging 0" +[[ ! -z ${O2_DPL_EXIT_TRANSITION_TIMEOUT_DEFAULT:-} ]] && ARGS_ALL+=" --exit-transition-timeout $O2_DPL_EXIT_TRANSITION_TIMEOUT_DEFAULT" +[[ ! -z ${O2_DPL_PROCESSING_TRANSITION_TIMEOUT_DEFAULT:-} ]] && ARGS_ALL+=" --data-processing-timeout $O2_DPL_PROCESSING_TRANSITION_TIMEOUT_DEFAULT" + +true + fi # getCommonArgs.sh sourced diff --git a/DATA/common/setenv.sh b/DATA/common/setenv.sh index 2f2ca7491..5dc2c3513 100755 --- a/DATA/common/setenv.sh +++ b/DATA/common/setenv.sh @@ -43,6 +43,7 @@ if [[ ! -z ${WORKFLOW_DETECTORS_EXCLUDE:-} ]]; then done fi +if [[ -z "${WORKFLOW_DETECTORS_GPU+x}" ]]; then export WORKFLOW_DETECTORS_GPU="TPC"; fi if [[ -z "${WORKFLOW_DETECTORS_QC+x}" ]] || [[ "0$WORKFLOW_DETECTORS_QC" == "0ALL" ]]; then export WORKFLOW_DETECTORS_QC="$WORKFLOW_DETECTORS,$LIST_OF_GLORECO,TOF_MATCH"; fi if [[ -z "${WORKFLOW_DETECTORS_CALIB+x}" ]] || [[ "0$WORKFLOW_DETECTORS_CALIB" == "0ALL" ]]; then export WORKFLOW_DETECTORS_CALIB=$WORKFLOW_DETECTORS; fi if [[ -z "${WORKFLOW_DETECTORS_RECO+x}" ]] || [[ "0$WORKFLOW_DETECTORS_RECO" == "0ALL" ]]; then export WORKFLOW_DETECTORS_RECO=$WORKFLOW_DETECTORS; fi @@ -90,14 +91,24 @@ if [[ -z "${FILEWORKDIR:-}" ]]; then export FILEWORKDIR=`pwd`; fi # if [[ -z "${FILEWORKDIRRUN:-}" ]]; then export FILEWORKDIRRUN=$FILEWORKDIR; fi # directory where to find the run-related files (grp, collision context) if [[ -z "${RAWINPUTDIR:-}" ]]; then export RAWINPUTDIR=$FILEWORKDIR; fi # Directory where to find input files (raw files / raw tf files / ctf files) if [[ -z "${EPNSYNCMODE:-}" ]]; then export EPNSYNCMODE=0; fi # Is this workflow supposed to run on EPN for sync processing? Will enable InfoLogger / metrics / fetching QC JSONs from consul... -if [[ -z "${BEAMTYPE:-}" ]]; then export BEAMTYPE=PbPb; fi # Beam type, must be PbPb, pp, pPb, cosmic, technical +if [[ -z "${BEAMTYPE:-}" ]]; then export BEAMTYPE=PbPb; fi # Beam type, must be PbPb, pp, pPb, pO, Op, OO, NeNe cosmic, technical if [[ -z "${RUNTYPE:-}" ]]; then export RUNTYPE=Standalone; fi # Run Type, standalone for local tests, otherwise PHYSICS, COSMICS, TECHNICAL, SYNTHETIC -if [[ -z "${IS_SIMULATED_DATA:-}" ]]; then export IS_SIMULATED_DATA=1; fi # processing simulated data -if [[ -z "${IS_TRIGGERED_DATA:-}" ]]; then export IS_TRIGGERED_DATA=0; fi # processing triggered data (TPC triggered instead of continuous) -if [[ -z "${CTF_DIR:-}" ]]; then CTF_DIR=$FILEWORKDIR; fi # Directory where to store CTFs -if [[ -z "${CALIB_DIR:-}" ]]; then CALIB_DIR="/dev/null"; fi # Directory where to store output from calibration workflows, /dev/null : skip their writing +if [[ -z "${IS_SIMULATED_DATA:-}" && $RUNTYPE == "SYNTHETIC" ]]; then export IS_SIMULATED_DATA=1; fi # For SYNTHETIC runs we always process simulated data +if [[ -z "${IS_SIMULATED_DATA:-}" && ( $RUNTYPE == "PHYSICS" || $RUNTYPE == "COSMICS" ) ]]; then export IS_SIMULATED_DATA=0; fi # For PHYSICS runs we always process simulated data +if [[ -z "${IS_SIMULATED_DATA:-}" ]]; then export IS_SIMULATED_DATA=1; fi # processing simulated data +if [[ -z "${IS_TRIGGERED_DATA:-}" ]]; then export IS_TRIGGERED_DATA=0; fi # processing triggered data (TPC triggered instead of continuous) +if [[ -z "${CTF_DIR:-}" ]]; then CTF_DIR=$FILEWORKDIR; fi # Directory where to store CTFs +if [[ -z "${CALIB_DIR:-}" ]]; then CALIB_DIR="/dev/null"; fi # Directory where to store output from calibration workflows, /dev/null : skip their writing if [[ -z "${EPN2EOS_METAFILES_DIR:-}" ]]; then EPN2EOS_METAFILES_DIR="/dev/null"; fi # Directory where to store epn2eos files metada, /dev/null : skip their writing -if [[ -z "${TPC_CORR_SCALING:-}" ]]; then export TPC_CORR_SCALING=""; fi # TPC corr.map lumi scaling options, any combination of --require-ctp-lumi, --corrmap-lumi-mean , --corrmap-lumi-inst , --ctp-lumi-factor , --ctp-lumi-source +if [[ -z "${DCSCCDBSERVER:-}" ]]; then export DCSCCDBSERVER="http://alio2-cr1-flp199-ib:8083"; fi # server for transvering calibration data to DCS +if [[ -z "${DCSCCDBSERVER_PERS:-}" ]]; then export DCSCCDBSERVER_PERS="http://alio2-cr1-flp199-ib:8084"; fi # persistent server for transvering calibration data to DCS + +if [[ $BEAMTYPE == "pO" ]] || [[ $BEAMTYPE == "Op" ]] || [[ $BEAMTYPE == "Op" ]] || [[ $BEAMTYPE == "OO" ]] || [[ $BEAMTYPE == "NeNe" ]] ; then + export LIGHTNUCLEI=1 +else + export LIGHTNUCLEI=0 +fi + if [[ $EPNSYNCMODE == 0 ]]; then if [[ -z "${SHMSIZE:-}" ]]; then export SHMSIZE=$(( 8 << 30 )); fi # Size of shared memory for messages if [[ -z "${NGPUS:-}" ]]; then export NGPUS=1; fi # Number of GPUs to use, data distributed round-robin @@ -109,12 +120,12 @@ if [[ $EPNSYNCMODE == 0 ]]; then else # Defaults when running on the EPN if [[ "0${GEN_TOPO_CALIB_WORKFLOW:-}" != "01" ]]; then if [[ -z "${GEN_TOPO_CALIB_NCORES:-}" ]]; then - if [[ -z "${SHMSIZE:-}" ]]; then export SHMSIZE=$(( 32 << 30 )); fi + if [[ -z "${SHMSIZE:-}" ]]; then export SHMSIZE=$(( 32 << 30 )); fi else - if [[ -z "${SHMSIZE:-}" ]]; then export SHMSIZE=$(( ($GEN_TOPO_CALIB_NCORES * 2) << 30 )); fi + if [[ -z "${SHMSIZE:-}" ]]; then export SHMSIZE=$(( ($GEN_TOPO_CALIB_NCORES * 2) << 30 )); fi fi else - if [[ -z "${SHMSIZE:-}" ]]; then export SHMSIZE=$(( 112 << 30 )); fi + if [[ -z "${SHMSIZE:-}" ]]; then export SHMSIZE=$(( 112 << 30 )); fi fi if [[ -z "${NGPUS:-}" ]]; then export NGPUS=4; fi if [[ -z "${EXTINPUT:-}" ]]; then export EXTINPUT=1; fi @@ -122,7 +133,7 @@ else # Defaults when running on the EPN if [[ -z "${SHMTHROW:-}" ]]; then export SHMTHROW=0; fi if [[ -z "${TIMEFRAME_SHM_LIMIT:-}" ]]; then export TIMEFRAME_SHM_LIMIT=$(( $SHMSIZE / 2 )); fi if [[ -z "${EDJSONS_DIR:-}" ]]; then export EDJSONS_DIR="/scratch/services/ed/jsons_${RUNTYPE}"; fi - if [[ -z "${WORKFLOW_DETECTORS_FLP_PROCESSING+x}" ]]; then export WORKFLOW_DETECTORS_FLP_PROCESSING="TOF,CTP"; fi # Current default in sync processing is that FLP processing is only enabled for TOF + if [[ -z "${WORKFLOW_DETECTORS_FLP_PROCESSING+x}" ]]; then export WORKFLOW_DETECTORS_FLP_PROCESSING="CTP"; fi # Current default in sync processing is that FLP processing is only enabled for TOF if [[ -z "${GEN_TOPO_AUTOSCALE_PROCESSES:-}" ]]; then export GEN_TOPO_AUTOSCALE_PROCESSES=1; fi # On the EPN we should make sure to always use the node to the full extent fi # Some more options for running on the EPN @@ -139,7 +150,7 @@ if [[ `uname` == Darwin ]]; then export UDS_PREFIX=; else export UDS_PREFIX="@"; # Env variables required for workflow setup if [[ $SYNCMODE == 1 ]]; then - if [[ -z "${WORKFLOW_DETECTORS_MATCHING+x}" ]]; then export WORKFLOW_DETECTORS_MATCHING="ITSTPC,ITSTPCTRD,ITSTPCTOF,ITSTPCTRDTOF,PRIMVTX"; fi # Select matchings that are enabled in sync mode + if [[ -z "${WORKFLOW_DETECTORS_MATCHING+x}" ]]; then export WORKFLOW_DETECTORS_MATCHING="ITSTPC,ITSTPCTRD,ITSTPCTOF,ITSTPCTRDTOF,PRIMVTX,SECVTX"; fi # Select matchings that are enabled in sync mode else if [[ -z "${WORKFLOW_DETECTORS_MATCHING+x}" ]]; then export WORKFLOW_DETECTORS_MATCHING="ALL"; fi # All matching / vertexing enabled in async mode fi @@ -151,6 +162,14 @@ DISABLE_ROOT_INPUT="--disable-root-input" : ${DISABLE_DIGIT_CLUSTER_INPUT="--clusters-from-upstream"} # Special detector related settings +if [[ -z "${TPC_CORR_SCALING:-}" ]]; then # TPC corr.map lumi scaling options, any combination of --lumi-type <0,1,2> --corrmap-lumi-mode <0,1> and TPCCorrMap... configurable param + TPC_CORR_SCALING= + if [[ $BEAMTYPE == "pp" || $LIGHTNUCLEI == "1" ]] && has_detector CTP; then TPC_CORR_SCALING+="--lumi-type 1 --corrmap-lumi-mode 1"; fi + if [[ $BEAMTYPE == "PbPb" ]] && has_detector CTP; then TPC_CORR_SCALING+="--lumi-type 1 --corrmap-lumi-mode 1 TPCCorrMap.lumiInstFactor=2.414"; fi + if [[ $BEAMTYPE == "cosmic" ]]; then TPC_CORR_SCALING=" TPCCorrMap.lumiMean=-1;"; fi # for COSMICS we disable all corrections + export TPC_CORR_SCALING=$TPC_CORR_SCALING +fi + MID_FEEID_MAP="$FILEWORKDIR/mid-feeId_mapper.txt" ITSMFT_STROBES="" @@ -174,16 +193,27 @@ fi # Assemble matching sources TRD_SOURCES= TOF_SOURCES= +HMP_SOURCES= TRACK_SOURCES= +: ${TRACK_SOURCES_GLO:=} has_detectors_reco ITS TPC && has_detector_matching ITSTPC && add_comma_separated TRACK_SOURCES "ITS-TPC" has_detectors_reco TPC TRD && has_detector_matching TPCTRD && { add_comma_separated TRD_SOURCES TPC; add_comma_separated TRACK_SOURCES "TPC-TRD"; } -has_detectors_reco ITS TPC TRD && has_detector_matching ITSTPCTRD && { add_comma_separated TRD_SOURCES ITS-TPC; add_comma_separated TRACK_SOURCES "ITS-TPC-TRD"; } +has_detectors_reco ITS TPC TRD && has_detector_matching ITSTPC && has_detector_matching ITSTPCTRD && { add_comma_separated TRD_SOURCES ITS-TPC; add_comma_separated TRACK_SOURCES "ITS-TPC-TRD"; } has_detectors_reco TPC TOF && has_detector_matching TPCTOF && { add_comma_separated TOF_SOURCES TPC; add_comma_separated TRACK_SOURCES "TPC-TOF"; } has_detectors_reco ITS TPC TOF && has_detector_matching ITSTPC && has_detector_matching ITSTPCTOF && { add_comma_separated TOF_SOURCES ITS-TPC; add_comma_separated TRACK_SOURCES "ITS-TPC-TOF"; } has_detectors_reco TPC TRD TOF && has_detector_matching TPCTRD && has_detector_matching TPCTRDTOF && { add_comma_separated TOF_SOURCES TPC-TRD; add_comma_separated TRACK_SOURCES "TPC-TRD-TOF"; } -has_detectors_reco ITS TPC TRD TOF && has_detector_matching ITSTPCTRD && has_detector_matching ITSTPCTRDTOF && { add_comma_separated TOF_SOURCES ITS-TPC-TRD; add_comma_separated TRACK_SOURCES "ITS-TPC-TRD-TOF"; } +has_detectors_reco ITS TPC TRD TOF && has_detector_matching ITSTPC && has_detector_matching ITSTPCTRD && has_detector_matching ITSTPCTRDTOF && { add_comma_separated TOF_SOURCES ITS-TPC-TRD; add_comma_separated TRACK_SOURCES "ITS-TPC-TRD-TOF"; } +has_detectors_reco HMP ITS TPC && has_detector_matching ITSTPC && add_comma_separated HMP_SOURCES "ITS-TPC" +has_detectors_reco HMP ITS TPC TRD && has_detector_matching ITSTPC && has_detector_matching ITSTPCTRD && add_comma_separated HMP_SOURCES "ITS-TPC-TRD" +has_detectors_reco HMP ITS TPC TOF && has_detector_matching ITSTPC && has_detector_matching ITSTPCTOF && add_comma_separated HMP_SOURCES "ITS-TPC-TOF" +has_detectors_reco HMP ITS TPC TRD TOF && has_detector_matching ITSTPC && has_detector_matching ITSTPCTRD && has_detector_matching ITSTPCTRDTOF && add_comma_separated HMP_SOURCES "ITS-TPC-TRD-TOF" +has_detectors_reco HMP TPC TRD && has_detector_matching TPCTRD && add_comma_separated HMP_SOURCES "TPC-TRD" +has_detectors_reco HMP TPC TOF && has_detector_matching TPCTOF && add_comma_separated HMP_SOURCES "TPC-TOF" +has_detectors_reco HMP TPC TRD TOF && has_detector_matching TPCTRD && has_detector_matching TPCTRDTOF && add_comma_separated HMP_SOURCES "TPC-TRD-TOF" has_detectors_reco MFT MCH && has_detector_matching MFTMCH && add_comma_separated TRACK_SOURCES "MFT-MCH" has_detectors_reco MCH MID && has_detector_matching MCHMID && add_comma_separated TRACK_SOURCES "MCH-MID" +[[ "0$TRACK_SOURCES_GLO" == "0" ]] && TRACK_SOURCES_GLO=$TRACK_SOURCES + for det in `echo $LIST_OF_DETECTORS | sed "s/,/ /g"`; do if [[ $LIST_OF_ASYNC_RECO_STEPS =~ (^| )${det}( |$) ]]; then has_detector ${det} && has_processing_step ${det}_RECO && add_comma_separated TRACK_SOURCES "$det" @@ -192,16 +222,28 @@ for det in `echo $LIST_OF_DETECTORS | sed "s/,/ /g"`; do fi done +if [[ "0${WORKFLOW_DETECTORS_USE_GLOBAL_READER_TRACKS:-}" == "0ALL" ]]; then export WORKFLOW_DETECTORS_USE_GLOBAL_READER_TRACKS=$TRACK_SOURCES; +elif [[ "0${WORKFLOW_DETECTORS_USE_GLOBAL_READER_TRACKS:-}" == "0ALLSINGLE" ]]; then export WORKFLOW_DETECTORS_USE_GLOBAL_READER_TRACKS=$WORKFLOW_DETECTORS; +else export WORKFLOW_DETECTORS_USE_GLOBAL_READER_TRACKS=${WORKFLOW_DETECTORS_USE_GLOBAL_READER_TRACKS:-}; fi +if [[ "0${WORKFLOW_DETECTORS_USE_GLOBAL_READER_CLUSTERS:-}" == "0ALL" ]]; then export WORKFLOW_DETECTORS_USE_GLOBAL_READER_CLUSTERS=$TRACK_SOURCES; +elif [[ "0${WORKFLOW_DETECTORS_USE_GLOBAL_READER_CLUSTERS:-}" == "0ALLSINGLE" ]]; then export WORKFLOW_DETECTORS_USE_GLOBAL_READER_CLUSTERS=$WORKFLOW_DETECTORS; +else export WORKFLOW_DETECTORS_USE_GLOBAL_READER_CLUSTERS=${WORKFLOW_DETECTORS_USE_GLOBAL_READER_CLUSTERS:-}; fi +if [[ ! -z ${WORKFLOW_DETECTORS_EXCLUDE_GLOBAL_READER_TRACKS:-} ]]; then + for i in ${WORKFLOW_DETECTORS_EXCLUDE_GLOBAL_READER_TRACKS//,/ }; do + export WORKFLOW_DETECTORS_USE_GLOBAL_READER_TRACKS=$(echo $WORKFLOW_DETECTORS_USE_GLOBAL_READER_TRACKS | sed -e "s/,$i,/,/g" -e "s/^$i,//" -e "s/,$i"'$'"//" -e "s/^$i"'$'"//") + done +fi +if [[ ! -z ${WORKFLOW_DETECTORS_EXCLUDE_GLOBAL_READER_CLUSTERS:-} ]]; then + for i in ${WORKFLOW_DETECTORS_EXCLUDE_GLOBAL_READER_CLUSTERS//,/ }; do + export WORKFLOW_DETECTORS_USE_GLOBAL_READER_CLUSTERS=$(echo $WORKFLOW_DETECTORS_USE_GLOBAL_READER_CLUSTERS | sed -e "s/,$i,/,/g" -e "s/^$i,//" -e "s/,$i"'$'"//" -e "s/^$i"'$'"//") + done +fi + : ${VERTEXING_SOURCES:="$TRACK_SOURCES"} : ${VERTEX_TRACK_MATCHING_SOURCES:="$TRACK_SOURCES"} [[ ! -z $VERTEXING_SOURCES ]] && PVERTEX_CONFIG+=" --vertexing-sources $VERTEXING_SOURCES" [[ ! -z $VERTEX_TRACK_MATCHING_SOURCES ]] && PVERTEX_CONFIG+=" --vertex-track-matching-sources $VERTEX_TRACK_MATCHING_SOURCES" -if [[ -z ${SVERTEXING_SOURCES:-} ]]; then - SVERTEXING_SOURCES="$VERTEXING_SOURCES" - [[ -z ${TPC_TRACKS_SVERTEXING:-} ]] && SVERTEXING_SOURCES=$(echo $SVERTEXING_SOURCES | sed -E -e "s/(^|,)TPC(-TRD|-TOF)+//g" -e "s/,TPC,/,/") -fi - # this option requires well calibrated timing beween different detectors, at the moment suppress it #has_detector_reco FT0 && PVERTEX_CONFIG+=" --validate-with-ft0" diff --git a/DATA/common/setenv_calib.sh b/DATA/common/setenv_calib.sh index edcd4f28f..6ae6749cc 100755 --- a/DATA/common/setenv_calib.sh +++ b/DATA/common/setenv_calib.sh @@ -13,12 +13,14 @@ SOURCE_GUARD_SETENV_CALIB=1 # define the conditions for each calibration if has_detector_calib ITS && has_detectors_reco ITS && has_detector_matching PRIMVTX && [[ ! -z "$VERTEXING_SOURCES" ]]; then CAN_DO_CALIB_PRIMVTX_MEANVTX=1; else CAN_DO_CALIB_PRIMVTX_MEANVTX=0; fi +if has_detector_calib ITS ; then CAN_DO_CALIB_ITS_DEADMAP_TIME=1; else CAN_DO_CALIB_ITS_DEADMAP_TIME=0; fi +if has_detector_calib MFT ; then CAN_DO_CALIB_MFT_DEADMAP_TIME=1; else CAN_DO_CALIB_MFT_DEADMAP_TIME=0; fi if has_detector_calib TOF && has_detector_reco TOF; then CAN_DO_CALIB_TOF_DIAGNOSTICS=1; CAN_DO_CALIB_TOF_INTEGRATEDCURR=1; else CAN_DO_CALIB_TOF_DIAGNOSTICS=0; CAN_DO_CALIB_TOF_INTEGRATEDCURR=0; fi -if has_detector_calib TOF && has_detector_reco TOF && (( has_detectors_reco ITS TPC && has_detector_matching ITSTPCTOF ) || ( has_detectors_reco ITS TPC TRD && has_detector_matching ITSTPCTRDTOF )); then CAN_DO_CALIB_TOF_LHCPHASE=1; CAN_DO_CALIB_TOF_CHANNELOFFSETS=1; else CAN_DO_CALIB_TOF_LHCPHASE=0; CAN_DO_CALIB_TOF_CHANNELOFFSETS=0; fi -if has_detector_calib TPC && has_detectors ITS TPC TOF TRD && has_detector_matching ITSTPCTRDTOF; then CAN_DO_CALIB_TPC_SCDCALIB=1; else CAN_DO_CALIB_TPC_SCDCALIB=0; fi +if has_detector_calib TOF && has_detector_reco TOF && ( ( has_detectors_reco ITS TPC && has_detector_matching ITSTPCTOF ) || ( has_detectors_reco ITS TPC TRD && has_detector_matching ITSTPCTRDTOF ) ); then CAN_DO_CALIB_TOF_LHCPHASE=1; CAN_DO_CALIB_TOF_CHANNELOFFSETS=1; else CAN_DO_CALIB_TOF_LHCPHASE=0; CAN_DO_CALIB_TOF_CHANNELOFFSETS=0; fi +if has_detector_calib TPC && has_detectors ITS TPC && has_detector_matching ITSTPC; then CAN_DO_CALIB_TPC_SCDCALIB=1; else CAN_DO_CALIB_TPC_SCDCALIB=0; fi if has_detector_calib TPC && has_processing_step TPC_DEDX; then CAN_DO_CALIB_TPC_TIMEGAIN=1; CAN_DO_CALIB_TPC_RESPADGAIN=1; else CAN_DO_CALIB_TPC_TIMEGAIN=0; CAN_DO_CALIB_TPC_RESPADGAIN=0; fi if has_detector_calib TPC && has_detectors ITS TPC && has_detector_matching ITSTPC; then CAN_DO_CALIB_TPC_VDRIFTTGL=1; else CAN_DO_CALIB_TPC_VDRIFTTGL=0; fi -if has_detector_calib TPC; then CAN_DO_CALIB_TPC_IDC=1; CAN_DO_CALIB_TPC_SAC=1; else CAN_DO_CALIB_TPC_IDC=0; CAN_DO_CALIB_TPC_SAC=0; fi +if has_detector_calib TPC; then CAN_DO_CALIB_TPC_IDC=1; CAN_DO_CALIB_TPC_SAC=1; CAN_DO_CALIB_TPC_CMV=1; else CAN_DO_CALIB_TPC_IDC=0; CAN_DO_CALIB_TPC_SAC=0; CAN_DO_CALIB_TPC_CMV=0; fi if [[ ! -z ${FLP_IDS:-} && ! $FLP_IDS =~ (^|,)"145"(,|$) ]] || [[ "${GEN_TOPO_DEPLOYMENT_TYPE:-}" == "ALICE_STAGING" ]]; then CAN_DO_CALIB_TPC_SAC=0; fi if has_detector_calib TRD && has_detectors ITS TPC TRD && has_detector_matching ITSTPCTRD; then CAN_DO_CALIB_TRD_VDRIFTEXB=1; CAN_DO_CALIB_TRD_GAIN=1; CAN_DO_CALIB_TRD_T0=1; else CAN_DO_CALIB_TRD_VDRIFTEXB=0; CAN_DO_CALIB_TRD_GAIN=0; CAN_DO_CALIB_TRD_T0=0; fi if has_detector_calib EMC && has_detector_reco EMC; then CAN_DO_CALIB_EMC_BADCHANNELCALIB=1; CAN_DO_CALIB_EMC_TIMECALIB=1; else CAN_DO_CALIB_EMC_BADCHANNELCALIB=0; CAN_DO_CALIB_EMC_TIMECALIB=0; fi @@ -28,16 +30,21 @@ if has_detector_calib FT0 && has_detector_reco FT0; then CAN_DO_CALIB_FT0_TIMEOF if has_detector_calib FV0 && has_processing_step FV0_RECO; then CAN_DO_CALIB_FV0_INTEGRATEDCURR=1; else CAN_DO_CALIB_FV0_INTEGRATEDCURR=0; fi if has_detector_calib FDD && has_processing_step FDD_RECO; then CAN_DO_CALIB_FDD_INTEGRATEDCURR=1; else CAN_DO_CALIB_FDD_INTEGRATEDCURR=0; fi if has_detector_calib ZDC && has_processing_step ZDC_RECO; then CAN_DO_CALIB_ZDC_TDC=1; else CAN_DO_CALIB_ZDC_TDC=0; fi +if [[ $SYNCMODE == 1 ]] && has_processing_step ENTROPY_ENCODER && [[ ! -z "$WORKFLOW_DETECTORS_CTF" ]] && [[ $WORKFLOW_DETECTORS_CTF != "NONE" ]]; then CAN_DO_CALIB_RCT_UPDATER=1; else CAN_DO_CALIB_RCT_UPDATER=0; fi # for async recalibration if has_detector_calib EMC && has_detector_reco EMC && [[ $SYNCMODE != 1 ]]; then CAN_DO_CALIB_EMC_ASYNC_RECALIB=1; else CAN_DO_CALIB_EMC_ASYNC_RECALIB=0; fi +if [[ $SYNCMODE != 1 ]] && has_detector_reco TPC; then CAN_DO_CALIB_ASYNC_EXTRACTTPCCURRENTS=1; else CAN_DO_CALIB_ASYNC_EXTRACTTPCCURRENTS=0; fi +if [[ $SYNCMODE != 1 ]] && has_detector_reco TPC; then CAN_DO_CALIB_ASYNC_EXTRACTTIMESERIES=1; else CAN_DO_CALIB_ASYNC_EXTRACTTIMESERIES=0; fi +if [[ $SYNCMODE != 1 ]] && has_detector_reco TPC; then CAN_DO_CALIB_ASYNC_EXTRACTCOSMICS=1; else CAN_DO_CALIB_ASYNC_EXTRACTCOSMICS=0; fi # additional individual settings for calibration workflows if has_detector CTP; then export CALIB_TPC_SCDCALIB_CTP_INPUT="--enable-ctp"; else export CALIB_TPC_SCDCALIB_CTP_INPUT=""; fi if [[ ${DISABLE_TRD_PH:-} == 1 ]]; then CAN_DO_CALIB_TRD_T0=0; fi -# the slot length needs to be known both on the aggregator and the processing nodes, therefore it is defined (in seconds!) here -: ${CALIB_TPC_SCDCALIB_SLOTLENGTH:=600} -if [[ $BEAMTYPE != "cosmic" ]] || [[ ${FORCECALIBRATIONS:-} == 1 ]] ; then +: ${CALIB_TPC_SCDCALIB_SLOTLENGTH:=300} # the slot length needs to be known both on the aggregator and the processing nodes, therefore it is defined (in seconds!) here +: ${CALIB_TPC_SCDCALIB_SENDTRKDATA:=1} # by default, we want to write the track information in addition to unbinned residuals to allow finer filtering offline + +if [[ $BEAMTYPE != "cosmic" ]] || [[ ${FORCECALIBRATIONS:-} == 1 ]] ; then # Calibrations enabled in non-COSMIC runs # here we won't deal with calibrations only for async! e.g. EMC_ASYNC_RECALIB; we want that they are always explicitly enabled @@ -46,6 +53,16 @@ if [[ $BEAMTYPE != "cosmic" ]] || [[ ${FORCECALIBRATIONS:-} == 1 ]] ; then if [[ -z ${CALIB_PRIMVTX_MEANVTX+x} ]]; then CALIB_PRIMVTX_MEANVTX=1; fi fi + # calibrations for ITS + if [[ $CAN_DO_CALIB_ITS_DEADMAP_TIME == 1 ]]; then + if [[ -z ${CALIB_ITS_DEADMAP_TIME+x} ]]; then CALIB_ITS_DEADMAP_TIME=1; fi + fi + + # calibrations for MFT + if [[ $CAN_DO_CALIB_MFT_DEADMAP_TIME == 1 ]]; then + if [[ -z ${CALIB_MFT_DEADMAP_TIME+x} ]]; then CALIB_MFT_DEADMAP_TIME=1; fi + fi + # calibrations for TOF if [[ $CAN_DO_CALIB_TOF_DIAGNOSTICS == 1 ]]; then if [[ -z ${CALIB_TOF_DIAGNOSTICS+x} ]]; then CALIB_TOF_DIAGNOSTICS=1; fi @@ -73,16 +90,6 @@ if [[ $BEAMTYPE != "cosmic" ]] || [[ ${FORCECALIBRATIONS:-} == 1 ]] ; then if [[ $CAN_DO_CALIB_TPC_VDRIFTTGL == 1 ]]; then if [[ -z ${CALIB_TPC_VDRIFTTGL+x} ]]; then CALIB_TPC_VDRIFTTGL=1; fi fi - # IDCs - if [[ $CAN_DO_CALIB_TPC_IDC == 1 ]]; then - if [[ -z ${CALIB_TPC_IDC+x} ]] || [[ $CALIB_TPC_IDC == 0 ]]; then - CALIB_TPC_IDC=0; # default is off - fi - fi - # SAC - if [[ $CAN_DO_CALIB_TPC_SAC == 1 ]]; then - if [[ -z ${CALIB_TPC_SAC+x} ]]; then CALIB_TPC_SAC=0; fi # default is off - fi # calibrations for TRD if [[ $CAN_DO_CALIB_TRD_VDRIFTEXB == 1 ]] ; then @@ -145,6 +152,40 @@ if [[ $BEAMTYPE != "cosmic" ]] || [[ ${FORCECALIBRATIONS:-} == 1 ]] ; then fi fi +# Calibrations irrespective of COSMIC or non-COSMIC run: + +# when possible, run RCT updater +if [[ $CAN_DO_CALIB_RCT_UPDATER == 1 ]]; then + if [[ -z ${CALIB_RCT_UPDATER+x} ]]; then CALIB_RCT_UPDATER=1; fi +fi + +# IDCs (by default we enable it for running the synch. reco on the EPNs, but not on staging since we have only 1 calibration node available) +if [[ $CAN_DO_CALIB_TPC_IDC == 1 ]]; then + if [[ -z ${CALIB_TPC_IDC+x} ]]; then + if [[ $EPNSYNCMODE == 1 ]] && [[ "${GEN_TOPO_DEPLOYMENT_TYPE:-}" != "ALICE_STAGING" ]]; then + CALIB_TPC_IDC=1; + else + CALIB_TPC_IDC=0; + fi + fi +fi +# SAC (by default we enable it for running the synch. reco on the EPNs) +if [[ $CAN_DO_CALIB_TPC_SAC == 1 ]]; then + if [[ -z ${CALIB_TPC_SAC+x} ]]; then + if [[ $EPNSYNCMODE == 1 ]]; then + CALIB_TPC_SAC=1; + else + CALIB_TPC_SAC=0; + fi + fi +fi +# CMV (be default, it is always disabled for now) +if [[ $CAN_DO_CALIB_TPC_CMV == 1 ]]; then + if [[ -z ${CALIB_TPC_CMV+x} ]]; then + CALIB_TPC_CMV=0; + fi +fi + ( [[ -z ${CALIB_FT0_INTEGRATEDCURR:-} ]] || [[ $CAN_DO_CALIB_FT0_INTEGRATEDCURR == 0 ]] ) && CALIB_FT0_INTEGRATEDCURR=0 ( [[ -z ${CALIB_FV0_INTEGRATEDCURR:-} ]] || [[ $CAN_DO_CALIB_FV0_INTEGRATEDCURR == 0 ]] ) && CALIB_FV0_INTEGRATEDCURR=0 ( [[ -z ${CALIB_FDD_INTEGRATEDCURR:-} ]] || [[ $CAN_DO_CALIB_FDD_INTEGRATEDCURR == 0 ]] ) && CALIB_FDD_INTEGRATEDCURR=0 @@ -159,6 +200,7 @@ fi ( [[ -z ${CALIB_TPC_RESPADGAIN:-} ]] || [[ $CAN_DO_CALIB_TPC_RESPADGAIN == 0 ]] ) && CALIB_TPC_RESPADGAIN=0 ( [[ -z ${CALIB_TPC_IDC:-} ]] || [[ $CAN_DO_CALIB_TPC_IDC == 0 ]] ) && CALIB_TPC_IDC=0 ( [[ -z ${CALIB_TPC_SAC:-} ]] || [[ $CAN_DO_CALIB_TPC_SAC == 0 ]] ) && CALIB_TPC_SAC=0 +( [[ -z ${CALIB_TPC_CMV:-} ]] || [[ $CAN_DO_CALIB_TPC_CMV == 0 ]] ) && CALIB_TPC_CMV=0 ( [[ -z ${CALIB_TPC_VDRIFTTGL:-} ]] || [[ $CAN_DO_CALIB_TPC_VDRIFTTGL == 0 ]] ) && CALIB_TPC_VDRIFTTGL=0 ( [[ -z ${CALIB_TRD_VDRIFTEXB:-} ]] || [[ $CAN_DO_CALIB_TRD_VDRIFTEXB == 0 ]] ) && CALIB_TRD_VDRIFTEXB=0 ( [[ -z ${CALIB_TRD_GAIN:-} ]] || [[ $CAN_DO_CALIB_TRD_GAIN == 0 ]] ) && CALIB_TRD_GAIN=0 @@ -172,10 +214,20 @@ fi ( [[ -z ${CALIB_PHS_L1PHASE:-} ]] || [[ $CAN_DO_CALIB_PHS_L1PHASE == 0 ]] ) && CALIB_PHS_L1PHASE=0 ( [[ -z ${CALIB_CPV_GAIN:-} ]] || [[ $CAN_DO_CALIB_CPV_GAIN == 0 ]] ) && CALIB_CPV_GAIN=0 ( [[ -z ${CALIB_ZDC_TDC:-} ]] || [[ $CAN_DO_CALIB_ZDC_TDC == 0 ]] ) && CALIB_ZDC_TDC=0 +( [[ -z ${CALIB_ITS_DEADMAP_TIME:-} ]] || [[ $CAN_DO_CALIB_ITS_DEADMAP_TIME == 0 ]] ) && CALIB_ITS_DEADMAP_TIME=0 +( [[ -z ${CALIB_MFT_DEADMAP_TIME:-} ]] || [[ $CAN_DO_CALIB_MFT_DEADMAP_TIME == 0 ]] ) && CALIB_MFT_DEADMAP_TIME=0 +( [[ -z ${CALIB_RCT_UPDATER:-} ]] || [[ $CAN_DO_CALIB_RCT_UPDATER == 0 ]] ) && CALIB_RCT_UPDATER=0 # for async: ( [[ -z ${CALIB_EMC_ASYNC_RECALIB:-} ]] || [[ $CAN_DO_CALIB_EMC_ASYNC_RECALIB == 0 ]] ) && CALIB_EMC_ASYNC_RECALIB=0 +( [[ -z ${CALIB_ASYNC_EXTRACTTPCCURRENTS:-} ]] || [[ $CAN_DO_CALIB_ASYNC_EXTRACTTPCCURRENTS == 0 ]] ) && CALIB_ASYNC_EXTRACTTPCCURRENTS=0 +( [[ -z ${CALIB_ASYNC_DISABLE3DCURRENTS:-} ]] || [[ $CAN_DO_CALIB_ASYNC_DISABLE3DCURRENTS == 0 ]] ) && CALIB_ASYNC_DISABLE3DCURRENTS=0 +: ${ON_SKIMMED_DATA:=0} +( [[ -z ${CALIB_ASYNC_EXTRACTTIMESERIES:-} ]] || [[ $CAN_DO_CALIB_ASYNC_EXTRACTTIMESERIES == 0 ]] ) && CALIB_ASYNC_EXTRACTTIMESERIES=0 +( [[ -z ${CALIB_ASYNC_EXTRACTCOSMICS:-} ]] || [[ $CAN_DO_CALIB_ASYNC_EXTRACTCOSMICS == 0 ]] ) && CALIB_ASYNC_EXTRACTCOSMICS=0 + if [[ "0${GEN_TOPO_VERBOSE:-}" == "01" ]]; then + echo "CALIB_RCT_UPDATER = ${CALIB_RCT_UPDATER:-}" 1>&2 echo "CALIB_PRIMVTX_MEANVTX = $CALIB_PRIMVTX_MEANVTX" 1>&2 echo "CALIB_TOF_LHCPHASE = $CALIB_TOF_LHCPHASE" 1>&2 echo "CALIB_TOF_CHANNELOFFSETS = $CALIB_TOF_CHANNELOFFSETS" 1>&2 @@ -207,9 +259,20 @@ fi # define spec for proxy for TF-based outputs from BARREL if [[ -z ${CALIBDATASPEC_BARREL_TF:-} ]]; then + # RCT updater + if [[ $CALIB_RCT_UPDATER == 1 ]]; then add_semicolon_separated CALIBDATASPEC_BARREL_TF "calibRCT:CTF/DONE/0"; fi # prim vtx if [[ $CALIB_PRIMVTX_MEANVTX == 1 ]]; then add_semicolon_separated CALIBDATASPEC_BARREL_TF "pvtx:GLO/PVTX/0"; fi + # ITS + if [[ $CALIB_ITS_DEADMAP_TIME == 1 ]]; then + add_semicolon_separated CALIBDATASPEC_BARREL_TF "itsChipStatus:ITS/CHIPSSTATUS/0" + add_semicolon_separated CALIBDATASPEC_BARREL_TF "itsErrorInfo:ITS/ErrorInfo/0" + fi + + # MFT + if [[ $CALIB_MFT_DEADMAP_TIME == 1 ]]; then add_semicolon_separated CALIBDATASPEC_BARREL_TF "mftChipStatus:MFT/CHIPSSTATUS/0"; fi + # TOF if [[ $CALIB_TOF_LHCPHASE == 1 ]] || [[ $CALIB_TOF_CHANNELOFFSETS == 1 ]]; then add_semicolon_separated CALIBDATASPEC_BARREL_TF "calibTOF:TOF/CALIBDATA/0"; fi if [[ $CALIB_TOF_DIAGNOSTICS == 1 ]]; then add_semicolon_separated CALIBDATASPEC_BARREL_TF "diagWords:TOF/DIAFREQ/0"; fi @@ -217,6 +280,7 @@ if [[ -z ${CALIBDATASPEC_BARREL_TF:-} ]]; then # TPC if [[ $CALIB_TPC_SCDCALIB == 1 ]]; then add_semicolon_separated CALIBDATASPEC_BARREL_TF "unbinnedTPCResiduals:GLO/UNBINNEDRES/0" + add_semicolon_separated CALIBDATASPEC_BARREL_TF "detectorInfoResiduals:GLO/DETINFORES/0" add_semicolon_separated CALIBDATASPEC_BARREL_TF "trackReferences:GLO/TRKREFS/0" fi if [[ $CALIB_TPC_SCDCALIB == 1 ]] && [[ ${CALIB_TPC_SCDCALIB_SENDTRKDATA:-} == "1" ]]; then add_semicolon_separated CALIBDATASPEC_BARREL_TF "tpcInterpTrkData:GLO/TRKDATA/0"; fi @@ -255,6 +319,14 @@ if [[ -z ${CALIBDATASPEC_TPCIDC_C:-} ]]; then # TPC if [[ $CALIB_TPC_IDC == 1 ]]; then add_semicolon_separated CALIBDATASPEC_TPCIDC_C "idcsgroupc:TPC/IDCGROUPC"; fi fi +# define spec for proxy for TPC CMVs +if [[ -z ${CALIBDATASPEC_TPCCMV:-} ]]; then + # TPC + if [[ $CALIB_TPC_CMV == 1 ]]; then + add_semicolon_separated CALIBDATASPEC_TPCCMV "cmvgroup:TPC/CMVGROUP"; + add_semicolon_separated CALIBDATASPEC_TPCCMV "cmvorbit:TPC/CMVORBITINFO"; + fi +fi # define spec for proxy for TPC SAC if [[ -z ${CALIBDATASPEC_TPCSAC:-} ]]; then @@ -293,11 +365,6 @@ fi # define spec for proxy for TF-based outputs from forward detectors if [[ -z ${CALIBDATASPEC_FORWARD_TF:-} ]]; then - # ZDC - if [[ $CALIB_ZDC_TDC == 1 ]]; then - add_semicolon_separated CALIBDATASPEC_FORWARD_TF "tdcZDC:ZDC/TDCCALIBDATA/0" - add_semicolon_separated CALIBDATASPEC_FORWARD_TF "histoZDC:ZDC/TDC_1DH" - fi # FT0 if [[ $CALIB_FT0_TIMEOFFSET == 1 ]]; then add_semicolon_separated CALIBDATASPEC_FORWARD_TF "timeSpectraFT0:FT0/TIME_SPECTRA/0" @@ -316,6 +383,11 @@ if [[ -z ${CALIBDATASPEC_FORWARD_SPORADIC:-} ]]; then if [[ $CALIB_FDD_INTEGRATEDCURR == 1 ]]; then add_semicolon_separated CALIBDATASPEC_FORWARD_SPORADIC "integrCurrFDD:FDD/IFDDC/0" fi + # ZDC + if [[ $CALIB_ZDC_TDC == 1 ]]; then + add_semicolon_separated CALIBDATASPEC_FORWARD_SPORADIC "tdcZDC:ZDC/TDCCALIBDATA/0" + add_semicolon_separated CALIBDATASPEC_FORWARD_SPORADIC "histoZDC:ZDC/TDC_1DH" + fi fi if [[ "0${GEN_TOPO_VERBOSE:-}" == "01" ]]; then @@ -380,12 +452,15 @@ get_proxy_connection() if [[ $3 == "timeframe" ]]; then PROXY_CONN+=" --environment DPL_OUTPUT_PROXY_ORDERED=1" elif [[ $3 == "sporadic" ]]; then - PROXY_CONN+=" --environment \"DPL_OUTPUT_PROXY_WHENANY=1 DPL_DONT_DROP_OLD_TIMESLICE=1\"" + PROXY_CONN+=" --environment \"DPL_OUTPUT_PROXY_WHENANY=1 DPL_DONT_DROP_OLD_TIMESLICE=1\" --sporadic-inputs" else echo "invalid option $3, must be (sporadic|timeframe)" 1>&2 exit 1 fi fi + if [[ $2 == "input" && $3 == "sporadic" ]]; then + PROXY_CONN+=" --sporadic-outputs" + fi if [[ "0${GEN_TOPO_VERBOSE:-}" == "01" ]]; then echo PROXY_CONN = $PROXY_CONN 1>&2 fi diff --git a/DATA/production/README.md b/DATA/production/README.md index fd44b56d9..236e4201b 100644 --- a/DATA/production/README.md +++ b/DATA/production/README.md @@ -8,61 +8,8 @@ Standalone calibration workflows are contained in `standalone-calibration.desc`. If processing is to be disabled, please use the `no-processing` workflow in `no-processing.desc`. -# Configuration options -You can use the following options to change the workflow behavior: -- `DDMODE` (default `processing`) : Must be `processing` (synchronous processing) or `processing-disk` (synchronous processing + storing of raw time frames to disk, note that this is the raw time frame not the CTF!). The `DDMODE` `discard` and `disk` are not compatible with the synchronous processing workflow, you must use the `no-processing.desc` workflow instead!. -- `WORKFLOW_DETECTORS` (default `ALL`) : Comma-separated list of detectors for which the processing is enabled. If these are less detectors than participating in the run, data of the other detectors is ignored. If these are more detectors than participating in the run, the processes for the additional detectors will be started but will not do anything. -- `WORKFLOW_DETECTORS_QC` (default `ALL`) : Comma-separated list of detectors for which to run QC, can be a subset of `WORKFLOW_DETECTORS` (for standalone detectors QC) and `WORKFLOW_DETECTORS_MATCHING` (for matching/vertexing QC). If a detector (matching/vertexing step) is not listed in `WORKFLOW_DETECTORS` (`WORKFLOW_DETECTORS_MATCHING`), the QC is automatically disabled for that detector. Only active if the `WORKFLOW_PARAMETER=QC` is set. -- `WORKFLOW_DETECTORS_CALIB` (default `ALL`) : Comma-separated list of detectors for which to run calibration, can be a subset of `WORKFLOW_DETECTORS`. If a detector is not listed in `WORKFLOW_DETECTORS`, the calibration is automatically disabled for that detector. Only active if the `WORKFLOW_PARAMETER=CALIB` is set. -- `WORKFLOW_DETECTORS_FLP_PROCESSING` (default `TOF` for sync processing on EPN, `NONE` otherwise) : Signals that these detectors have processing on the FLP enabled. The corresponding steps are thus inactive in the EPN epl-workflow, and the raw-proxy is configured to receive the FLP-processed data instead of the raw data in that case. -- `WORKFLOW_DETECTORS_RECO` (default `ALL`) : Comma-separated list of detectors for which to run reconstruction. -- `WORKFLOW_DETECTORS_CTF` (default `ALL`) : Comma-separated list of detectors to include in CTF. -- `WORKFLOW_DETECTORS_MATCHING` (default selected corresponding to default workflow for sync or async mode respectively) : Comma-separated list of matching / vertexing algorithms to run. Use `ALL` to enable all of them. Currently supported options (see LIST_OF_GLORECO in common/setenv.h): `ITSTPC`, `TPCTRD`, `ITSTPCTRD`, `TPCTOF`, `ITSTPCTOF`, `MFTMCH`, `PRIMVTX`, `SECVTX`. -- `WORKFLOW_EXTRA_PROCESSING_STEPS` Enable additional processing steps not in the preset for the SYNC / ASYNC mode. Possible values are: `MID_RECO` `MCH_RECO` `MFT_RECO` `FDD_RECO` `FV0_RECO` `ZDC_RECO` `ENTROPY_ENCODER` `MATCH_ITSTPC` `MATCH_TPCTRD` `MATCH_ITSTPCTRD` `MATCH_TPCTOF` `MATCH_ITSTPCTOF` `MATCH_MFTMCH` `MATCH_MFTMCH` `MATCH_PRIMVTX` `MATCH_SECVTX`. (Here `_RECO` means full async reconstruction, and can be used to enable it also in sync mode.) -- `WORKFLOW_PARAMETERS` (default `NONE`) : Comma-separated list, enables additional features of the workflow. Currently the following features are available: - - `GPU` : Performs the TPC processing on the GPU, otherwise everything is processed on the CPU. - - `CTF` : Write the CTF to disk (CTF creation is always enabled, but if this parameter is missing, it is not stored). - - `EVENT_DISPLAY` : Enable JSON export for event display. - - `QC` : Enable QC. - - `CALIB` : Enable calibration (not yet working!) -- `RECO_NUM_NODES_OVERRIDE` (default `0`) : Overrides the number of EPN nodes used for the reconstruction (`0` or empty means default). -- `MULTIPLICITY_FACTOR_RAWDECODERS` (default `1`) : Scales the number of parallel processes used for raw decoding by this factor. -- `MULTIPLICITY_FACTOR_CTFENCODERS` (default `1`) : Scales the number of parallel processes used for CTF encoding by this factor. -- `MULTIPLICITY_FACTOR_REST` (default `1`) : Scales the number of other reconstruction processes by this factor. -- `QC_JSON_EXTRA` (default `NONE`) : extra QC jsons to add (if does not fit to those defined in WORKFLOW_DETECTORS_QC & (WORKFLOW_DETECTORS | WORKFLOW_DETECTORS_MATCHING) -Most of these settings are configurable in the AliECS GUI. But some of the uncommon settings (`WORKFLOW_DETECTORS_FLP_PROCESSING`, `WORKFLOW_DETECTORS_CTF`, `WORKFLOW_DETECTORS_RECO`, `WORKFLOW_DETECTORS_MATCHING`, `WORKFLOW_EXTRA_PROCESSING_STEPS`, advanced `MULTIPLICITY_FACTOR` settings) can only be set via the "Additional environment variables field" in the GUI using bash syntax, e.g. `WORKFLOW_DETECTORS_FLP_PROCESSING=TPC`. - -# Process multiplicity factors -- The production workflow has internally a default value how many instances of a process to run in parallel (which was tuned for Pb-Pb processing) -- Some critical processes for synchronous pp processing are automatically scaled by the inverse of the number of nodes, i.e. the multiplicity is increased by a factor of 2 if 125 instead of 250 nodes are used, to enable the processing using only a subset of the nodes. -- Factors can be provided externally to scale the multiplicity of processes further. All these factors are multiplied. - - One factor can be provided based on the type of the processes: raw decoder (`MULTIPLICITY_FACTOR_RAWDECODERS`), CTF encoder (`MULTIPLICITY_FACTOR_CTFENCODERS`), or other reconstruction process (`MULTIPLICITY_FACTOR_REST`) - - One factor can be provided per detector via `MULTIPLICITY_FACTOR_DETECTOR_[DET]` using the 3 character detector representation, or `MATCH` for the global matching and vertexing workflows. - - One factor can be provided per process via `MULTIPLICITY_FACTOR_PROCESS_[PROCESS_NAME]`. In the process name, dashes `-` must be replaced by underscores `_`. -- The multiplicity of an individual process can be overridden externally (this is an override, no scaling factor) by using `MULTIPLICITY_PROCESS_[PROCESS_NAME]`. In the process name, dashes `-` must be replaced by underscores `_`. -- For example, creating the workflow with `MULTIPLICITY_FACTOR_RAWDECODERS=2 MULTIPLICITY_FACTOR_DETECTOR_ITS=3 MULTIPLICITY_FACTOR_PROCESS_mft_stf_decoder=5` will scale the number of ITS raw decoders by 6, of other ITS processes by 3, of other raw decoders by 2, and will run exactly 5 `mft-stf-decoder` processes. - -# Additional custom control variables -For user modification of the workflow settings, the folloing *EXTRA* environment variables exist: -- `ARGS_ALL_EXTRA` : Extra command line options added to all workflows -- `ALL_EXTRA_CONFIG` : Extra config key values added to all workflows -- `GPU_EXTRA_CONFIG` : Extra options added to the configKeyValues of the GPU workflow -- `ARGS_EXTRA_PROCESS_[WORKFLOW_NAME]` : Extra command line arguments for the workflow binary `WORKFLOW_NAME`. Dashes `-` must be replaced by underscores `_` in the name! E.g. `ARGS_EXTRA_PROCESS_o2_tof_reco_workflow='--output-type clusters'` -- `CONFIG_EXTRA_PROCESS_[WORKFLOW_NAME]` : Extra `--configKeyValues` arguments for the workflow binary `WORKFLOW_NAME`. Dashes `-` must be replaced by underscores `_` in the name! E.g. `CONFIG_EXTRA_PROCESS_o2_gpu_reco_workflow='GPU_proc.debugLevel=1;GPU_proc.ompKernels=0;'` - -**IMPORTANT:** When providing additional environment variables please always use single quotes `'` instead of double quotes `"`, because otherwise there can be issues with whitespaces. E.g. `ARGS_EXTRA_PROCESS_o2_eve_display='--filter-time-min 0 --filter-time-max 120'` does work while `ARGS_EXTRA_PROCESS_o2_eve_display="--filter-time-min 0 --filter-time-max 120"` does not. - -In case the CTF dictionaries were created from the data drastically different from the one being compressed, the default memory allocation for the CTF buffer might be insufficient. One can apply scaling factor to the buffer size estimate (default=1.5) of particular detector by defining variable e.g. `TPC_ENC_MEMFACT=3.5` - -# File input for ctf-reader / raw-tf-reader -- The variable `$INPUT_FILE_LIST` can be a comma-seperated list of files, or a file with a file-list of CTFs/raw TFs. -- The variable `$INPUT_FILE_COPY_CMD` can provide a custom copy command (default is to fetch the files from EOS). - -# Remarks on QC -The JSON files for the individual detectors are merged into one JSON file, which is cached during the run on the shared EPN home folder. -The default JSON file per detector is defined in `qc-workflow.sh`. -JSONs per detector can be overridden by exporting `QC_JSON_[DETECTOR_NAME]`, e.g. `QC_JSON_TPC`, when creating the workflow. -The global section of the merged qc JSON config is taken from qc-sync/qc-global.json +# Options for dpl-workflow.sh +Refer to https://github.com/AliceO2Group/AliceO2/blob/dev/prodtests/full-system-test/documentation/dpl-workflow-options.md # run-workflow-on-inputlist.sh `O2/prodtests/full-system-test/run-workflow-on-inputlist.sh` is a small tool to run the `dpl-workflow.sh` on a list of files. diff --git a/DATA/production/calib/emc-pedestal-aggregator.sh b/DATA/production/calib/emc-pedestal-aggregator.sh new file mode 100755 index 000000000..890a7babc --- /dev/null +++ b/DATA/production/calib/emc-pedestal-aggregator.sh @@ -0,0 +1,49 @@ +#!/bin/bash + +source common/setenv.sh + +# --------------------------------------------------------------------------------------------------------------------- +# Set general arguments +source common/getCommonArgs.sh + +INPTYPE="" +#PROXY_INSPEC="A:EMC/PEDDATA/0;eos:***/INFORMATION" +PROXY_INSPEC="A:EMC/PEDDATA/0" + +CCDBPATH1="http://o2-ccdb.internal" +CCDBPATH2="$DCSCCDBSERVER_PERS" +if [[ $RUNTYPE == "SYNTHETIC" || "${GEN_TOPO_DEPLOYMENT_TYPE:-}" == "ALICE_STAGING" || ! -z $ISTEST ]]; then + CCDBPATH1="http://ccdb-test.cern.ch:8080" + CCDBPATH2="http://ccdb-test.cern.ch:8080" +fi + +QC_STANDALONE=0 +if [[ ! -z ${EMC_PEDQC_STANDALONE:-} ]]; then + QC_STANDALONE=${EMC_PEDQC_STANDALONE} +fi + +QC_CONFIG="/o2/components/qc/ANY/any/emc-pedestal-qc" +QC_OPT= +if [ $QC_STANDALONE -gt 0 ]; then + QC_CONFIG="/o2/components/qc/ANY/any/emc-pedestal-qc-standalone" +else + QC_CONFIG="/o2/components/qc/ANY/any/emc-pedestal-qc" + QC_OPT="--local --host localhost" +fi + +WORKFLOW= +add_W o2-dpl-raw-proxy "--proxy-name emc-pedestal-input-proxy --dataspec \"$PROXY_INSPEC\" --network-interface ib0 --channel-config \"name=emc-pedestal-input-proxy,method=bind,type=pull,rateLogging=1,transport=zeromq\"" "" 0 +add_W o2-calibration-emcal-pedestal-calib-workflow --addRunNumber +add_W o2-calibration-ccdb-populator-workflow "--ccdb-path=\"$CCDBPATH1\" --sspec-min 0 --sspec-max 0" +add_W o2-calibration-ccdb-populator-workflow "--ccdb-path=\"$CCDBPATH2\" --sspec-min 1 --sspec-max 1 --name-extention dcs" +add_QC_from_consul "${QC_CONFIG}" "${QC_OPT}" +WORKFLOW+="o2-dpl-run $ARGS_ALL $GLOBALDPLOPT" + +if [ $WORKFLOWMODE == "print" ]; then + echo Workflow command: + echo $WORKFLOW | sed "s/| */|\n/g" +else + # Execute the command we have assembled + WORKFLOW+=" --$WORKFLOWMODE ${WORKFLOWMODE_FILE}" + eval $WORKFLOW +fi diff --git a/DATA/production/calib/emc-pedestal-processing.sh b/DATA/production/calib/emc-pedestal-processing.sh new file mode 100755 index 000000000..023344251 --- /dev/null +++ b/DATA/production/calib/emc-pedestal-processing.sh @@ -0,0 +1,28 @@ +#!/bin/bash + +source common/setenv.sh + +# --------------------------------------------------------------------------------------------------------------------- +# Set general arguments +source common/getCommonArgs.sh + +PROXY_INSPEC="A:EMC/RAWDATA;dd:FLP/DISTSUBTIMEFRAME/0" + +PROXY_OUTSPEC="downstream:EMC/PEDDATA/0" + +[[ -z $NEMCPROCPIPELINES ]] && NEMCPROCPIPELINES=30 + +WORKFLOW= +add_W o2-dpl-raw-proxy "--dataspec \"$PROXY_INSPEC\" --inject-missing-data --channel-config \"name=readout-proxy,type=pull,method=connect,address=ipc://@$INRAWCHANNAME,transport=shmem,rateLogging=1\"" "" 0 +add_W o2-calibration-emcal-pedestal-processor-workflow "--pipeline PedestalProcessor:${NEMCPROCPIPELINES}" +add_W o2-dpl-output-proxy "--dataspec \"$PROXY_OUTSPEC\" --proxy-channel-name emc-pedestal-input-proxy --channel-config \"name=emc-pedestal-input-proxy,method=connect,type=push,transport=zeromq,rateLogging=1\"" "" 0 +WORKFLOW+="o2-dpl-run ${ARGS_ALL} ${GLOBALDPLOPT}" + +if [ $WORKFLOWMODE == "print" ]; then + echo Workflow command: + echo $WORKFLOW | sed "s/| */|\n/g" +else + # Execute the command we have assembled + WORKFLOW+=" --$WORKFLOWMODE ${WORKFLOWMODE_FILE}" + eval $WORKFLOW +fi \ No newline at end of file diff --git a/DATA/production/calib/hmp-pedestals-processing.sh b/DATA/production/calib/hmp-pedestals-processing.sh index 6c05350a4..e7fccfa04 100755 --- a/DATA/production/calib/hmp-pedestals-processing.sh +++ b/DATA/production/calib/hmp-pedestals-processing.sh @@ -57,7 +57,7 @@ fi SPEC_PARAM="" if [ $HMP_NODCSCCDB_REC == 'false' ]; then - SPEC_PARAM+="--use-dcsccdb --dcsccdb-uri 'http://alio2-cr1-flp199.cern.ch:8083' --dcsccdb-alivehours 3 " + SPEC_PARAM+="--use-dcsccdb --dcsccdb-uri $DCSCCDBSERVER_PERS --dcsccdb-alivehours 3 " fi if [ $HMP_CCDB_REC == 'true' ]; then @@ -74,7 +74,7 @@ SPEC_PARAM+="--pedestals-tag ${HMP_PED_TAG} --sigmacut ${HMP_SIGMACUT}" # Here we compose the workflow # Start with an empty workflow WORKFLOW= -add_W o2-dpl-raw-proxy "--dataspec \"$PROXY_INSPEC\" --channel-config \"name=readout-proxy,type=pull,method=connect,address=ipc://@$INRAWCHANNAME,rateLogging=1,transport=shmem\"" "" 0 +add_W o2-dpl-raw-proxy "--dataspec \"$PROXY_INSPEC\" --inject-missing-data --channel-config \"name=readout-proxy,type=pull,method=connect,address=ipc://@$INRAWCHANNAME,rateLogging=1,transport=shmem\"" "" 0 add_W o2-hmpid-raw-to-pedestals-workflow "--fast-decode $SPEC_PARAM" # Finally add the o2-dpl-run workflow manually, allow for either printing the workflow or creating a topology (default) diff --git a/DATA/production/calib/its-noise-aggregator.sh b/DATA/production/calib/its-noise-aggregator.sh index 97bfcf696..5df11299c 100755 --- a/DATA/production/calib/its-noise-aggregator.sh +++ b/DATA/production/calib/its-noise-aggregator.sh @@ -17,7 +17,7 @@ fi if [[ -z $NTHREADS ]] ; then NTHREADS=1; fi CCDBPATH1="http://o2-ccdb.internal" -CCDBPATH2="http://alio2-cr1-flp199.cern.ch:8083" +CCDBPATH2="$DCSCCDBSERVER_PERS" if [[ $RUNTYPE == "SYNTHETIC" || "${GEN_TOPO_DEPLOYMENT_TYPE:-}" == "ALICE_STAGING" || ! -z $ISTEST ]]; then CCDBPATH1="http://ccdb-test.cern.ch:8080" CCDBPATH2="http://ccdb-test.cern.ch:8080" diff --git a/DATA/production/calib/its-noise-processing.sh b/DATA/production/calib/its-noise-processing.sh index 3d0181a14..fdd8b62c8 100755 --- a/DATA/production/calib/its-noise-processing.sh +++ b/DATA/production/calib/its-noise-processing.sh @@ -20,7 +20,7 @@ fi [[ -z $NITSDECTPIPELINES ]] && NITSDECTPIPELINES=6 WORKFLOW= -add_W o2-dpl-raw-proxy "--dataspec \"$PROXY_INSPEC\" --channel-config \"name=readout-proxy,type=pull,method=connect,address=ipc://@$INRAWCHANNAME,transport=shmem,rateLogging=1\"" "" 0 +add_W o2-dpl-raw-proxy "--dataspec \"$PROXY_INSPEC\" --inject-missing-data --channel-config \"name=readout-proxy,type=pull,method=connect,address=ipc://@$INRAWCHANNAME,transport=shmem,rateLogging=1\"" "" 0 add_W o2-itsmft-stf-decoder-workflow "${OUTTYPE} --nthreads ${NITSDECTHREADS} --pipeline its-stf-decoder:${NITSDECTPIPELINES} --ignore-noise-map" add_W o2-dpl-output-proxy "--dataspec \"$PROXY_OUTSPEC\" --proxy-channel-name its-noise-input-proxy --channel-config \"name=its-noise-input-proxy,method=connect,type=push,transport=zeromq,rateLogging=1\"" "" 0 WORKFLOW+="o2-dpl-run ${ARGS_ALL} ${GLOBALDPLOPT}" diff --git a/DATA/production/calib/its-threshold-aggregator.sh b/DATA/production/calib/its-threshold-aggregator.sh index cc788d79e..b0449e1fb 100755 --- a/DATA/production/calib/its-threshold-aggregator.sh +++ b/DATA/production/calib/its-threshold-aggregator.sh @@ -10,8 +10,8 @@ PROXY_INSPEC="tunestring:ITS/TSTR;runtype:ITS/RUNT;fittype:ITS/FITT;scantype:ITS CCDBPATH1="" CCDBPATH2="" -if [ $RUNTYPE_ITS == "tuning" ] || [ $RUNTYPE_ITS == "digital" ] || [ $RUNTYPE_ITS == "tuningbb" ]; then - CCDBPATH1="http://alio2-cr1-flp199.cern.ch:8083" +if [[ $RUNTYPE_ITS == "tuning" ]] || [[ $RUNTYPE_ITS == *digital* ]] || [[ $RUNTYPE_ITS == "tuningbb" ]]; then + CCDBPATH1="$DCSCCDBSERVER_PERS" CCDBPATH2="http://o2-ccdb.internal" else CCDBPATH1="http://o2-ccdb.internal" @@ -26,7 +26,7 @@ WORKFLOW= add_W o2-dpl-raw-proxy "--exit-transition-timeout 20 --proxy-name its-thr-input-proxy --dataspec \"$PROXY_INSPEC\" --network-interface ib0 --channel-config \"name=its-thr-input-proxy,method=bind,type=pull,rateLogging=0,transport=zeromq\"" "" 0 add_W o2-its-threshold-aggregator-workflow "-b" "" 0 add_W o2-calibration-ccdb-populator-workflow "--ccdb-path=\"$CCDBPATH1\" --sspec-min 0 --sspec-max 0 --name-extention dcs" -if [ $RUNTYPE_ITS == "digital" ]; then +if [[ $RUNTYPE_ITS == *digital* ]]; then add_W o2-calibration-ccdb-populator-workflow "--ccdb-path=\"$CCDBPATH2\" --sspec-min 1 --sspec-max 1" fi diff --git a/DATA/production/calib/its-threshold-processing.sh b/DATA/production/calib/its-threshold-processing.sh index 8a768f8c1..8bb2a6fdf 100755 --- a/DATA/production/calib/its-threshold-processing.sh +++ b/DATA/production/calib/its-threshold-processing.sh @@ -14,30 +14,32 @@ PROXY_OUTSPEC="tunestring:ITS/TSTR;runtype:ITS/RUNT;fittype:ITS/FITT;scantype:IT CHIPMODBASE=5 NDECODERS=6 +ADDITIONAL_OPTIONS_DEC="" +ADDITIONAL_OPTIONS_CAL="" if [ $RUNTYPE_ITS == "digital" ]; then CHIPMODBASE=10 fi +if [ $RUNTYPE_ITS == "digitalnomask" ]; then + CHIPMODBASE=10 + ADDITIONAL_OPTIONS_CAL="--ninj 5" +fi if [ $RUNTYPE_ITS == "thrfull" ]; then CHIPMODBASE=20 NDECODERS=10 fi -ADDITIONAL_OPTIONS_DEC="" -ADDITIONAL_OPTIONS_CAL="" if [ $RUNTYPE_ITS == "tuningbb" ]; then ADDITIONAL_OPTIONS_CAL="--min-vcasn 30 --max-vcasn 130" fi -if [[ $RUNTYPE_ITS == "tot1row" || $RUNTYPE_ITS == "vresetd" ]]; then - ADDITIONAL_OPTIONS_DEC="--allow-empty-rofs" +if [[ $RUNTYPE_ITS == "tot1row" || $RUNTYPE_ITS == "vresetd-2d" ]]; then ADDITIONAL_OPTIONS_CAL="--ninj 10" fi if [ $RUNTYPE_ITS == "totfullfast" ]; then - ADDITIONAL_OPTIONS_DEC="--allow-empty-rofs" ADDITIONAL_OPTIONS_CAL="--calculate-slope --charge-a 30 --charge-b 60 --ninj 10" fi WORKFLOW= -add_W o2-dpl-raw-proxy "--exit-transition-timeout 20 --dataspec \"$PROXY_INSPEC\" --channel-config \"name=readout-proxy,type=pull,method=connect,address=ipc://@$INRAWCHANNAME,rateLogging=0,transport=shmem\"" "" 0 -add_W o2-itsmft-stf-decoder-workflow "${ADDITIONAL_OPTIONS_DEC} --condition-tf-per-query -1 --condition-backend \"http://localhost:8084\" --ignore-dist-stf --nthreads 1 --no-clusters --no-cluster-patterns --pipeline its-stf-decoder:${NDECODERS} --enable-calib-data --digits" +add_W o2-dpl-raw-proxy "--exit-transition-timeout 20 --dataspec \"$PROXY_INSPEC\" --inject-missing-data --channel-config \"name=readout-proxy,type=pull,method=connect,address=ipc://@$INRAWCHANNAME,rateLogging=0,transport=shmem\"" "" 0 +add_W o2-itsmft-stf-decoder-workflow "${ADDITIONAL_OPTIONS_DEC} --allow-empty-rofs --always-parse-trigger --condition-tf-per-query -1 --condition-backend \"http://localhost:8084\" --ignore-dist-stf --nthreads 1 --no-clusters --no-cluster-patterns --pipeline its-stf-decoder:${NDECODERS} --enable-calib-data --digits" for i in $(seq 0 $((CHIPMODBASE-1))) do add_W o2-its-threshold-calib-workflow "-b ${ADDITIONAL_OPTIONS_CAL} --enable-single-pix-tag --ccdb-mgr-url=\"http://localhost:8084\" --nthreads 1 --chip-mod-selector $i --chip-mod-base $CHIPMODBASE --fittype derivative --output-dir \"/data/calibration\" --meta-output-dir \"/data/epn2eos_tool/epn2eos\" --meta-type \"calibration\"" "" 0 diff --git a/DATA/production/calib/mch-badchannel-aggregator.sh b/DATA/production/calib/mch-badchannel-aggregator.sh index e362e373a..0f962b8f4 100755 --- a/DATA/production/calib/mch-badchannel-aggregator.sh +++ b/DATA/production/calib/mch-badchannel-aggregator.sh @@ -15,12 +15,25 @@ MCH_MAX_NOISE=${MCH_MAX_NOISE:-2.0} MCH_MIN_ENTRIES=${MCH_MIN_ENTRIES:-100} MCH_MIN_FRACTION=${MCH_MIN_FRACTION:-0.5} MCH_END_OF_STREAM_ONLY=${MCH_END_OF_STREAM_ONLY:-true} -BADCHANNEL_CONFIG="${ARGS_ALL_CONFIG};MCHBadChannelCalibratorParam.maxPed=${MCH_MAX_PEDESTAL};MCHBadChannelCalibratorParam.maxNoise=${MCH_MAX_NOISE};MCHBadChannelCalibratorParam.minRequiredNofEntriesPerChannel=${MCH_MIN_ENTRIES};MCHBadChannelCalibratorParam.minRequiredCalibratedFraction=${MCH_MIN_FRACTION};MCHBadChannelCalibratorParam.onlyAtEndOfStream=${MCH_END_OF_STREAM_ONLY}" +BADCHANNEL_CONFIG="${ARGS_ALL_CONFIG};MCHBadChannelCalibratorParam.maxPed=${MCH_MAX_PEDESTAL};MCHBadChannelCalibratorParam.maxNoise=${MCH_MAX_NOISE};MCHBadChannelCalibratorParam.minRequiredNofEntriesPerChannel=${MCH_MIN_ENTRIES};MCHBadChannelCalibratorParam.minRequiredCalibratedFraction=${MCH_MIN_FRACTION};MCHBadChannelCalibratorParam.onlyAtEndOfStream=${MCH_END_OF_STREAM_ONLY};" + +if [ -n "${MCH_NTHREADS}" ]; then + BADCHANNEL_CONFIG+="MCHBadChannelCalibratorParam.nThreads=${MCH_NTHREADS};" +fi + +MCH_LOGGING_OPT= +if [ -n "${MCH_LOGGING_INTERVAL}" ]; then + MCH_LOGGING_OPT="--logging-interval ${MCH_LOGGING_INTERVAL}" +fi WORKFLOW="o2-dpl-raw-proxy $ARGS_ALL --proxy-name mch-badchannel-input-proxy --dataspec \"$PROXY_INSPEC\" --network-interface ib0 --channel-config \"name=mch-badchannel-input-proxy,method=bind,type=pull,rateLogging=0,transport=zeromq\" | " -WORKFLOW+="o2-calibration-mch-badchannel-calib-workflow $ARGS_ALL --configKeyValues \"$BADCHANNEL_CONFIG\" | " -WORKFLOW+="o2-calibration-ccdb-populator-workflow $ARGS_ALL --configKeyValues \"$ARGS_ALL_CONFIG\" --ccdb-path=\"http://o2-ccdb.internal\" --sspec-min 0 --sspec-max 0 | " -WORKFLOW+="o2-calibration-ccdb-populator-workflow $ARGS_ALL --configKeyValues \"$ARGS_ALL_CONFIG\" --ccdb-path=\"http://ali-calib-dcs.cern.ch:8083\" --sspec-min 1 --sspec-max 1 --name-extention dcs | " +WORKFLOW+="o2-calibration-mch-badchannel-calib-workflow $ARGS_ALL --configKeyValues \"$BADCHANNEL_CONFIG\" ${MCH_LOGGING_OPT} | " + +if [ -z "${MCH_SKIP_CCDB_UPLOAD}" ]; then + WORKFLOW+="o2-calibration-ccdb-populator-workflow $ARGS_ALL --configKeyValues \"$ARGS_ALL_CONFIG\" --ccdb-path=\"http://o2-ccdb.internal\" --sspec-min 0 --sspec-max 0 | " + WORKFLOW+="o2-calibration-ccdb-populator-workflow $ARGS_ALL --configKeyValues \"$ARGS_ALL_CONFIG\" --ccdb-path=\"$DCSCCDBSERVER_PERS\" --sspec-min 1 --sspec-max 1 --name-extention dcs | " +fi + add_QC_from_consul "/o2/components/qc/ANY/any/mch-badchannel" "" WORKFLOW+="o2-dpl-run $ARGS_ALL $GLOBALDPLOPT" diff --git a/DATA/production/calib/mch-badchannel-processing.sh b/DATA/production/calib/mch-badchannel-processing.sh index cb1227e1f..738020262 100755 --- a/DATA/production/calib/mch-badchannel-processing.sh +++ b/DATA/production/calib/mch-badchannel-processing.sh @@ -1,4 +1,3 @@ - #!/bin/bash source common/setenv.sh @@ -10,7 +9,7 @@ source common/getCommonArgs.sh PROXY_INSPEC="A:MCH/RAWDATA;B:FLP/DISTSUBTIMEFRAME/0" PROXY_OUTSPEC="downstream:MCH/PDIGITS/0" -WORKFLOW="o2-dpl-raw-proxy ${ARGS_ALL} --dataspec \"$PROXY_INSPEC\" --channel-config \"name=readout-proxy,type=pull,method=connect,address=ipc://@$INRAWCHANNAME,transport=shmem,rateLogging=0\" | " +WORKFLOW="o2-dpl-raw-proxy ${ARGS_ALL} --dataspec \"$PROXY_INSPEC\" --inject-missing-data --channel-config \"name=readout-proxy,type=pull,method=connect,address=ipc://@$INRAWCHANNAME,transport=shmem,rateLogging=0\" | " WORKFLOW+="o2-mch-pedestal-decoding-workflow --pipeline mch-pedestal-decoder:${MULTIPLICITY_FACTOR_RAWDECODERS} --logging-interval 10 ${ARGS_ALL} --configKeyValues \"$ARGS_ALL_CONFIG\" | " WORKFLOW+="o2-dpl-output-proxy ${ARGS_ALL} --dataspec \"$PROXY_OUTSPEC\" --proxy-channel-name mch-badchannel-input-proxy --channel-config \"name=mch-badchannel-input-proxy,method=connect,type=push,transport=zeromq,rateLogging=0\" | " WORKFLOW+="o2-dpl-run ${ARGS_ALL} ${GLOBALDPLOPT}" diff --git a/DATA/production/calib/mft-noise-aggregator.sh b/DATA/production/calib/mft-noise-aggregator.sh index 3fa908025..5bb5feeca 100755 --- a/DATA/production/calib/mft-noise-aggregator.sh +++ b/DATA/production/calib/mft-noise-aggregator.sh @@ -9,9 +9,9 @@ source common/getCommonArgs.sh PROXY_INSPEC="A:MFT/DIGITS/0;B:MFT/DIGITSROF/0" WORKFLOW="o2-dpl-raw-proxy $ARGS_ALL --proxy-name mft-noise-input-proxy --dataspec \"$PROXY_INSPEC\" --network-interface ib0 --channel-config \"name=mft-noise-input-proxy,method=bind,type=pull,rateLogging=0,transport=zeromq\" | " -WORKFLOW+="o2-calibration-mft-calib-workflow $ARGS_ALL --configKeyValues \"$ARGS_ALL_CONFIG\" --useDigits --prob-threshold 1e-5 --send-to-server DCS-CCDB --path-CCDB \"/MFT/Calib/NoiseMap\" --path-DCS \"/MFT/Config/NoiseMap\" | " +WORKFLOW+="o2-calibration-mft-calib-workflow $ARGS_ALL --configKeyValues \"$ARGS_ALL_CONFIG\" --useDigits --prob-threshold 1e-5 --send-to-server DCS-CCDB --path-CCDB \"/MFT/Calib/NoiseMap\" --path-DCS \"/MFT/Config/NoiseMap\" --path-CCDB-single \"/MFT/Calib/NoiseMapSingle\" | " WORKFLOW+="o2-calibration-ccdb-populator-workflow $ARGS_ALL --configKeyValues \"$ARGS_ALL_CONFIG\" --ccdb-path=\"http://o2-ccdb.internal\" --sspec-min 0 --sspec-max 0 | " -WORKFLOW+="o2-calibration-ccdb-populator-workflow $ARGS_ALL --configKeyValues \"$ARGS_ALL_CONFIG\" --ccdb-path=\"http://ali-calib-dcs.cern.ch:8083\" --sspec-min 1 --sspec-max 1 --name-extention dcs | " +WORKFLOW+="o2-calibration-ccdb-populator-workflow $ARGS_ALL --configKeyValues \"$ARGS_ALL_CONFIG\" --ccdb-path=\"$DCSCCDBSERVER_PERS\" --sspec-min 1 --sspec-max 1 --name-extention dcs | " WORKFLOW+="o2-dpl-run $ARGS_ALL $GLOBALDPLOPT" if [ $WORKFLOWMODE == "print" ]; then diff --git a/DATA/production/calib/mft-noise-processing.sh b/DATA/production/calib/mft-noise-processing.sh index 4a8491844..cbcc52de8 100755 --- a/DATA/production/calib/mft-noise-processing.sh +++ b/DATA/production/calib/mft-noise-processing.sh @@ -9,7 +9,7 @@ source common/getCommonArgs.sh PROXY_INSPEC="A:MFT/RAWDATA;B:FLP/DISTSUBTIMEFRAME/0" PROXY_OUTSPEC="downstream:MFT/DIGITS/0;downstream:MFT/DIGITSROF/0" -WORKFLOW="o2-dpl-raw-proxy ${ARGS_ALL} --dataspec \"$PROXY_INSPEC\" --channel-config \"name=readout-proxy,type=pull,method=connect,address=ipc://@$INRAWCHANNAME,transport=shmem,rateLogging=0\" | " +WORKFLOW="o2-dpl-raw-proxy ${ARGS_ALL} --dataspec \"$PROXY_INSPEC\" --inject-missing-data --channel-config \"name=readout-proxy,type=pull,method=connect,address=ipc://@$INRAWCHANNAME,transport=shmem,rateLogging=0\" | " WORKFLOW+="o2-itsmft-stf-decoder-workflow ${ARGS_ALL} --configKeyValues \"$ARGS_ALL_CONFIG\" --runmft --digits --no-clusters --no-cluster-patterns --ignore-noise-map --nthreads 5 | " WORKFLOW+="o2-dpl-output-proxy ${ARGS_ALL} --dataspec \"$PROXY_OUTSPEC\" --proxy-channel-name mft-noise-input-proxy --channel-config \"name=mft-noise-input-proxy,method=connect,type=push,transport=zeromq,rateLogging=0\" | " WORKFLOW+="o2-dpl-run ${ARGS_ALL} ${GLOBALDPLOPT}" diff --git a/DATA/production/calib/mid-badchannels.sh b/DATA/production/calib/mid-badchannels.sh index 7137d4366..594c0406e 100755 --- a/DATA/production/calib/mid-badchannels.sh +++ b/DATA/production/calib/mid-badchannels.sh @@ -1,16 +1,17 @@ #!/usr/bin/env bash # shellcheck disable=SC1091 +# Common environment. Notice that this sources common/gen_topo_helper_functions.sh source common/setenv.sh -# shellcheck disable=SC1091 +# Set general arguments source common/getCommonArgs.sh MID_PROXY_INSPEC_EOS="eos:***/INFORMATION" MID_PROXY_INSPEC_DD="dd:FLP/DISTSUBTIMEFRAME/0" MID_RAW_PROXY_INSPEC="A:MID/RAWDATA;$MID_PROXY_INSPEC_DD;$MID_PROXY_INSPEC_EOS" MID_DPL_CHANNEL_CONFIG="name=readout-proxy,type=pull,method=connect,address=ipc://@$INRAWCHANNAME,transport=shmem,rateLogging=1" -CONSUL_ENDPOINT="alio2-cr1-hv-con01.cern.ch:8500" + if [[ -z $CTF_CONFIG ]]; then CTF_CONFIG="--report-data-size-interval 250"; fi if [[ -z $CTF_DIR ]]; then CTF_DIR="$FILEWORKDIR"; fi if [[ -z $CTF_MINSIZE ]]; then CTF_MINSIZE="2000000000"; fi @@ -18,13 +19,32 @@ if [[ -z $CTF_MAX_PER_FILE ]]; then CTF_MAX_PER_FILE="10000"; fi if [[ -z $EPN2EOS_METAFILES_DIR ]]; then EPN2EOS_METAFILES_DIR="/dev/null"; fi CONFIG_CTF="--output-dir \"$CTF_DIR\" $CTF_CONFIG --output-type ctf --min-file-size ${CTF_MINSIZE} --max-ctf-per-file ${CTF_MAX_PER_FILE} --onlyDet MID $CTF_MAXDETEXT --meta-output-dir $EPN2EOS_METAFILES_DIR" -WORKFLOW="o2-dpl-raw-proxy $ARGS_ALL --dataspec \"$MID_RAW_PROXY_INSPEC\" --channel-config \"$MID_DPL_CHANNEL_CONFIG\" | " -WORKFLOW+="o2-mid-raw-to-digits-workflow $ARGS_ALL | " -workflow_has_parameter CTF && WORKFLOW+="o2-mid-entropy-encoder-workflow $ARGS_ALL | o2-ctf-writer-workflow $ARGS_ALL $CONFIG_CTF | " -WORKFLOW+="o2-mid-calibration-workflow $ARGS_ALL | " -WORKFLOW+="o2-calibration-ccdb-populator-workflow $ARGS_ALL --configKeyValues \"$ARGS_ALL_CONFIG\" --ccdb-path=\"http://o2-ccdb.internal\" --sspec-min 0 --sspec-max 0 | " -WORKFLOW+="o2-calibration-ccdb-populator-workflow $ARGS_ALL --configKeyValues \"$ARGS_ALL_CONFIG\" --ccdb-path=\"http://alio2-cr1-flp199.cern.ch:8083\" --sspec-min 1 --sspec-max 1 --name-extention dcs | " -workflow_has_parameter QC && WORKFLOW+="o2-qc $ARGS_ALL --config consul-json://${CONSUL_ENDPOINT}/o2/components/qc/ANY/any/mid-calib-qcmn --local --host localhost | " +# CCDB destination for uploads +if [[ -z ${CCDB_POPULATOR_UPLOAD_PATH} ]]; then + if [[ $RUNTYPE == "SYNTHETIC" || "${GEN_TOPO_DEPLOYMENT_TYPE:-}" == "ALICE_STAGING" ]]; then + CCDB_POPULATOR_UPLOAD_PATH="http://ccdb-test.cern.ch:8080" + CCDB_POPULATOR_UPLOAD_PATH_DCS="$CCDB_POPULATOR_UPLOAD_PATH" + else + CCDB_POPULATOR_UPLOAD_PATH="http://o2-ccdb.internal" + CCDB_POPULATOR_UPLOAD_PATH_DCS="$DCSCCDBSERVER_PERS" + fi +fi +if [[ "${GEN_TOPO_VERBOSE:-}" == "1" ]]; then + echo "CCDB_POPULATOR_UPLOAD_PATH = $CCDB_POPULATOR_UPLOAD_PATH" 1>&2 + echo "CCDB_POPULATOR_UPLOAD_PATH_DCS = $CCDB_POPULATOR_UPLOAD_PATH_DCS" 1>&2 +fi + +WORKFLOW="" +add_W o2-dpl-raw-proxy "--dataspec \"$MID_RAW_PROXY_INSPEC\" --channel-config \"$MID_DPL_CHANNEL_CONFIG\"" "" 0 +add_W o2-mid-raw-to-digits-workflow "" "" 0 +workflow_has_parameter CTF && { + add_W o2-mid-entropy-encoder-workflow "" "" 0 + add_W o2-ctf-writer-workflow "$CONFIG_CTF" "" 0 +} +add_W o2-mid-calibration-workflow "" "" 0 +add_W o2-calibration-ccdb-populator-workflow "--ccdb-path=\"$CCDB_POPULATOR_UPLOAD_PATH\" --sspec-min 0 --sspec-max 0" +add_W o2-calibration-ccdb-populator-workflow "--ccdb-path=\"$CCDB_POPULATOR_UPLOAD_PATH_DCS\" --sspec-min 1 --sspec-max 1 --name-extention dcs" +workflow_has_parameter QC && add_QC_from_apricot "components/qc/ANY/any/mid-calib-qcmn" "--local --host localhost" WORKFLOW+="o2-dpl-run $ARGS_ALL $GLOBALDPLOPT" if [ "$WORKFLOWMODE" == "print" ]; then diff --git a/DATA/production/calib/phs-led.sh b/DATA/production/calib/phs-led.sh index a838f3c5c..dfd762745 100755 --- a/DATA/production/calib/phs-led.sh +++ b/DATA/production/calib/phs-led.sh @@ -4,36 +4,67 @@ source common/setenv.sh source common/getCommonArgs.sh -if [ -z $PHS_MAX_STATISTICS ]; then - PHS_MAX_STATISTICS=1000000 +source common/gen_topo_helper_functions.sh + +max_statistics=50000 +if [[ ! -z ${PHS_MAX_STATISTICS:-} ]]; then + max_statistics=$PHS_MAX_STATISTICS fi -PROXY_INSPEC="A:PHS/RAWDATA;dd:FLP/DISTSUBTIMEFRAME/0;eos:***/INFORMATION" +PROXY_INSPEC="A:PHS/RAWDATA;dd:FLP/DISTSUBTIMEFRAME/0" EXTRA_CONFIG=" " -if [ -z $PHS_CCDB_PATH ]; then - PHS_CCDB_PATH="http://o2-ccdb.internal" +push_ccdb_path="http://o2-ccdb.internal" +pull_ccdb_path="http://o2-ccdb.internal" +if [[ ! -z ${PUHS_CCDB_PATH:-} ]]; then + push_ccdb_path=$PUHS_CCDB_PATH +fi + +if [[ ! -z ${PULL_CCDB_PATH:-} ]]; then + pull_ccdb_path=$PULL_CCDB_PATH +fi + +if [[ $RUNTYPE == "SYNTHETIC" || "${GEN_TOPO_DEPLOYMENT_TYPE:-}" == "ALICE_STAGING" ]]; then + push_ccdb_path="http://ccdb-test.cern.ch:8080" +fi + +QC_CONFIG="/o2/components/qc/ANY/any/phs-led-qc" + +WORKFLOW= +add_W o2-dpl-raw-proxy "--dataspec \"$PROXY_INSPEC\" --inject-missing-data --channel-config \"name=readout-proxy,type=pull,method=connect,address=ipc://@tf-builder-pipe-0,transport=shmem,rateLogging=1\"" "" 0 +add_W o2-phos-reco-workflow "--input-type raw --output-type cells --disable-root-input --disable-root-output --keepHGLG on --condition-backend ${pull_ccdb_path}" +add_W o2-phos-calib-workflow "--hglgratio on --statistics ${max_statistics} --forceupdate" +add_W o2-calibration-ccdb-populator-workflow "--ccdb-path ${push_ccdb_path}" +workflow_has_parameter QC && add_QC_from_consul "${QC_CONFIG}" + +WORKFLOW+="o2-dpl-run ${ARGS_ALL} ${GLOBALDPLOPT}" + +if [ $WORKFLOWMODE == "print" ]; then + echo Workflow command: + echo $WORKFLOW | sed "s/| */|\n/g" +else + # Execute the command we have assembled + WORKFLOW+=" --$WORKFLOWMODE ${WORKFLOWMODE_FILE}" + eval $WORKFLOW fi -QC_CONFIG=consul-json://alio2-cr1-hv-con01.cern.ch:8500/o2/components/qc/ANY/any/phs-led-qc - -o2-dpl-raw-proxy $ARGS_ALL \ - --dataspec "$PROXY_INSPEC" \ - --readout-proxy '--channel-config "name=readout-proxy,type=pull,method=connect,address=ipc://@tf-builder-pipe-0,transport=shmem,rateLogging=1"' \ - | o2-phos-reco-workflow $ARGS_ALL \ - --input-type raw \ - --output-type cells \ - --disable-root-input \ - --disable-root-output \ - --keepHGLG on \ - | o2-phos-calib-workflow $ARGS_ALL \ - --hglgratio on \ - --statistics $PHS_MAX_STATISTICS \ - --configKeyValues "NameConf.mCCDBServer=${PHS_CCDB_PATH}" \ - --forceupdate \ - | o2-qc $ARGS_ALL \ - --config $QC_CONFIG \ - | o2-calibration-ccdb-populator-workflow $ARGS_ALL \ - --ccdb-path $PHS_CCDB_PATH \ - | o2-dpl-run $ARGS_ALL --dds ${WORKFLOWMODE_FILE} +#o2-dpl-raw-proxy $ARGS_ALL \ +# --dataspec "$PROXY_INSPEC" --inject-missing-data \ +# --readout-proxy '--channel-config "name=readout-proxy,type=pull,method=connect,address=ipc://@tf-builder-pipe-0,transport=shmem,rateLogging=1"' \ +# | o2-phos-reco-workflow $ARGS_ALL \ +# --input-type raw \ +# --output-type cells \ +# --disable-root-input \ +# --disable-root-output \ +# --keepHGLG on \ +# | o2-phos-calib-workflow $ARGS_ALL \ +# --hglgratio on \ +# --statistics $PHS_MAX_STATISTICS \ +# --configKeyValues "NameConf.mCCDBServer=${PHS_CCDB_PATH}" \ +# --forceupdate \ +# | o2-qc $ARGS_ALL \ +# --config $QC_CONFIG \ +# | o2-calibration-ccdb-populator-workflow $ARGS_ALL \ +# --ccdb-path $PHS_CCDB_PATH \ +# | o2-dpl-run $ARGS_ALL --dds ${WORKFLOWMODE_FILE} diff --git a/DATA/production/calib/phs-pedestal.sh b/DATA/production/calib/phs-pedestal.sh index a7a7e8f6d..a3b2d9d3e 100755 --- a/DATA/production/calib/phs-pedestal.sh +++ b/DATA/production/calib/phs-pedestal.sh @@ -4,36 +4,67 @@ source common/setenv.sh source common/getCommonArgs.sh -if [ -z $PHS_MAX_STATISTICS ] ; then - PHS_MAX_STATISTICS=10000 +source common/gen_topo_helper_functions.sh + +max_statistics=5000 +if [[ ! -z ${PHS_MAX_STATISTICS:-} ]]; then + max_statistics=$PHS_MAX_STATISTICS +fi + +PROXY_INSPEC="A:PHS/RAWDATA;dd:FLP/DISTSUBTIMEFRAME/0" + + +push_ccdb_path="http://o2-ccdb.internal" +pull_ccdb_path="http://o2-ccdb.internal" +if [[ ! -z ${PUHS_CCDB_PATH:-} ]]; then + push_ccdb_path=$PUHS_CCDB_PATH fi -PROXY_INSPEC="A:PHS/RAWDATA;dd:FLP/DISTSUBTIMEFRAME/0;eos:***/INFORMATION" +if [[ ! -z ${PULL_CCDB_PATH:-} ]]; then + pull_ccdb_path=$PULL_CCDB_PATH +fi + +if [[ $RUNTYPE == "SYNTHETIC" || "${GEN_TOPO_DEPLOYMENT_TYPE:-}" == "ALICE_STAGING" ]]; then + push_ccdb_path="http://ccdb-test.cern.ch:8080" +fi + +QC_CONFIG="/o2/components/qc/ANY/any/phs-pedestal-qc" + +WORKFLOW= +add_W o2-dpl-raw-proxy "--dataspec \"$PROXY_INSPEC\" --inject-missing-data --channel-config \"name=readout-proxy,type=pull,method=connect,address=ipc://@tf-builder-pipe-0,transport=shmem,rateLogging=1\"" "" 0 +add_W o2-phos-reco-workflow "--input-type raw --output-type cells --pedestal on --disable-root-input --disable-root-output --condition-backend ${pull_ccdb_path}" +add_W o2-phos-calib-workflow "--pedestals --statistics ${max_statistics} --forceupdate" +#add_W o2-calibration-ccdb-populator-workflow "--ccdb-path ${push_ccdb_path}" +workflow_has_parameter QC && add_QC_from_consul "${QC_CONFIG}" +add_W o2-calibration-ccdb-populator-workflow "--ccdb-path ${push_ccdb_path}" -EXTRA_CONFIG=" " +WORKFLOW+="o2-dpl-run ${ARGS_ALL} ${GLOBALDPLOPT}" -if [ -z $PHS_CCDB_PATH ] ; then - PHS_CCDB_PATH="http://o2-ccdb.internal" +if [ $WORKFLOWMODE == "print" ]; then + echo Workflow command: + echo $WORKFLOW | sed "s/| */|\n/g" +else + # Execute the command we have assembled + WORKFLOW+=" --$WORKFLOWMODE ${WORKFLOWMODE_FILE}" + eval $WORKFLOW fi -QC_CONFIG=consul-json://alio2-cr1-hv-con01.cern.ch:8500/o2/components/qc/ANY/any/phs-pedestal-qc - -o2-dpl-raw-proxy $ARGS_ALL \ - --dataspec "$PROXY_INSPEC" \ - --readout-proxy '--channel-config "name=readout-proxy,type=pull,method=connect,address=ipc://@tf-builder-pipe-0,transport=shmem,rateLogging=1"' \ - | o2-phos-reco-workflow $ARGS_ALL \ - --input-type raw \ - --output-type cells \ - --pedestal on \ - --disable-root-input \ - --disable-root-output \ - | o2-phos-calib-workflow $ARGS_ALL \ - --pedestals \ - --statistics $PHS_MAX_STATISTICS \ - --configKeyValues "NameConf.mCCDBServer=${PHS_CCDB_PATH}" \ - --forceupdate \ - | o2-qc $ARGS_ALL \ - --config $QC_CONFIG \ - | o2-calibration-ccdb-populator-workflow $ARGS_ALL \ - --ccdb-path $PHS_CCDB_PATH \ - | o2-dpl-run $ARGS_ALL --dds ${WORKFLOWMODE_FILE} +#o2-dpl-raw-proxy $ARGS_ALL \ +# --dataspec "$PROXY_INSPEC" --inject-missing-data \ +# --readout-proxy '--channel-config "name=readout-proxy,type=pull,method=connect,address=ipc://@tf-builder-pipe-0,transport=shmem,rateLogging=1"' \ +# | o2-phos-reco-workflow $ARGS_ALL \ +# --input-type raw \ +# --output-type cells \ +# --pedestal on \ +# --disable-root-input \ +# --disable-root-output \ +# | o2-phos-calib-workflow $ARGS_ALL \ +# --pedestals \ +# --statistics $PHS_MAX_STATISTICS \ +# --configKeyValues "NameConf.mCCDBServer=${PHS_CCDB_PATH}" \ +# --forceupdate \ +# | o2-qc $ARGS_ALL \ +# --config $QC_CONFIG \ +# | o2-calibration-ccdb-populator-workflow $ARGS_ALL \ +# --ccdb-path $PHS_CCDB_PATH \ +# | o2-dpl-run $ARGS_ALL --dds ${WORKFLOWMODE_FILE} diff --git a/DATA/production/calib/tof-standalone-cosmic-reco-time-calib.sh b/DATA/production/calib/tof-standalone-cosmic-reco-time-calib.sh index 04d35cc29..5c96e7c7b 100755 --- a/DATA/production/calib/tof-standalone-cosmic-reco-time-calib.sh +++ b/DATA/production/calib/tof-standalone-cosmic-reco-time-calib.sh @@ -13,7 +13,7 @@ PROXY_OUTSPEC="calclus:TOF/INFOCALCLUS;cosmics:TOF/INFOCOSMICS;trkcos:TOF/INFOTR MYDIR="$(dirname $(readlink -f $0))/../../testing/detectors/TOF" WORKFLOW= -add_W o2-dpl-raw-proxy "${ARGS_ALL} --dataspec ${PROXY_INSPEC} --channel-config \"name=readout-proxy,type=pull,method=connect,address=ipc://@$INRAWCHANNAME,transport=shmem,rateLogging=0\" " +add_W o2-dpl-raw-proxy "${ARGS_ALL} --dataspec ${PROXY_INSPEC} --inject-missing-data --channel-config \"name=readout-proxy,type=pull,method=connect,address=ipc://@$INRAWCHANNAME,transport=shmem,rateLogging=0\" " add_W o2-tof-reco-workflow "--input-type raw --output-type clusters ${ARGS_ALL} --configKeyValues ${ARGS_ALL_CONFIG} --disable-root-output --calib-cluster --cluster-time-window 10000 --cosmics --pipeline \"tof-compressed-decoder:${NTHREADS},TOFClusterer:${NTHREADS}\" " add_W o2-qc "${ARGS_ALL} --config json://${MYDIR}/qc-full.json --local --host epn " add_W o2-dpl-output-proxy "${ARGS_ALL} --dataspec ${PROXY_OUTSPEC} --proxy-channel-name tof-time-calib-input-proxy --channel-config \"name=tof-time-calib-input-proxy,method=connect,type=push,transport=zeromq,rateLogging=0\" " diff --git a/DATA/production/calib/tof-standalone-reco.sh b/DATA/production/calib/tof-standalone-reco.sh index bd14b936e..67d3700ed 100755 --- a/DATA/production/calib/tof-standalone-reco.sh +++ b/DATA/production/calib/tof-standalone-reco.sh @@ -13,7 +13,7 @@ PROXY_OUTSPEC="diagWords:TOF/DIAFREQ" MYDIR="$(dirname $(readlink -f $0))/../../testing/detectors/TOF" WORKFLOW= -add_W o2-dpl-raw-proxy "${ARGS_ALL} --dataspec ${PROXY_INSPEC} --channel-config \"name=readout-proxy,type=pull,method=connect,address=ipc://@$INRAWCHANNAME,transport=shmem,rateLogging=0\" " +add_W o2-dpl-raw-proxy "${ARGS_ALL} --dataspec ${PROXY_INSPEC} --inject-missing-data --channel-config \"name=readout-proxy,type=pull,method=connect,address=ipc://@$INRAWCHANNAME,transport=shmem,rateLogging=0\" " add_W o2-tof-reco-workflow "--input-type raw --output-type clusters ${ARGS_ALL} --configKeyValues ${ARGS_ALL_CONFIG} --disable-root-output --calib-cluster --cluster-time-window 10000 --cosmics --pipeline \"tof-compressed-decoder:${NTHREADS},TOFClusterer:${NTHREADS}\" " add_W o2-qc "${ARGS_ALL} --config json://${MYDIR}/qc-full.json --local --host epn " add_W o2-dpl-output-proxy "${ARGS_ALL} --dataspec ${PROXY_OUTSPEC} --proxy-channel-name tof-diagn-input-proxy --channel-config \"name=tof-diagn-input-proxy,method=connect,type=push,transport=zeromq,rateLogging=0\" " diff --git a/DATA/production/calib/tpc-laser-aggregator.sh b/DATA/production/calib/tpc-laser-aggregator.sh index 01f7864a1..5dd282ade 100755 --- a/DATA/production/calib/tpc-laser-aggregator.sh +++ b/DATA/production/calib/tpc-laser-aggregator.sh @@ -4,22 +4,25 @@ source common/setenv.sh source common/getCommonArgs.sh +FILEWORKDIR="/home/wiechula/processData/inputFilesTracking/triggeredLaser" + PROXY_INSPEC="A:TPC/LASERTRACKS;B:TPC/CEDIGITS;D:TPC/CLUSREFS" -CALIB_CONFIG="TPCCalibPulser.FirstTimeBin=450;TPCCalibPulser.LastTimeBin=550;TPCCalibPulser.NbinsQtot=250;TPCCalibPulser.XminQtot=2;TPCCalibPulser.XmaxQtot=502;TPCCalibPulser.MinimumQtot=8;TPCCalibPulser.MinimumQmax=6;TPCCalibPulser.XminT0=450;TPCCalibPulser.XmaxT0=550;TPCCalibPulser.NbinsT0=400;keyval.output_dir=/dev/null" +CALIB_CONFIG="TPCCalibPulser.FirstTimeBin=450;TPCCalibPulser.LastTimeBin=550;TPCCalibPulser.NbinsQtot=600;TPCCalibPulser.XminQtot=2;TPCCalibPulser.XmaxQtot=1202;TPCCalibPulser.MinimumQtot=8;TPCCalibPulser.MinimumQmax=6;TPCCalibPulser.XminT0=450;TPCCalibPulser.XmaxT0=550;TPCCalibPulser.NbinsT0=400;keyval.output_dir=/dev/null" -CCDB_PATH="http://o2-ccdb.internal" +[[ $RUNTYPE == "SYNTHETIC" ]] && CCDB_PATH="http://ccdb-test.cern.ch:8080" || CCDB_PATH="http://o2-ccdb.internal" HOST=localhost -QC_CONFIG="consul-json://alio2-cr1-hv-con01.cern.ch:8500/o2/components/qc/ANY/any/tpc-raw-qcmn" +QC_CONFIG="consul-json://alio2-cr1-hv-con01.cern.ch:8500/o2/components/qc/ANY/any/tpc-raw-qcmn?run_type=${RUNTYPE:-}" +QC_CONFIG="components/qc/ANY/any/tpc-raw-qcmn" max_events=300 publish_after=440 min_tracks=0 num_lanes=36 - +REMAP="--condition-remap \"file://${FILEWORKDIR}=GLO/Config/GRPECS,GLO/Config/GRPMagField,TPC/Calib/LaserTracks\" " if [[ ! -z ${TPC_CALIB_MAX_EVENTS:-} ]]; then max_events=${TPC_CALIB_MAX_EVENTS} fi @@ -44,16 +47,21 @@ fi -o2-dpl-raw-proxy $ARGS_ALL \ - --proxy-name tpc-laser-input-proxy \ - --dataspec "${PROXY_INSPEC}" \ - --network-interface ib0 \ - --channel-config "name=tpc-laser-input-proxy,method=bind,type=pull,rateLogging=0,transport=zeromq" \ - | o2-tpc-calib-laser-tracks ${ARGS_ALL} --use-filtered-tracks ${EXTRA_CONFIG_TRACKS} --min-tfs=${min_tracks} \ - --condition-remap "file:///home/wiechula/processData/inputFilesTracking/triggeredLaser/=GLO/Config/GRPECS;file:///home/wiechula/processData/inputFilesTracking/triggeredLaser/=GLO/Config/GRPMagField;file:///home/wiechula/processData/inputFilesTracking/triggeredLaser=TPC/Calib/LaserTracks" \ - | o2-tpc-calib-pad-raw ${ARGS_ALL} \ - --configKeyValues ${CALIB_CONFIG} ${EXTRA_CONFIG} \ - | o2-calibration-ccdb-populator-workflow ${ARGS_ALL} \ - --ccdb-path ${CCDB_PATH} \ - | o2-dpl-run $ARGS_ALL --dds ${WORKFLOWMODE_FILE} +WORKFLOW= +add_W o2-dpl-raw-proxy " --proxy-name tpc-laser-input-proxy --dataspec \"$PROXY_INSPEC\" --network-interface ib0 --channel-config \"name=tpc-laser-input-proxy,method=bind,type=pull,rateLogging=0,transport=zeromq\"" "" 0 +add_W o2-tpc-calib-laser-tracks "${REMAP} --use-filtered-tracks ${EXTRA_CONFIG_TRACKS} --min-tfs=${min_tracks} " +add_W o2-tpc-calib-pad-raw " ${EXTRA_CONFIG}" "${CALIB_CONFIG}" +add_W o2-calibration-ccdb-populator-workflow "--ccdb-path ${CCDB_PATH}" "" 0 +#add_QC_from_apricot "${QC_CONFIG_CONSUL}" "--local --host lcoalhost" + +WORKFLOW+="o2-dpl-run ${ARGS_ALL} ${GLOBALDPLOPT}" +if [ $WORKFLOWMODE == "print" ]; then + echo Workflow command: + echo $WORKFLOW | sed "s/| */|\n/g" +else + # Execute the command we have assembled + WORKFLOW+=" --$WORKFLOWMODE ${WORKFLOWMODE_FILE}" + eval $WORKFLOW +fi + diff --git a/DATA/production/calib/tpc-laser-filter.sh b/DATA/production/calib/tpc-laser-filter.sh index fb3c0cf3d..1bc4be8ab 100755 --- a/DATA/production/calib/tpc-laser-filter.sh +++ b/DATA/production/calib/tpc-laser-filter.sh @@ -4,23 +4,32 @@ source common/setenv.sh source common/getCommonArgs.sh -source common/gen_topo_helper_functions.sh +source common/gen_topo_helper_functions.sh FILEWORKDIR="/home/wiechula/processData/inputFilesTracking/triggeredLaser" FILEWORKDIR2="/home/epn/odc/files/" -ARGS_ALL_CONFIG="NameConf.mDirGRP=$FILEWORKDIR;NameConf.mDirGeom=$FILEWORKDIR2;NameConf.mDirCollContext=$FILEWORKDIR;NameConf.mDirMatLUT=$FILEWORKDIR;keyval.input_dir=$FILEWORKDIR;keyval.output_dir=/dev/null" +#ARGS_ALL_CONFIG+="NameConf.mDirGRP=$FILEWORKDIR;NameConf.mDirGeom=$FILEWORKDIR2;NameConf.mDirCollContext=$FILEWORKDIR;NameConf.mDirMatLUT=$FILEWORKDIR;keyval.input_dir=$FILEWORKDIR;keyval.output_dir=/dev/null" +#ARGS_ALL_CONFIG+="NameConf.mDirGRP=$FILEWORKDIR;NameConf.mDirGeom=$FILEWORKDIR2;NameConf.mDirCollContext=$FILEWORKDIR;NameConf.mDirMatLUT=$FILEWORKDIR" -if [ $NUMAGPUIDS != 0 ]; then +GPUTYPE=${GPUTYPE:-CPU} +HOSTMEMSIZE=0 +if workflow_has_parameter GPU; then + GPUTYPE=HIP + GPUMEMSIZE=$(( 24 << 30 )) + HOSTMEMSIZE=$(( 5 << 30 )) +fi + +if [ ${NUMAGPUIDS} != 0 ]; then ARGS_ALL+=" --child-driver 'numactl --membind $NUMAID --cpunodebind $NUMAID'" fi -if [ $GPUTYPE == "HIP" ]; then - if [ $NUMAID == 0 ] || [ $NUMAGPUIDS == 0 ]; then +if [ ${GPUTYPE} == "HIP" ]; then + if [ ${NUMAID} == 0 ] || [ ${NUMAGPUIDS} == 0 ]; then export TIMESLICEOFFSET=0 else - export TIMESLICEOFFSET=$NGPUS + export TIMESLICEOFFSET=${NGPUS} fi GPU_CONFIG_KEY+="GPU_proc.deviceNum=0;" GPU_CONFIG+=" --environment ROCR_VISIBLE_DEVICES={timeslice${TIMESLICEOFFSET}}" @@ -29,55 +38,62 @@ else GPU_CONFIG_KEY+="GPU_proc.deviceNum=-2;" fi -if [ $GPUTYPE != "CPU" ]; then +if [ ${GPUTYPE} != "CPU" ]; then GPU_CONFIG_KEY+="GPU_proc.forceMemoryPoolSize=$GPUMEMSIZE;" - if [ $HOSTMEMSIZE == "0" ]; then + if [ ${HOSTMEMSIZE} == "0" ]; then HOSTMEMSIZE=$(( 1 << 30 )) fi fi -if [ $HOSTMEMSIZE != "0" ]; then +if [ ${HOSTMEMSIZE} != "0" ]; then GPU_CONFIG_KEY+="GPU_proc.forceHostMemoryPoolSize=$HOSTMEMSIZE;" fi -PROXY_INSPEC="A:TPC/RAWDATA;dd:FLP/DISTSUBTIMEFRAME/0;eos:***/INFORMATION" -CALIB_INSPEC="A:TPC/RAWDATA;dd:FLP/DISTSUBTIMEFRAME/0;eos:***/INFORMATION" +PROXY_INSPEC="A:TPC/RAWDATA;dd:FLP/DISTSUBTIMEFRAME/0" +CALIB_INSPEC="A:TPC/RAWDATA;dd:FLP/DISTSUBTIMEFRAME/0" PROXY_OUTSPEC="A:TPC/LASERTRACKS;B:TPC/CEDIGITS;D:TPC/CLUSREFS" - -LASER_DECODER_ADD='' - HOST=localhost -QC_CONFIG="consul-json://alio2-cr1-hv-con01.cern.ch:8500/o2/components/qc/ANY/any/tpc-laser-calib-qcmn" - -if [[ ! -z ${TPC_LASER_ILBZS:-} ]]; then - LASER_DECODER_ADD="--pedestal-url /home/wiechula/processData/inputFilesTracking/triggeredLaser/pedestals.openchannels.root -decode-type 0" +QC_CONFIG="consul-json://alio2-cr1-hv-con01.cern.ch:8500/o2/components/qc/ANY/any/tpc-laser-calib-qcmn?run_type=${RUNTYPE:-}" +QC_CONFIG="components/qc/ANY/any/tpc-laser-calib-qcmn?run_type=${RUNTYPE:-}" + + +RAWDIGIT_CONFIG="TPCDigitDump.NoiseThreshold=3;TPCDigitDump.LastTimeBin=600;NameConf.mDirGRP=$FILEWORKDIR;NameConf.mDirGeom=$FILEWORKDIR2;NameConf.mDirCollContext=$FILEWORKDIR;NameConf.mDirMatLUT=$FILEWORKDIR" +REMAP="--condition-remap \"file://${FILEWORKDIR}=GLO/Config/GRPECS,GLO/Config/GRPMagField,TPC/Calib/LaserTracks\" " +RECO_CONFIG="NameConf.mDirGRP=$FILEWORKDIR;" +RECO_CONFIG+="NameConf.mDirGeom=$FILEWORKDIR2;" +RECO_CONFIG+="NameConf.mDirCollContext=$FILEWORKDIR;" +RECO_CONFIG+="NameConf.mDirMatLUT=$FILEWORKDIR;" +RECO_CONFIG+="align-geom.mDetectors=none;" +RECO_CONFIG+="GPU_global.deviceType=$GPUTYPE;" +RECO_CONFIG+="GPU_proc.tpcIncreasedMinClustersPerRow=500000;" +RECO_CONFIG+="GPU_proc.ignoreNonFatalGPUErrors=1;$GPU_CONFIG_KEY;" +RECO_CONFIG+="GPU_global.tpcTriggeredMode=1;" +RECO_CONFIG+="GPU_rec_tpc.clusterError2AdditionalY=0.1;" +RECO_CONFIG+="GPU_rec_tpc.clusterError2AdditionalZ=0.15;" +RECO_CONFIG+="GPU_rec_tpc.clustersShiftTimebinsClusterizer=35;" +RECO_CONFIG+="GPU_proc.memoryScalingFactor=2;" +RECO_CONFIG+="GPU_proc_param.tpcTriggerHandling=0;" +# relax tolerances on tracking and selection cut to deal with very low laser intensities +RECO_CONFIG+="GPU_rec_tpc.trackFollowingMaxRowGap=15;GPU_rec_tpc.trackFollowingMaxRowGapSeed=15;GPU_rec_tpc.minTrackdEdxMax=8;GPU_rec_tpc.adddEdxSubThresholdClusters=0;" + + +WORKFLOW= +add_W o2-dpl-raw-proxy "--dataspec \"$PROXY_INSPEC\" --inject-missing-data --channel-config \"name=readout-proxy,type=pull,method=connect,address=ipc://@tf-builder-pipe-0,transport=shmem,rateLogging=1\"" "" 0 +add_W o2-tpc-raw-to-digits-workflow "--ignore-grp --input-spec \"$CALIB_INSPEC\" --remove-duplicates --pipeline tpc-raw-to-digits-0:20 --send-ce-digits " "${RAWDIGIT_CONFIG}" +add_W o2-tpc-scaler-workflow "--disable-IDC-scalers --disable-ctp-lumi-request" +add_W o2-tpc-reco-workflow " --input-type digitizer --output-type \"tracks,disable-writer,clusters\" --disable-mc --pipeline tpc-zsEncoder:20,tpc-tracker:8 ${GPU_CONFIG} ${REMAP} " "${RECO_CONFIG}" +add_W o2-tpc-laser-track-filter "" "" 0 +add_W o2-dpl-output-proxy " --proxy-name tpc-laser-input-proxy --proxy-channel-name tpc-laser-input-proxy --dataspec \"$PROXY_OUTSPEC\" --channel-config \"name=tpc-laser-input-proxy,method=connect,type=push,transport=zeromq,rateLogging=0\" " "" 0 +add_QC_from_apricot "${QC_CONFIG}" "--local --host localhost" + +WORKFLOW+="o2-dpl-run ${ARGS_ALL} ${GLOBALDPLOPT}" +if [ $WORKFLOWMODE == "print" ]; then + echo Workflow command: + echo $WORKFLOW | sed "s/| */|\n/g" +else + # Execute the command we have assembled + WORKFLOW+=" --$WORKFLOWMODE ${WORKFLOWMODE_FILE}" + eval $WORKFLOW fi -o2-dpl-raw-proxy $ARGS_ALL \ - --dataspec "$PROXY_INSPEC" \ - --readout-proxy "--channel-config 'name=readout-proxy,type=pull,method=connect,address=ipc://@tf-builder-pipe-0,transport=shmem,rateLogging=1'" \ - | o2-tpc-raw-to-digits-workflow $ARGS_ALL ${LASER_DECODER_ADD} \ - --input-spec "$CALIB_INSPEC" \ - --configKeyValues "TPCDigitDump.NoiseThreshold=3;TPCDigitDump.LastTimeBin=600;$ARGS_ALL_CONFIG" \ - --pedestal-url /home/wiechula/processData/inputFilesTracking/triggeredLaser/pedestals.openchannels.root \ - --pipeline tpc-raw-to-digits-0:20 \ - --remove-duplicates \ - --send-ce-digits \ - | o2-tpc-reco-workflow $ARGS_ALL ${TPC_CORR_SCALING:-} \ - --input-type digitizer \ - --output-type "tracks,disable-writer" \ - --disable-mc \ - --pipeline tpc-zsEncoder:20,tpc-tracker:8 \ - $GPU_CONFIG \ - --condition-remap "file:///home/wiechula/processData/inputFilesTracking/triggeredLaser/=GLO/Config/GRPECS;file:///home/wiechula/processData/inputFilesTracking/triggeredLaser/=GLO/Config/GRPMagField;file:///home/wiechula/processData/inputFilesTracking/triggeredLaser=TPC/Calib/LaserTracks" \ - --configKeyValues "${ARGS_ALL_CONFIG};align-geom.mDetectors=none;GPU_global.deviceType=$GPUTYPE;GPU_proc.tpcIncreasedMinClustersPerRow=500000;GPU_proc.ignoreNonFatalGPUErrors=1;$GPU_CONFIG_KEY;GPU_global.tpcTriggeredMode=1;GPU_rec_tpc.clusterError2AdditionalY=0.1;GPU_rec_tpc.clusterError2AdditionalZ=0.15;GPU_rec_tpc.clustersShiftTimebinsClusterizer=35;GPU_proc.memoryScalingFactor=2" \ - | o2-tpc-laser-track-filter $ARGS_ALL \ - | o2-dpl-output-proxy ${ARGS_ALL} \ - --dataspec "$PROXY_OUTSPEC" \ - --proxy-name tpc-laser-input-proxy \ - --proxy-channel-name tpc-laser-input-proxy \ - --channel-config "name=tpc-laser-input-proxy,method=connect,type=push,transport=zeromq,rateLogging=0" \ - | o2-qc ${ARGS_ALL} --config ${QC_CONFIG} --local --host ${HOST} \ - | o2-dpl-run $ARGS_ALL --dds ${WORKFLOWMODE_FILE} ${GLOBALDPLOPT} - diff --git a/DATA/production/calib/tpc-laser.sh b/DATA/production/calib/tpc-laser.sh index 76ac79ec0..0ace43717 100755 --- a/DATA/production/calib/tpc-laser.sh +++ b/DATA/production/calib/tpc-laser.sh @@ -4,13 +4,19 @@ source common/setenv.sh source common/getCommonArgs.sh -source common/gen_topo_helper_functions.sh +source common/gen_topo_helper_functions.sh FILEWORKDIR="/home/wiechula/processData/inputFilesTracking/triggeredLaser" FILEWORKDIR2="/home/epn/odc/files/" -ARGS_ALL_CONFIG="NameConf.mDirGRP=$FILEWORKDIR;NameConf.mDirGeom=$FILEWORKDIR2;NameConf.mDirCollContext=$FILEWORKDIR;NameConf.mDirMatLUT=$FILEWORKDIR;keyval.input_dir=$FILEWORKDIR;keyval.output_dir=/dev/null" +GPUTYPE=${GPUTYPE:-CPU} +HOSTMEMSIZE=0 +if workflow_has_parameter GPU; then + GPUTYPE=HIP + GPUMEMSIZE=$(( 24 << 30 )) + HOSTMEMSIZE=$(( 5 << 30 )) +fi if [ $NUMAGPUIDS != 0 ]; then ARGS_ALL+=" --child-driver 'numactl --membind $NUMAID --cpunodebind $NUMAID'" @@ -39,16 +45,18 @@ if [ $HOSTMEMSIZE != "0" ]; then GPU_CONFIG_KEY+="GPU_proc.forceHostMemoryPoolSize=$HOSTMEMSIZE;" fi -PROXY_INSPEC="A:TPC/RAWDATA;dd:FLP/DISTSUBTIMEFRAME/0;eos:***/INFORMATION" -CALIB_INSPEC="A:TPC/RAWDATA;dd:FLP/DISTSUBTIMEFRAME/0;eos:***/INFORMATION" +PROXY_INSPEC="A:TPC/RAWDATA;dd:FLP/DISTSUBTIMEFRAME/0" +CALIB_INSPEC="A:TPC/RAWDATA;dd:FLP/DISTSUBTIMEFRAME/0" -CALIB_CONFIG="TPCCalibPulser.FirstTimeBin=450;TPCCalibPulser.LastTimeBin=550;TPCCalibPulser.NbinsQtot=250;TPCCalibPulser.XminQtot=2;TPCCalibPulser.XmaxQtot=502;TPCCalibPulser.MinimumQtot=8;TPCCalibPulser.MinimumQmax=6;TPCCalibPulser.XminT0=450;TPCCalibPulser.XmaxT0=550;TPCCalibPulser.NbinsT0=400;keyval.output_dir=/dev/null" +CALIB_CONFIG="TPCCalibPulser.FirstTimeBin=450;TPCCalibPulser.LastTimeBin=550;TPCCalibPulser.NbinsQtot=400;TPCCalibPulser.XminQtot=2;TPCCalibPulser.XmaxQtot=802;TPCCalibPulser.MinimumQtot=8;TPCCalibPulser.MinimumQmax=6;TPCCalibPulser.XminT0=450;TPCCalibPulser.XmaxT0=550;TPCCalibPulser.NbinsT0=400;keyval.output_dir=/dev/null" CCDB_PATH="http://o2-ccdb.internal" HOST=localhost -QC_CONFIG="consul-json://alio2-cr1-hv-con01.cern.ch:8500/o2/components/qc/ANY/any/tpc-raw-qcmn" + +QC_CONFIG="consul-json://alio2-cr1-hv-con01.cern.ch:8500/o2/components/qc/ANY/any/tpc-raw-qcmn?run_type=${RUNTYPE:-}" +QC_CONFIG="components/qc/ANY/any/tpc-raw-qcmn" max_events=300 publish_after=440 @@ -69,7 +77,7 @@ if [[ ! -z ${TPC_CALIB_LANES_PAD_RAW:-} ]]; then num_lanes=${TPC_CALIB_LANES_PAD_RAW} fi -EXTRA_CONFIG="--calib-type ce --publish-after-tfs ${publish_after} --max-events ${max_events} --lanes ${num_lanes} --check-calib-infos" +EXTRA_CONFIG="--calib-type ce --publish-after-tfs ${publish_after} --max-events ${max_events} --lanes ${num_lanes} --check-calib-infos" LASER_DECODER_ADD='' @@ -83,31 +91,57 @@ if [[ ${TPC_CALIB_TRACKS_PUBLISH_EOS:-} == 1 ]]; then EXTRA_CONFIG_TRACKS="--only-publish-on-eos" fi +RAWDIGIT_CONFIG="TPCDigitDump.NoiseThreshold=3;TPCDigitDump.LastTimeBin=600" +REMAP="--condition-remap \"file://${FILEWORKDIR}=GLO/Config/GRPECS,GLO/Config/GRPMagField,TPC/Calib/LaserTracks\" " +RECO_CONFIG="align-geom.mDetectors=none;GPU_global.deviceType=$GPUTYPE;GPU_proc.tpcIncreasedMinClustersPerRow=500000;GPU_proc.ignoreNonFatalGPUErrors=1;$GPU_CONFIG_KEY;GPU_global.tpcTriggeredMode=1;GPU_rec_tpc.clusterError2AdditionalY=0.1;GPU_rec_tpc.clusterError2AdditionalZ=0.15;GPU_rec_tpc.clustersShiftTimebinsClusterizer=35;" +# relax tolerances on tracking and selection cut to deal with very low laser intensities +RECO_CONFIG+="GPU_rec_tpc.trackFollowingMaxRowGap=15;GPU_rec_tpc.trackFollowingMaxRowGapSeed=15;GPU_rec_tpc.minTrackdEdxMax=8;GPU_rec_tpc.adddEdxSubThresholdClusters=0;" + +WORKFLOW= +add_W o2-dpl-raw-proxy "--dataspec \"$PROXY_INSPEC\" --inject-missing-data --channel-config \"name=readout-proxy,type=pull,method=connect,address=ipc://@tf-builder-pipe-0,transport=shmem,rateLogging=1\"" "" 0 +add_W o2-tpc-raw-to-digits-workflow "--ignore-grp --input-spec \"$CALIB_INSPEC\" --remove-duplicates --pipeline tpc-raw-to-digits-0:20 --send-ce-digits " "${RAWDIGIT_CONFIG}" +add_W o2-tpc-scaler-workflow "--disable-IDC-scalers --disable-ctp-lumi-request" +add_W o2-tpc-reco-workflow "--input-type digitizer --output-type \"tracks,disable-writer\" --disable-mc --pipeline tpc-zsEncoder:20,tpc-tracker:8 ${GPU_CONFIG} ${REMAP}" "${RECO_CONFIG}" +add_W o2-tpc-laser-track-filter "" "" 0 +add_W o2-tpc-calib-laser-tracks "--use-filtered-tracks ${EXTRA_CONFIG_TRACKS} --min-tfs=${min_tracks}" +add_W o2-tpc-calib-pad-raw " ${EXTRA_CONFIG}" "${CALIB_CONFIG}" +add_W o2-calibration-ccdb-populator-workflow "--ccdb-path ${CCDB_PATH}" "" 0 +add_QC_from_apricot "${QC_CONFIG_CONSUL}" "--local --host lcoalhost" + +WORKFLOW+="o2-dpl-run ${ARGS_ALL} ${GLOBALDPLOPT}" +if [ $WORKFLOWMODE == "print" ]; then + echo Workflow command: + echo $WORKFLOW | sed "s/| */|\n/g" +else + # Execute the command we have assembled + WORKFLOW+=" --$WORKFLOWMODE ${WORKFLOWMODE_FILE}" + eval $WORKFLOW +fi -o2-dpl-raw-proxy $ARGS_ALL \ - --dataspec "$PROXY_INSPEC" \ - --readout-proxy "--channel-config 'name=readout-proxy,type=pull,method=connect,address=ipc://@tf-builder-pipe-0,transport=shmem,rateLogging=1'" \ - | o2-tpc-raw-to-digits-workflow $ARGS_ALL ${LASER_DECODER_ADD}\ - --input-spec "$CALIB_INSPEC" \ - --configKeyValues "TPCDigitDump.NoiseThreshold=3;TPCDigitDump.LastTimeBin=600;$ARGS_ALL_CONFIG" \ - --pipeline tpc-raw-to-digits-0:20 \ - --remove-duplicates \ - --send-ce-digits \ - | o2-tpc-reco-workflow $ARGS_ALL \ - --input-type digitizer \ - --output-type "tracks,disable-writer" \ - --disable-mc \ - --pipeline tpc-zsEncoder:20,tpc-tracker:8 \ - $GPU_CONFIG \ - --condition-remap "file:///home/wiechula/processData/inputFilesTracking/triggeredLaser/=GLO/Config/GRPECS;file:///home/wiechula/processData/inputFilesTracking/triggeredLaser/=GLO/Config/GRPMagField;file:///home/wiechula/processData/inputFilesTracking/triggeredLaser=TPC/Calib/LaserTracks" \ - --configKeyValues "${ARGS_ALL_CONFIG};align-geom.mDetectors=none;GPU_global.deviceType=$GPUTYPE;GPU_proc.tpcIncreasedMinClustersPerRow=500000;GPU_proc.ignoreNonFatalGPUErrors=1;$GPU_CONFIG_KEY;GPU_global.tpcTriggeredMode=1;GPU_rec_tpc.clusterError2AdditionalY=0.1;GPU_rec_tpc.clusterError2AdditionalZ=0.15;GPU_rec_tpc.clustersShiftTimebinsClusterizer=35" \ - | o2-tpc-laser-track-filter $ARGS_ALL \ - | o2-tpc-calib-laser-tracks $ARGS_ALL --use-filtered-tracks ${EXTRA_CONFIG_TRACKS} --min-tfs=${min_tracks}\ - | o2-tpc-calib-pad-raw ${ARGS_ALL} \ - --configKeyValues ${CALIB_CONFIG} ${EXTRA_CONFIG} \ - | o2-calibration-ccdb-populator-workflow $ARGS_ALL \ - --ccdb-path ${CCDB_PATH} \ - | o2-qc ${ARGS_ALL} --config ${QC_CONFIG} --local --host ${HOST} \ - | o2-dpl-run ${ARGS_ALL} --dds ${WORKFLOWMODE_FILE} ${GLOBALDPLOPT} +#o2-dpl-raw-proxy $ARGS_ALL --inject-missing-data \ +# --dataspec "$PROXY_INSPEC" \ +# --readout-proxy "--channel-config 'name=readout-proxy,type=pull,method=connect,address=ipc://@tf-builder-pipe-0,transport=shmem,rateLogging=1'" \ +# | o2-tpc-raw-to-digits-workflow $ARGS_ALL ${LASER_DECODER_ADD}\ +# --input-spec "$CALIB_INSPEC" \ +# --configKeyValues ";$ARGS_ALL_CONFIG" \ +# --pipeline tpc-raw-to-digits-0:20 \ +# --remove-duplicates \ +# --send-ce-digits \ +# | o2-tpc-reco-workflow $ARGS_ALL \ +# --input-type digitizer \ +# --output-type "tracks,disable-writer" \ +# --disable-mc \ +# --pipeline tpc-zsEncoder:20,tpc-tracker:8 \ +# $GPU_CONFIG \ +# --condition-remap "file:///home/wiechula/processData/inputFilesTracking/triggeredLaser/=GLO/Config/GRPECS;file:///home/wiechula/processData/inputFilesTracking/triggeredLaser/=GLO/Config/GRPMagField;file:///home/wiechula/processData/inputFilesTracking/triggeredLaser=TPC/Calib/LaserTracks" \ +# --configKeyValues "${ARGS_ALL_CONFIG};align-geom.mDetectors=none;GPU_global.deviceType=$GPUTYPE;GPU_proc.tpcIncreasedMinClustersPerRow=500000;GPU_proc.ignoreNonFatalGPUErrors=1;$GPU_CONFIG_KEY;GPU_global.tpcTriggeredMode=1;GPU_rec_tpc.clusterError2AdditionalY=0.1;GPU_rec_tpc.clusterError2AdditionalZ=0.15;GPU_rec_tpc.clustersShiftTimebinsClusterizer=35" \ +# | o2-tpc-laser-track-filter $ARGS_ALL \ +# | o2-tpc-calib-laser-tracks $ARGS_ALL --use-filtered-tracks ${EXTRA_CONFIG_TRACKS} --min-tfs=${min_tracks}\ +# | o2-tpc-calib-pad-raw ${ARGS_ALL} \ +# --configKeyValues ${CALIB_CONFIG} ${EXTRA_CONFIG} \ +# | o2-calibration-ccdb-populator-workflow $ARGS_ALL \ +# --ccdb-path ${CCDB_PATH} \ +# | o2-qc ${ARGS_ALL} --config ${QC_CONFIG} --local --host ${HOST} \ +# | o2-dpl-run ${ARGS_ALL} --dds ${WORKFLOWMODE_FILE} ${GLOBALDPLOPT} diff --git a/DATA/production/calib/tpc-pedestal.sh b/DATA/production/calib/tpc-pedestal.sh index 31039db4f..22fbd4392 100755 --- a/DATA/production/calib/tpc-pedestal.sh +++ b/DATA/production/calib/tpc-pedestal.sh @@ -10,41 +10,53 @@ if [ $NUMAGPUIDS != 0 ]; then ARGS_ALL+=" --child-driver 'numactl --membind $NUMAID --cpunodebind $NUMAID'" fi -PROXY_INSPEC="A:TPC/RAWDATA;dd:FLP/DISTSUBTIMEFRAME/0;eos:***/INFORMATION" -CALIB_INSPEC="A:TPC/RAWDATA;dd:FLP/DISTSUBTIMEFRAME/0;eos:***/INFORMATION" - -CALIB_CONFIG="TPCCalibPedestal.LastTimeBin=12000;keyval.output_dir=/dev/null" +PROXY_INSPEC="A:TPC/RAWDATA;dd:FLP/DISTSUBTIMEFRAME/0" +CALIB_INSPEC="A:TPC/RAWDATA;dd:FLP/DISTSUBTIMEFRAME/0" CCDB_PATH="http://o2-ccdb.internal" HOST=localhost -QC_CONFIG="consul-json://alio2-cr1-hv-con01.cern.ch:8500/o2/components/qc/ANY/any/tpc-pedestal-calib-qcmn" +#QC_CONFIG="consul-json://alio2-cr1-hv-con01.cern.ch:8500/o2/components/qc/ANY/any/tpc-pedestal-calib-qcmn" +QC_CONFIG="components/qc/ANY/any/tpc-pedestal-calib-qcmn?run_type=${RUNTYPE:-}" +CALIB_CONFIG="TPCCalibPedestal.ADCMin=20" + +# ===| configuration from environment variables |=============================== +max_events=${TPC_CALIB_MAX_EVENTS:-50} +publish_after=${TPC_CALIB_PUBLISH_AFTER:-400} +sendToDCS=${TPC_CALIB_SEND_TO_DCS:-1} + +# ===| ccdb populator setup |=================================================== +# the production CCDB populator will accept subspecs in this range +CCDBPRO_SUBSPEC_MIN=0 +CCDBPRO_SUBSPEC_MAX=32767 +CCDBPATHPRO="http://o2-ccdb.internal" + +# the DCS CCDB populator will accept subspecs in this range +CCDBDCS_SUBSPEC_MIN=32768 +CCDBDCS_SUBSPEC_MAX=65535 +CCDBPATHDCS="$DCSCCDBSERVER_PERS" -max_events=50 -publish_after=400 +################################################################################################################################# -if [[ ! -z ${TPC_CALIB_MAX_EVENTS:-} ]]; then - max_events=${TPC_CALIB_MAX_EVENTS} -fi -if [[ ! -z ${TPC_CALIB_PUBLISH_AFTER:-} ]]; then - publish_after=${TPC_CALIB_PUBLISH_AFTER} +WORKFLOW= +add_W o2-dpl-raw-proxy "--dataspec \"$PROXY_INSPEC\" --inject-missing-data --channel-config \"name=readout-proxy,type=pull,method=connect,address=ipc://@tf-builder-pipe-0,transport=shmem,rateLogging=1\"" "" 0 +add_W o2-tpc-calib-pad-raw "--input-spec \"$CALIB_INSPEC\" --publish-after-tfs ${publish_after} --max-events ${max_events} --lanes 36 --send-to-dcs-ccdb $sendToDCS" "${CALIB_CONFIG}" +add_W o2-calibration-ccdb-populator-workflow "--ccdb-path \"$CCDBPATHPRO\" --sspec-min $CCDBPRO_SUBSPEC_MIN --sspec-max $CCDBPRO_SUBSPEC_MAX" "" 0 +if [[ $sendToDCS -eq 1 ]]; then + add_W o2-calibration-ccdb-populator-workflow "--ccdb-path \"$CCDBPATHDCS\" --sspec-min $CCDBDCS_SUBSPEC_MIN --sspec-max $CCDBDCS_SUBSPEC_MAX --name-extention dcs" "" 0 fi +add_QC_from_apricot "${QC_CONFIG}" "--local --host localhost" -EXTRA_CONFIG=" --publish-after-tfs ${publish_after} --max-events ${max_events} --lanes 36" +WORKFLOW+="o2-dpl-run ${ARGS_ALL} ${GLOBALDPLOPT}" +if [ $WORKFLOWMODE == "print" ]; then + echo Workflow command: + echo $WORKFLOW | sed "s/| */|\n/g" +else + # Execute the command we have assembled + WORKFLOW+=" --$WORKFLOWMODE ${WORKFLOWMODE_FILE}" + eval $WORKFLOW +fi -################################################################################################################################# - -o2-dpl-raw-proxy ${ARGS_ALL} \ - --dataspec "${PROXY_INSPEC}" \ - --readout-proxy '--channel-config "name=readout-proxy,type=pull,method=connect,address=ipc://@tf-builder-pipe-0,transport=shmem,rateLogging=1"' \ - | o2-tpc-calib-pad-raw ${ARGS_ALL} \ - --input-spec "${CALIB_INSPEC}" \ - --configKeyValues "${CALIB_CONFIG}" \ - ${EXTRA_CONFIG} \ - | o2-calibration-ccdb-populator-workflow ${ARGS_ALL} \ - --ccdb-path ${CCDB_PATH} \ - | o2-qc ${ARGS_ALL} --config ${QC_CONFIG} --local --host ${HOST} \ - | o2-dpl-run ${ARGS_ALL} --dds ${WORKFLOWMODE_FILE} ${GLOBALDPLOPT} diff --git a/DATA/production/calib/tpc-pulser-long.sh b/DATA/production/calib/tpc-pulser-long.sh index 91c9959b9..d233510a6 100755 --- a/DATA/production/calib/tpc-pulser-long.sh +++ b/DATA/production/calib/tpc-pulser-long.sh @@ -1,4 +1,3 @@ - #!/usr/bin/env bash source common/setenv.sh @@ -18,8 +17,8 @@ CCDB_PATH="http://o2-ccdb.internal" HOST=localhost -QC_CONFIG="consul-json://alio2-cr1-hv-con01.cern.ch:8500/o2/components/qc/ANY/any/tpc-raw-qcmn" - +#QC_CONFIG="consul-json://alio2-cr1-hv-con01.cern.ch:8500/o2/components/qc/ANY/any/tpc-raw-qcmn" +QC_CONFIG="components/qc/ANY/any/tpc-pulser-calib-qcmn?run_type=${RUNTYPE:-}" max_events=1000000 publish_after=200 @@ -35,15 +34,32 @@ EXTRA_CONFIG="--calib-type pulser --reset-after-publish --publish-after-tfs ${pu ################################################################################################################################# +WORKFLOW= +add_W o2-dpl-raw-proxy "--dataspec \"$PROXY_INSPEC\" --inject-missing-data --channel-config \"name=readout-proxy,type=pull,method=connect,address=ipc://@tf-builder-pipe-0,transport=shmem,rateLogging=1\"" "" 0 +add_W o2-tpc-calib-pad-raw "--input-spec \"$CALIB_INSPEC\" --calib-type pulser --reset-after-publish --publish-after-tfs ${publish_after} --max-events ${max_events} --lanes 36 --check-calib-infos" +add_W o2-calibration-ccdb-populator-workflow "--ccdb-path \"http://o2-ccdb.internal\" " "" 0 +add_QC_from_apricot "${QC_CONFIG}" "--local --host localhost" + +WORKFLOW+="o2-dpl-run ${ARGS_ALL} ${GLOBALDPLOPT}" + +if [ $WORKFLOWMODE == "print" ]; then + echo Workflow command: + echo $WORKFLOW | sed "s/| */|\n/g" +else + # Execute the command we have assembled + WORKFLOW+=" --$WORKFLOWMODE ${WORKFLOWMODE_FILE}" + eval $WORKFLOW +fi + -o2-dpl-raw-proxy ${ARGS_ALL} \ - --dataspec ${PROXY_INSPEC} \ - --readout-proxy '--channel-config "name=readout-proxy,type=pull,method=connect,address=ipc://@tf-builder-pipe-0,transport=shmem,rateLogging=1"' \ - | o2-tpc-calib-pad-raw ${ARGS_ALL} \ - --input-spec ${CALIB_INSPEC} \ - --configKeyValues "${CALIB_CONFIG}" \ - ${EXTRA_CONFIG} \ - | o2-calibration-ccdb-populator-workflow ${ARGS_ALL} \ - --ccdb-path ${CCDB_PATH} \ - | o2-qc ${ARGS_ALL} --config ${QC_CONFIG} --local --host ${HOST} \ - | o2-dpl-run ${ARGS_ALL} --dds ${WORKFLOWMODE_FILE} +#o2-dpl-raw-proxy ${ARGS_ALL} --inject-missing-data \ +# --dataspec ${PROXY_INSPEC} \ +# --readout-proxy '--channel-config "name=readout-proxy,type=pull,method=connect,address=ipc://@tf-builder-pipe-0,transport=shmem,rateLogging=1"' \ +# | o2-tpc-calib-pad-raw ${ARGS_ALL} \ +# --input-spec ${CALIB_INSPEC} \ +# --configKeyValues "${CALIB_CONFIG}" \ +# ${EXTRA_CONFIG} \ +# | o2-calibration-ccdb-populator-workflow ${ARGS_ALL} \ +# --ccdb-path ${CCDB_PATH} \ +# | o2-qc ${ARGS_ALL} --config ${QC_CONFIG} --local --host ${HOST} \ +# | o2-dpl-run ${ARGS_ALL} --dds ${WORKFLOWMODE_FILE} diff --git a/DATA/production/calib/tpc-pulser.sh b/DATA/production/calib/tpc-pulser.sh index 9541e42ca..7800c0c81 100755 --- a/DATA/production/calib/tpc-pulser.sh +++ b/DATA/production/calib/tpc-pulser.sh @@ -1,4 +1,3 @@ - #!/usr/bin/env bash source common/setenv.sh @@ -11,8 +10,8 @@ if [ $NUMAGPUIDS != 0 ]; then ARGS_ALL+=" --child-driver 'numactl --membind $NUMAID --cpunodebind $NUMAID'" fi -PROXY_INSPEC="A:TPC/RAWDATA;dd:FLP/DISTSUBTIMEFRAME/0;eos:***/INFORMATION" -CALIB_INSPEC="A:TPC/RAWDATA;dd:FLP/DISTSUBTIMEFRAME/0;eos:***/INFORMATION" +PROXY_INSPEC="A:TPC/RAWDATA;dd:FLP/DISTSUBTIMEFRAME/0" +CALIB_INSPEC="A:TPC/RAWDATA;dd:FLP/DISTSUBTIMEFRAME/0" CALIB_CONFIG="TPCCalibPulser.FirstTimeBin=80;TPCCalibPulser.LastTimeBin=260;TPCCalibPulser.NbinsQtot=250;TPCCalibPulser.XminQtot=10;TPCCalibPulser.XmaxQtot=510;TPCCalibPulser.NbinsWidth=100;TPCCalibPulser.XminWidth=0.3;TPCCalibPulser.XmaxWidth=0.7;TPCCalibPulser.MinimumQtot=30;TPCCalibPulser.MinimumQmax=25;TPCCalibPulser.XminT0=125;TPCCalibPulser.XmaxT0=145;TPCCalibPulser.NbinsT0=800;keyval.output_dir=/dev/null" @@ -20,7 +19,7 @@ CCDB_PATH="http://o2-ccdb.internal" HOST=localhost -QC_CONFIG="consul-json://alio2-cr1-hv-con01.cern.ch:8500/o2/components/qc/ANY/any/tpc-pulser-calib-qcmn" +QC_CONFIG="components/qc/ANY/any/tpc-pulser-calib-qcmn?run_type=${RUNTYPE:-}" max_events=200 publish_after=230 @@ -33,19 +32,22 @@ if [[ ! -z ${TPC_CALIB_PUBLISH_AFTER:-} ]]; then publish_after=${TPC_CALIB_PUBLISH_AFTER} fi -EXTRA_CONFIG="--calib-type pulser --publish-after-tfs ${publish_after} --max-events ${max_events} --lanes 36 --check-calib-infos" - ################################################################################################################################# -o2-dpl-raw-proxy ${ARGS_ALL} \ - --dataspec "${PROXY_INSPEC}" \ - --readout-proxy '--channel-config "name=readout-proxy,type=pull,method=connect,address=ipc://@tf-builder-pipe-0,transport=shmem,rateLogging=1"' \ - | o2-tpc-calib-pad-raw ${ARGS_ALL} \ - --input-spec "${CALIB_INSPEC}" \ - --configKeyValues "${CALIB_CONFIG}" \ - ${EXTRA_CONFIG} \ - | o2-calibration-ccdb-populator-workflow ${ARGS_ALL} \ - --ccdb-path ${CCDB_PATH} \ - | o2-qc ${ARGS_ALL} --config ${QC_CONFIG} --local --host ${HOST} \ - | o2-dpl-run ${ARGS_ALL} --dds ${WORKFLOWMODE_FILE} ${GLOBALDPLOPT} +WORKFLOW= +add_W o2-dpl-raw-proxy "--dataspec \"$PROXY_INSPEC\" --inject-missing-data --channel-config \"name=readout-proxy,type=pull,method=connect,address=ipc://@tf-builder-pipe-0,transport=shmem,rateLogging=1\"" "" 0 +add_W o2-tpc-calib-pad-raw "--input-spec \"$CALIB_INSPEC\" --calib-type pulser --publish-after-tfs ${publish_after} --max-events ${max_events} --lanes 36 --check-calib-infos" "${CALIB_CONFIG}" +add_W o2-calibration-ccdb-populator-workflow "--ccdb-path \"http://o2-ccdb.internal\" " "" 0 +add_QC_from_apricot "${QC_CONFIG}" "--local --host localhost" + +WORKFLOW+="o2-dpl-run ${ARGS_ALL} ${GLOBALDPLOPT}" + +if [ $WORKFLOWMODE == "print" ]; then + echo Workflow command: + echo $WORKFLOW | sed "s/| */|\n/g" +else + # Execute the command we have assembled + WORKFLOW+=" --$WORKFLOWMODE ${WORKFLOWMODE_FILE}" + eval $WORKFLOW +fi \ No newline at end of file diff --git a/DATA/production/common/getStat.sh b/DATA/production/common/getStat.sh index 5ae1bf9de..00c5a9c52 100755 --- a/DATA/production/common/getStat.sh +++ b/DATA/production/common/getStat.sh @@ -12,8 +12,9 @@ nCTFsFilesFailed=`grep "FileFetcher: non-zero exit code [0-9]*" $1 | wc -l` nCTFsProcessed=`grep "Read CTF" $1 | tail -1 | sed 's/^.*Read CTF \([0-9]*\).*$/\1/'` nCTFsProcessed=$((nCTFsProcessed + 1)) -if [[ $nCTFsFilesInspected != $((nCTFsFilesFailed + nCTFsFilesOK)) ]]; then +if [[ $nCTFsFilesInspected -gt 0 && $nCTFsFilesInspected != $((nCTFsFilesFailed + nCTFsFilesOK)) ]]; then echo "Something went wrong with parsing the log file: CTF files inspected ($nCTFsFilesInspected) is not the sum of those successfully processed ($nCTFsFilesOK) and those that failed ($nCTFsFilesFailed)" + exit 8 fi while read -r line; do currentPVs=`echo $line | sed 's/^.*Found \([0-9]*\) PVs.*/\1/'` diff --git a/DATA/production/common/readAO2Ds.C b/DATA/production/common/readAO2Ds.C index 7b5c9c84b..3a25e4342 100644 --- a/DATA/production/common/readAO2Ds.C +++ b/DATA/production/common/readAO2Ds.C @@ -35,7 +35,18 @@ int readAO2Ds(const char* filename = "AO2D.root") std::cout << onameKeyInDir.Data() << std::endl; } TTree* t = (TTree*)d->Get(onameKeyInDir.Data()); - if (onameKeyInDir.BeginsWith("O2track") && !onameKeyInDir.Contains("O2tracked")) { + // Check that every branch has the same number of entries as the tree itself. + // A mismatch indicates a corrupted or truncated write. + long long treeEntries = t->GetEntries(); + for (auto* b : *t->GetListOfBranches()) { + auto* br = (TBranch*)b; + if (br->GetEntries() != treeEntries) { + printf("MISMATCH %s/%s branch %s: tree=%lld branch=%lld\n", + onameKeyInFile.Data(), onameKeyInDir.Data(), br->GetName(), treeEntries, br->GetEntries()); + retCode |= 4; + } + } + if (onameKeyInDir.BeginsWith("O2track") && !onameKeyInDir.Contains("O2tracked") && !onameKeyInDir.Contains("O2trackqa")) { vectNEntriesPerTree.push_back({onameKeyInDir.Data(), t->GetEntries()}); } } diff --git a/DATA/production/common/setVarsFromALIEN_PROC_ID.sh b/DATA/production/common/setVarsFromALIEN_PROC_ID.sh index 3dab53050..e94c922e1 100644 --- a/DATA/production/common/setVarsFromALIEN_PROC_ID.sh +++ b/DATA/production/common/setVarsFromALIEN_PROC_ID.sh @@ -3,18 +3,30 @@ # Script to set a couple of variables that depend on ALIEN_PROC_ID. # Since several scripts might need this, it is in a separate script. -# let's get the last 10 (for INT) and 5 (for int16) digits of ALIEN_PROC_ID, to be passed to define NUMAID and shm-segment-id via O2JOBID, which are int and int16 respectively. Then we make them an int or int16 -ALIEN_PROC_ID_MAX_NDIGITS_INT32=10 -ALIEN_PROC_ID_MAX_NDIGITS_INT16=9 -ALIEN_PROC_ID_LENTGH=`echo $ALIEN_PROC_ID | wc -l` -ALIEN_PROC_ID_OFFSET_INT32=$((ALIEN_PROC_ID_LENTGH - ALIEN_PROC_ID_MAX_NDIGITS_INT32)) -ALIEN_PROC_ID_OFFSET_INT16=$((ALIEN_PROC_ID_LENTGH - ALIEN_PROC_ID_MAX_NDIGITS_INT16)) -# lets's take the last 10 (int) or 5 (int16) digits -ALIEN_PROC_ID_OFFSET_INT32=${ALIEN_PROC_ID:$((ALIEN_PROC_ID_OFFSET_INT32))} -ALIEN_PROC_ID_OFFSET_INT16=${ALIEN_PROC_ID:$((ALIEN_PROC_ID_OFFSET_INT16))} +# let's get the last 9 (for int) and 8 (for int16) digits of ALIEN_PROC_ID, to be passed to define NUMAID and shm-segment-id via O2JOBID, which are int and int16 respectively. Then we make them an int or int16 +ALIEN_PROC_ID_MAX_NDIGITS_INT32=9 +ALIEN_PROC_ID_MAX_NDIGITS_INT16=8 +if [[ -n $ALIEN_PROC_ID ]]; then + echo "ALIEN_PROC_ID for current job = ${ALIEN_PROC_ID}" +else + ALIEN_PROC_ID=123456789 + echo "ALIEN_PROC_ID was not defined for current job, setting to ${ALIEN_PROC_ID}" +fi + +if [[ -n ${ALIEN_JDL_PACKAGES} ]] && [[ ${#ALIEN_PROC_ID} -lt ${ALIEN_PROC_ID_MAX_NDIGITS_INT32} ]]; then # we are on the grid, and we expect to have the PROC_ID + echo "We cannot determine O2JOBID, the job id is too short (${ALIEN_PROC_ID}), we need at least ${ALIEN_PROC_ID_MAX_NDIGITS_INT32} digits, returning error" + exit 2 +fi + +ALIEN_PROC_ID_OFFSET_INT32=$((10#${ALIEN_PROC_ID: -${ALIEN_PROC_ID_MAX_NDIGITS_INT32}})) +echo "ALIEN_PROC_ID_OFFSET_INT32 = $ALIEN_PROC_ID_OFFSET_INT32" + +ALIEN_PROC_ID_OFFSET_INT16=$((10#${ALIEN_PROC_ID: -${ALIEN_PROC_ID_MAX_NDIGITS_INT16}})) +echo "ALIEN_PROC_ID_OFFSET_INT16 = $ALIEN_PROC_ID_OFFSET_INT16" + # let's make them int32 or int16, but not with the max possible value (which would be 0x7fffffff and 0x7fff respectively) # but a bit less, to allow to add [0, 15] on top afterwards if needed (e.g. we usually add # the NUMAID), see https://github.com/AliceO2Group/O2DPG/pull/993#pullrequestreview-1393401475 -export O2JOBID=$((ALIEN_PROC_ID_OFFSET_INT32 & 0x7ffffff0)) -export O2JOBSHMID=$((ALIEN_PROC_ID_OFFSET_INT16 & 0x7ff0)) -echo "ALIEN_PROC_ID = $ALIEN_PROC_ID, we will set O2JOBID = $O2JOBID, SHMEMID = $SHMEMID" +export O2JOBID=$(((ALIEN_PROC_ID_OFFSET_INT32 & 0x7ffffff) * 16)) +export O2JOBSHMID=$(((ALIEN_PROC_ID_OFFSET_INT16 & 0x7ff) * 16)) +echo "ALIEN_PROC_ID = $ALIEN_PROC_ID, we will set O2JOBID = $O2JOBID, SHMEMID = $O2JOBSHMID" diff --git a/DATA/production/configurations/2021/OCT/apass4/setenv_extra.sh b/DATA/production/configurations/2021/OCT/apass4/setenv_extra.sh index 45c4885ee..b6c1fdf96 100644 --- a/DATA/production/configurations/2021/OCT/apass4/setenv_extra.sh +++ b/DATA/production/configurations/2021/OCT/apass4/setenv_extra.sh @@ -65,7 +65,7 @@ export ITSTPCMATCH="tpcitsMatch.maxVDriftUncertainty=0.2;tpcitsMatch.safeMarginT export CONFIG_EXTRA_PROCESS_o2_tpcits_match_workflow="$VDRIFT;$ITSEXTRAERR;$ITSTPCMATCH" # ad-hoc settings for TOF matching -export ARGS_EXTRA_PROCESS_o2_tof_matcher_workflow="--output-type matching-info,calib-info --enable-dia --use-fit" +export ARGS_EXTRA_PROCESS_o2_tof_matcher_workflow="--output-type matching-info,calib-info --enable-dia" export CONFIG_EXTRA_PROCESS_o2_tof_matcher_workflow="$VDRIFT;$ITSEXTRAERR" # ad-hoc settings for TRD matching diff --git a/DATA/production/configurations/2021/OCT/apass5/setenv_extra.sh b/DATA/production/configurations/2021/OCT/apass5/setenv_extra.sh index 45c4885ee..b6c1fdf96 100644 --- a/DATA/production/configurations/2021/OCT/apass5/setenv_extra.sh +++ b/DATA/production/configurations/2021/OCT/apass5/setenv_extra.sh @@ -65,7 +65,7 @@ export ITSTPCMATCH="tpcitsMatch.maxVDriftUncertainty=0.2;tpcitsMatch.safeMarginT export CONFIG_EXTRA_PROCESS_o2_tpcits_match_workflow="$VDRIFT;$ITSEXTRAERR;$ITSTPCMATCH" # ad-hoc settings for TOF matching -export ARGS_EXTRA_PROCESS_o2_tof_matcher_workflow="--output-type matching-info,calib-info --enable-dia --use-fit" +export ARGS_EXTRA_PROCESS_o2_tof_matcher_workflow="--output-type matching-info,calib-info --enable-dia" export CONFIG_EXTRA_PROCESS_o2_tof_matcher_workflow="$VDRIFT;$ITSEXTRAERR" # ad-hoc settings for TRD matching diff --git a/DATA/production/configurations/2021/ctf_recreation/ctf_recreation.sh b/DATA/production/configurations/2021/ctf_recreation/ctf_recreation.sh index bbf361c95..be5085e9f 100755 --- a/DATA/production/configurations/2021/ctf_recreation/ctf_recreation.sh +++ b/DATA/production/configurations/2021/ctf_recreation/ctf_recreation.sh @@ -72,7 +72,7 @@ fi # if "$DETCONFIG" is set explicitly, this has the second highest priority # last option is to have it from the JDL if [[ -z "$DETCONFIG" ]]; then - if [[ -z "ALIEN_JDL_DETCONFIG" ]]; then + if [[ -z "$ALIEN_JDL_DETCONFIG" ]]; then echo "nothing set the detector configuration to use, exiting" exit 4 else diff --git a/DATA/production/configurations/2022/LHC22f/apass1/async_pass.sh b/DATA/production/configurations/2022/LHC22f/apass1/async_pass.sh index 3dc46a02c..ce764852f 100755 --- a/DATA/production/configurations/2022/LHC22f/apass1/async_pass.sh +++ b/DATA/production/configurations/2022/LHC22f/apass1/async_pass.sh @@ -504,7 +504,7 @@ else echo "nCTFsFilesInspected_step1 = $nCTFsFilesInspected_step1, nCTFsFilesInspected_step2 = $nCTFsFilesInspected_step2" > validation_error.message echo "nCTFsFilesOK_step1 = $nCTFsFilesOK_step1, nCTFsFilesOK_step2 = $nCTFsFilesOK_step2" > validation_error.message echo "nCTFsProcessed_step1 = $nCTFsProcessed_step1, nCTFsProcessed_step2 = $nCTFsProcessed_step2" > validation_error.message - exit 1000 + exit 255 fi fi fi @@ -663,7 +663,7 @@ if [[ $ALIEN_JDL_AODOFF != 1 ]]; then CURRENT_POOL_SIZE=`jobs -r | wc -l` done < $JOB_LIST # collecting return codes of the merging processes - for i in ${!arr[@]}; do + for i in "${!arr[@]}"; do wait ${arr[$i]} exitcode=$? if [[ $exitcode -ne 0 ]]; then diff --git a/DATA/production/configurations/2022/LHC22f/apass1/setenv_extra.sh b/DATA/production/configurations/2022/LHC22f/apass1/setenv_extra.sh index ca64edd66..e814f833a 100644 --- a/DATA/production/configurations/2022/LHC22f/apass1/setenv_extra.sh +++ b/DATA/production/configurations/2022/LHC22f/apass1/setenv_extra.sh @@ -72,6 +72,7 @@ fi CTP_BC_SHIFT=0 if [[ $ALIEN_JDL_LPMANCHORYEAR == "2022" ]]; then CTP_BC_SHIFT=-294 +fi if [[ $RUNNUMBER -ge 538923 ]] && [[ $RUNNUMBER -le 539700 ]]; then # 3 BC offset (future direction) in CTP data observed for LHC23zd - LHC23zs CTP_BC_SHIFT=-3 @@ -349,7 +350,7 @@ export CONFIG_EXTRA_PROCESS_o2_tpcits_match_workflow+=";$ITSEXTRAERR;$ITSTPCMATC has_detector FT0 && export ARGS_EXTRA_PROCESS_o2_tpcits_match_workflow="$ARGS_EXTRA_PROCESS_o2_tpcits_match_workflow --use-ft0" # ad-hoc settings for TOF matching -export ARGS_EXTRA_PROCESS_o2_tof_matcher_workflow="$ARGS_EXTRA_PROCESS_o2_tof_matcher_workflow --output-type matching-info,calib-info --enable-dia --use-fit" +export ARGS_EXTRA_PROCESS_o2_tof_matcher_workflow="$ARGS_EXTRA_PROCESS_o2_tof_matcher_workflow --output-type matching-info,calib-info --enable-dia" export CONFIG_EXTRA_PROCESS_o2_tof_matcher_workflow+=";$ITSEXTRAERR;$VDRIFTPARAMOPTION;$TRACKTUNETPCINNER;" # ad-hoc settings for TRD matching diff --git a/DATA/production/configurations/2022/MayJunePilotBeam/apass1/setenv_extra.sh b/DATA/production/configurations/2022/MayJunePilotBeam/apass1/setenv_extra.sh index c70f06a51..42edfbcf5 100644 --- a/DATA/production/configurations/2022/MayJunePilotBeam/apass1/setenv_extra.sh +++ b/DATA/production/configurations/2022/MayJunePilotBeam/apass1/setenv_extra.sh @@ -95,7 +95,7 @@ export ITSTPCMATCH="tpcitsMatch.maxVDriftUncertainty=0.2;tpcitsMatch.safeMarginT export CONFIG_EXTRA_PROCESS_o2_tpcits_match_workflow="TPCGasParam.DriftV=$VDRIFT;$ITSEXTRAERR;$ITSTPCMATCH" # ad-hoc settings for TOF matching -export ARGS_EXTRA_PROCESS_o2_tof_matcher_workflow="--output-type matching-info,calib-info --enable-dia --use-fit" +export ARGS_EXTRA_PROCESS_o2_tof_matcher_workflow="--output-type matching-info,calib-info --enable-dia" export CONFIG_EXTRA_PROCESS_o2_tof_matcher_workflow="TPCGasParam.DriftV=$VDRIFT;$ITSEXTRAERR" # ad-hoc settings for TRD matching diff --git a/DATA/production/configurations/2022/MayJunePilotBeam/apass2/setenv_extra.sh b/DATA/production/configurations/2022/MayJunePilotBeam/apass2/setenv_extra.sh index b2c79141f..095511c42 100644 --- a/DATA/production/configurations/2022/MayJunePilotBeam/apass2/setenv_extra.sh +++ b/DATA/production/configurations/2022/MayJunePilotBeam/apass2/setenv_extra.sh @@ -111,7 +111,7 @@ if [[ $WORKFLOW_DETECTORS =~ (^|,)"FT0"(,|$) ]] ; then fi # ad-hoc settings for TOF matching -export ARGS_EXTRA_PROCESS_o2_tof_matcher_workflow="--output-type matching-info,calib-info --enable-dia --use-fit" +export ARGS_EXTRA_PROCESS_o2_tof_matcher_workflow="--output-type matching-info,calib-info --enable-dia" export CONFIG_EXTRA_PROCESS_o2_tof_matcher_workflow="$ITSEXTRAERR;$VDRIFTPARAMOPTION;" # ad-hoc settings for TRD matching diff --git a/DATA/production/configurations/2022/extractCalib/setenv_extra.sh b/DATA/production/configurations/2022/extractCalib/setenv_extra.sh index a4fac7e44..10bf1543c 100644 --- a/DATA/production/configurations/2022/extractCalib/setenv_extra.sh +++ b/DATA/production/configurations/2022/extractCalib/setenv_extra.sh @@ -69,7 +69,7 @@ if [[ $WORKFLOW_DETECTORS =~ (^|,)"FT0"(,|$) ]] ; then fi # ad-hoc settings for TOF matching -export ARGS_EXTRA_PROCESS_o2_tof_matcher_workflow="--output-type matching-info,calib-info --enable-dia --use-fit" +export ARGS_EXTRA_PROCESS_o2_tof_matcher_workflow="--output-type matching-info,calib-info --enable-dia" export CONFIG_EXTRA_PROCESS_o2_tof_matcher_workflow="$ITSEXTRAERR;$VDRIFTPARAMOPTION;" # ad-hoc settings for TRD matching diff --git a/DATA/production/configurations/CTFSkimming/ctf-skim-workflow.sh b/DATA/production/configurations/CTFSkimming/ctf-skim-workflow.sh index 57df502b6..8ede2eff4 100755 --- a/DATA/production/configurations/CTFSkimming/ctf-skim-workflow.sh +++ b/DATA/production/configurations/CTFSkimming/ctf-skim-workflow.sh @@ -3,6 +3,22 @@ # Non-zero exit code already if one command in a pipe fails set -o pipefail +if [[ -z "$NHBPERTF" ]]; then # try to extract from JDL variables + NHBPERTF=32 + if [[ ! -z "$ALIEN_JDL_NHBPERTF" ]] && [[ "$ALIEN_JDL_NHBPERTF" != 0 ]]; then + NHBPERTF=$ALIEN_JDL_NHBPERTF + echo "Set NHBF per TF to $NHBPERTF from ALIEN_JDL_NHBPERTF" + elif [[ ! -z "$ALIEN_JDL_LPMRUNNUMBER" ]]; then + [[ "$ALIEN_JDL_LPMRUNNUMBER" -lt 534125 ]] && NHBPERTF=128 || NHBPERTF=32 + echo "Set NHBF per TF to $NHBPERTF from ALIEN_JDL_LPMRUNNUMBER=$ALIEN_JDL_LPMRUNNUMBER" + else + echo "Set NHBF per TF to $NHBPERTF as default" + fi +else + echo "Set NHBF per TF to $NHBPERTF as requested explicitly" +fi +export NHBPERTF + # --------------------------------------------------------------------------------------------------------------------- # Get this script's directory and load common settings (use zsh first (e.g. on Mac) and fallback on `readlink -f` if zsh is not there) : ${GEN_TOPO_MYDIR:="$O2DPG_ROOT/DATA/production"} @@ -26,6 +42,7 @@ if [[ ! -z "$ALIEN_JDL_SHMSIZE" ]]; then export SHMSIZE=$ALIEN_JDL_SHMSIZE; fi if [[ ! -z "$ALIEN_JDL_MULTIPLICITYPROCESSTPCENTROPYDECODER" ]]; then export MULTIPLICITY_PROCESS_tpc_entropy_decoder=$ALIEN_JDL_MULTIPLICITYPROCESSTPCENTROPYDECODER; fi if [[ ! -z "$ALIEN_JDL_MULTIPLICITYPROCESSTPCENTROPYENCODER" ]]; then export MULTIPLICITY_PROCESS_tpc_entropy_encoder=$ALIEN_JDL_MULTIPLICITYPROCESSTPCENTROPYENCODER; fi + # Set general arguments source $GEN_TOPO_MYDIR/getCommonArgs.sh || { echo "getCommonArgs.sh failed" 1>&2 && exit 1; } @@ -43,6 +60,10 @@ source $O2JOBIDscript { source $O2DPG_ROOT/DATA/production/workflow-multiplicities.sh; [[ $? != 0 ]] && echo "workflow-multiplicities.sh failed" 1>&2 && exit 1; } +# entropy encoding/decoding mode, '' is equivalent to '--ans-version compat' (compatible with < 09/2023 data), +# use '--ans-version 1.0 --ctf-dict none' for the new per-TF dictionary mode +: ${RANS_OPT:="--ans-version 1.0 --ctf-dict none"} + WORKFLOW= # Make sure we start with an empty workflow POSITIONAL=() @@ -68,7 +89,7 @@ done if [[ -z ${IRFRAMES:-} ]] || [[ -z ${CTFLIST:-} ]] ; then echo "Format: ${0##*/} -f -c " - exit 1 + exit 1 fi [[ "0${ALLOW_MISSING_DET:-}" == "00" ]] && ALLOW_MISSING_DET= || ALLOW_MISSING_DET="--allow-missing-detectors" @@ -82,29 +103,29 @@ if [[ -z ${NO_ITSMFT_MASKING:-} ]] ; then has_detector_ctf MFT && export ARGS_EXTRA_PROCESS_o2_ctf_reader_workflow="$ARGS_EXTRA_PROCESS_o2_ctf_reader_workflow --mft-digits" fi -add_W o2-ctf-reader-workflow "--ctf-data-subspec 1 --ir-frames-files $IRFRAMES $SKIP_SKIMMED_OUT_TF --ctf-input $CTFLIST ${INPUT_FILE_COPY_CMD+--copy-cmd} ${INPUT_FILE_COPY_CMD} --onlyDet $WORKFLOW_DETECTORS $ALLOW_MISSING_DET --pipeline $(get_N tpc-entropy-decoder TPC REST 1 TPCENTDEC)" +add_W o2-ctf-reader-workflow "--ctf-data-subspec 1 --ir-frames-files $IRFRAMES $SKIP_SKIMMED_OUT_TF --ctf-input $CTFLIST ${INPUT_FILE_COPY_CMD+--copy-cmd} ${INPUT_FILE_COPY_CMD} --onlyDet $WORKFLOW_DETECTORS $ALLOW_MISSING_DET --pipeline $(get_N tpc-entropy-decoder TPC REST 1 TPCENTDEC)" "HBFUtils.nHBFPerTF=$NHBPERTF" if [[ -z ${NO_ITSMFT_MASKING:-} ]] ; then has_detector_ctf ITS && add_W o2-its-reco-workflow "--digits-from-upstream --disable-mc --disable-tracking --disable-root-output --pipeline $(get_N its-tracker ITS REST 1 ITSTRK)" "ITSClustererParam.maxBCDiffToMaskBias=10;" - has_detector_ctf MFT && add_W o2-mft-reco-workflow "--digits-from-upstream --disable-mc --disable-tracking --disable-root-output --pipeline $(get_N mft-tracker MFT REST 1 MFTTRK)" "MFTClustererParam.maxBCDiffToMaskBias=10;" + has_detector_ctf MFT && add_W o2-mft-reco-workflow "--digits-from-upstream --disable-mc --disable-tracking --disable-root-output --pipeline $(get_N mft-tracker MFT REST 1 MFTTRK)" "MFTClustererParam.maxBCDiffToMaskBias=10;" fi -has_detector_ctf ITS && add_W o2-itsmft-entropy-encoder-workflow "--select-ir-frames --irframe-margin-bwd ${ITS_MARGIN_BWD:-$DEF_MARGIN_BWD} --irframe-margin-fwd ${ITS_MARGIN_FWD:-$DEF_MARGIN_FWD} --mem-factor ${ITS_ENC_MEMFACT:-1.5} --pipeline $(get_N its-entropy-encoder ITS CTF 1)" -has_detector_ctf MFT && add_W o2-itsmft-entropy-encoder-workflow "--select-ir-frames --irframe-margin-bwd ${MFT_MARGIN_BWD:-$DEF_MARGIN_BWD} --irframe-margin-fwd ${MFT_MARGIN_FWD:-$DEF_MARGIN_FWD} --mem-factor ${MFT_ENC_MEMFACT:-1.5} --runmft true --pipeline $(get_N mft-entropy-encoder MFT CTF 1)" -has_detector_ctf TPC && add_W o2-tpc-reco-workflow "--select-ir-frames --irframe-margin-bwd ${TPC_MARGIN_BWD:-$DEF_MARGIN_BWD} --irframe-margin-fwd ${TPC_MARGIN_FWD:-$DEF_MARGIN_FWD} --mem-factor ${TPC_ENC_MEMFACT:-1.} --input-type compressed-clusters-flat --output-type encoded-clusters,disable-writer --pipeline $(get_N tpc-entropy-encoder TPC CTF 1 TPCENT)" -has_detector_ctf TRD && add_W o2-trd-entropy-encoder-workflow "--select-ir-frames --irframe-margin-bwd ${TRD_MARGIN_BWD:-$DEF_MARGIN_BWD} --irframe-margin-fwd ${TRD_MARGIN_FWD:-$DEF_MARGIN_FWD} --mem-factor ${TRD_ENC_MEMFACT:-1.5} --pipeline $(get_N trd-entropy-encoder TRD CTF 1 TRDENT)" -has_detector_ctf TOF && add_W o2-tof-entropy-encoder-workflow "--select-ir-frames --irframe-margin-bwd ${TOF_MARGIN_BWD:-$DEF_MARGIN_BWD} --irframe-margin-fwd ${TOF_MARGIN_FWD:-$DEF_MARGIN_FWD} --mem-factor ${TOF_ENC_MEMFACT:-1.5} --pipeline $(get_N tof-entropy-encoder TOF CTF 1)" -has_detector_ctf FT0 && add_W o2-ft0-entropy-encoder-workflow "--select-ir-frames --irframe-margin-bwd ${FT0_MARGIN_BWD:-$DEF_MARGIN_BWD} --irframe-margin-fwd ${FT0_MARGIN_FWD:-$DEF_MARGIN_FWD} --mem-factor ${FT0_ENC_MEMFACT:-1.5} --pipeline $(get_N ft0-entropy-encoder FT0 CTF 1)" -has_detector_ctf FV0 && add_W o2-fv0-entropy-encoder-workflow "--select-ir-frames --irframe-margin-bwd ${FV0_MARGIN_BWD:-$DEF_MARGIN_BWD} --irframe-margin-fwd ${FV0_MARGIN_FWD:-$DEF_MARGIN_FWD} --mem-factor ${FV0_ENC_MEMFACT:-1.5} --pipeline $(get_N fv0-entropy-encoder FV0 CTF 1)" -has_detector_ctf FDD && add_W o2-fdd-entropy-encoder-workflow "--select-ir-frames --irframe-margin-bwd ${FDD_MARGIN_BWD:-$DEF_MARGIN_BWD} --irframe-margin-fwd ${FDD_MARGIN_FWD:-$DEF_MARGIN_FWD} --mem-factor ${FDD_ENC_MEMFACT:-1.5} --pipeline $(get_N fdd-entropy-encoder FDD CTF 1)" -has_detector_ctf MID && add_W o2-mid-entropy-encoder-workflow "--select-ir-frames --irframe-margin-bwd ${MID_MARGIN_BWD:-$DEF_MARGIN_BWD} --irframe-margin-fwd ${MID_MARGIN_FWD:-$DEF_MARGIN_FWD} --mem-factor ${MID_ENC_MEMFACT:-1.5} --pipeline $(get_N mid-entropy-encoder MID CTF 1)" -has_detector_ctf MCH && add_W o2-mch-entropy-encoder-workflow "--select-ir-frames --irframe-margin-bwd ${MCH_MARGIN_BWD:-$DEF_MARGIN_BWD} --irframe-margin-fwd ${MCH_MARGIN_FWD:-$DEF_MARGIN_FWD} --mem-factor ${MCH_ENC_MEMFACT:-1.5} --pipeline $(get_N mch-entropy-encoder MCH CTF 1)" -has_detector_ctf PHS && add_W o2-phos-entropy-encoder-workflow "--select-ir-frames --irframe-margin-bwd ${PHS_MARGIN_BWD:-$DEF_MARGIN_BWD} --irframe-margin-fwd ${PHS_MARGIN_FWD:-$DEF_MARGIN_FWD} --mem-factor ${PHS_ENC_MEMFACT:-1.5} --pipeline $(get_N phos-entropy-encoder PHS CTF 1)" -has_detector_ctf CPV && add_W o2-cpv-entropy-encoder-workflow "--select-ir-frames --irframe-margin-bwd ${CPV_MARGIN_BWD:-$DEF_MARGIN_BWD} --irframe-margin-fwd ${CPV_MARGIN_FWD:-$DEF_MARGIN_FWD} --mem-factor ${CPV_ENC_MEMFACT:-1.5} --pipeline $(get_N cpv-entropy-encoder CPV CTF 1)" -has_detector_ctf EMC && add_W o2-emcal-entropy-encoder-workflow "--select-ir-frames --irframe-margin-bwd ${EMC_MARGIN_BWD:-$DEF_MARGIN_BWD} --irframe-margin-fwd ${EMC_MARGIN_FWD:-$DEF_MARGIN_FWD} --mem-factor ${EMC_ENC_MEMFACT:-1.5} --pipeline $(get_N emcal-entropy-encoder EMC CTF 1)" -has_detector_ctf ZDC && add_W o2-zdc-entropy-encoder-workflow "--select-ir-frames --irframe-margin-bwd ${ZDC_MARGIN_BWD:-$DEF_MARGIN_BWD} --irframe-margin-fwd ${ZDC_MARGIN_FWD:-$DEF_MARGIN_FWD} --mem-factor ${ZDC_ENC_MEMFACT:-1.5} --pipeline $(get_N zdc-entropy-encoder ZDC CTF 1)" -has_detector_ctf HMP && add_W o2-hmpid-entropy-encoder-workflow "--select-ir-frames --irframe-margin-bwd ${HMP_MARGIN_BWD:-$DEF_MARGIN_BWD} --irframe-margin-fwd ${HMP_MARGIN_FWD:-$DEF_MARGIN_FWD} --mem-factor ${HMP_ENC_MEMFACT:-1.5} --pipeline $(get_N hmpid-entropy-encoder HMP CTF 1)" -has_detector_ctf CTP && add_W o2-ctp-entropy-encoder-workflow "--select-ir-frames --irframe-margin-bwd ${CTP_MARGIN_BWD:-$DEF_MARGIN_BWD} --irframe-margin-fwd ${CTP_MARGIN_FWD:-$DEF_MARGIN_FWD} --mem-factor ${CTP_ENC_MEMFACT:-1.5} --pipeline $(get_N its-entropy-encoder CTP CTF 1)" +has_detector_ctf ITS && add_W o2-itsmft-entropy-encoder-workflow "$RANS_OPT --select-ir-frames --irframe-margin-bwd ${ITS_MARGIN_BWD:-$DEF_MARGIN_BWD} --irframe-margin-fwd ${ITS_MARGIN_FWD:-$DEF_MARGIN_FWD} --mem-factor ${ITS_ENC_MEMFACT:-1.5} --pipeline $(get_N its-entropy-encoder ITS CTF 1)" +has_detector_ctf MFT && add_W o2-itsmft-entropy-encoder-workflow "$RANS_OPT --select-ir-frames --irframe-margin-bwd ${MFT_MARGIN_BWD:-$DEF_MARGIN_BWD} --irframe-margin-fwd ${MFT_MARGIN_FWD:-$DEF_MARGIN_FWD} --mem-factor ${MFT_ENC_MEMFACT:-1.5} --runmft true --pipeline $(get_N mft-entropy-encoder MFT CTF 1)" +has_detector_ctf TPC && add_W o2-tpc-reco-workflow "$RANS_OPT --select-ir-frames --irframe-margin-bwd ${TPC_MARGIN_BWD:-$DEF_MARGIN_BWD} --irframe-margin-fwd ${TPC_MARGIN_FWD:-$DEF_MARGIN_FWD} --mem-factor ${TPC_ENC_MEMFACT:-1.} --input-type compressed-clusters-flat-for-encode --output-type encoded-clusters,disable-writer --pipeline $(get_N tpc-entropy-encoder TPC CTF 1 TPCENT)" +has_detector_ctf TRD && add_W o2-trd-entropy-encoder-workflow "$RANS_OPT --select-ir-frames --irframe-margin-bwd ${TRD_MARGIN_BWD:-$DEF_MARGIN_BWD} --irframe-margin-fwd ${TRD_MARGIN_FWD:-$DEF_MARGIN_FWD} --mem-factor ${TRD_ENC_MEMFACT:-1.5} --pipeline $(get_N trd-entropy-encoder TRD CTF 1 TRDENT)" +has_detector_ctf TOF && add_W o2-tof-entropy-encoder-workflow "$RANS_OPT --select-ir-frames --irframe-margin-bwd ${TOF_MARGIN_BWD:-$DEF_MARGIN_BWD} --irframe-margin-fwd ${TOF_MARGIN_FWD:-$DEF_MARGIN_FWD} --mem-factor ${TOF_ENC_MEMFACT:-1.5} --pipeline $(get_N tof-entropy-encoder TOF CTF 1)" +has_detector_ctf FT0 && add_W o2-ft0-entropy-encoder-workflow "$RANS_OPT --select-ir-frames --irframe-margin-bwd ${FT0_MARGIN_BWD:-$DEF_MARGIN_BWD} --irframe-margin-fwd ${FT0_MARGIN_FWD:-$DEF_MARGIN_FWD} --mem-factor ${FT0_ENC_MEMFACT:-1.5} --pipeline $(get_N ft0-entropy-encoder FT0 CTF 1)" +has_detector_ctf FV0 && add_W o2-fv0-entropy-encoder-workflow "$RANS_OPT --select-ir-frames --irframe-margin-bwd ${FV0_MARGIN_BWD:-$DEF_MARGIN_BWD} --irframe-margin-fwd ${FV0_MARGIN_FWD:-$DEF_MARGIN_FWD} --mem-factor ${FV0_ENC_MEMFACT:-1.5} --pipeline $(get_N fv0-entropy-encoder FV0 CTF 1)" +has_detector_ctf FDD && add_W o2-fdd-entropy-encoder-workflow "$RANS_OPT --select-ir-frames --irframe-margin-bwd ${FDD_MARGIN_BWD:-$DEF_MARGIN_BWD} --irframe-margin-fwd ${FDD_MARGIN_FWD:-$DEF_MARGIN_FWD} --mem-factor ${FDD_ENC_MEMFACT:-1.5} --pipeline $(get_N fdd-entropy-encoder FDD CTF 1)" +has_detector_ctf MID && add_W o2-mid-entropy-encoder-workflow "$RANS_OPT --select-ir-frames --irframe-margin-bwd ${MID_MARGIN_BWD:-$DEF_MARGIN_BWD} --irframe-margin-fwd ${MID_MARGIN_FWD:-$DEF_MARGIN_FWD} --mem-factor ${MID_ENC_MEMFACT:-1.5} --pipeline $(get_N mid-entropy-encoder MID CTF 1)" +has_detector_ctf MCH && add_W o2-mch-entropy-encoder-workflow "$RANS_OPT --select-ir-frames --irframe-margin-bwd ${MCH_MARGIN_BWD:-$DEF_MARGIN_BWD} --irframe-margin-fwd ${MCH_MARGIN_FWD:-$DEF_MARGIN_FWD} --mem-factor ${MCH_ENC_MEMFACT:-1.5} --pipeline $(get_N mch-entropy-encoder MCH CTF 1)" +has_detector_ctf PHS && add_W o2-phos-entropy-encoder-workflow "$RANS_OPT --select-ir-frames --irframe-margin-bwd ${PHS_MARGIN_BWD:-$DEF_MARGIN_BWD} --irframe-margin-fwd ${PHS_MARGIN_FWD:-$DEF_MARGIN_FWD} --mem-factor ${PHS_ENC_MEMFACT:-1.5} --pipeline $(get_N phos-entropy-encoder PHS CTF 1)" +has_detector_ctf CPV && add_W o2-cpv-entropy-encoder-workflow "$RANS_OPT --select-ir-frames --irframe-margin-bwd ${CPV_MARGIN_BWD:-$DEF_MARGIN_BWD} --irframe-margin-fwd ${CPV_MARGIN_FWD:-$DEF_MARGIN_FWD} --mem-factor ${CPV_ENC_MEMFACT:-1.5} --pipeline $(get_N cpv-entropy-encoder CPV CTF 1)" +has_detector_ctf EMC && add_W o2-emcal-entropy-encoder-workflow "$RANS_OPT --select-ir-frames --irframe-margin-bwd ${EMC_MARGIN_BWD:-$DEF_MARGIN_BWD} --irframe-margin-fwd ${EMC_MARGIN_FWD:-$DEF_MARGIN_FWD} --mem-factor ${EMC_ENC_MEMFACT:-1.5} --pipeline $(get_N emcal-entropy-encoder EMC CTF 1)" +has_detector_ctf ZDC && add_W o2-zdc-entropy-encoder-workflow "$RANS_OPT --select-ir-frames --irframe-margin-bwd ${ZDC_MARGIN_BWD:-$DEF_MARGIN_BWD} --irframe-margin-fwd ${ZDC_MARGIN_FWD:-$DEF_MARGIN_FWD} --mem-factor ${ZDC_ENC_MEMFACT:-1.5} --pipeline $(get_N zdc-entropy-encoder ZDC CTF 1)" +has_detector_ctf HMP && add_W o2-hmpid-entropy-encoder-workflow "$RANS_OPT --select-ir-frames --irframe-margin-bwd ${HMP_MARGIN_BWD:-$DEF_MARGIN_BWD} --irframe-margin-fwd ${HMP_MARGIN_FWD:-$DEF_MARGIN_FWD} --mem-factor ${HMP_ENC_MEMFACT:-1.5} --pipeline $(get_N hmpid-entropy-encoder HMP CTF 1)" +has_detector_ctf CTP && add_W o2-ctp-entropy-encoder-workflow "$RANS_OPT --select-ir-frames --irframe-margin-bwd ${CTP_MARGIN_BWD:-$DEF_MARGIN_BWD} --irframe-margin-fwd ${CTP_MARGIN_FWD:-$DEF_MARGIN_FWD} --mem-factor ${CTP_ENC_MEMFACT:-1.5} --pipeline $(get_N its-entropy-encoder CTP CTF 1)" add_W o2-ctf-writer-workflow "--output-dir $CTF_DIR --min-file-size ${CTF_MINSIZE} --max-ctf-per-file ${CTF_MAX_PER_FILE} --onlyDet $WORKFLOW_DETECTORS_CTF $CTF_MAXDETEXT" diff --git a/DATA/production/configurations/asyncReco/async_pass.sh b/DATA/production/configurations/asyncReco/async_pass.sh index 8e879a06d..6b8e60c2c 100755 --- a/DATA/production/configurations/asyncReco/async_pass.sh +++ b/DATA/production/configurations/asyncReco/async_pass.sh @@ -12,34 +12,82 @@ # function to run AOD merging run_AOD_merging() { - o2-aod-merger --input list_$1.list --output $1/AO2D_merged.root > $1/merging.log - exitcode=$? + cd $1 + if [[ -f "AO2D.root" ]]; then + ls "AO2D.root" > list_$1.list + echo "Checking AO2Ds with un-merged DFs in $1" + timeStartCheck=`date +%s` + time root -l -b -q $O2DPG_ROOT/DATA/production/common/readAO2Ds.C > checkAO2D.log + exitcode=$? + timeEndCheck=`date +%s` + timeUsedCheck=$(( $timeEndCheck-$timeStartCheck )) + echo "Time spent to check unmerged AODs in dir $1 = $timeUsedCheck s" + if [[ $exitcode -ne 0 ]]; then + echo "exit code from AO2D check is " $exitcode > validation_error.message + echo "exit code from AO2D check is " $exitcode + echo "This means that the check for unmerged AODs in $1 FAILED, we'll make the whole processing FAIL" + return $exitcode + fi + if [[ -z $ALIEN_JDL_DONOTMERGEAODS ]] || [[ $ALIEN_JDL_DONOTMERGEAODS == 0 ]]; then + echo "Merging AOD from the list list_$1.list" + o2-aod-merger --input list_$1.list --verbosity 1 --output AO2D_merged.root > merging.log + exitcode=$? + if [[ $exitcode -ne 0 ]]; then + echo "Exit code from the process merging DFs inside AO2D for $1 is " $exitcode > validation_error.message + echo "Exit code from the process merging DFs inside AO2D for $1 is " $exitcode + echo "This means that the merging of DFs for $1 FAILED, we'll make the whole processing FAIL" + return $exitcode + fi + # now checking merged AODs + echo "Checking AO2Ds with merged DFs in $AOD_DIR" + timeStartCheckMergedAOD=`date +%s` + time root -l -b -q '$O2DPG_ROOT/DATA/production/common/readAO2Ds.C("AO2D_merged.root")' > checkAO2D_merged.log + exitcode=$? + timeEndCheckMergedAOD=`date +%s` + timeUsedCheckMergedAOD=$(( $timeEndCheckMergedAOD-$timeStartCheckMergedAOD )) + echo "Time spent to check unmerged AODs in dir $1 = $timeUsedCheckMergedAOD s" + if [[ $exitcode -ne 0 ]]; then + echo "exit code from AO2D in $1 with merged DFs check is " $exitcode > validation_error.message + echo "exit code from AO2D in $1 with merged DFs check is " $exitcode + echo "This means that the check for merged AODs in $1 FAILED, we'll make the whole processing FAIL" + else + echo "All ok, replacing initial AO2D.root file in $1 with the one with merged DFs" + mv AO2D_merged.root AO2D.root + fi + cd .. + fi + fi return $exitcode } timeStartFullProcessing=`date +%s` # to skip positional arg parsing before the randomizing part. -inputarg="${1}" +export inputarg="${1}" if [[ "${1##*.}" == "root" ]]; then - #echo ${1##*.} - #echo "alien://${1}" > list.list - #export MODE="remote" - echo "${1}" > list.list - if [[ ! -z $ASYNC_BENCHMARK_ITERATIONS ]]; then - for i in `seq 1 $ASYNC_BENCHMARK_ITERATIONS`; do echo "${1}" >> list.list; done - fi - export MODE="LOCAL" - shift + #echo ${1##*.} + #echo "alien://${1}" > list.list + #export MODE="remote" + echo "${1}" > list.list + if [[ ! -z $ASYNC_BENCHMARK_ITERATIONS ]]; then + for i in `seq 1 $ASYNC_BENCHMARK_ITERATIONS`; do echo "${1}" >> list.list; done + fi + export MODE="LOCAL" + shift elif [[ "${1##*.}" == "xml" ]]; then + if [[ $ALIEN_JDL_DOWNLOADINPUTFILES == "1" ]]; then + echo "Downloading input files done by the job agent" + sed -rn 's/.*file\ name="(o2_ctf[^"]*)".*/\1/p' $1 > list.list + else sed -rn 's/.*turl="([^"]*)".*/\1/p' $1 > list.list - export MODE="remote" - shift + fi + export MODE="remote" + shift elif [[ $1 != "list.list" && "${1##*.}" == "list" ]]; then - cp $1 list.list - export MODE="remote" - shift + cp $1 list.list + export MODE="remote" + shift fi # Could need sometimes to iterate just a subset of the input files @@ -122,7 +170,7 @@ export timeUsed=0 ###if [[ $MODE == "remote" ]]; then # run specific archive if [[ ! -f runInput_$RUNNUMBER.tgz ]]; then - echo "No runInput_$RUNNUMBER.tgz, let's hope we don't need it" + echo "No runInput_$RUNNUMBER.tgz, let's hope we don't need it" else tar -xzvf runInput_$RUNNUMBER.tgz fi @@ -149,6 +197,22 @@ if [[ -n "$ALIEN_JDL_DOMEANVTXCALIB" ]]; then export ADD_CALIB=1 fi +if [[ "$ALIEN_JDL_EXTRACTCURRENTS" == "1" ]]; then + export ADD_CALIB=1 +fi + +if [[ "$ALIEN_JDL_EXTRACTTIMESERIES" == "1" ]]; then + export ADD_CALIB=1 +fi + +if [[ "${ALIEN_JDL_EXTRACTCOSMICS:-}" == "1" ]]; then + export ADD_CALIB=1 +fi + +if [[ "$ALIEN_JDL_DOTPCTIMEGAINCALIB" == "1" ]]; then + export ADD_CALIB=1 +fi + # AOD file size if [[ -n "$ALIEN_JDL_AODFILESIZE" ]]; then export AOD_FILE_SIZE="$ALIEN_JDL_AODFILESIZE" @@ -224,13 +288,13 @@ fi ln -sf $O2DPG_ROOT/DATA/common/setenv.sh ln -sf $O2DPG_ROOT/DATA/common/getCommonArgs.sh -# TFDELAY and throttling -export TFDELAYSECONDS=40 -if [[ -n "$ALIEN_JDL_TFDELAYSECONDS" ]]; then - TFDELAYSECONDS="$ALIEN_JDL_TFDELAYSECONDS" -# ...otherwise, it depends on whether we have throttling -elif [[ -n "$ALIEN_JDL_USETHROTTLING" ]]; then - TFDELAYSECONDS=1 +# throttling and TF-delay +: ${TFDELAYSECONDS:=0} +if [[ -n "$ALIEN_JDL_NOTFDELAY" ]] && [[ "$ALIEN_JDL_NOTFDELAY" -gt 0 ]] ; then + TFDELAYSECONDS=0 +fi + +if [[ -n "$ALIEN_JDL_USETHROTTLING" ]] && [[ -z "$TIMEFRAME_RATE_LIMIT" ]] ; then export TIMEFRAME_RATE_LIMIT=1 fi @@ -242,13 +306,13 @@ if [[ ! -z "$ALIEN_JDL_DDSHMSIZE" ]]; then export DDSHMSIZE=$ALIEN_JDL_DDSHMSIZE SETTING_ROOT_OUTPUT="ENABLE_ROOT_OUTPUT_o2_mch_reco_workflow= ENABLE_ROOT_OUTPUT_o2_muon_tracks_matcher_workflow= ENABLE_ROOT_OUTPUT_o2_aod_producer_workflow= ENABLE_ROOT_OUTPUT_o2_qc= " -if [[ -n $ALIEN_JDL_LPMCPASSMODE ]] && [[ $ALIEN_JDL_LPMCPASSMODE != "-1" ]]; then +if ([[ -n $ALIEN_JDL_LPMCPASSMODE ]] && [[ $ALIEN_JDL_LPMCPASSMODE != "-1" ]]) || [[ $ALIEN_JDL_KEEPTOFMATCHOUTPUT == "1" ]]; then SETTING_ROOT_OUTPUT+="ENABLE_ROOT_OUTPUT_o2_tof_matcher_workflow= " fi if [[ $ALIEN_JDL_DOEMCCALIB == "1" ]]; then SETTING_ROOT_OUTPUT+="ENABLE_ROOT_OUTPUT_o2_emcal_emc_offline_calib_workflow= " fi -if [[ $DO_TPC_RESIDUAL_EXTRACTION == "1" ]]; then +if [[ -n "$DO_TPC_RESIDUAL_EXTRACTION" && $DO_TPC_RESIDUAL_EXTRACTION != "0" ]]; then SETTING_ROOT_OUTPUT+="ENABLE_ROOT_OUTPUT_o2_calibration_residual_aggregator= " fi if [[ $ALIEN_JDL_DOTRDVDRIFTEXBCALIB == "1" ]]; then @@ -324,8 +388,8 @@ if [[ -n $ALIEN_JDL_PACKAGES ]]; then # if we have this env variable, it means t break else if [[ "$((SUBJOBIDX%KEEPRATIO))" -eq "0" ]]; then - keep=1 - break + keep=1 + break fi fi done < tmp.tmp @@ -337,13 +401,13 @@ if [[ -n $ALIEN_JDL_PACKAGES ]]; then # if we have this env variable, it means t else # in LOCAL mode, by default we keep all intermediate files echo -e "\n\n**** RUNNING IN LOCAL MODE ****" - keep=1 if [[ "$DO_NOT_KEEP_OUTPUT_IN_LOCAL" -eq 1 ]]; then - echo -e "**** ONLY SOME WORKFLOWS WILL HAVE THE ROOT OUTPUT SAVED ****\n\n" + echo -e "**** DO_NOT_KEEP_OUTPUT_IN_LOCAL ENABLED, NOT SETTING keep=0, NOT ENFORCING FULL ROOT OUTPUT ****\n\n" keep=0; else echo -e "**** WE KEEP ALL ROOT OUTPUT ****"; echo -e "**** IF YOU WANT TO REMOVE ROOT OUTPUT FILES FOR PERFORMANCE STUDIES OR SIMILAR, PLEASE SET THE ENV VAR DO_NOT_KEEP_OUTPUT_IN_LOCAL ****\n\n" + keep=1 fi fi @@ -353,72 +417,63 @@ fi echo "SETTING_ROOT_OUTPUT = $SETTING_ROOT_OUTPUT" # Enabling GPUs -if [[ -n "$ALIEN_JDL_USEGPUS" && $ALIEN_JDL_USEGPUS != 0 ]]; then - echo "Enabling GPUS" - export GPUTYPE="HIP" - export GPUMEMSIZE=$((25 << 30)) - if [[ "0$ASYNC_PASS_NO_OPTIMIZED_DEFAULTS" != "01" ]]; then - if [[ "ALIEN_JDL_USEFULLNUMADOMAIN" == 0 ]]; then +if [[ $ASYNC_PASS_NO_OPTIMIZED_DEFAULTS != 1 ]]; then + export OPTIMIZED_PARALLEL_ASYNC_AUTO_SHM_LIMIT=1 + if [[ $ALIEN_JDL_USEGPUS == 1 ]] ; then + echo "Enabling GPUS" + if [[ -z $ALIEN_JDL_SITEARCH ]]; then echo "ERROR: Must set ALIEN_JDL_SITEARCH to define GPU architecture!"; exit 1; fi + if [[ $ALIEN_JDL_SITEARCH == "NERSC" || $ALIEN_JDL_SITEARCH == "GENERIC_NVIDIA" || $ALIEN_JDL_SITEARCH == "GENERIC_AMD" ]]; then # Disable mlock / ulimit / gpu memory registration - has performance impact, but doesn't work at NERSC for now + export SETENV_NO_ULIMIT=1 + export CONFIG_EXTRA_PROCESS_o2_gpu_reco_workflow+="GPU_proc.noGPUMemoryRegistration=1;" + fi + ALIEN_JDL_SITEARCH_TMP=$ALIEN_JDL_SITEARCH + if [[ $ALIEN_JDL_SITEARCH == "EPN_MI100" ]]; then + ALIEN_JDL_SITEARCH_TMP=EPN + export EPN_NODE_MI100=1 + elif [[ $ALIEN_JDL_SITEARCH == "EPN_MI50" ]]; then + ALIEN_JDL_SITEARCH_TMP=EPN + fi + if [[ $ALIEN_JDL_SITEARCH == "GENERIC_NVIDIA" ]]; then + export OPTIMIZED_PARALLEL_ASYNC=8cpu_NVIDIA + elif [[ $ALIEN_JDL_SITEARCH == "GENERIC_AMD" ]]; then + export OPTIMIZED_PARALLEL_ASYNC=8cpu_AMD + elif [[ "ALIEN_JDL_USEFULLNUMADOMAIN" == 0 ]]; then if [[ $keep -eq 0 ]]; then - if [[ $ALIEN_JDL_UNOPTIMIZEDGPUSETTINGS != 1 ]]; then - export OPTIMIZED_PARALLEL_ASYNC=pp_1gpu # sets the multiplicities to optimized defaults for this configuration (1 job with 1 gpu on EPNs) - else - # forcing multiplicities to be 1 - export MULTIPLICITY_PROCESS_tof_matcher=1 - export MULTIPLICITY_PROCESS_mch_cluster_finder=1 - export MULTIPLICITY_PROCESS_tpc_entropy_decoder=1 - export MULTIPLICITY_PROCESS_itstpc_track_matcher=1 - export MULTIPLICITY_PROCESS_its_tracker=1 - export OMP_NUM_THREADS=4 - fi - export TIMEFRAME_RATE_LIMIT=8 + if [[ $ALIEN_JDL_UNOPTIMIZEDGPUSETTINGS != 1 ]]; then + export OPTIMIZED_PARALLEL_ASYNC=pp_1gpu_${ALIEN_JDL_SITEARCH_TMP} # (16 cores, 1 gpu per job, pp) + else + export OPTIMIZED_PARALLEL_ASYNC=pp_1gpu_${ALIEN_JDL_SITEARCH_TMP}_unoptimized # (16 cores, 1 gpu per job, pp, low CPU multiplicities) + fi else - export TIMEFRAME_RATE_LIMIT=4 + export OPTIMIZED_PARALLEL_ASYNC=keep_root fi - export SHMSIZE=30000000000 else - export DPL_SMOOTH_RATE_LIMITING=1 if [[ $BEAMTYPE == "pp" ]]; then - export OPTIMIZED_PARALLEL_ASYNC=pp_4gpu # sets the multiplicities to optimized defaults for this configuration (1 Numa, pp) - export TIMEFRAME_RATE_LIMIT=45 - export SHMSIZE=100000000000 + if [[ $ALIEN_JDL_SITEARCH =~ ^EPN ]]; then + export OPTIMIZED_PARALLEL_ASYNC=pp_4gpu_${ALIEN_JDL_SITEARCH_TMP} # (64 cores, 1 NUMA, 4 gpu per job, pp) + else + export OPTIMIZED_PARALLEL_ASYNC=pp_gpu_${ALIEN_JDL_SITEARCH_TMP} # (64 cores, 1 gpu per job, pp) + fi else # PbPb - export OPTIMIZED_PARALLEL_ASYNC=PbPb_4gpu # sets the multiplicities to optimized defaults for this configuration (1 Numa, PbPb) - export TIMEFRAME_RATE_LIMIT=20 - export SHMSIZE=128000000000 + if [[ $ALIEN_JDL_SITEARCH =~ ^EPN ]]; then + export OPTIMIZED_PARALLEL_ASYNC=PbPb_4gpu_${ALIEN_JDL_SITEARCH_TMP} # (64 cores, 1 NUMA 4 gpu per job, PbPb) + else + export OPTIMIZED_PARALLEL_ASYNC=PbPb_gpu_${ALIEN_JDL_SITEARCH_TMP} # (64 cores, 1 gpu per job, PbPb) + fi fi fi - fi -else - # David, Oct 13th - # the optimized settings for the 8 core GRID queue without GPU are - # (overwriting the values above) - # - if [[ "0$ASYNC_PASS_NO_OPTIMIZED_DEFAULTS" != "01" ]]; then - if [[ "$ALIEN_JDL_EPNFULLNUMACPUONLY" != 1 ]]; then - export TIMEFRAME_RATE_LIMIT=3 - if [[ $BEAMTYPE == "pp" ]]; then - if (( $(echo "$RUN_IR > 800000" | bc -l) )); then - export TIMEFRAME_RATE_LIMIT=1 - elif (( $(echo "$RUN_IR < 50000" | bc -l) )); then - export TIMEFRAME_RATE_LIMIT=6 - fi - export OPTIMIZED_PARALLEL_ASYNC=pp_8cpu # sets the multiplicities to optimized defaults for this configuration (grid) - export SHMSIZE=16000000000 - else # PbPb - export TIMEFRAME_RATE_LIMIT=2 - export OPTIMIZED_PARALLEL_ASYNC=pp_8cpu - export SHMSIZE=16000000000 - fi + else + export SETENV_NO_ULIMIT=1 + export DPL_DEFAULT_PIPELINE_LENGTH=16 # to avoid memory issues - affects performance, so don't do with GPUs + if [[ $ALIEN_JDL_EPNFULLNUMACPUONLY != 1 ]]; then + export OPTIMIZED_PARALLEL_ASYNC=8cpu # (8 cores per job, grid) else - export OPTIMIZED_PARALLEL_ASYNC=pp_64cpu # to use EPNs with full NUMA domain but without GPUs - export TIMEFRAME_RATE_LIMIT=32 - SHMSIZE=90000000000 + export OPTIMIZED_PARALLEL_ASYNC=pp_64cpu # (64 cores per job, 1 NUMA, EPN) fi fi fi -echo "[INFO (async_pass.sh)] envvars were set to TFDELAYSECONDS ${TFDELAYSECONDS} TIMEFRAME_RATE_LIMIT ${TIMEFRAME_RATE_LIMIT}" +echo "[INFO (async_pass.sh)] envvars were set to TFDELAYSECONDS ${TFDELAYSECONDS} TIMEFRAME_RATE_LIMIT ${TIMEFRAME_RATE_LIMIT} OPTIMIZED_PARALLEL_ASYNC ${OPTIMIZED_PARALLEL_ASYNC:-NONE}" [[ -z $NTIMEFRAMES ]] && export NTIMEFRAMES=-1 @@ -434,14 +489,23 @@ if [[ -f "getStat.sh" ]]; then STATSCRIPT="getStat.sh" fi +# exclude some detectors QC +WORKFLOW_DETECTORS_EXCLUDE_QC_SCRIPT=${ALIEN_JDL_WORKFLOWDETECTORSEXCLUDEQC:-} + # reco and matching # print workflow -if [[ $ALIEN_JDL_SPLITWF != "1" ]]; then - env $SETTING_ROOT_OUTPUT IS_SIMULATED_DATA=0 WORKFLOWMODE=print TFDELAY=$TFDELAYSECONDS ./run-workflow-on-inputlist.sh $INPUT_TYPE list.list > workflowconfig.log +if [[ $ALIEN_JDL_SSPLITWF != "1" ]]; then + env $SETTING_ROOT_OUTPUT IS_SIMULATED_DATA=0 WORKFLOWMODE=print TFDELAY=$TFDELAYSECONDS WORKFLOW_DETECTORS_EXCLUDE_QC=$WORKFLOW_DETECTORS_EXCLUDE_QC_SCRIPT ./run-workflow-on-inputlist.sh $INPUT_TYPE list.list > workflowconfig.log + exitcode=$? + if [[ $exitcode -ne 0 ]]; then + echo "exit code from printing workflow is $exitcode" > validation_error.message + echo "exit code from printing workflow is $exitcode" + exit $exitcode + fi # run it if [[ "0$RUN_WORKFLOW" != "00" ]]; then timeStart=`date +%s` - time env $SETTING_ROOT_OUTPUT IS_SIMULATED_DATA=0 WORKFLOWMODE=run TFDELAY=$TFDELAYSECONDS ./run-workflow-on-inputlist.sh $INPUT_TYPE list.list + time env $SETTING_ROOT_OUTPUT IS_SIMULATED_DATA=0 WORKFLOWMODE=run TFDELAY=$TFDELAYSECONDS WORKFLOW_DETECTORS_EXCLUDE_QC=$WORKFLOW_DETECTORS_EXCLUDE_QC_SCRIPT ./run-workflow-on-inputlist.sh $INPUT_TYPE list.list exitcode=$? timeEnd=`date +%s` timeUsed=$(( $timeUsed+$timeEnd-$timeStart )) @@ -455,24 +519,39 @@ if [[ $ALIEN_JDL_SPLITWF != "1" ]]; then fi mv latest.log latest_reco_1.log $STATSCRIPT latest_reco_1.log + exitcode=$? + echo "exit code is $exitcode" + if [[ $exitcode -ne 0 ]]; then + echo "exit code from processing is " $exitcode > validation_error.message + echo "exit code from processing is " $exitcode + exit $exitcode + fi fi else # running the wf in split mode echo "We will run the workflow in SPLIT mode!" + echo "ALIEN_JDL_STARTSPLITSTEP = $ALIEN_JDL_STARTSPLITSTEP" + echo "ALIEN_JDL_SSPLITSTEP = $ALIEN_JDL_SSPLITSTEP" WORKFLOW_PARAMETERS_START=$WORKFLOW_PARAMETERS - if [[ -z "$ALIEN_JDL_SPLITSTEP" ]] || [[ "$ALIEN_JDL_SPLITSTEP" -eq 1 ]] || ( [[ -n $ALIEN_JDL_STARTSPLITSTEP ]] && [[ "$ALIEN_JDL_STARTSPLITSTEP" -le 1 ]]) || [[ "$ALIEN_JDL_SPLITSTEP" -eq "all" ]]; then + if ([[ -z "$ALIEN_JDL_STARTSPLITSTEP" ]] && [[ -z "$ALIEN_JDL_SSPLITSTEP" ]]) || [[ "$ALIEN_JDL_SSPLITSTEP" -eq 1 ]] || ( [[ -n $ALIEN_JDL_STARTSPLITSTEP ]] && [[ "$ALIEN_JDL_STARTSPLITSTEP" -le 1 ]]) || [[ "$ALIEN_JDL_SSPLITSTEP" == "all" ]]; then # 1. TPC decoding + reco - echo "Step 1) Decoding and reconstructing TPC" - echo "Step 1) Decoding and reconstructing TPC" > workflowconfig.log + echo "Step 1) Decoding and reconstructing TPC+CTP" + echo "Step 1) Decoding and reconstructing TPC+CTP" > workflowconfig.log for i in AOD QC CALIB CALIB_LOCAL_INTEGRATED_AGGREGATOR; do export WORKFLOW_PARAMETERS=$(echo $WORKFLOW_PARAMETERS | sed -e "s/,$i,/,/g" -e "s/^$i,//" -e "s/,$i"'$'"//" -e "s/^$i"'$'"//") done - env DISABLE_ROOT_OUTPUT=0 IS_SIMULATED_DATA=0 WORKFLOWMODE=print TFDELAY=$TFDELAYSECONDS WORKFLOW_DETECTORS=TPC WORKFLOW_DETECTORS_MATCHING= ./run-workflow-on-inputlist.sh $INPUT_TYPE list.list >> workflowconfig.log + env DISABLE_ROOT_OUTPUT=0 IS_SIMULATED_DATA=0 WORKFLOWMODE=print TFDELAY=$TFDELAYSECONDS WORKFLOW_DETECTORS=TPC,CTP WORKFLOW_DETECTORS_MATCHING= ./run-workflow-on-inputlist.sh $INPUT_TYPE list.list >> workflowconfig.log + exitcode=$? + if [[ $exitcode -ne 0 ]]; then + echo "exit code from printing workflow (Step 1) is $exitcode" > validation_error.message + echo "exit code from printing workflow (Step 1) is $exitcode" + exit $exitcode + fi # run it if [[ "0$RUN_WORKFLOW" != "00" ]]; then timeStart=`date +%s` - time env DISABLE_ROOT_OUTPUT=0 IS_SIMULATED_DATA=0 WORKFLOWMODE=run TFDELAY=$TFDELAYSECONDS WORKFLOW_DETECTORS=TPC WORKFLOW_DETECTORS_MATCHING= ./run-workflow-on-inputlist.sh $INPUT_TYPE list.list + time env DISABLE_ROOT_OUTPUT=0 IS_SIMULATED_DATA=0 WORKFLOWMODE=run TFDELAY=$TFDELAYSECONDS WORKFLOW_DETECTORS=TPC,CTP WORKFLOW_DETECTORS_MATCHING= ./run-workflow-on-inputlist.sh $INPUT_TYPE list.list exitcode=$? timeEnd=`date +%s` timeUsed=$(( $timeUsed+$timeEnd-$timeStart )) @@ -480,16 +559,25 @@ else echo "Time spent in running the workflow, Step 1 = $delta s" echo "exitcode = $exitcode" if [[ $exitcode -ne 0 ]]; then - echo "exit code from Step 1 of processing is " $exitcode > validation_error.message - echo "exit code from Step 1 of processing is " $exitcode - exit $exitcode + echo "exit code from Step 1 of processing is " $exitcode > validation_error.message + echo "exit code from Step 1 of processing is " $exitcode + exit $exitcode fi mv latest.log latest_reco_1.log + if [[ -f performanceMetrics.json ]]; then + mv performanceMetrics.json performanceMetrics_1.json + fi $STATSCRIPT latest_reco_1.log reco_1 + exitcode=$? + if [[ $exitcode -ne 0 ]]; then + echo "exit code from processing is " $exitcode > validation_error.message + echo "exit code from processing is " $exitcode + exit $exitcode + fi fi fi - if [[ -z "$ALIEN_JDL_SPLITSTEP" ]] || [[ "$ALIEN_JDL_SPLITSTEP" -eq 2 ]] || ( [[ -n $ALIEN_JDL_STARTSPLITSTEP ]] && [[ "$ALIEN_JDL_STARTSPLITSTEP" -le 2 ]]) || [[ "$ALIEN_JDL_SPLITSTEP" -eq "all" ]]; then + if ([[ -z "$ALIEN_JDL_STARTSPLITSTEP" ]] && [[ -z "$ALIEN_JDL_SSPLITSTEP" ]]) || [[ "$ALIEN_JDL_SSPLITSTEP" -eq 2 ]] || ( [[ -n $ALIEN_JDL_STARTSPLITSTEP ]] && [[ "$ALIEN_JDL_STARTSPLITSTEP" -le 2 ]]) || [[ "$ALIEN_JDL_SSPLITSTEP" == "all" ]]; then # 2. the other detectors decoding + reco WORKFLOW_PARAMETERS=$WORKFLOW_PARAMETERS_START echo "Step 2) Decoding and reconstructing ALL-TPC" @@ -497,11 +585,17 @@ else for i in AOD QC CALIB CALIB_LOCAL_INTEGRATED_AGGREGATOR; do export WORKFLOW_PARAMETERS=$(echo $WORKFLOW_PARAMETERS | sed -e "s/,$i,/,/g" -e "s/^$i,//" -e "s/,$i"'$'"//" -e "s/^$i"'$'"//") done - env DISABLE_ROOT_OUTPUT=0 IS_SIMULATED_DATA=0 WORKFLOWMODE=print TFDELAY=$TFDELAYSECONDS WORKFLOW_DETECTORS=ALL WORKFLOW_DETECTORS_EXCLUDE=TPC WORKFLOW_DETECTORS_MATCHING= ./run-workflow-on-inputlist.sh $INPUT_TYPE list.list >> workflowconfig.log + env DISABLE_ROOT_OUTPUT=0 IS_SIMULATED_DATA=0 WORKFLOWMODE=print TFDELAY=$TFDELAYSECONDS WORKFLOW_DETECTORS=ALL WORKFLOW_DETECTORS_EXCLUDE=TPC,$DETECTORS_EXCLUDE WORKFLOW_DETECTORS_MATCHING= ./run-workflow-on-inputlist.sh $INPUT_TYPE list.list >> workflowconfig.log + exitcode=$? + if [[ $exitcode -ne 0 ]]; then + echo "exit code from printing workflow (Step 2) is $exitcode" > validation_error.message + echo "exit code from printing workflow (Step 2) is $exitcode" + exit $exitcode + fi # run it if [[ "0$RUN_WORKFLOW" != "00" ]]; then timeStart=`date +%s` - time env DISABLE_ROOT_OUTPUT=0 IS_SIMULATED_DATA=0 WORKFLOWMODE=run TFDELAY=$TFDELAYSECONDS WORKFLOW_DETECTORS=ALL WORKFLOW_DETECTORS_EXCLUDE=TPC WORKFLOW_DETECTORS_MATCHING= ./run-workflow-on-inputlist.sh $INPUT_TYPE list.list + time env DISABLE_ROOT_OUTPUT=0 IS_SIMULATED_DATA=0 WORKFLOWMODE=run TFDELAY=$TFDELAYSECONDS WORKFLOW_DETECTORS=ALL WORKFLOW_DETECTORS_EXCLUDE=TPC,$DETECTORS_EXCLUDE WORKFLOW_DETECTORS_MATCHING= ./run-workflow-on-inputlist.sh $INPUT_TYPE list.list exitcode=$? timeEnd=`date +%s` timeUsed=$(( $timeUsed+$timeEnd-$timeStart )) @@ -509,49 +603,90 @@ else echo "Time spent in running the workflow, Step 2 = $delta s" echo "exitcode = $exitcode" if [[ $exitcode -ne 0 ]]; then - echo "exit code from Step 2 of processing is " $exitcode > validation_error.message - echo "exit code from Step 2 of processing is " $exitcode - exit $exitcode + echo "exit code from Step 2 of processing is " $exitcode > validation_error.message + echo "exit code from Step 2 of processing is " $exitcode + exit $exitcode fi mv latest.log latest_reco_2.log + if [[ -f performanceMetrics.json ]]; then + mv performanceMetrics.json performanceMetrics_2.json + fi $STATSCRIPT latest_reco_2.log reco_2 + exitcode=$? + if [[ $exitcode -ne 0 ]]; then + echo "exit code from processing is " $exitcode > validation_error.message + echo "exit code from processing is " $exitcode + exit $exitcode + fi # let's compare to previous step if [[ -f latest_reco_1.log ]]; then - nCTFsFilesInspected_step1=`ls [0-9]*_[0-9]*_[0-9]*_[0-9]*_[0-9]*_reco_1.stat | sed 's/\(^[0-9]*\)_.*/\1/'` - nCTFsFilesOK_step1=`ls [0-9]*_[0-9]*_[0-9]*_[0-9]*_[0-9]*_reco_1.stat | sed 's/^[0-9]*_\([0-9]*\)_.*/\1/'` - nCTFsFilesFailed_step1=`ls [0-9]*_[0-9]*_[0-9]*_[0-9]*_[0-9]*_reco_1.stat | sed 's/^[0-9]*_[0-9]*_\([0-9]*\)_.*/\1/'` - nCTFsProcessed_step1=`ls [0-9]*_[0-9]*_[0-9]*_[0-9]*_[0-9]*_reco_1.stat | sed 's/^[0-9]*_[0-9]*_[0-9]*_\([0-9]*\).*/\1/'` - nCTFsFilesInspected_step2=`ls [0-9]*_[0-9]*_[0-9]*_[0-9]*_[0-9]*_reco_2.stat | sed 's/\(^[0-9]*\)_.*/\1/'` - nCTFsFilesOK_step2=`ls [0-9]*_[0-9]*_[0-9]*_[0-9]*_[0-9]*_reco_1.stat | sed 's/^[0-9]*_\([0-9]*\)_.*/\1/'` - nCTFsFilesFailed_step2=`ls [0-9]*_[0-9]*_[0-9]*_[0-9]*_[0-9]*_reco_2.stat | sed 's/^[0-9]*_[0-9]*_\([0-9]*\)_.*/\1/'` - nCTFsProcessed_step2=`ls [0-9]*_[0-9]*_[0-9]*_[0-9]*_[0-9]*_reco_2.stat | sed 's/^[0-9]*_[0-9]*_[0-9]*_\([0-9]*\).*/\1/'` - if [[ $nCTFsFilesInspected_step1 != $nCTFsFilesInspected_step2 ]] || [[ $nCTFsFilesFailed_step1 != $nCTFsFilesFailed_step2 ]] || [[ $nCTFsFilesOK_step1 != $nCTFsFilesOK_step2 ]] || [[ $nCTFsProcessed_step1 != $nCTFsProcessed_step2 ]]; then - echo "Inconsistency between step 1 and step 2 in terms of number of CTFs (files or single CTFs) found:" - echo "nCTFsFilesInspected_step1 = $nCTFsFilesInspected_step1, nCTFsFilesInspected_step2 = $nCTFsFilesInspected_step2" - echo "nCTFsFilesOK_step1 = $nCTFsFilesOK_step1, nCTFsFilesOK_step2 = $nCTFsFilesOK_step2" - echo "nCTFsFilesFailed_step1 = $nCTFsFilesFailed_step1, nCTFsFilesFailed_step2 = $nCTFsFilesFailed_step2" - echo "nCTFsProcessed_step1 = $nCTFsProcessed_step1, nCTFsProcessed_step2 = $nCTFsProcessed_step2" - echo "Inconsistency between step 1 and step 2 in terms of number of CTFs (files or single CTFs) found:" > validation_error.message - echo "nCTFsFilesInspected_step1 = $nCTFsFilesInspected_step1, nCTFsFilesInspected_step2 = $nCTFsFilesInspected_step2" > validation_error.message - echo "nCTFsFilesOK_step1 = $nCTFsFilesOK_step1, nCTFsFilesOK_step2 = $nCTFsFilesOK_step2" > validation_error.message - echo "nCTFsProcessed_step1 = $nCTFsProcessed_step1, nCTFsProcessed_step2 = $nCTFsProcessed_step2" > validation_error.message - exit 1000 - fi + nCTFsFilesInspected_step1=`ls [0-9]*_[0-9]*_[0-9]*_[0-9]*_[0-9]*_reco_1.stat | sed 's/\(^[0-9]*\)_.*/\1/'` + nCTFsFilesOK_step1=`ls [0-9]*_[0-9]*_[0-9]*_[0-9]*_[0-9]*_reco_1.stat | sed 's/^[0-9]*_\([0-9]*\)_.*/\1/'` + nCTFsFilesFailed_step1=`ls [0-9]*_[0-9]*_[0-9]*_[0-9]*_[0-9]*_reco_1.stat | sed 's/^[0-9]*_[0-9]*_\([0-9]*\)_.*/\1/'` + nCTFsProcessed_step1=`ls [0-9]*_[0-9]*_[0-9]*_[0-9]*_[0-9]*_reco_1.stat | sed 's/^[0-9]*_[0-9]*_[0-9]*_\([0-9]*\).*/\1/'` + nCTFsFilesInspected_step2=`ls [0-9]*_[0-9]*_[0-9]*_[0-9]*_[0-9]*_reco_2.stat | sed 's/\(^[0-9]*\)_.*/\1/'` + nCTFsFilesOK_step2=`ls [0-9]*_[0-9]*_[0-9]*_[0-9]*_[0-9]*_reco_1.stat | sed 's/^[0-9]*_\([0-9]*\)_.*/\1/'` + nCTFsFilesFailed_step2=`ls [0-9]*_[0-9]*_[0-9]*_[0-9]*_[0-9]*_reco_2.stat | sed 's/^[0-9]*_[0-9]*_\([0-9]*\)_.*/\1/'` + nCTFsProcessed_step2=`ls [0-9]*_[0-9]*_[0-9]*_[0-9]*_[0-9]*_reco_2.stat | sed 's/^[0-9]*_[0-9]*_[0-9]*_\([0-9]*\).*/\1/'` + if [[ $nCTFsFilesInspected_step1 != $nCTFsFilesInspected_step2 ]] || [[ $nCTFsFilesFailed_step1 != $nCTFsFilesFailed_step2 ]] || [[ $nCTFsFilesOK_step1 != $nCTFsFilesOK_step2 ]] || [[ $nCTFsProcessed_step1 != $nCTFsProcessed_step2 ]]; then + echo "Inconsistency between step 1 and step 2 in terms of number of CTFs (files or single CTFs) found:" + echo "nCTFsFilesInspected_step1 = $nCTFsFilesInspected_step1, nCTFsFilesInspected_step2 = $nCTFsFilesInspected_step2" + echo "nCTFsFilesOK_step1 = $nCTFsFilesOK_step1, nCTFsFilesOK_step2 = $nCTFsFilesOK_step2" + echo "nCTFsFilesFailed_step1 = $nCTFsFilesFailed_step1, nCTFsFilesFailed_step2 = $nCTFsFilesFailed_step2" + echo "nCTFsProcessed_step1 = $nCTFsProcessed_step1, nCTFsProcessed_step2 = $nCTFsProcessed_step2" + echo "Inconsistency between step 1 and step 2 in terms of number of CTFs (files or single CTFs) found:" > validation_error.message + echo "nCTFsFilesInspected_step1 = $nCTFsFilesInspected_step1, nCTFsFilesInspected_step2 = $nCTFsFilesInspected_step2" > validation_error.message + echo "nCTFsFilesOK_step1 = $nCTFsFilesOK_step1, nCTFsFilesOK_step2 = $nCTFsFilesOK_step2" > validation_error.message + echo "nCTFsProcessed_step1 = $nCTFsProcessed_step1, nCTFsProcessed_step2 = $nCTFsProcessed_step2" > validation_error.message + exit 255 + fi fi fi fi - if [[ -z "$ALIEN_JDL_SPLITSTEP" ]] || [[ "$ALIEN_JDL_SPLITSTEP" -eq 3 ]] || ( [[ -n $ALIEN_JDL_STARTSPLITSTEP ]] && [[ "$ALIEN_JDL_STARTSPLITSTEP" -le 3 ]]) || [[ "$ALIEN_JDL_SPLITSTEP" -eq "all" ]]; then - # 3. matching, QC, calib, AOD + if ([[ -z "$ALIEN_JDL_STARTSPLITSTEP" ]] && [[ -z "$ALIEN_JDL_SSPLITSTEP" ]]) || [[ "$ALIEN_JDL_SSPLITSTEP" -eq 3 ]] || ( [[ -n $ALIEN_JDL_STARTSPLITSTEP ]] && [[ "$ALIEN_JDL_STARTSPLITSTEP" -le 3 ]]) || [[ "$ALIEN_JDL_SSPLITSTEP" == "all" ]]; then + # 3. matching, calib, AOD, potentially QC WORKFLOW_PARAMETERS=$WORKFLOW_PARAMETERS_START - echo "Step 3) matching, QC, calib, AOD" - echo -e "\nStep 3) matching, QC, calib, AOD" >> workflowconfig.log - export TIMEFRAME_RATE_LIMIT=0 - env $SETTING_ROOT_OUTPUT IS_SIMULATED_DATA=0 WORKFLOWMODE=print TFDELAY=$TFDELAYSECONDS WORKFLOW_DETECTORS=ALL WORKFLOW_DETECTORS_USE_GLOBAL_READER=ALL WORKFLOW_DETECTORS_EXCLUDE_QC=CPV ./run-workflow-on-inputlist.sh $INPUT_TYPE list.list >> workflowconfig.log + if [[ "$ALIEN_JDL_KEEPQCSEPARATE" == "1" ]]; then + echo "QC will be run as last step, removing it from 3rd step" + for i in QC; do + export WORKFLOW_PARAMETERS=$(echo $WORKFLOW_PARAMETERS | sed -e "s/,$i,/,/g" -e "s/^$i,//" -e "s/,$i"'$'"//" -e "s/^$i"'$'"//") + done + fi + echo "WORKFLOW_PARAMETERS=$WORKFLOW_PARAMETERS" + echo "Step 3) matching, calib, AOD, potentially QC" + echo -e "\nStep 3) matching, calib, AOD, potentially QC" >> workflowconfig.log + export ALIEN_JDL_PROCESSITSDEADMAP= # disable ITS dead map calibration, if it was requested then it was already done at step 2 + export ALIEN_JDL_PROCESSMFTDEADMAP= # disable MFT dead map calibration, if it was requested then it was already done at step 2 + # This uses the same time frame rate limiting as in full wf, unless differently specified in the JDL + export TIMEFRAME_RATE_LIMIT=${ALIEN_JDL_TIMEFRAMERATELIMITSSPLITWF:-${TIMEFRAME_RATE_LIMIT}} + echo "Removing detectors $DETECTORS_EXCLUDE" + if [[ $ALIEN_JDL_USEREADERDELAY == 1 ]]; then + # if we add a delay, the rate limiting should be disabled + TIMEFRAME_RATE_LIMIT=0 + READER_DELAY=${ALIEN_JDL_READERDELAY:-30} + export ARGS_EXTRA_PROCESS_o2_global_track_cluster_reader+=" --reader-delay $READER_DELAY " + fi + echo "extra args are $ARGS_EXTRA_PROCESS_o2_global_track_cluster_reader" + WORKFLOW_DETECTORS_EXCLUDE_QC_SCRIPT+=",CPV" + READ_TRACKS="ALLSINGLE" + READ_CLUSTERS="ALLSINGLE" + if [[ "$ALIEN_JDL_KEEPQCSEPARATE" == "1" ]]; then + STEP_3_ROOT_OUTPUT="DISABLE_ROOT_OUTPUT=0" + else + STEP_3_ROOT_OUTPUT=$SETTING_ROOT_OUTPUT + fi + env $STEP_3_ROOT_OUTPUT IS_SIMULATED_DATA=0 WORKFLOWMODE=print TFDELAY=$TFDELAYSECONDS WORKFLOW_DETECTORS=ALL WORKFLOW_DETECTORS_EXCLUDE=$DETECTORS_EXCLUDE WORKFLOW_DETECTORS_USE_GLOBAL_READER_TRACKS=$READ_TRACKS WORKFLOW_DETECTORS_USE_GLOBAL_READER_CLUSTERS=$READ_CLUSTERS WORKFLOW_DETECTORS_EXCLUDE_GLOBAL_READER_TRACKS=HMP WORKFLOW_DETECTORS_EXCLUDE_QC=$WORKFLOW_DETECTORS_EXCLUDE_QC_SCRIPT,$DETECTORS_EXCLUDE ./run-workflow-on-inputlist.sh $INPUT_TYPE list.list >> workflowconfig.log + exitcode=$? + if [[ $exitcode -ne 0 ]]; then + echo "exit code from printing workflow (Step 3) is $exitcode" > validation_error.message + echo "exit code from printing workflow (Step 3) is $exitcode" + exit $exitcode + fi # run it if [[ "0$RUN_WORKFLOW" != "00" ]]; then timeStart=`date +%s` - time env $SETTING_ROOT_OUTPUT IS_SIMULATED_DATA=0 WORKFLOWMODE=run TFDELAY=$TFDELAYSECONDS WORKFLOW_DETECTORS=ALL WORKFLOW_DETECTORS_USE_GLOBAL_READER=ALL WORKFLOW_DETECTORS_EXCLUDE_QC=CPV ./run-workflow-on-inputlist.sh $INPUT_TYPE list.list + time env $STEP_3_ROOT_OUTPUT IS_SIMULATED_DATA=0 WORKFLOWMODE=run TFDELAY=$TFDELAYSECONDS WORKFLOW_DETECTORS=ALL WORKFLOW_DETECTORS_EXCLUDE=$DETECTORS_EXCLUDE WORKFLOW_DETECTORS_USE_GLOBAL_READER_TRACKS=$READ_TRACKS WORKFLOW_DETECTORS_USE_GLOBAL_READER_CLUSTERS=$READ_CLUSTERS WORKFLOW_DETECTORS_EXCLUDE_GLOBAL_READER_TRACKS=HMP WORKFLOW_DETECTORS_EXCLUDE_QC=$WORKFLOW_DETECTORS_EXCLUDE_QC_SCRIPT,$DETECTORS_EXCLUDE ./run-workflow-on-inputlist.sh $INPUT_TYPE list.list exitcode=$? timeEnd=`date +%s` timeUsed=$(( $timeUsed+$timeEnd-$timeStart )) @@ -559,27 +694,80 @@ else echo "Time spent in running the workflow, Step 3 = $delta s" echo "exitcode = $exitcode" if [[ $exitcode -ne 0 ]]; then - echo "exit code from Step 3 of processing is " $exitcode > validation_error.message - echo "exit code from Step 3 of processing is " $exitcode - exit $exitcode + echo "exit code from Step 3 of processing is " $exitcode > validation_error.message + echo "exit code from Step 3 of processing is " $exitcode + exit $exitcode fi mv latest.log latest_reco_3.log + if [[ -f performanceMetrics.json ]]; then + mv performanceMetrics.json performanceMetrics_3.json + fi + fi + fi + if [[ "$ALIEN_JDL_KEEPQCSEPARATE" == "1" ]]; then + if ([[ -z "$ALIEN_JDL_STARTSPLITSTEP" ]] && [[ -z "$ALIEN_JDL_SSPLITSTEP" ]]) || [[ "$ALIEN_JDL_SSPLITSTEP" -eq 4 ]] || ( [[ -n $ALIEN_JDL_STARTSPLITSTEP ]] && [[ "$ALIEN_JDL_STARTSPLITSTEP" -le 4 ]]) || [[ "$ALIEN_JDL_SSPLITSTEP" == "all" ]]; then + # 4. QC + WORKFLOW_PARAMETERS="QC" + echo "WORKFLOW_PARAMETERS=$WORKFLOW_PARAMETERS" + echo "Step 4) QC" + echo -e "\nStep 4) QC" >> workflowconfig.log + echo "Removing detectors $DETECTORS_EXCLUDE" + echo "The rate limiting will be the same as in step 3: TIMEFRAME_RATE_LIMIT = ${TIMEFRAME_RATE_LIMIT}" + READ_TRACKS="ALL" + READ_CLUSTERS="ALL" + export GLOBAL_READER_NEEDS_PV="1" + export GLOBAL_READER_NEEDS_SV="0" + export SVERTEXING_SOURCES="none" + WORKFLOW_DETECTORS_EXCLUDE_QC_SCRIPT+=",CPV" + echo "QC_JSON_FROM_OUTSIDE = $QC_JSON_FROM_OUTSIDE" + env $SETTING_ROOT_OUTPUT IS_SIMULATED_DATA=0 WORKFLOWMODE=print TFDELAY=$TFDELAYSECONDS WORKFLOW_DETECTORS=ALL WORKFLOW_DETECTORS_EXCLUDE=$DETECTORS_EXCLUDE WORKFLOW_DETECTORS_USE_GLOBAL_READER_TRACKS=$READ_TRACKS WORKFLOW_DETECTORS_USE_GLOBAL_READER_CLUSTERS=$READ_CLUSTERS WORKFLOW_DETECTORS_EXCLUDE_GLOBAL_READER_TRACKS= WORKFLOW_DETECTORS_EXCLUDE_QC=$WORKFLOW_DETECTORS_EXCLUDE_QC_SCRIPT,$DETECTORS_EXCLUDE ./run-workflow-on-inputlist.sh $INPUT_TYPE list.list >> workflowconfig.log + exitcode=$? + if [[ $exitcode -ne 0 ]]; then + echo "exit code from printing workflow (Step 4) is $exitcode" > validation_error.message + echo "exit code from printing workflow (Step 4) is $exitcode" + exit $exitcode + fi + # run it + if [[ "0$RUN_WORKFLOW" != "00" ]]; then + timeStart=`date +%s` + time env $SETTING_ROOT_OUTPUT IS_SIMULATED_DATA=0 WORKFLOWMODE=run TFDELAY=$TFDELAYSECONDS WORKFLOW_DETECTORS=ALL WORKFLOW_DETECTORS_EXCLUDE=$DETECTORS_EXCLUDE WORKFLOW_DETECTORS_USE_GLOBAL_READER_TRACKS=$READ_TRACKS WORKFLOW_DETECTORS_USE_GLOBAL_READER_CLUSTERS=$READ_CLUSTERS WORKFLOW_DETECTORS_EXCLUDE_GLOBAL_READER_TRACKS= WORKFLOW_DETECTORS_EXCLUDE_QC=$WORKFLOW_DETECTORS_EXCLUDE_QC_SCRIPT,$DETECTORS_EXCLUDE ./run-workflow-on-inputlist.sh $INPUT_TYPE list.list + exitcode=$? + timeEnd=`date +%s` + timeUsed=$(( $timeUsed+$timeEnd-$timeStart )) + delta=$(( $timeEnd-$timeStart )) + echo "Time spent in running the workflow, Step 4 = $delta s" + echo "exitcode = $exitcode" + if [[ $exitcode -ne 0 ]]; then + echo "exit code from Step 4 of processing is " $exitcode > validation_error.message + echo "exit code from Step 4 of processing is " $exitcode + exit $exitcode + fi + mv latest.log latest_reco_4.log + if [[ -f performanceMetrics.json ]]; then + mv performanceMetrics.json performanceMetrics_4.json + fi + fi fi fi fi # now extract all performance metrics -IFS=$'\n' -if [[ -f "performanceMetrics.json" ]]; then - timeStart=`date +%s` - for workflow in `grep ': {' performanceMetrics.json`; do - strippedWorkflow=`echo $workflow | cut -d\" -f2` - cat performanceMetrics.json | jq '.'\"${strippedWorkflow}\"'' > ${strippedWorkflow}_metrics.json - done - timeEnd=`date +%s` - timeUsed=$(( $timeUsed+$timeEnd-$timeStart )) - delta=$(( $timeEnd-$timeStart )) - echo "Time spent in splitting the metrics files = $delta s" +if [[ $ALIEN_JDL_EXTRACTMETRICS == "1" ]]; then + IFS=$'\n' + timeStart=`date +%s` + for perfMetricsFiles in performanceMetrics.json performanceMetrics_1.json performanceMetrics_2.json performanceMetrics_3.json performanceMetrics_4.json ; do + suffix=`echo $perfMetricsFiles | sed 's/performanceMetrics\(.*\).json/\1/'` + if [[ -f "performanceMetrics.json" ]]; then + for workflow in `grep ': {' $perfMetricsFiles`; do + strippedWorkflow=`echo $workflow | cut -d\" -f2` + cat $perfMetricsFiles | jq '.'\"${strippedWorkflow}\"'' > ${strippedWorkflow}_metrics${suffix}.json + done + fi + done + timeEnd=`date +%s` + timeUsed=$(( $timeUsed+$timeEnd-$timeStart )) + delta=$(( $timeEnd-$timeStart )) + echo "Time spent in splitting the metrics files = $delta s" fi if [[ $ALIEN_JDL_AODOFF != 1 ]]; then @@ -597,21 +785,21 @@ if [[ $ALIEN_JDL_AODOFF != 1 ]]; then if [[ $AOD_LIST_COUNT -ge 2 ]]; then AOD_LAST=`find . -name AO2D.root | sort | tail -1` CURRENT_SIZE=`wc -c $AOD_LAST | awk '{print $1}'` - echo current size = $CURRENT_SIZE + echo "current size of last AOD file = $CURRENT_SIZE" PERCENT=`echo "scale=2; $CURRENT_SIZE/($AOD_FILE_SIZE*10^6)*100" | bc -l` - echo percent = $PERCENT + echo "percentage compared to AOD_FILE_SIZE (= $AOD_FILE_SIZE) = $PERCENT" if (( $(echo "$PERCENT < $MIN_ALLOWED_AOD_PERCENT_SIZE" | bc -l) )); then AOD_LAST_BUT_ONE=`find . -name AO2D.root | sort | tail -2 | head -1` - echo "Too small, merging $AOD_LAST with previous file $AOD_LAST_BUT_ONE" - ls $PWD/$AOD_LAST > list.list - ls $PWD/$AOD_LAST_BUT_ONE >> list.list + echo "Last AOD file too small, merging $AOD_LAST with previous file $AOD_LAST_BUT_ONE" + ls $PWD/$AOD_LAST > listAOD.list + ls $PWD/$AOD_LAST_BUT_ONE >> listAOD.list echo "List of files for merging:" - cat list.list + cat listAOD.list mkdir tmpAOD cd tmpAOD - ln -s ../list.list . + ln -s ../listAOD.list . timeStart=`date +%s` - time o2-aod-merger --input list.list + time o2-aod-merger --input listAOD.list > merging_lastAOD.log exitcode=$? timeEnd=`date +%s` timeUsed=$(( $timeUsed+$timeEnd-$timeStart )) @@ -619,9 +807,9 @@ if [[ $ALIEN_JDL_AODOFF != 1 ]]; then echo "Time spent in merging last AOD files, to reach a good size for that too = $delta s" echo "exitcode = $exitcode" if [[ $exitcode -ne 0 ]]; then - echo "exit code from aod-merger for latest file is " $exitcode > validation_error.message - echo "exit code from aod-merger for latest file is " $exitcode - exit $exitcode + echo "exit code from aod-merger for latest file is " $exitcode > validation_error.message + echo "exit code from aod-merger for latest file is " $exitcode + exit $exitcode fi MERGED_SIZE=`wc -c AO2D.root | awk '{print $1}'` echo "Size of merged file: $MERGED_SIZE" @@ -657,88 +845,44 @@ if [[ $ALIEN_JDL_AODOFF != 1 ]]; then for (( i = 1; i <=$AOD_LIST_COUNT; i++)); do AOD_FILE=`echo $AOD_LIST | cut -d' ' -f$i` AOD_DIR=`dirname $AOD_FILE | sed -e 's|./||'` - cd $AOD_DIR - if [[ -f "AO2D.root" ]]; then - echo "Checking AO2Ds with un-merged DFs in $AOD_DIR" - timeStartCheck=`date +%s` - time root -l -b -q $O2DPG_ROOT/DATA/production/common/readAO2Ds.C > checkAO2D.log - exitcode=$? - timeEndCheck=`date +%s` - timeUsedCheck=$(( $timeUsedCheck+$timeEndCheck-$timeStartCheck )) - if [[ $exitcode -ne 0 ]]; then - echo "exit code from AO2D check is " $exitcode > validation_error.message - echo "exit code from AO2D check is " $exitcode - fi - fi - cd - - ls $AOD_FILE > list_$AOD_DIR.list echo "$AOD_DIR" >> $JOB_LIST done - if [[ -z $ALIEN_JDL_DONOTMERGEAODS ]] || [[ $ALIEN_JDL_DONOTMERGEAODS == 0 ]]; then - # spawning the parallel merging - timeStartMerge=`date +%s` - arr=() - aods=() - mergedok=() - i=0 - while IFS= read -r line; do - while [[ $CURRENT_POOL_SIZE -ge $MAX_POOL_SIZE ]]; do - CURRENT_POOL_SIZE=`jobs -r | wc -l` - sleep 1 - done - run_AOD_merging $line & - arr[$i]=$! - aods[$i]=$line - i=$((i+1)) + # spawning the parallel merging + timeStartMerge=`date +%s` + arr=() + aods=() + mergedok=() + i=0 + while IFS= read -r line; do + while [[ $CURRENT_POOL_SIZE -ge $MAX_POOL_SIZE ]]; do CURRENT_POOL_SIZE=`jobs -r | wc -l` - done < $JOB_LIST - # collecting return codes of the merging processes - for i in ${!arr[@]}; do - wait ${arr[$i]} - exitcode=$? - if [[ $exitcode -ne 0 ]]; then - echo "Exit code from the process merging DFs inside AO2D for ${aods[$i]} is " $exitcode > validation_error.message - echo "Exit code from the process merging DFs inside AO2D for ${aods[$i]} is " $exitcode - echo "As a consequence, we will keep the AO2Ds with unmerged DFs for ${aods[$i]}" - mergedok[$((10#${aods[$i]}))]=0 - else - echo "Merging of DFs inside the AO2D in ${aods[$i]} worked correctly" - mergedok[$((10#${aods[$i]}))]=1 - fi - done - timeEndMerge=`date +%s` - timeUsedMerge=$(( $timeUsedMerge+$timeEndMerge-$timeStartMerge )) - # Checking the merged AODs, and replacing the original ones with the merged ones if all is ok - # This loop could be merged with the above, but for now we keep them separate - for (( i = 1; i <=$AOD_LIST_COUNT; i++)); do - AOD_FILE=`echo $AOD_LIST | cut -d' ' -f$i` - AOD_DIR=`dirname $AOD_FILE | sed -e 's|./||'` - echo "Inspecting $AOD_DIR:" - if [[ ${mergedok[$((10#$AOD_DIR))]} == 0 ]]; then - echo "Merging for $AOD_DIR DID NOT work, we will do nothing for this file" - continue - else - echo "Merging for $AOD_DIR DID work, let's continue" - fi - cd $AOD_DIR - # now checking them - echo "Checking AO2Ds with merged DFs in $AOD_DIR" - timeStartCheckMergedAOD=`date +%s` - time root -l -b -q '$O2DPG_ROOT/DATA/production/common/readAO2Ds.C("AO2D_merged.root")' > checkAO2D_merged.log - exitcode=$? - timeEndCheckMergedAOD=`date +%s` - timeUsedCheckMergedAOD=$(( $timeUsedCheckMergedAOD+$timeEndCheckMergedAOD-$timeStartCheckMergedAOD )) - if [[ $exitcode -ne 0 ]]; then - echo "exit code from AO2D with merged DFs check is " $exitcode > validation_error.message - echo "exit code from AO2D with merged DFs check is " $exitcode - echo "We will keep the AO2Ds with unmerged DFs" - else - echo "All ok, replacing initial AO2D.root file with the one with merged DFs" - mv AO2D_merged.root AO2D.root - fi - cd .. + sleep 1 done - fi + run_AOD_merging $line & + arr[$i]=$! + aods[$i]=$line + i=$((i+1)) + CURRENT_POOL_SIZE=`jobs -r | wc -l` + done < $JOB_LIST + # collecting return codes of the merging processes + for i in "${!arr[@]}"; do + wait ${arr[$i]} + exitcode=$? + if [[ $exitcode -ne 0 ]]; then + echo "Exit code from the process check+merging+check_mergedAODs for ${aods[$i]} is " $exitcode > validation_error.message + echo "Exit code from the process check+merging+check_mergedAODs for ${aods[$i]} is " $exitcode + echo "This means that the process check+merging+check_mergedAODs for ${aods[$i]} FAILED, we make the whole processing FAIL" + exit $exitcode + mergedok[$((10#${aods[$i]}))]=0 + else + echo "Merging of DFs inside the AO2D in ${aods[$i]} worked correctly" + mergedok[$((10#${aods[$i]}))]=1 + fi + done + timeEndMerge=`date +%s` + timeUsedMerge=$(( $timeUsedMerge+$timeEndMerge-$timeStartMerge )) + echo "--> Total Time spent in checking and merging AODs = $timeUsedMerge s" + # running analysis QC if requested if [[ $ALIEN_JDL_RUNANALYSISQC == 1 ]]; then for (( i = 1; i <=$AOD_LIST_COUNT; i++)); do @@ -755,40 +899,32 @@ if [[ $ALIEN_JDL_AODOFF != 1 ]]; then timeUsedAnalysisQC=$(( $timeUsedAnalysisQC+$timeEndAnalysisQC-$timeStartAnalysisQC )) echo "exitcode = $exitcode" if [[ $exitcode -ne 0 ]]; then - echo "exit code from Analysis QC is " $exitcode > validation_error.message - echo "exit code from Analysis QC is " $exitcode - exit $exitcode + echo "exit code from Analysis QC is " $exitcode > validation_error.message + echo "exit code from Analysis QC is " $exitcode + exit $exitcode fi if [[ -f "Analysis/MergedAnalyses/AnalysisResults.root" ]]; then - mv Analysis/MergedAnalyses/AnalysisResults.root . + mv Analysis/MergedAnalyses/AnalysisResults.root . else - echo "No Analysis/MergedAnalyses/AnalysisResults.root found! check analysis QC" + echo "No Analysis/MergedAnalyses/AnalysisResults.root found! check analysis QC" fi if ls Analysis/*/*.log 1> /dev/null 2>&1; then - mv Analysis/*/*.log . + mv Analysis/*/*.log . fi cd .. done - else - echo "Analysis QC will not be run, ALIEN_JDL_RUNANALYSISQC = $ALIEN_JDL_RUNANALYSISQC" - fi - echo "Time spent in checking initial AODs = $timeUsedCheck s" - if [[ -z $ALIEN_JDL_DONOTMERGEAODS ]] || [[ $ALIEN_JDL_DONOTMERGEAODS == 0 ]]; then - echo "Time spent in merging AODs = $timeUsedMerge s" - echo "Time spent in checking final AODs = $timeUsedCheckMergedAOD s" - fi - if [[ $ALIEN_JDL_RUNANALYSISQC == 1 ]]; then echo "Time spent in AnalysisQC = $timeUsedAnalysisQC s" else + echo "Analysis QC will not be run, ALIEN_JDL_RUNANALYSISQC = $ALIEN_JDL_RUNANALYSISQC" echo "No timing reported for Analysis QC, since it was not run" fi fi timeEndFullProcessing=`date +%s` -timeUsedFullProcessing=$(( $timeEndFullProcessing+$timeStartFullProcessing )) +timeUsedFullProcessing=$(( $timeEndFullProcessing-$timeStartFullProcessing )) -echo "Time used for processing = $timeUsedFullProcessing s" +echo "Total time used for processing = $timeUsedFullProcessing s" if [[ $ALIEN_JDL_QCOFF != 1 ]]; then # copying the QC json file here diff --git a/DATA/production/configurations/asyncReco/setenv_extra.sh b/DATA/production/configurations/asyncReco/setenv_extra.sh index ebe3c4a2a..a0de92f8b 100644 --- a/DATA/production/configurations/asyncReco/setenv_extra.sh +++ b/DATA/production/configurations/asyncReco/setenv_extra.sh @@ -3,43 +3,117 @@ # process flags passed to the script -if [[ -z "$ALIEN_JDL_USEGPUS" || $ALIEN_JDL_USEGPUS != 1 ]]; then - export SETENV_NO_ULIMIT=1 +# check if this is a production on skimmed data +if grep -q /skimmed/ wn.xml ; then + export ON_SKIMMED_DATA=1; fi -# to avoid memory issues - we don't do this on the EPNs, since it can affect the performance -if [[ $ALIEN_JDL_USEGPUS != 1 ]]; then - export DPL_DEFAULT_PIPELINE_LENGTH=16 +if [[ $RUNNUMBER -lt 544772 ]]; then + # these runs were using external dictionaries + : ${RANS_OPT:="--ans-version compat"} + export RANS_OPT fi +echo "RSRUNNUMBER = $RUNNUMBER RANS_OPT = $RANS_OPT" + +# IR, duration, B field, detector list +if [[ -z $RUN_IR ]] || [[ -z $RUN_DURATION ]] || [[ -z $RUN_BFIELD ]]; then + echo "In setenv_extra: time used so far = $timeUsed" + timeStart=`date +%s` + time o2-calibration-get-run-parameters -r $RUNNUMBER + timeEnd=`date +%s` + timeUsed=$(( $timeUsed+$timeEnd-$timeStart )) + delta=$(( $timeEnd-$timeStart )) + echo "Time spent in getting run parameters = $delta s" + export RUN_IR=`cat IR.txt` + export RUN_DURATION=`cat Duration.txt` + export RUN_BFIELD=`cat BField.txt` + export RUN_DETECTOR_LIST=`cat DetList.txt` +fi + +BABSI=$(printf "%.0f" "${RUN_BFIELD#-}") +[[ "$BABSI" -lt 12500 && "$BABSI" -gt 11500 ]] && LOWFIELD=1 || LOWFIELD=0 + +echo -e "\n" +echo "Printing run features" +echo "DETECTOR LIST for current run ($RUNNUMBER) = $RUN_DETECTOR_LIST" +echo "DURATION for current run ($RUNNUMBER) = $RUN_DURATION" +echo "B FIELD for current run ($RUNNUMBER) = $RUN_BFIELD (LOWFIELD = ${LOWFIELD})" +echo "IR for current run ($RUNNUMBER) = $RUN_IR" +if (( $(echo "$RUN_IR <= 0" | bc -l) )); then + echo "Changing run IR to 1 Hz, because $RUN_IR makes no sense" + RUN_IR=1 +fi +echo "BeamType = $BEAMTYPE" +echo "PERIOD = $PERIOD" + + +if [[ $BEAMTYPE == "pO" ]] || [[ $BEAMTYPE == "Op" ]] || [[ $BEAMTYPE == "Op" ]] || [[ $BEAMTYPE == "OO" ]] || [[ $BEAMTYPE == "NeNe" ]] ; then + export LIGHTNUCLEI=1 +else + export LIGHTNUCLEI=0 +fi + # detector list +echo -e "\n" +echo "Printing detector list for reconstruction" if [[ -n $ALIEN_JDL_WORKFLOWDETECTORS ]]; then + echo "WORKFLOW_DETECTORS taken from JDL, ALIEN_JDL_WORKFLOWDETECTORS = $ALIEN_JDL_WORKFLOWDETECTORS" export WORKFLOW_DETECTORS=$ALIEN_JDL_WORKFLOWDETECTORS else - export WORKFLOW_DETECTORS=ITS,TPC,TOF,FV0,FT0,FDD,MID,MFT,MCH,TRD,EMC,PHS,CPV,HMP,ZDC,CTP + export WORKFLOW_DETECTORS=`echo $RUN_DETECTOR_LIST | sed 's/ /,/g'` if [[ $RUNNUMBER == 528529 ]] || [[ $RUNNUMBER == 528530 ]]; then # removing MID for these runs: it was noisy and therefore declared bad, and makes the reco crash - export WORKFLOW_DETECTORS=ITS,TPC,TOF,FV0,FT0,FDD,MFT,MCH,TRD,EMC,PHS,CPV,HMP,ZDC,CTP + echo "Excluding MID since RUNNUMBER = $RUNNUMBER" + export WORKFLOW_DETECTORS_EXCLUDE="MID" fi + # list of detectors to possibly exclude + if [[ -n $ALIEN_JDL_DETECTORSEXCLUDE ]]; then + echo "DETECTORS_EXCLUDE taken from JDL, ALIEN_JDL_DETECTORSEXCLUDE = $ALIEN_JDL_DETECTORSEXCLUDE" + export DETECTORS_EXCLUDE=$ALIEN_JDL_DETECTORSEXCLUDE # will be used in the async_pass.sh if we run in split mode + if [[ -z ${WORKFLOW_DETECTORS_EXCLUDE:+x} ]]; then # there is no WORKFLOW_DETECTORS_EXCLUDE, or it is NULL + export WORKFLOW_DETECTORS_EXCLUDE=$DETECTORS_EXCLUDE + else + export WORKFLOW_DETECTORS_EXCLUDE+=",$DETECTORS_EXCLUDE" + fi + fi +fi + +if [[ -n $ALIEN_JDL_WORKFLOWDETECTORSRECO ]]; then + echo "WORKFLOW_DETECTORS_RECO taken fron JDL, ALIEN_JDL_WORKFLOWDETECTORSRECO = $ALIEN_JDL_WORKFLOWDETECTORSRECO" + export WORKFLOW_DETECTORS_RECO="$ALIEN_JDL_WORKFLOWDETECTORSRECO" +fi + +if [[ -n $ALIEN_JDL_WORKFLOWDETECTORSMATCHING ]]; then + echo "WORKFLOW_DETECTORS_MATCHING taken fron JDL, ALIEN_JDL_WORKFLOWDETECTORSMATCHING = $ALIEN_JDL_WORKFLOWDETECTORSMATCHING" + export WORKFLOW_DETECTORS_MATCHING="$ALIEN_JDL_WORKFLOWDETECTORSMATCHING" fi +echo "Final settings for detectors to be processed:" +echo "WORKFLOW_DETECTORS = $WORKFLOW_DETECTORS" +echo "WORKFLOW_DETECTORS_EXCLUDE = $WORKFLOW_DETECTORS_EXCLUDE" + # ad-hoc settings for CTF reader: we are on the grid, we read the files remotely -echo "*********************** mode = ${MODE}" -unset ARGS_EXTRA_PROCESS_o2_ctf_reader_workflow +echo -e "\nProcessing mode = ${MODE}" +#unset ARGS_EXTRA_PROCESS_o2_ctf_reader_workflow if [[ $MODE == "remote" ]]; then if [[ $ALIEN_JDL_REMOTEREADING != 1 ]]; then + echo "Files will be copied locally: we expect that the JDL has the \"nodownload\" option" export INPUT_FILE_COPY_CMD="\"alien_cp ?src file://?dst\"" - export ARGS_EXTRA_PROCESS_o2_ctf_reader_workflow="$ARGS_EXTRA_PROCESS_o2_ctf_reader_workflow --remote-regex \"^alien:///alice/data/.+\"" + export ARGS_EXTRA_PROCESS_o2_ctf_reader_workflow+=" --remote-regex \"^alien:///alice/data/.+\"" else + echo "Files will NOT be copied locally: we expect that the job agent takes care" export INPUT_FILE_COPY_CMD="no-copy" fi fi +echo -e "\nSetting up workflow options" + # adjusting for trigger LM_L0 correction, which was not there before July 2022 if [[ $PERIOD == "LHC22c" ]] || [[ $PERIOD == "LHC22d" ]] || [[ $PERIOD == "LHC22e" ]] || [[ $PERIOD == "JUN" ]] || [[ $PERIOD == "LHC22f" ]] ; then if [[ $ALIEN_JDL_LPMPRODUCTIONTYPE != "MC" ]]; then - export ARGS_EXTRA_PROCESS_o2_ctf_reader_workflow="$ARGS_EXTRA_PROCESS_o2_ctf_reader_workflow --correct-trd-trigger-offset" + export ARGS_EXTRA_PROCESS_o2_ctf_reader_workflow+=" --correct-trd-trigger-offset" fi fi @@ -58,21 +132,40 @@ if [[ $remappingITS == 1 ]] || [[ $remappingMFT == 1 ]]; then REMAPPING=$REMAPPING\" fi -echo remapping = $REMAPPING -echo "BeamType = $BEAMTYPE" -echo "PERIOD = $PERIOD" +# generic remapping string +if [[ -n "$ALIEN_JDL_REMAPPINGS" ]]; then + if [[ -n "$REMAPPING" ]]; then + REMAPPING="${REMAPPING::-1};$ALIEN_JDL_REMAPPINGS\"" + else + REMAPPING="--condition-remap \"$ALIEN_JDL_REMAPPINGS\"" + fi +fi + +echo "Remapping = $REMAPPING" # needed if we need more wf export ADD_EXTRA_WORKFLOW= # other ad-hoc settings for CTF reader -export ARGS_EXTRA_PROCESS_o2_ctf_reader_workflow="$ARGS_EXTRA_PROCESS_o2_ctf_reader_workflow --allow-missing-detectors $REMAPPING" +export ARGS_EXTRA_PROCESS_o2_ctf_reader_workflow+=" --allow-missing-detectors $REMAPPING" + +# possibility to only process some TFs +if [[ ! -z ${ALIEN_JDL_RUN_TIME_SPAN_FILE} ]]; then + export ARGS_EXTRA_PROCESS_o2_ctf_reader_workflow+=" --run-time-span-file $ALIEN_JDL_RUN_TIME_SPAN_FILE " + # the following option makes sense only if we have the previous + if [[ ${ALIEN_JDL_INVERT_IRFRAME_SELECTION} == 1 ]]; then + export ARGS_EXTRA_PROCESS_o2_ctf_reader_workflow+=" --invert-irframe-selection " + fi +fi + +# other settings echo RUN = $RUNNUMBER if [[ $RUNNUMBER -ge 521889 ]]; then - export ARGS_EXTRA_PROCESS_o2_ctf_reader_workflow="$ARGS_EXTRA_PROCESS_o2_ctf_reader_workflow --its-digits --mft-digits" - export DISABLE_DIGIT_CLUSTER_INPUT="--digits-from-upstream" - MAXBCDIFFTOMASKBIAS_ITS="ITSClustererParam.maxBCDiffToMaskBias=10" - MAXBCDIFFTOMASKBIAS_MFT="MFTClustererParam.maxBCDiffToMaskBias=10" + export ITSMFT_RECO_RERUN_CLUSTERIZER=1 + MAXBCDIFFTOMASKBIAS_ITS="ITSClustererParam.maxBCDiffToMaskBias=-10" # this explicitly disables ITS masking + MAXBCDIFFTOSQUASHBIAS_ITS="ITSClustererParam.maxBCDiffToSquashBias=10" # this explicitly enables ITS squashing + MAXBCDIFFTOMASKBIAS_MFT="MFTClustererParam.maxBCDiffToMaskBias=-10" # this explicitly disables MFT masking + MAXBCDIFFTOSQUASHBIAS_MFT="MFTClustererParam.maxBCDiffToSquashBias=10" # this explicitly enables MFT squashing fi # shift by +1 BC TRD(2), PHS(4), CPV(5), EMC(6), HMP(7) and by (orbitShift-1)*3564+1 BCs the ZDC since it internally resets the orbit to 1 at SOR and BC is shifted by -1 like for triggered detectors. # run 520403: orbitShift = 59839744 --> final shift = 213268844053 @@ -175,6 +268,7 @@ else echo "************************************************************" fi +echo -e "\nTPC vdrift:" # TPC vdrift PERIODLETTER=${PERIOD: -1} VDRIFTPARAMOPTION= @@ -193,30 +287,7 @@ else echo "TPC vdrift will be taken from CCDB" fi -# IR -if [[ -z $RUN_IR ]] || [[ -z $RUN_DURATION ]] || [[ -z $RUN_BFIELD ]]; then - cp $O2DPG_ROOT/DATA/production/common/getIRandDuration.C ./ - echo "In setenv_extra: time used so far = $timeUsed" - timeStart=`date +%s` - time o2-calibration-get-run-parameters -r $RUNNUMBER - timeEnd=`date +%s` - timeUsed=$(( $timeUsed+$timeEnd-$timeStart )) - delta=$(( $timeEnd-$timeStart )) - echo "Time spent in getting IR and duration of the run = $delta s" - export RUN_IR=`cat IR.txt` - export RUN_DURATION=`cat Duration.txt` - export RUN_BFIELD=`cat BField.txt` - export RUN_DETECTOR_LIST=`cat DetList.txt` -fi -echo "DETECTOR LIST for current run ($RUNNUMBER) = $RUN_DETECTOR_LIST" -echo "DURATION for current run ($RUNNUMBER) = $RUN_DURATION" -echo "B FIELD for current run ($RUNNUMBER) = $RUN_BFIELD" -echo "IR for current run ($RUNNUMBER) = $RUN_IR" -if (( $(echo "$RUN_IR <= 0" | bc -l) )); then - echo "Changing run IR to 1 Hz, because $RUN_IR makes no sense" - RUN_IR=1 -fi - +echo -e "\nTPC calib configuration:" # Let's check if ZDC is in the detector list; this is needed for TPC dist correction scaling in PbPb 2023 SCALE_WITH_ZDC=1 SCALE_WITH_FT0=1 @@ -228,28 +299,43 @@ isFT0inDataTaking=`echo $RUN_DETECTOR_LIST | grep FT0` # For runs shorter than 10 minutes we have only a single slot. # In that case we have to adopt the slot length in order to # set the maximum number of processed tracks per TF correctly -if (( RUN_DURATION < 600 )); then +if (( RUN_DURATION < 300 )); then export CALIB_TPC_SCDCALIB_SLOTLENGTH=$RUN_DURATION fi -echo "BeamType = $BEAMTYPE" - -if [[ $ALIEN_JDL_ENABLEMONITORING == "1" ]]; then +if [[ $ALIEN_JDL_ENABLEMONITORING != "0" ]]; then # add the performance metrics export ENABLE_METRICS=1 - export ARGS_ALL_EXTRA="$ARGS_ALL_EXTRA --resources-monitoring 50 --resources-monitoring-dump-interval 50" + export ARGS_ALL_EXTRA+=" --resources-monitoring 50 --resources-monitoring-dump-interval 50" else # remove monitoring-backend export ENABLE_METRICS=0 fi +if [ -z ${ALIEN_JDL_LPMANCHOREDPASSNUMBER+x} ]; then + export ANCHORED_PASS_NUMBER=$(echo $ALIEN_JDL_LPMPASSNAME | sed 's/^apass//' | cut -c1-1) + echo "using ANCHORED_PASS_NUMBER from pass name $ALIEN_JDL_LPMPASSNAME, ANCHORED_PASS_NUMBER=$ANCHORED_PASS_NUMBER" +else + # variable exported, takes precedence over anything else + export ANCHORED_PASS_NUMBER=$ALIEN_JDL_LPMANCHOREDPASSNUMBER + echo "using ANCHORED_PASS_NUMBER from $ALIEN_JDL_LPMANCHOREDPASSNUMBER defined by JDL, ANCHORED_PASS_NUMBER=$ANCHORED_PASS_NUMBER" +fi + +echo "Checking ANCHORED_PASS_NUMBER" +if [ -n "$ANCHORED_PASS_NUMBER" ] && [ "$ANCHORED_PASS_NUMBER" -eq "$ANCHORED_PASS_NUMBER" ] 2>/dev/null; then + echo "ANCHORED_PASS_NUMBER set to $ANCHORED_PASS_NUMBER" +else + echo "[Warning] undetermined ANCHORED_PASS_NUMBER" +fi + + #ALIGNLEVEL=0: before December 2022 alignment, 1: after December 2022 alignment ALIGNLEVEL=1 if [[ "0$OLDVERSION" == "01" ]] && [[ $BEAMTYPE == "PbPb" || $PERIOD == "MAY" || $PERIOD == "JUN" || $PERIOD == "LHC22c" || $PERIOD == "LHC22d" || $PERIOD == "LHC22e" || $PERIOD == "LHC22f" ]]; then ALIGNLEVEL=0 if [[ $ALIEN_JDL_LPMPRODUCTIONTYPE == "MC" ]]; then # extract pass number - ANCHORED_PASS=$ALIEN_JDL_LPMANCHOREPASSNAME + ANCHORED_PASS=$ALIEN_JDL_LPMANCHOREDPASSNAME ANCHORED_PASS_NUMBER=`echo $ANCHORED_PASS | sed 's/^apass//'` echo "ANCHORED_PASS_NUMER = $ANCHORED_PASS_NUMBER" if [[ $PERIOD == "MAY" || $PERIOD == "JUN" ]] && [[ $ANCHORED_PASS_NUMBER -gt 1 ]]; then @@ -260,6 +346,8 @@ if [[ "0$OLDVERSION" == "01" ]] && [[ $BEAMTYPE == "PbPb" || $PERIOD == "MAY" || fi fi +EXTRA_PRIMVTX_TimeMargin="" + # some settings in common between workflows and affecting ITS-TPC matching : ${CUT_MATCH_CHI2:=250} if [[ $ALIGNLEVEL == 0 ]]; then @@ -272,72 +360,187 @@ elif [[ $ALIGNLEVEL == 1 ]]; then ERRIB="100e-8" ERROB="100e-8" [[ -z $TPCITSTIMEERR ]] && TPCITSTIMEERR="0.2" - [[ -z $ITS_CONFIG || "$ITS_CONFIG" != *"--tracking-mode"* ]] && export ITS_CONFIG+=" --tracking-mode async" - CUT_MATCH_CHI2=160 - export ITSTPCMATCH="tpcitsMatch.safeMarginTimeCorrErr=10.;tpcitsMatch.cutMatchingChi2=$CUT_MATCH_CHI2;;tpcitsMatch.crudeAbsDiffCut[0]=6;tpcitsMatch.crudeAbsDiffCut[1]=6;tpcitsMatch.crudeAbsDiffCut[2]=0.3;tpcitsMatch.crudeAbsDiffCut[3]=0.3;tpcitsMatch.crudeAbsDiffCut[4]=5;tpcitsMatch.crudeNSigma2Cut[0]=100;tpcitsMatch.crudeNSigma2Cut[1]=100;tpcitsMatch.crudeNSigma2Cut[2]=100;tpcitsMatch.crudeNSigma2Cut[3]=100;tpcitsMatch.crudeNSigma2Cut[4]=100;" + if [[ $ALIEN_JDL_LPMANCHORYEAR == "2023" && $BEAMTYPE == "PbPb" && $ANCHORED_PASS_NUMBER -lt 5 ]] || [[ $PERIOD == "LHC24al" ]] ; then + [[ $ALIEN_JDL_LPMANCHORYEAR == "2023" ]] && [[ $BEAMTYPE == "PbPb" ]] && CUT_MATCH_CHI2=80 || CUT_MATCH_CHI2=100 + export ITSTPCMATCH="tpcitsMatch.safeMarginTimeCorrErr=2.;tpcitsMatch.XMatchingRef=60.;tpcitsMatch.cutMatchingChi2=$CUT_MATCH_CHI2;;tpcitsMatch.crudeAbsDiffCut[0]=6;tpcitsMatch.crudeAbsDiffCut[1]=6;tpcitsMatch.crudeAbsDiffCut[2]=0.3;tpcitsMatch.crudeAbsDiffCut[3]=0.3;tpcitsMatch.crudeAbsDiffCut[4]=2.5;tpcitsMatch.crudeNSigma2Cut[0]=64;tpcitsMatch.crudeNSigma2Cut[1]=64;tpcitsMatch.crudeNSigma2Cut[2]=64;tpcitsMatch.crudeNSigma2Cut[3]=64;tpcitsMatch.crudeNSigma2Cut[4]=64;" + export ITSTPCMATCH="${ITSTPCMATCH};tpcitsMatch.askMinTPCRow[0]=20;tpcitsMatch.askMinTPCRow[1]=20;tpcitsMatch.askMinTPCRow[2]=20;tpcitsMatch.askMinTPCRow[3]=20;tpcitsMatch.askMinTPCRow[4]=20;tpcitsMatch.askMinTPCRow[5]=20;tpcitsMatch.askMinTPCRow[6]=20;tpcitsMatch.askMinTPCRow[7]=20;tpcitsMatch.askMinTPCRow[8]=20;tpcitsMatch.askMinTPCRow[9]=20;tpcitsMatch.askMinTPCRow[10]=20;tpcitsMatch.askMinTPCRow[12]=20;tpcitsMatch.askMinTPCRow[13]=20;tpcitsMatch.askMinTPCRow[14]=20;" + export ITSTPCMATCH="${ITSTPCMATCH};tpcitsMatch.askMinTPCRow[15]=20;tpcitsMatch.askMinTPCRow[16]=20;tpcitsMatch.askMinTPCRow[17]=20;tpcitsMatch.askMinTPCRow[18]=20;tpcitsMatch.askMinTPCRow[19]=20;tpcitsMatch.askMinTPCRow[20]=20;tpcitsMatch.askMinTPCRow[21]=20;tpcitsMatch.askMinTPCRow[22]=20;tpcitsMatch.askMinTPCRow[23]=20;tpcitsMatch.askMinTPCRow[24]=20;tpcitsMatch.askMinTPCRow[25]=20;tpcitsMatch.askMinTPCRow[26]=20;tpcitsMatch.askMinTPCRow[27]=20;tpcitsMatch.askMinTPCRow[28]=20;tpcitsMatch.askMinTPCRow[29]=20;" + export ITSTPCMATCH="${ITSTPCMATCH};tpcitsMatch.askMinTPCRow[30]=20;tpcitsMatch.askMinTPCRow[31]=20;tpcitsMatch.askMinTPCRow[32]=20;tpcitsMatch.askMinTPCRow[33]=20;tpcitsMatch.askMinTPCRow[34]=20;tpcitsMatch.askMinTPCRow[35]=20;" + if [[ $RUNNUMBER -ge 544512 ]] ; then + # runs below as well as LHC24al suffer from TPC IROC11 missing but should not reprocessed with tpcitsMatch.askMinTPCRow[11]=78 + RUNSI11BAD=(544947 545289 545004 545060 544931 545184 545345 544963 544991 544911 545103 545086) + [[ $PERIOD != "LHC24al" ]] && APPLYS11=1 || APPLYS11=0 + for irs11 in "${RUNSI11BAD[@]}" ; do + if [[ $RUNNUMBER == $irs11 ]] ; then + APPLYS11=0; + break; + fi + done + fi + [[ $APPLYS11 == 1 ]] && export ITSTPCMATCH="${ITSTPCMATCH};tpcitsMatch.askMinTPCRow[11]=78;" || export ITSTPCMATCH="${ITSTPCMATCH};tpcitsMatch.askMinTPCRow[11]=20;" + elif [[ $BEAMTYPE == "PbPb" && ( ( $ALIEN_JDL_LPMANCHORYEAR == "2023" && $ANCHORED_PASS_NUMBER -gt 4 ) || ( $ALIEN_JDL_LPMANCHORYEAR == "2024" && $ANCHORED_PASS_NUMBER -gt 1 ) ) ]] ; then + export ITSTPCMATCH="${ITSTPCMATCH};tpcitsMatch.askMinTPCRow[0]=78;tpcitsMatch.askMinTPCRow[1]=78;tpcitsMatch.askMinTPCRow[2]=78;tpcitsMatch.askMinTPCRow[3]=78;tpcitsMatch.askMinTPCRow[4]=78;tpcitsMatch.askMinTPCRow[5]=78;tpcitsMatch.askMinTPCRow[6]=78;tpcitsMatch.askMinTPCRow[7]=78;tpcitsMatch.askMinTPCRow[8]=78;tpcitsMatch.askMinTPCRow[9]=78;tpcitsMatch.askMinTPCRow[10]=78;tpcitsMatch.askMinTPCRow[11]=78;tpcitsMatch.askMinTPCRow[12]=78;tpcitsMatch.askMinTPCRow[13]=78;tpcitsMatch.askMinTPCRow[14]=78;tpcitsMatch.askMinTPCRow[15]=78;tpcitsMatch.askMinTPCRow[16]=78;tpcitsMatch.askMinTPCRow[17]=78;tpcitsMatch.askMinTPCRow[18]=78;tpcitsMatch.askMinTPCRow[19]=78;tpcitsMatch.askMinTPCRow[20]=78;tpcitsMatch.askMinTPCRow[21]=78;tpcitsMatch.askMinTPCRow[22]=78;tpcitsMatch.askMinTPCRow[23]=78;tpcitsMatch.askMinTPCRow[24]=78;tpcitsMatch.askMinTPCRow[25]=78;tpcitsMatch.askMinTPCRow[26]=78;tpcitsMatch.askMinTPCRow[27]=78;tpcitsMatch.askMinTPCRow[28]=78;tpcitsMatch.askMinTPCRow[29]=78;tpcitsMatch.askMinTPCRow[30]=78;tpcitsMatch.askMinTPCRow[31]=78;tpcitsMatch.askMinTPCRow[32]=78;tpcitsMatch.askMinTPCRow[33]=78;tpcitsMatch.askMinTPCRow[34]=78;tpcitsMatch.askMinTPCRow[35]=78;" + elif [[ $BEAMTYPE == "pp" || $LIGHTNUCLEI == "1" ]] ; then + export ITSTPCMATCH="${ITSTPCMATCH};tpcitsMatch.askMinTPCRow[0]=78;tpcitsMatch.askMinTPCRow[1]=78;tpcitsMatch.askMinTPCRow[2]=78;tpcitsMatch.askMinTPCRow[3]=78;tpcitsMatch.askMinTPCRow[4]=78;tpcitsMatch.askMinTPCRow[5]=78;tpcitsMatch.askMinTPCRow[6]=78;tpcitsMatch.askMinTPCRow[7]=78;tpcitsMatch.askMinTPCRow[8]=78;tpcitsMatch.askMinTPCRow[9]=78;tpcitsMatch.askMinTPCRow[10]=78;tpcitsMatch.askMinTPCRow[11]=78;tpcitsMatch.askMinTPCRow[12]=78;tpcitsMatch.askMinTPCRow[13]=78;tpcitsMatch.askMinTPCRow[14]=78;tpcitsMatch.askMinTPCRow[15]=78;tpcitsMatch.askMinTPCRow[16]=78;tpcitsMatch.askMinTPCRow[17]=78;tpcitsMatch.askMinTPCRow[18]=78;tpcitsMatch.askMinTPCRow[19]=78;tpcitsMatch.askMinTPCRow[20]=78;tpcitsMatch.askMinTPCRow[21]=78;tpcitsMatch.askMinTPCRow[22]=78;tpcitsMatch.askMinTPCRow[23]=78;tpcitsMatch.askMinTPCRow[24]=78;tpcitsMatch.askMinTPCRow[25]=78;tpcitsMatch.askMinTPCRow[26]=78;tpcitsMatch.askMinTPCRow[27]=78;tpcitsMatch.askMinTPCRow[28]=78;tpcitsMatch.askMinTPCRow[29]=78;tpcitsMatch.askMinTPCRow[30]=78;tpcitsMatch.askMinTPCRow[31]=78;tpcitsMatch.askMinTPCRow[32]=78;tpcitsMatch.askMinTPCRow[33]=78;tpcitsMatch.askMinTPCRow[34]=78;tpcitsMatch.askMinTPCRow[35]=78;" + fi + + + # settings to improve inner pad-rows contribution + export CONFIG_EXTRA_PROCESS_o2_gpu_reco_workflow+=";GPU_rec_tpc.trackletMinSharedNormFactor=1.;GPU_rec_tpc.trackletMaxSharedFraction=0.3;GPU_rec_tpc.rejectIFCLowRadiusCluster=1;" + if grep extrapolationTrackingRowRange $O2_ROOT/include/GPU/GPUSettingsList.h &> /dev/null ; then + export CONFIG_EXTRA_PROCESS_o2_gpu_reco_workflow+="GPU_rec_tpc.extrapolationTrackingRowRange=100;" + else + export CONFIG_EXTRA_PROCESS_o2_gpu_reco_workflow+="GPU_rec_tpc.globalTrackingRowRange=100;" + fi + + if [[ -n "$ALIEN_JDL_TPCDEDXCLMASK" ]]; then + CONFIG_EXTRA_PROCESS_o2_gpu_reco_workflow+="GPU_rec_tpc.dEdxClusterRejectionFlagMask=$ALIEN_JDL_TPCDEDXCLMASK;" + fi + + if [[ -n "$ALIEN_JDL_TPCDEDXCLMASKALT" ]]; then + CONFIG_EXTRA_PROCESS_o2_gpu_reco_workflow+="GPU_rec_tpc.dEdxClusterRejectionFlagMaskAlt=$ALIEN_JDL_TPCDEDXCLMASKALT;" + fi + + if [[ -n "$ALIEN_JDL_TPCEDGETWOPADS" ]]; then + CONFIG_EXTRA_PROCESS_o2_gpu_reco_workflow+="GPU_rec_tpc.cfEdgeTwoPads=$ALIEN_JDL_TPCEDGETWOPADS;" + fi + + if [[ -n "$ALIEN_JDL_TPCCLUSTERFILTER" ]]; then + CONFIG_EXTRA_PROCESS_o2_gpu_reco_workflow+="GPU_proc.tpcUseOldCPUDecoding=1;GPU_proc.tpcApplyClusterFilterOnCPU=$ALIEN_JDL_TPCCLUSTERFILTER;" + fi + + if [[ -n "$ALIEN_JDL_TPCMEMORYSCALING" ]]; then + CONFIG_EXTRA_PROCESS_o2_gpu_reco_workflow+="GPU_proc.memoryScalingFactor=$ALIEN_JDL_TPCMEMORYSCALING;" + fi + + if [[ -n "$ALIEN_JDL_TPCCHICUTOPT" ]]; then # 0 or 1 to disable or enable (default) the chi2 cut both on one-side and smoothed Kalman chi2 + CONFIG_EXTRA_PROCESS_o2_gpu_reco_workflow+="GPU_rec_tpc.mergerInterpolateRejectAlsoOnCurrentPosition=$ALIEN_JDL_TPCCHICUTOPT;" + fi + + if [[ -n "$ALIEN_JDL_TPCCLUSEDGEREDEF" ]]; then # if >0 (float) undo the edge cluster bit for TPC clusters with distance to the edge exceeding this value + CONFIG_EXTRA_PROCESS_o2_gpu_reco_workflow+="GPU_rec_tpc.clustersEdgeFixDistance=$ALIEN_JDL_TPCCLUSEDGEREDEF;" + fi - # enabling TPC calibration scaling + #-------------------------------------- TPC corrections ----------------------------------------------- + # we need to provide to TPC + # 1) interaction rate info (lumi) used for scaling or errors and possible of the corrections : INST_IR_FOR_TPC + # 2) what to use for corrections scaling (lumi or IDC scalers or no scaling at all) : TPC_SCALING_SOURCE # the default is to use CTP, unless specified differently in the JDL... INST_IR_FOR_TPC=${ALIEN_JDL_INSTIRFORTPC-CTP} - #...but for 2022 data, where we will rely on different settings depending on the period; note that if ALIEN_JDL_INSTIRFORTPC is set, it has precedence + TPC_SCALING_SOURCE=${ALIEN_JDL_TPCSCALINGSOURCE-IDCCCDB} + # MEAN_IR_FOR_TPC allows (1) to alter the map mean IR if >0 or (2) disable all corrections if <0 + MEAN_IR_FOR_TPC=${ALIEN_JDL_MEANIRFORTPC-} + # optional mean IR to impose for the ref. map + MEAN_IR_REF_FOR_TPC=${ALIEN_JDL_MEANIRREFFORTPC-} + if [[ $ALIEN_JDL_LPMANCHORYEAR == "2022" ]]; then - INST_IR_FOR_TPC=${ALIEN_JDL_INSTIRFORTPC-CTPCCDB} + INST_IR_FOR_TPC=${ALIEN_JDL_INSTIRFORTPC-CTPCCDB} # by default override inst.IR by the mean IR from CCDB and use it for scaling fi + if [[ $PERIOD == "LHC22s" ]]; then - INST_IR_FOR_TPC=${ALIEN_JDL_INSTIRFORTPC-0} # in this way, only TPC/Calib/CorrectionMaps is applied, and we know that for 22s it is the same as TPC/Calib/CorrectionMapsRef; note that if ALIEN_JDL_INSTIRFORTPC is set, it has precedence + TPC_SCALING_SOURCE=${ALIEN_JDL_TPCSCALINGSOURCE-NO_SCALING} # in this way, only TPC/Calib/CorrectionMaps is applied, and we know that for 22s it is the same as TPC/Calib/CorrectionMapsRef; elif [[ $PERIOD == @(LHC22c|LHC22d|LHC22e|JUN|LHC22f) ]]; then INST_IR_FOR_TPC=${ALIEN_JDL_INSTIRFORTPC-1} # scaling with very small value for low IR fi - # in MC, we set it to a negative value to disable completely the corrections (not yet operational though, please check O2); + # in MC, we disable completely the corrections # note that if ALIEN_JDL_INSTIRFORTPC is set, it has precedence if [[ $ALIEN_JDL_LPMPRODUCTIONTYPE == "MC" ]] && [[ $O2DPG_ENABLE_TPC_DISTORTIONS != "ON" ]]; then - INST_IR_FOR_TPC=${ALIEN_JDL_INSTIRFORTPC--1} - fi - - # now we set the options - if [[ $INST_IR_FOR_TPC -gt 0 ]]; then # externally imposed IR for scaling - echo "Applying externally provided IR for scaling, $INST_IR_FOR_TPC Hz" - export TPC_CORR_SCALING+=" --corrmap-lumi-inst $INST_IR_FOR_TPC " - elif [[ $INST_IR_FOR_TPC == 0 ]]; then # when zero, only the TPC/Calib/CorrectionMaps is applied - echo "Passed valued for scaling is zero, only TPC/Calib/CorrectionMaps will be applied" - export TPC_CORR_SCALING+=" --corrmap-lumi-inst $INST_IR_FOR_TPC " - elif [[ $INST_IR_FOR_TPC -lt 0 ]]; then # do not apply any correction - echo "Passed valued for scaling is smaller than zero, no scaling will be applied" - echo "NOTA BENE: In the future, this value will signal to not apply any correction at all, which is not operational yet (but please check, as it depends on O2)" - export TPC_CORR_SCALING+=" --corrmap-lumi-inst $INST_IR_FOR_TPC " - elif [[ $INST_IR_FOR_TPC == "CTPCCDB" ]]; then # using what we have in the CCDB CTP counters, extracted at the beginning of the script - echo "Using CTP CCDB which gave the mean IR of the run at the beginning of the script ($RUN_IR Hz)" - export TPC_CORR_SCALING+=" --corrmap-lumi-inst $RUN_IR " + MEAN_IR_FOR_TPC=${ALIEN_JDL_MEANIRFORTPC--1} + fi + + DISABLE_CORRECTIONS= + [[ -n "$ALIEN_JDL_MSHAPECORRECTION" && $ALIEN_JDL_MSHAPECORRECTION == "0" ]] && ENABLE_MSHAPE=0 || ENABLE_MSHAPE=1 + + if [[ -n $MEAN_IR_FOR_TPC ]] ; then # firs check if corrections were not disabled via MEAN_IR_FOR_TPC + if (( $(echo "$MEAN_IR_FOR_TPC > 0" | bc -l) )) ; then # positive value overrides map mean lumi + echo "Applying externally provided map mean IR for scaling, $MEAN_IR_FOR_TPC" + export TPC_CORR_SCALING+=";TPCCorrMap.lumiMean=$MEAN_IR_FOR_TPC;" # take mean lumy at face value + elif (( $(echo "$MEAN_IR_FOR_TPC < 0" | bc -l) )) ; then # negative mean lumi disables all corrections + echo "Negative MEAN_IR_FOR_TPC -> all TPC corrections will be ignored" + export TPC_CORR_SCALING+=" --lumi-type 0 " + export TPC_CORR_SCALING+=";TPCCorrMap.lumiMean=$MEAN_IR_FOR_TPC;" + ENABLE_MSHAPE=0 + DISABLE_CORRECTIONS=1 + else + echo "Did not recognize MEAN_IR_FOR_TPC = $MEAN_IR_FOR_TPC" + return 1 + fi + fi # MEAN_IR_FOR_TPC overridden + + if [[ -n $MEAN_IR_REF_FOR_TPC ]] ; then + echo "Applying externally provided map mean reference map IR, $MEAN_IR_REF_FOR_TPC" + export TPC_CORR_SCALING+=";TPCCorrMap.lumiMeanRef=$MEAN_IR_REF_FOR_TPC" + fi + + + # set IR for TPC, even if it is not used for corrections scaling + if [[ "$INST_IR_FOR_TPC" =~ ^-?[0-9]*\.?[0-9]+$ ]] && (( $(echo "$INST_IR_FOR_TPC > 0" | bc -l) )) ; then # externally imposed CTP IR + echo "Applying externally provided istantaneous IR $INST_IR_FOR_TPC Hz" + export TPC_CORR_SCALING+=";TPCCorrMap.lumiInst=$INST_IR_FOR_TPC" elif [[ $INST_IR_FOR_TPC == "CTP" ]]; then if ! has_detector CTP ; then - echo "TPC correction with CTP Lumi is requested but CTP is not in the WORKFLOW_DETECTORS=$WORKFLOW_DETECTORS" + echo "CTP Lumi is for TPC corrections but CTP is not in the WORKFLOW_DETECTORS=$WORKFLOW_DETECTORS" return 1 fi echo "Using CTP inst lumi stored in data" - export TPC_CORR_SCALING+=" --require-ctp-lumi " - else - echo "Unknown setting for INST_IR_FOR_TPC = $INST_IR_FOR_TPC (with ALIEN_JDL_INST_IR_FOR_TPC = $ALIEN_JDL_INST_IR_FOR_TPC)" + elif [[ $INST_IR_FOR_TPC == "CTPCCDB" ]]; then # using what we have in the CCDB CTP counters, extracted at the beginning of the script + echo "Using CTP CCDB which gave the mean IR of the run at the beginning of the script ($RUN_IR Hz)" + export TPC_CORR_SCALING+=";TPCCorrMap.lumiInst=$RUN_IR" + else echo "Unknown setting for INST_IR_FOR_TPC = $INST_IR_FOR_TPC (with ALIEN_JDL_INSTIRFORTPC = $ALIEN_JDL_INSTIRFORTPC)" return 1 fi - if [[ -n $ALIEN_JDL_MEANIRFORTPC && $ALIEN_JDL_MEANIRFORTPC > 0 ]]; then # externally imposed TPC map mean IR for scaling - export TPC_CORR_SCALING+=" --corrmap-lumi-mean $ALIEN_JDL_MEANIRFORTPC " + # now set the source of the corrections + if [[ $DISABLE_CORRECTIONS != 1 ]] ; then + if [[ $TPC_SCALING_SOURCE == "NO_SCALING" ]]; then + echo "NO SCALING is requested: only TPC/Calib/CorrectionMapsV2... will be applied" + export TPC_CORR_SCALING+=" --lumi-type 0 " + elif [[ $TPC_SCALING_SOURCE == "CTP" ]]; then + echo "CTP Lumi from data will be used for TPC scaling" + export TPC_CORR_SCALING+=" --lumi-type 1 " + if [[ $ALIEN_JDL_USEDERIVATIVESFORSCALING == "1" ]]; then + export TPC_CORR_SCALING+=" --corrmap-lumi-mode 1 " + fi + elif [[ $TPC_SCALING_SOURCE == "IDCCCDB" ]]; then + echo "TPC correction with IDC from CCDB will be used" + export TPC_CORR_SCALING+=" --lumi-type 2 " + if [[ $ALIEN_JDL_USEDERIVATIVESFORSCALING == "1" ]]; then + export TPC_CORR_SCALING+=" --corrmap-lumi-mode 1 " + fi + else + echo "Unknown setting for TPC_SCALING_SOURCE = $TPC_SCALING_SOURCE (with ALIEN_JDL_TPCSCALINGSOURCE = $ALIEN_JDL_TPCSCALINGSOURCE)" + fi + fi + + if ! has_detector CTP ; then + echo "CTP is not in the list of detectors, disabling CTP Lumi input request" + export TPC_CORR_SCALING+=" --disable-ctp-lumi-request " fi - if [[ $ALIEN_JDL_LPMANCHORYEAR == "2023" ]] && [[ $BEAMTYPE == "PbPb" ]]; then - unset TPC_CORR_SCALING - export TPC_CORR_SCALING=" --ctp-lumi-factor 2.414 --require-ctp-lumi" - if [[ $SCALE_WITH_ZDC == 0 ]]; then - # scaling with FT0 - if [[ $SCALE_WITH_FT0 == 1 ]]; then - export TPC_CORR_SCALING=" --ctp-lumi-source 1 --ctp-lumi-factor 135. --require-ctp-lumi " - else - echo "Neither ZDC nor FT0 are in the run, and this is from 2023 PbPb: we cannot scale TPC ditortion corrections, aborting..." - return 1 - fi + if [[ $ENABLE_MSHAPE == "1" ]]; then + export TPC_CORR_SCALING+=" --enable-M-shape-correction " + fi + + if [[ $ALIEN_JDL_LPMANCHORYEAR -ge 2023 ]] && [[ $BEAMTYPE == "PbPb" ]] ; then + # adding additional cluster errors + # the values below should be squared, but the validation of those values (0.01 and 0.0225) is ongoing + TPCEXTRAERR=";GPU_rec_tpc.clusterError2AdditionalYSeeding=0.1;GPU_rec_tpc.clusterError2AdditionalZSeeding=0.15;" + if [[ $SCALE_WITH_ZDC == 1 ]]; then + echo "For 2023 PbPb ZDC inst. lumi applying factor 2.414" + export TPC_CORR_SCALING+=";TPCCorrMap.lumiInstFactor=2.414;" + elif [[ $SCALE_WITH_FT0 == 1 ]]; then + echo "For 2023 PbPb FT0 inst. lumi applying factor 135." + export TPC_CORR_SCALING+="TPCCorrMap.lumiInstFactor=135.;" + else + echo "Neither ZDC nor FT0 are in the run, and this is from 2023 PbPb: we cannot scale TPC ditortion corrections, aborting..." + return 1 fi fi + echo "Final setting for TPC scaling is:" + echo $TPC_CORR_SCALING + #-------------------------------------- TPC corrections (end)-------------------------------------------- + if [[ $PERIOD != @(LHC22c|LHC22d|LHC22e|JUN|LHC22f) ]] ; then echo "Setting TPCCLUSTERTIMESHIFT to 0" TPCCLUSTERTIMESHIFT=0 @@ -345,15 +548,12 @@ elif [[ $ALIGNLEVEL == 1 ]]; then echo "We are in period $PERIOD, we need to keep the correction for the TPC cluster time, since no new vdrift was extracted" fi - TRACKTUNETPCINNER="trackTuneParams.tpcCovInnerType=1;trackTuneParams.tpcCovInner[0]=0.01;trackTuneParams.tpcCovInner[1]=1.;trackTuneParams.tpcCovInner[2]=4e-7;trackTuneParams.tpcCovInner[3]=4.e-5;trackTuneParams.tpcCovInner[4]=6.8e-6;" - TRACKTUNETPCOUTER="trackTuneParams.tpcCovOuterType=1;trackTuneParams.tpcCovOuter[0]=0.01;trackTuneParams.tpcCovOuter[1]=1.;trackTuneParams.tpcCovOuter[2]=4e-7;trackTuneParams.tpcCovOuter[3]=4.e-5;trackTuneParams.tpcCovOuter[4]=6.8e-6;" + TRACKTUNETPCINNER="trackTuneParams.tpcCovInnerType=2;trackTuneParams.tpcCovInner[0]=0.05;trackTuneParams.tpcCovInner[1]=0.2;trackTuneParams.tpcCovInner[2]=0.0003;trackTuneParams.tpcCovInner[3]=0.0013;trackTuneParams.tpcCovInner[4]=0.0059300284;trackTuneParams.tpcCovInnerSlope[0]=1.4794650254467986e-08;trackTuneParams.tpcCovInnerSlope[1]=5.9178601017871944e-08;trackTuneParams.tpcCovInnerSlope[2]=8.87679015268079e-11;trackTuneParams.tpcCovInnerSlope[3]=3.846609066161676e-10;trackTuneParams.tpcCovInnerSlope[4]=1.7546539235412473e-09;" + TRACKTUNETPCOUTER="trackTuneParams.tpcCovOuterType=2;trackTuneParams.tpcCovOuter[0]=0.05;trackTuneParams.tpcCovOuter[1]=0.2;trackTuneParams.tpcCovOuter[2]=0.0003;trackTuneParams.tpcCovOuter[3]=0.0013;trackTuneParams.tpcCovOuter[4]=0.0059300284;trackTuneParams.tpcCovOuterSlope[0]=1.4794650254467986e-08;trackTuneParams.tpcCovOuterSlope[1]=5.9178601017871944e-08;trackTuneParams.tpcCovOuterSlope[2]=8.87679015268079e-11;trackTuneParams.tpcCovOuterSlope[3]=3.846609066161676e-10;trackTuneParams.tpcCovOuterSlope[4]=1.7546539235412473e-09;" fi -# adding additional cluster errors -# the values below should be squared, but the validation of those values (0.01 and 0.0225) is ongoing -TPCEXTRAERR=";GPU_rec_tpc.clusterError2AdditionalY=0.1;GPU_rec_tpc.clusterError2AdditionalZ=0.15;" -TRACKTUNETPC="$TPCEXTRAERR" +TRACKTUNETPC=${TPCEXTRAERR-} # combining parameters [[ ! -z ${TRACKTUNETPCINNER:-} || ! -z ${TRACKTUNETPCOUTER:-} ]] && TRACKTUNETPC="$TRACKTUNETPC;trackTuneParams.sourceLevelTPC=true;$TRACKTUNETPCINNER;$TRACKTUNETPCOUTER" @@ -363,11 +563,29 @@ export ITSEXTRAERR="ITSCATrackerParam.sysErrY2[0]=$ERRIB;ITSCATrackerParam.sysEr # ad-hoc options for ITS reco workflow EXTRA_ITSRECO_CONFIG= if [[ $BEAMTYPE == "PbPb" ]]; then - EXTRA_ITSRECO_CONFIG="ITSCATrackerParam.trackletsPerClusterLimit=5.;ITSCATrackerParam.cellsPerClusterLimit=5.;ITSVertexerParam.clusterContributorsCut=16;ITSVertexerParam.lowMultBeamDistCut=0;" -elif [[ $BEAMTYPE == "pp" ]]; then - EXTRA_ITSRECO_CONFIG="ITSVertexerParam.phiCut=0.5;ITSVertexerParam.clusterContributorsCut=3;ITSVertexerParam.tanLambdaCut=0.2;" + # tracker memory + EXTRA_ITSRECO_CONFIG=";ITSCATrackerParam.perPrimaryVertexProcessing=false;ITSCATrackerParam.fataliseUponFailure=false;ITSCATrackerParam.dropTFUponFailure=true;ITSCATrackerParam.maxMemory=21474836480;" + if [[ -z "$ALIEN_JDL_DISABLE_UPC" || $ALIEN_JDL_DISABLE_UPC != 1 ]]; then + EXTRA_ITSRECO_CONFIG+=";ITSVertexerParam.nIterations=2;ITSCATrackerParam.doUPCIteration=true;" + fi + if [[ $LOWFIELD == "1" ]]; then + EXTRA_ITSRECO_CONFIG+=";ITSCATrackerParam.minPt=2.5;" # disables B-field scaling + fi +elif [[ $BEAMTYPE == "pp" || $LIGHTNUCLEI == "1" ]]; then + # allowed start layers + EXTRA_ITSRECO_CONFIG=";ITSCATrackerParam.startLayerMask[0]=127;ITSCATrackerParam.startLayerMask[1]=127;ITSCATrackerParam.startLayerMask[2]=127;" + # low pt-cutoffs + EXTRA_ITSRECO_CONFIG+=";ITSCATrackerParam.minPtIterLgt[0]=0.05;ITSCATrackerParam.minPtIterLgt[1]=0.05;ITSCATrackerParam.minPtIterLgt[2]=0.05;ITSCATrackerParam.minPtIterLgt[3]=0.05;ITSCATrackerParam.minPtIterLgt[4]=0.05;ITSCATrackerParam.minPtIterLgt[5]=0.05;ITSCATrackerParam.minPtIterLgt[6]=0.05;ITSCATrackerParam.minPtIterLgt[7]=0.05;ITSCATrackerParam.minPtIterLgt[8]=0.05;ITSCATrackerParam.minPtIterLgt[9]=0.09;ITSCATrackerParam.minPtIterLgt[10]=0.167;ITSCATrackerParam.minPtIterLgt[11]=0.125;" + # enable delta-rof tracking + EXTRA_ITSRECO_CONFIG+=";ITSCATrackerParam.addTimeError[0]=30;ITSCATrackerParam.addTimeError[1]=30;ITSCATrackerParam.addTimeError[2]=30;ITSCATrackerParam.addTimeError[3]=30;ITSCATrackerParam.addTimeError[4]=30;ITSCATrackerParam.addTimeError[5]=30;ITSCATrackerParam.addTimeError[6]=30;ITSVertexerParam.seedMemberRadiusTime=1;" +# this is to impose old pp pT cuts (overriding hardcoded pbpb24 apass1 settings) +# EXTRA_ITSRECO_CONFIG+=";ITSCATrackerParam.minPtIterLgt[0]=0.05;ITSCATrackerParam.minPtIterLgt[1]=0.05;ITSCATrackerParam.minPtIterLgt[2]=0.05;ITSCATrackerParam.minPtIterLgt[3]=0.05;ITSCATrackerParam.minPtIterLgt[4]=0.05;ITSCATrackerParam.minPtIterLgt[5]=0.05;ITSCATrackerParam.minPtIterLgt[6]=0.05;ITSCATrackerParam.minPtIterLgt[7]=0.05;ITSCATrackerParam.minPtIterLgt[8]=0.05;ITSCATrackerParam.minPtIterLgt[9]=0.05;ITSCATrackerParam.minPtIterLgt[10]=0.05;ITSCATrackerParam.minPtIterLgt[11]=0.05;" +fi +export CONFIG_EXTRA_PROCESS_o2_its_reco_workflow+=";$MAXBCDIFFTOMASKBIAS_ITS;$MAXBCDIFFTOSQUASHBIAS_ITS;$EXTRA_ITSRECO_CONFIG;" + +if [[ -n $ALIEN_JDL_ITSRECOARGS ]] ; then + export ARGS_EXTRA_PROCESS_o2_its_reco_workflow+="$ALIEN_JDL_ITSRECOARGS" fi -export CONFIG_EXTRA_PROCESS_o2_its_reco_workflow+=";$MAXBCDIFFTOMASKBIAS_ITS;$EXTRA_ITSRECO_CONFIG;" # in the ALIGNLEVEL there was inconsistency between the internal errors of sync_misaligned and ITSEXTRAERR if [[ $ALIGNLEVEL != 0 ]]; then @@ -376,33 +594,35 @@ fi # ad-hoc options for GPU reco workflow export CONFIG_EXTRA_PROCESS_o2_gpu_reco_workflow+=";GPU_global.dEdxDisableResidualGainMap=1;$TRACKTUNETPC;$VDRIFTPARAMOPTION;" -if [[ $ALIEN_JDL_LPMPRODUCTIONTYPE == "MC" ]]; then - export CONFIG_EXTRA_PROCESS_o2_gpu_reco_workflow+=";GPU_global.dEdxDisableResidualGain=1" -fi + +# if ITS is running on GPU we need to give the workflow most options from ITS reco +has_detector_gpu ITS && export CONFIG_EXTRA_PROCESS_o2_gpu_reco_workflow+=";$CONFIG_EXTRA_PROCESS_o2_its_reco_workflow;" + [[ ! -z $TPCCLUSTERTIMESHIFT ]] && [[ $ALIEN_JDL_LPMPRODUCTIONTYPE != "MC" ]] && export CONFIG_EXTRA_PROCESS_o2_gpu_reco_workflow+=";GPU_rec_tpc.clustersShiftTimebins=$TPCCLUSTERTIMESHIFT;" # ad-hoc settings for TOF reco # export ARGS_EXTRA_PROCESS_o2_tof_reco_workflow+="--use-ccdb --ccdb-url-tof \"http://alice-ccdb.cern.ch\"" # since commit on Dec, 4 -export ARGS_EXTRA_PROCESS_o2_tof_reco_workflow="$ARGS_EXTRA_PROCESS_o2_tof_reco_workflow --use-ccdb" +export ARGS_EXTRA_PROCESS_o2_tof_reco_workflow+=" --use-ccdb" # ad-hoc options for primary vtx workflow #export PVERTEXER="pvertexer.acceptableScale2=9;pvertexer.minScale2=2.;pvertexer.nSigmaTimeTrack=4.;pvertexer.timeMarginTrackTime=0.5;pvertexer.timeMarginVertexTime=7.;pvertexer.nSigmaTimeCut=10;pvertexer.dbscanMaxDist2=30;pvertexer.dcaTolerance=3.;pvertexer.pullIniCut=100;pvertexer.addZSigma2=0.1;pvertexer.tukey=20.;pvertexer.addZSigma2Debris=0.01;pvertexer.addTimeSigma2Debris=1.;pvertexer.maxChi2Mean=30;pvertexer.timeMarginReattach=3.;pvertexer.addTimeSigma2Debris=1.;" # following comment https://alice.its.cern.ch/jira/browse/O2-2691?focusedCommentId=278262&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-278262 #export PVERTEXER="pvertexer.acceptableScale2=9;pvertexer.minScale2=2.;pvertexer.nSigmaTimeTrack=4.;pvertexer.timeMarginTrackTime=0.5;pvertexer.timeMarginVertexTime=7.;pvertexer.nSigmaTimeCut=10;pvertexer.dbscanMaxDist2=36;pvertexer.dcaTolerance=3.;pvertexer.pullIniCut=100;pvertexer.addZSigma2=0.1;pvertexer.tukey=20.;pvertexer.addZSigma2Debris=0.01;pvertexer.addTimeSigma2Debris=1.;pvertexer.maxChi2Mean=30;pvertexer.timeMarginReattach=3.;pvertexer.addTimeSigma2Debris=1.;pvertexer.dbscanDeltaT=24;pvertexer.maxChi2TZDebris=100;pvertexer.maxMultRatDebris=1.;pvertexer.dbscanAdaptCoef=20.;pvertexer.timeMarginVertexTime=1.3" # updated on 7 Sept 2022 -EXTRA_PRIMVTX_TimeMargin="" if [[ $BEAMTYPE == "PbPb" || $PERIOD == "MAY" || $PERIOD == "JUN" || $PERIOD == LHC22* || $PERIOD == LHC23* ]]; then EXTRA_PRIMVTX_TimeMargin="pvertexer.timeMarginVertexTime=1.3" fi export PVERTEXER+=";pvertexer.acceptableScale2=9;pvertexer.minScale2=2;$EXTRA_PRIMVTX_TimeMargin;" if [[ $ALIGNLEVEL == 1 ]]; then - if [[ $BEAMTYPE == "pp" ]]; then + if [[ $BEAMTYPE == "PbPb" ]]; then + export PVERTEXER+=";pvertexer.addTimeSigma2Debris=1e-2;pvertexer.meanVertexExtraErrSelection=0.03;pvertexer.maxITSOnlyFraction=0.85;pvertexer.maxTDiffDebris=1.5;pvertexer.maxZDiffDebris=0.3;pvertexer.addZSigma2Debris=0.09;pvertexer.addTimeSigma2Debris=2.25;pvertexer.maxChi2TZDebris=100;pvertexer.maxMultRatDebris=1.;pvertexer.maxTDiffDebrisExtra=-1.;pvertexer.dbscanDeltaT=-0.55;pvertexer.maxTMAD=1.;pvertexer.maxZMAD=0.04;" + has_detector_reco FT0 && PVERTEX_CONFIG+=" --validate-with-ft0 " + elif [[ $BEAMTYPE == "pp" ]]; then export PVERTEXER+=";pvertexer.maxChi2TZDebris=40;pvertexer.maxChi2Mean=12;pvertexer.maxMultRatDebris=1.;pvertexer.addTimeSigma2Debris=1e-2;pvertexer.meanVertexExtraErrSelection=0.03;" - elif [[ $BEAMTYPE == "PbPb" ]]; then - # at the moment placeholder - export PVERTEXER+=";pvertexer.maxChi2Mean=12;pvertexer.addTimeSigma2Debris=1e-2;pvertexer.meanVertexExtraErrSelection=0.03;" + elif [[ $LIGHTNUCLEI == "1" ]]; then + export PVERTEXER+="pvertexer.maxChi2TZDebris=3000;pvertexer.maxTDiffDebris=0.7;pvertexer.maxZDiffDebris=0.5;pvertexer.maxMultRatDebris=0.25;pvertexer.addTimeSigma2Debris=0.2;pvertexer.addZSigma2Debris=0.2;pvertexer.maxChi2TZDebrisExtra=50;pvertexer.maxTDiffDebrisExtra=-1;pvertexer.maxZDiffDebrisExtra=0.03;pvertexer.maxMultRatDebrisExtra=0.25;pvertexer.meanVertexExtraErrSelection=0.03;pvertexer.maxITSOnlyFraction=0.8;pvertexer.meanVertexExtraErrSelection=0.03;" fi fi @@ -414,54 +634,83 @@ fi if [[ $ALIEN_JDL_DISABLECASCADES == 1 ]]; then export ARGS_EXTRA_PROCESS_o2_secondary_vertexing_workflow+=" --disable-cascade-finder " fi -export SVTX="svertexer.checkV0Hypothesis=false;svertexer.checkCascadeHypothesis=false" -# strangeness tracking -export STRK="" +# allow usage of TPC-only in svertexer (default: do use them, as in default in O2 and CCDB) +if [[ $ALIEN_JDL_DISABLETPCONLYFORV0S == 1 ]]; then + export CONFIG_EXTRA_PROCESS_o2_secondary_vertexing_workflow+=";svertexer.mExcludeTPCtracks=true" +fi export CONFIG_EXTRA_PROCESS_o2_primary_vertexing_workflow+=";$PVERTEXER;$VDRIFTPARAMOPTION;" -export CONFIG_EXTRA_PROCESS_o2_secondary_vertexing_workflow+=";$SVTX" -export CONFIG_EXTRA_PROCESS_o2_strangeness_tracking_workflow+=";$STRK" - export CONFIG_EXTRA_PROCESS_o2_tpcits_match_workflow+=";$ITSEXTRAERR;$ITSTPCMATCH;$TRACKTUNETPC;$VDRIFTPARAMOPTION;" [[ ! -z "${TPCITSTIMEBIAS}" ]] && export CONFIG_EXTRA_PROCESS_o2_tpcits_match_workflow+=";tpcitsMatch.globalTimeBiasMUS=$TPCITSTIMEBIAS;" [[ ! -z "${TPCITSTIMEERR}" ]] && export CONFIG_EXTRA_PROCESS_o2_tpcits_match_workflow+=";tpcitsMatch.globalTimeExtraErrorMUS=$TPCITSTIMEERR;" # enabling AfterBurner -has_detector FT0 && export ARGS_EXTRA_PROCESS_o2_tpcits_match_workflow="$ARGS_EXTRA_PROCESS_o2_tpcits_match_workflow --use-ft0" +has_detector FT0 && export ARGS_EXTRA_PROCESS_o2_tpcits_match_workflow+=" --use-ft0" # ad-hoc settings for TOF matching -export ARGS_EXTRA_PROCESS_o2_tof_matcher_workflow="$ARGS_EXTRA_PROCESS_o2_tof_matcher_workflow --output-type matching-info,calib-info --enable-dia --use-fit" +export ARGS_EXTRA_PROCESS_o2_tof_matcher_workflow+=" --output-type matching-info,calib-info --enable-dia" export CONFIG_EXTRA_PROCESS_o2_tof_matcher_workflow+=";$ITSEXTRAERR;$TRACKTUNETPC;$VDRIFTPARAMOPTION;" if [[ $ALIEN_JDL_LPMPASSNAME == "cpass0" ]]; then CONFIG_EXTRA_PROCESS_o2_tof_matcher_workflow+=";MatchTOF.nsigmaTimeCut=6;" + ARGS_EXTRA_PROCESS_o2_tof_reco_workflow+=" --for-calib" fi # ad-hoc settings for TRD matching export CONFIG_EXTRA_PROCESS_o2_trd_global_tracking+=";$ITSEXTRAERR;$TRACKTUNETPC;$VDRIFTPARAMOPTION;GPU_rec_trd.minTrackPt=0.3;" +# 2025/2026 are covered by better ITS alignment and it requires mean vertex. Also the TRD errors are improved +case "$PERIOD:$ANCHORED_PASS_NUMBER" in + "LHC26ak:1" | \ + "LHC26ah:2" | "LHC26aj:2" | \ + "LHC25an:2" | "LHC25ap:2" | "LHC25aq:2" | "LHC25ar:2" | "LHC25as:2" | \ + "LHC25ao:3" | \ + "LHC25aj:3" | "LHC25ak:3" | "LHC25al:3" | \ + "LHC25ad:3" | "LHC25ae:3" | "LHC25af:3" ) + export CONFIG_EXTRA_PROCESS_o2_trd_global_tracking+=";GPU_rec_trd.trkltResRPhiIdeal=0.2;GPU_rec_trd.trkltResVsTanPhiMisalign=1.;" + if [[ -z $ALIEN_JDL_MVBIAS ]]; then + export O2_DPL_MVBIAS=";mvbias.xyz[0]=0.0332;mvbias.xyz[1]=-0.1825;mvbias.xyz[2]=-0.2846;mvbias.slopeX=-4.27e-05;mvbias.slopeY=-6.96e-05;"; + fi +esac + # ad-hoc settings for FT0 -export ARGS_EXTRA_PROCESS_o2_ft0_reco_workflow="$ARGS_EXTRA_PROCESS_o2_ft0_reco_workflow --ft0-reconstructor" +export ARGS_EXTRA_PROCESS_o2_ft0_reco_workflow+=" --ft0-reconstructor" +if [[ $BEAMTYPE == "PbPb" ]]; then + export CONFIG_EXTRA_PROCESS_o2_ft0_reco_workflow+=";FT0TimeFilterParam.mAmpLower=10;" +fi # ad-hoc settings for FV0 -export ARGS_EXTRA_PROCESS_o2_fv0_reco_workflow="$ARGS_EXTRA_PROCESS_o2_fv0_reco_workflow --fv0-reconstructor" +export ARGS_EXTRA_PROCESS_o2_fv0_reco_workflow+=" --fv0-reconstructor" # ad-hoc settings for FDD #... # ad-hoc settings for MFT -if [[ $BEAMTYPE == "pp" || $PERIOD == "LHC22s" ]]; then - export CONFIG_EXTRA_PROCESS_o2_mft_reco_workflow+=";MFTTracking.RBins=30;MFTTracking.PhiBins=120;MFTTracking.ZVtxMin=-13;MFTTracking.ZVtxMax=13;MFTTracking.MFTRadLength=0.084;$MAXBCDIFFTOMASKBIAS_MFT" +if [[ $BEAMTYPE == "pp" || $LIGHTNUCLEI == "1" || $PERIOD == "LHC22s" ]]; then + export CONFIG_EXTRA_PROCESS_o2_mft_reco_workflow+=";MFTTracking.RBins=30;MFTTracking.PhiBins=120;MFTTracking.ZVtxMin=-13;MFTTracking.ZVtxMax=13;MFTTracking.MFTRadLength=0.084;$MAXBCDIFFTOMASKBIAS_MFT;$MAXBCDIFFTOSQUASHBIAS_MFT" else - export CONFIG_EXTRA_PROCESS_o2_mft_reco_workflow+=";MFTTracking.MFTRadLength=0.084;$MAXBCDIFFTOMASKBIAS_MFT" + export CONFIG_EXTRA_PROCESS_o2_mft_reco_workflow+=";MFTTracking.MFTRadLength=0.084;$MAXBCDIFFTOMASKBIAS_MFT;$MAXBCDIFFTOSQUASHBIAS_MFT" fi # ad-hoc settings for MCH -if [[ $BEAMTYPE == "pp" ]]; then +if [[ $BEAMTYPE == "pp" || $LIGHTNUCLEI == "1" ]]; then export CONFIG_EXTRA_PROCESS_o2_mch_reco_workflow+=";MCHTracking.chamberResolutionX=0.4;MCHTracking.chamberResolutionY=0.4;MCHTracking.sigmaCutForTracking=7;MCHTracking.sigmaCutForImprovement=6" fi +# ad-hoc settings for MFT-MCH matching +# Number of MFT-MCH matching candidates to be stored in AO2Ds +# Setting MUON_MATCHING_NCANDIDATES=0 disables the storage of multiple candidates +if [[ -z "${MUON_MATCHING_NCANDIDATES:-}" ]]; then + MUON_MATCHING_NCANDIDATES=0 # disable the saving of nCandidated by default + if [[ $BEAMTYPE == "pp" || $LIGHTNUCLEI == "1" ]]; then MUON_MATCHING_NCANDIDATES=5; fi + if [[ $BEAMTYPE == "PbPb" ]]; then MUON_MATCHING_NCANDIDATES=20; fi +fi +if [[ "x${MUON_MATCHING_NCANDIDATES}" != "x0" ]]; then + export CONFIG_EXTRA_PROCESS_o2_globalfwd_matcher_workflow+=";FwdMatching.saveMode=3;FwdMatching.nCandidates=${MUON_MATCHING_NCANDIDATES};" +fi + + # possibly adding calib steps as done online # could be done better, so that more could be enabled in one go if [[ $ADD_CALIB == "1" ]]; then @@ -489,23 +738,43 @@ if [[ $ADD_CALIB == "1" ]]; then export CALIB_ZDC_TDC=0 export CALIB_FT0_TIMEOFFSET=0 export CALIB_TPC_SCDCALIB=0 - if [[ $DO_TPC_RESIDUAL_EXTRACTION == "1" ]]; then + export CALIB_FT0_INTEGRATEDCURR=0 + export CALIB_FV0_INTEGRATEDCURR=0 + export CALIB_FDD_INTEGRATEDCURR=0 + export CALIB_TOF_INTEGRATEDCURR=0 + export CALIB_ITS_DEADMAP_TIME=0 + export CALIB_MFT_DEADMAP_TIME=0 + if [[ -n "$DO_TPC_RESIDUAL_EXTRACTION" && $DO_TPC_RESIDUAL_EXTRACTION != "0" ]]; then + : ${ALIEN_JDL_TPCRESIDUALTRKSOURCESMAPEXTRACTION:="ITS-TPC"} + : ${ALIEN_JDL_TPCRESIDUALMAXTRACKSPERSLOT:=-1} + : ${ALIEN_JDL_TPCRESIDUALADDTRACKSMAP:=35000000} + : ${ALIEN_JDL_TPCRESIDUAL_WRITEUNFILTERED:=1} + : ${ALIEN_JDL_TPCRESIDUAL_KEEPREJECTEDRES:=1} export CALIB_TPC_SCDCALIB=1 export CALIB_TPC_SCDCALIB_SENDTRKDATA=1 - export CONFIG_EXTRA_PROCESS_o2_tpc_scdcalib_interpolation_workflow="scdcalib.maxTracksPerCalibSlot=35000000;scdcalib.minPtNoOuterPoint=0.2;scdcalib.maxQ2Pt=5;scdcalib.minITSNClsNoOuterPoint=6;scdcalib.minITSNCls=4;scdcalib.minTPCNClsNoOuterPoint=90" - export ARGS_EXTRA_PROCESS_o2_tpc_scdcalib_interpolation_workflow="$ARGS_EXTRA_PROCESS_o2_tpc_scdcalib_interpolation_workflow --process-seeds --enable-itsonly" - # ad-hoc settings for TPC residual extraction - export ARGS_EXTRA_PROCESS_o2_calibration_residual_aggregator="$ARGS_EXTRA_PROCESS_o2_calibration_residual_aggregator --output-type trackParams,unbinnedResid" - if [[ $ALIEN_JDL_DEBUGRESIDUALEXTRACTION == "1" ]]; then - export CONFIG_EXTRA_PROCESS_o2_tpc_scdcalib_interpolation_workflow+=";scdcalib.maxTracksPerCalibSlot=-1;scdcalib.minPtNoOuterPoint=0.8;scdcalib.minTPCNClsNoOuterPoint=120" - export ARGS_EXTRA_PROCESS_o2_trd_global_tracking+="$ARGS_EXTRA_PROCESS_o2_trd_global_tracking --enable-qc" + export CONFIG_EXTRA_PROCESS_o2_tpc_scdcalib_interpolation_workflow+=";scdcalib.maxTracksPerCalibSlot=$ALIEN_JDL_TPCRESIDUALMAXTRACKSPERSLOT;scdcalib.additionalTracksMap=$ALIEN_JDL_TPCRESIDUALADDTRACKSMAP;scdcalib.minPtNoOuterPoint=0.2;scdcalib.maxQ2Pt=5;scdcalib.minITSNClsNoOuterPoint=6;scdcalib.minITSNCls=4;scdcalib.minTPCNClsNoOuterPoint=50;scdcalib.minTOFTRDPVContributors=2;" + CONFIG_EXTRA_PROCESS_o2_tpc_scdcalib_interpolation_workflow+=$([[ "$ALIEN_JDL_TPCRESIDUAL_WRITEUNFILTERED" == "1" ]] && printf "scdcalib.writeUnfiltered=true;" || printf "scdcalib.writeUnfiltered=false;") + CONFIG_EXTRA_PROCESS_o2_tpc_scdcalib_interpolation_workflow+=$([[ "$ALIEN_JDL_TPCRESIDUAL_KEEPREJECTEDRES" == "1" ]] && printf "scdcalib.keepRejectedResiduals=true;" || printf "scdcalib.keepRejectedResiduals=false;") + # additional scdcalib keys from the JDL, appended last so that they override the defaults above + if [[ -n "$ALIEN_JDL_TPCRESIDUAL_EXTRACONFIG" ]]; then + CONFIG_EXTRA_PROCESS_o2_tpc_scdcalib_interpolation_workflow+=";$ALIEN_JDL_TPCRESIDUAL_EXTRACONFIG;" + fi + + export ARGS_EXTRA_PROCESS_o2_tpc_scdcalib_interpolation_workflow+=" --tracking-sources-map-extraction $ALIEN_JDL_TPCRESIDUALTRKSOURCESMAPEXTRACTION" + if [[ ! $DO_TPC_RESIDUAL_EXTRACTION =~ "NOEXTCLROAD" ]]; then + clusterErrors=";GPU_rec_tpc.clusterError2AdditionalY=0.3;GPU_rec_tpc.clusterError2AdditionalZ=0.3" + + export CONFIG_EXTRA_PROCESS_o2_gpu_reco_workflow+=";$clusterErrors" + export CONFIG_EXTRA_PROCESS_o2_tpcits_match_workflow+=";$clusterErrors" + export CONFIG_EXTRA_PROCESS_o2_tof_matcher_workflow+=";$clusterErrors" + export CONFIG_EXTRA_PROCESS_o2_trd_global_tracking+=";$clusterErrors" fi fi export CALIB_EMC_ASYNC_RECALIB="$ALIEN_JDL_DOEMCCALIB" if [[ $ALIEN_JDL_DOTRDVDRIFTEXBCALIB == "1" ]]; then export CALIB_TRD_VDRIFTEXB="$ALIEN_JDL_DOTRDVDRIFTEXBCALIB" - export ARGS_EXTRA_PROCESS_o2_calibration_trd_workflow="$ARGS_EXTRA_PROCESS_o2_calibration_trd_workflow --enable-root-output" - export ARGS_EXTRA_PROCESS_o2_trd_global_tracking="$ARGS_EXTRA_PROCESS_o2_trd_global_tracking --enable-qc" + export ARGS_EXTRA_PROCESS_o2_calibration_trd_workflow+=" --enable-root-output" + export ARGS_EXTRA_PROCESS_o2_trd_global_tracking+=" --enable-qc" fi if [[ $ALIEN_JDL_DOMEANVTXCALIB == 1 ]]; then export CALIB_PRIMVTX_MEANVTX="$ALIEN_JDL_DOMEANVTXCALIB" @@ -514,31 +783,78 @@ if [[ $ADD_CALIB == "1" ]]; then export SVERTEXING_SOURCES=none # disable secondary vertexing fi if [[ $ALIEN_JDL_DOTRDGAINCALIB == 1 ]]; then - export CONFIG_EXTRA_PROCESS_o2_calibration_trd_workflow="TRDCalibParams.minEntriesChamberGainCalib=999999999;TRDCalibParams.minEntriesTotalGainCalib=10000;TRDCalibParams.nTrackletsMinGainCalib=4" - export ARGS_EXTRA_PROCESS_o2_calibration_trd_workflow="$ARGS_EXTRA_PROCESS_o2_calibration_trd_workflow --enable-root-output" + export CONFIG_EXTRA_PROCESS_o2_calibration_trd_workflow+=";TRDCalibParams.minEntriesChamberGainCalib=999999999;TRDCalibParams.minEntriesTotalGainCalib=10000;TRDCalibParams.nTrackletsMinGainCalib=4" + export ARGS_EXTRA_PROCESS_o2_calibration_trd_workflow+=" --enable-root-output" export CALIB_TRD_GAIN=1 fi + # extra workflows in case we want to process the currents for FT0, FV0, TOF, TPC + if [[ -n $ALIEN_JDL_EXTRACTCURRENTS ]] ; then + export CALIB_FT0_INTEGRATEDCURR=$ALIEN_JDL_EXTRACTCURRENTS + export CALIB_FV0_INTEGRATEDCURR=$ALIEN_JDL_EXTRACTCURRENTS + export CALIB_FDD_INTEGRATEDCURR=$ALIEN_JDL_EXTRACTCURRENTS + export CALIB_TOF_INTEGRATEDCURR=$ALIEN_JDL_EXTRACTCURRENTS + export CALIB_ASYNC_EXTRACTTPCCURRENTS=$ALIEN_JDL_EXTRACTCURRENTS + fi + if [[ -n $ALIEN_JDL_DISABLE3DCURRENTS ]]; then + export CALIB_ASYNC_DISABLE3DCURRENTS=$ALIEN_JDL_DISABLE3DCURRENTS + fi + + # extra workflows in case we want to process the currents for time series + if [[ -n $ALIEN_JDL_EXTRACTTIMESERIES ]] ; then + echo "Adding timeseries in setenv_extra.sh" + export CALIB_ASYNC_EXTRACTTIMESERIES=$ALIEN_JDL_EXTRACTTIMESERIES + if [[ -n $ALIEN_JDL_ENABLEUNBINNEDTIMESERIES ]]; then + export CALIB_ASYNC_ENABLEUNBINNEDTIMESERIES=$ALIEN_JDL_ENABLEUNBINNEDTIMESERIES + fi + if [[ -n $ALIEN_JDL_SAMPLINGFACTORTIMESERIES ]]; then + export CALIB_ASYNC_SAMPLINGFACTORTIMESERIES=$ALIEN_JDL_SAMPLINGFACTORTIMESERIES + fi + if [[ -n $ALIEN_JDL_TPCTIMESERIESMINMOMENTUM ]]; then + export TPCTIMESERIES_MIN_MOMENTUM=$ALIEN_JDL_TPCTIMESERIESMINMOMENTUM + fi + if [[ -n $ALIEN_JDL_TPCTIMESERIESMINCLUSTER ]]; then + export TPCTIMESERIES_MIN_CLUSTER=$ALIEN_JDL_TPCTIMESERIESMINCLUSTER + fi + if [[ -n $ALIEN_JDL_TPCTIMESERIESMAXTGL ]]; then + export TPCTIMESERIES_MAX_TGL=$ALIEN_JDL_TPCTIMESERIESMAXTGL + fi + if [[ -n $ALIEN_JDL_TPCTIMESERIESMULTMAX ]]; then + export TPCTIMESERIES_MULT_MAX=$ALIEN_JDL_TPCTIMESERIESMULTMAX + fi + fi + + # cosmic muons in collision data: matcher (TPC-only legs) + raw clusters of each cosmic -> o2_cosmics_full.root + if [[ -n ${ALIEN_JDL_EXTRACTCOSMICS:-} ]] ; then + echo "Adding cosmics matching and raw-cluster output in setenv_extra.sh" + export CALIB_ASYNC_EXTRACTCOSMICS=$ALIEN_JDL_EXTRACTCOSMICS + if [[ -n ${ALIEN_JDL_COSMICSPRESET:-} ]]; then + export COSMICS_PRESET=$ALIEN_JDL_COSMICSPRESET + fi + # optional: matcher settings on top of the preset (e.g. cosmicsMatch.minSeedDCAxyTOF=0), extra options (e.g. --debug-tree), and + # COSMICSPVVETO=0 to switch off the veto of legs belonging to primary-vertex contributors (on by default in calib-workflow.sh) + if [[ -n ${ALIEN_JDL_COSMICSCONFIG:-} ]]; then + export CONFIG_EXTRA_PROCESS_o2_cosmics_match_workflow+=";$ALIEN_JDL_COSMICSCONFIG" + fi + if [[ -n ${ALIEN_JDL_COSMICSARGS:-} ]]; then + export ARGS_EXTRA_PROCESS_o2_cosmics_match_workflow+=" $ALIEN_JDL_COSMICSARGS" + fi + if [[ -n ${ALIEN_JDL_COSMICSPVVETO:-} ]]; then + export COSMICS_PV_VETO=$ALIEN_JDL_COSMICSPVVETO + fi + fi if [[ $ALIEN_JDL_DOUPLOADSLOCALLY == 1 ]]; then export CCDB_POPULATOR_UPLOAD_PATH="file://$PWD" fi -fi -# extra workflows in case we want to process the currents for FT0, FV0, TOF, TPC -if [[ $ALIEN_JDL_EXTRACTCURRENTS == 1 ]]; then - if [[ -z "${WORKFLOW_DETECTORS_RECO+x}" ]] || [[ "0$WORKFLOW_DETECTORS_RECO" == "0ALL" ]]; then export WORKFLOW_DETECTORS_RECO=$WORKFLOW_DETECTORS; fi - has_detector_reco FT0 && add_comma_separated ADD_EXTRA_WORKFLOW "o2-ft0-integrate-cluster-workflow" - has_detector_reco FV0 && add_comma_separated ADD_EXTRA_WORKFLOW "o2-fv0-integrate-cluster-workflow" - has_detector_reco TOF && add_comma_separated ADD_EXTRA_WORKFLOW "o2-tof-integrate-cluster-workflow" - if [[ $ALIEN_JDL_DISABLE3DCURRENTS != 1 ]]; then - export ARGS_EXTRA_PROCESS_o2_tpc_integrate_cluster_workflow="$ARGS_EXTRA_PROCESS_o2_tpc_integrate_cluster_workflow--process-3D-currents --nSlicesTF 1" + # enable time gain calibration + if [[ $ALIEN_JDL_DOTPCTIMEGAINCALIB == 1 ]]; then + echo "Enabling TPC time gain calibration" + export CALIB_TPC_TIMEGAIN=1 + export ARGS_EXTRA_PROCESS_o2_tpc_calibrator_dedx+=" --dump-histograms 1 --min-entries 1" # write full calibration objects for time gain without rejecting low statistics timeslots + export ARGS_EXTRA_PROCESS_o2_tpc_miptrack_filter+=" --use-global-tracks" + export SCALEEVENTS_TPC_TIMEGAIN=${SCALEEVENTS_TPC_TIMEGAIN:-1} # use all TFs + export SCALETRACKS_TPC_TIMEGAIN=${SCALETRACKS_TPC_TIMEGAIN:--1} # use all tracks fi - has_detector_reco TPC && add_comma_separated ADD_EXTRA_WORKFLOW "o2-tpc-integrate-cluster-workflow" -fi - -# extra workflows in case we want to process the currents for time series -if [[ $ALIEN_JDL_EXTRACTTIMESERIES == 1 ]]; then - if [[ -z "${WORKFLOW_DETECTORS_RECO+x}" ]] || [[ "0$WORKFLOW_DETECTORS_RECO" == "0ALL" ]]; then export WORKFLOW_DETECTORS_RECO=$WORKFLOW_DETECTORS; fi - has_detector_reco TPC && has_detector_reco ITS && has_detector_reco FT0 && add_comma_separated ADD_EXTRA_WORKFLOW "o2-tpc-time-series-workflow" fi # Enabling AOD @@ -547,23 +863,71 @@ if [[ $ALIEN_JDL_AODOFF != "1" ]]; then fi # ad-hoc settings for AOD +echo -e "\nNeeded for AODs:" echo ALIEN_JDL_LPMPRODUCTIONTAG = $ALIEN_JDL_LPMPRODUCTIONTAG echo ALIEN_JDL_LPMPASSNAME = $ALIEN_JDL_LPMPASSNAME -export ARGS_EXTRA_PROCESS_o2_aod_producer_workflow="$ARGS_EXTRA_PROCESS_o2_aod_producer_workflow --aod-writer-maxfilesize $AOD_FILE_SIZE --lpmp-prod-tag $ALIEN_JDL_LPMPRODUCTIONTAG --reco-pass $ALIEN_JDL_LPMPASSNAME" +# Track QC table sampling +if [[ -n $ALIEN_JDL_TRACKQCFRACTION ]]; then + TRACKQC_FRACTION=$ALIEN_JDL_TRACKQCFRACTION +else + if [[ $ALIEN_JDL_ENABLEPERMILFULLTRACKQC == "1" ]]; then + PERMIL_FULLTRACKQC=${ALIEN_JDL_PERMILFULLTRACKQC:-100} + INVERSE_PERMIL_FULLTRACKQC=$((1000/PERMIL_FULLTRACKQC)) + if [[ -f wn.xml ]]; then + HASHCODE=`grep alien:// wn.xml | tr ' ' '\n' | grep ^lfn | cut -d\" -f2 | head -1 | cksum | cut -d ' ' -f 1` + else + HASHCODE=`echo "${inputarg}" | cksum | cut -d ' ' -f 1` + fi + if [[ "$((HASHCODE%INVERSE_PERMIL_FULLTRACKQC))" -eq "0" ]]; then + TRACKQC_FRACTION=1 + else + TRACKQC_FRACTION=0.1 + fi + else + TRACKQC_FRACTION=0.1 + fi +fi +echo TRACKQC_FRACTION = $TRACKQC_FRACTION +export ARGS_EXTRA_PROCESS_o2_aod_producer_workflow+=" --aod-writer-maxfilesize $AOD_FILE_SIZE --lpmp-prod-tag $ALIEN_JDL_LPMPRODUCTIONTAG --reco-pass $ALIEN_JDL_LPMPASSNAME --trackqc-fraction $TRACKQC_FRACTION" if [[ $PERIOD == "LHC22c" ]] || [[ $PERIOD == "LHC22d" ]] || [[ $PERIOD == "LHC22e" ]] || [[ $PERIOD == "JUN" ]] || [[ $PERIOD == "LHC22f" ]] || [[ $PERIOD == "LHC22m" ]] || [[ "$RUNNUMBER" == @(526463|526465|526466|526467|526468|526486|526505|526508|526510|526512|526525|526526|526528|526534|526559|526596|526606|526612|526638|526639|526641|526643|526647|526649|526689|526712|526713|526714|526715|526716|526719|526720|526776|526886|526926|526927|526928|526929|526934|526935|526937|526938|526963|526964|526966|526967|526968|527015|527016|527028|527031|527033|527034|527038|527039|527041|527057|527076|527108|527109|527228|527237|527259|527260|527261|527262|527345|527347|527349|527446|527518|527523|527734) ]] ; then - export ARGS_EXTRA_PROCESS_o2_aod_producer_workflow="$ARGS_EXTRA_PROCESS_o2_aod_producer_workflow --ctpreadout-create 1" + export ARGS_EXTRA_PROCESS_o2_aod_producer_workflow+=" --ctpreadout-create 1" +fi + +if [[ $ALIEN_JDL_THINAODS == "1" ]] ; then + export ARGS_EXTRA_PROCESS_o2_aod_producer_workflow+=" --thin-tracks" +fi + +if [[ $ALIEN_JDL_PREPROPAGATE == "1" ]] ; then + export ARGS_EXTRA_PROCESS_o2_aod_producer_workflow+=" --propagate-tracks --propagate-tracks-max-xiu 5" +fi + +if [[ $ALIEN_JDL_CONFIG2AODMETA == "1" ]] ; then + export ARGS_EXTRA_PROCESS_o2_aod_producer_workflow+=" --collect-config-files" +fi + + +# possibility to bias the MeanVertex from the CCDB, see https://github.com/AliceO2Group/AliceO2/pull/15549 +if [[ -n $ALIEN_JDL_MVBIAS ]]; then + export O2_DPL_MVBIAS=$ALIEN_JDL_MVBIAS +fi + +if [[ $ALIEN_JDL_LPMANCHORYEAR -ge 2025 ]] ; then + export CONFIG_EXTRA_PROCESS_o2_gpu_reco_workflow+=";GPU_rec_tpc.minTrackdEdxMax2Tot=-1.;" fi # Enabling QC if [[ $ALIEN_JDL_QCOFF != "1" ]]; then export WORKFLOW_PARAMETERS="QC,${WORKFLOW_PARAMETERS}" fi -export QC_CONFIG_PARAM="--local-batch=QC.root --override-values \"qc.config.Activity.number=$RUNNUMBER;qc.config.Activity.passName=$PASS;qc.config.Activity.periodName=$PERIOD\"" + +export QC_CONFIG_OVERRIDE+=";qc.config.Activity.number=$RUNNUMBER;qc.config.Activity.type=PHYSICS;qc.config.Activity.passName=$PASS;qc.config.Activity.periodName=$PERIOD;qc.config.Activity.beamType=$BEAMTYPE;" + +export QC_CONFIG_PARAM+=" --local-batch=QC.root " export GEN_TOPO_WORKDIR="./" #export QC_JSON_FROM_OUTSIDE="QC-20211214.json" if [[ -n $ALIEN_JDL_QCJSONFROMOUTSIDE ]]; then - QC_JSON_FROM_OUTSIDE=$ALIEN_JDL_QCJSONFROMOUTSIDE + export QC_JSON_FROM_OUTSIDE=$ALIEN_JDL_QCJSONFROMOUTSIDE fi if [[ ! -z $QC_JSON_FROM_OUTSIDE ]]; then sed -i 's/REPLACE_ME_RUNNUMBER/'"${RUNNUMBER}"'/g' $QC_JSON_FROM_OUTSIDE diff --git a/DATA/production/configurations/mapCreationGrid/README.md b/DATA/production/configurations/mapCreationGrid/README.md new file mode 100644 index 000000000..31dd71544 --- /dev/null +++ b/DATA/production/configurations/mapCreationGrid/README.md @@ -0,0 +1,407 @@ +# TPC Map Creation on the GRID + +Runs the ALICE TPC space-charge distortion map creation chain on the ALICE GRID, as a two-stage +pipeline: a heavy map-creation stage (hours per slot) followed by a fast post-processing stage +(minutes per slot). Ported from the original SLURM/Lustre-based production. + +The four ROOT macros this pipeline runs (`staticMapCreatorCPM.C`, `SmoothingExtrapolate.C`, +`voxResQA.C`, `TPCFastTransformInitCPM.C`) live in the main O2 repository, under +`Detectors/TPC/calibration/SpacePoints/macro/` and `GPU/TPCFastTransformation/macro/` respectively, and +install to `$O2_ROOT/share/macro/` as part of a normal O2 build -- this directory only holds the GRID +submission/orchestration layer (list discovery, manifest staging, `grid_submit.sh` wrappers, per-slot +job scripts). + +## Quickstart + +For a normal production on the official O2 build, with defaults left alone, only two settings usually +need changing: the bad-time-ranges list and how long each map should span. Everything else below can be +left as-is. + +1. **Build the slot lists** (local, once per batch of runs): + + ```bash + ./discoverResiduals.sh + ``` + + `` lists one run per line: ` `, e.g. `LHC26ak 572557`. + +2. **Submit stage 1 (map creation).** In `submitMapCreation.sh`, set: + - `BAD_RANGES_NAME` — the bad-time-ranges file for this data period (keep the default if it still + applies). + - `SLOT_LENGTH_MIN` — how many minutes of data go into one map, e.g. `5`. + - `JOBNAME` — any name that's unique to this submission; you'll need it again in step 3. + + Then: + + ```bash + ./submitMapCreation.sh [path/to/lists] + ``` + + `path/to/lists` is the `./lists` directory step 1 wrote (default: `./lists` in the current directory) + — only pass it explicitly if you're running this from a different directory than step 1. + + This takes hours per map — watch progress on alimonitor and wait for it to finish (or mostly finish) + before continuing. + +3. **Submit stage 2 (smoothing + FastTransform).** In `submitPostProcess.sh`, set `STAGE1_JOBNAME_TAG` + to the exact `JOBNAME` used in step 2, then: + + ```bash + ./submitPostProcess.sh + ``` + + This takes minutes per map and produces the final smoothed map, the `FT_*.root` FastTransform file, + and QA plots. + +Everything else in this document explains what the other settings do and how to debug things when they +don't go as expected — skip ahead to it only if you need to change something beyond the three above. + +## Prerequisites + +- A valid AliEn/JAliEn token (`alien-token-init `) and `alien.py` on your `PATH`. +- `O2DPG_ROOT` set (provides `grid_submit.sh`). +- **bash ≥ 4** for the locally-run scripts (`discoverResiduals.sh`, `makeResidualLists.sh`) — they use + `mapfile`. macOS still ships bash 3.2, so on a Mac install a newer one (`brew install bash`) and make + sure it precedes `/bin` on your `PATH`; `#!/usr/bin/env bash` then picks it up. Note this also makes + `set -e` stricter (an arithmetic `(( ))` evaluating to 0 aborts), which is the behaviour lxplus has. +- GNU `parallel` (used by `discoverResiduals.sh`). +- Write access to your AliEn home directory. The wrappers derive + `alien:///alice/cern.ch/user///MapCreation` automatically from `ALIEN_USER` (falling + back to your local username), the same way `grid_submit.sh` derives `MY_HOMEDIR` — **no per-user edit + needed**. Both wrappers print the resolved directory before doing anything, so check that line on your + first run: if your laptop's local username differs from your AliEn account, `export ALIEN_USER=`, or set `MAPCREATION_ALIEN_DIR` to override the whole path. This directory only holds + the slot manifest, the bad-time-ranges list, and the `stage1Workdir` pointer files -- not the actual + map data (see Step 3) -- and, if `MACRO_SOURCE=alien` (see below), a staged copy of the macros. +- A `--packagespec` tag whose O2 build includes the four macros above, **or** `MACRO_SOURCE=alien` (see + "Where the macros come from" below) if no such tag is published yet. + +## Where the macros come from (`MACRO_SOURCE`) + +Every payload script (`mapCreationJob.sh`, `postProcessJob.sh`) reads a +`MACRO_SOURCE` variable, substituted by its wrapper the same way as `JOBNAME_TAG`: + +- **`official` (default)**: runs the macros straight from the O2 package this job's `--packagespec` + loads (`$O2_ROOT/share/macro/.C`) — no staging or download at all. This is the same idiom + `DATA/production/configurations/asyncCalib/createCorrectionMap.sh` already uses for the older + `staticMapCreator.C`/`TPCFastTransformInit.C` pair. Requires `PACKAGESPEC` to resolve to an O2 build new + enough to include `staticMapCreatorCPM.C` et al. If it doesn't, the job fails fast with a clear error + rather than silently falling back. +- **`alien`**: stages a macro from a local path (`MACRO_LOCAL_PATH` in `submitMapCreation.sh`, + `MACRO_LOCAL_DIR` in `submitPostProcess.sh`) to AliEn, and jobs download it + from there instead. Use this until a `--packagespec` tag with the macros is published — point + `MACRO_LOCAL_PATH`/`MACRO_LOCAL_DIR` at your own O2 checkout's copy of the macro(s). + +## The chain, in order + +``` +discoverResiduals.sh (local) find residual files on AliEn, build per-run slot lists + │ + ▼ +submitMapCreation.sh (submits) STAGE 1: map creation (hours/slot) + │ └─ per-slot payload: mapCreationJob.sh + ▼ (wait for jobs to finish on alimonitor) + │ +submitPostProcess.sh (submits) STAGE 2: smoothing + FastTransform (minutes/slot) + └─ per-slot payload: postProcessJob.sh +``` + +Stage 1 and stage 2 are **independent GRID submissions**, not one script. If some stage-1 slots fail +or are still running, stage 2 simply skips those (see "Partial failure" below) — no need to wait for +100% of stage 1 before submitting stage 2, and a failed stage-2 subjob can be resubmitted on its own +without redoing the expensive stage-1 work for that slot. + +### Step 1 — discover residual files and build slot lists (local, once per batch of runs) + +```bash +./discoverResiduals.sh [--calib-pass cpass2_residuals] +``` + +`` is one run per line: ` `, e.g.: + +``` +LHC26ak 572557 +LHC26ak 572558 +``` + +This queries AliEn (`/alice/data/////`) for `o2tpc_residuals_*.root` +files and writes per-run slot lists under `./lists//`. Runs in parallel (`N_PARALLEL=8` in the +script). Safe to re-run; each run is independent. The period is resolved **per line**, so a batch file +may legitimately span periods; malformed rows are rejected up front rather than as one failed run +mid-batch. + +**One list per run — granularity comes from `SLOT_LENGTH_MIN`, not from the catalog.** Each residual +filename carries the aggregation slot label `___` its reco job +assigned, and those labels are **not** a uniform grid — same-length slots are not guaranteed, and a +run's catalog can hold overlapping/nested labels at different granularities. Example (run 572266): + +``` +0_105425 (22 files) 105426_210851 (6) 0_210851 (35) +210852_316277 (2) 210852_421703 (45) +``` + +Slicing per label would give overlapping slots with wildly uneven statistics — the 2- and 6-file ones +trip `MIN_TRACKS_PER_SLICE` and come back `badCalib_` while the data that would have fixed them sits in +a sibling list. So `makeResidualLists.sh` writes **one list per run** holding every file, and you choose +the map length with `SLOT_LENGTH_MIN` at submission time. It is then uniform across runs and independent +of whatever granularity the upstream catalog happens to have. + +`stageSlotsForGrid.py` cuts each run into windows of `SLOT_LENGTH_MIN` and writes **one manifest line +per window**, carrying that window's bounds and only the files whose label overlaps it. A file's +contents are always a subset of its label's range, so this selection never discards data that could +contribute, and it costs nothing — no file is opened. That matters: the macro's own per-file skip has to +*open* each out-of-window file to read its time range (~7 s each over `alien://`, at ~zero CPU), and +AliEn kills a payload whose CPU stays low for 15 min. For run 572266: + +| `SLOT_LENGTH_MIN` | windows × files per subjob (of 110) | +|---|---| +| `0` (one map per run) | 1 × 110 | +| `10` | 2 × (63, 47) | +| `5` | 4 × (57, 41, 47, 45) | + +A coarse-labelled file spanning several windows is deliberately kept by each of them; the macro's per-TF +`firstTFTime`/`lastTFTime` cut then decides which TFs each map actually uses, so nothing is +double-counted. That is why the per-subjob counts can sum to more than the total. + +A pure overlap test is too generous at window edges, though: the aggregator's coarse labels routinely +begin 1–2 ms before a window ends, and that was enough to drag all 45 of their files into a window whose +map they contribute nothing to (run 572266 at 5 min: window 1 took 88 files to use 41). So a file is +only assigned to a window when the overlap **exceeds `OVERLAP_MARGIN_MS`** (default 1000). The cost is +bounded exactly: if a label overlaps by X ms then at most X ms of that file's data belongs here, so +skipping at X ≤ margin loses at most `margin` ms of coverage per window edge — 1 s out of 300 s. The +margin is clamped to a tenth of the window, and `stageSlotsForGrid.py` verifies that every file still +lands in at least one window, warning loudly if the margin is ever set high enough to orphan one. +With it, run 572266 at 5 min gives 57 / 41 / 47 / 45 instead of 63 / 88 / 47 / 45. + +`mapCreationJob.sh` additionally *orders* each window's files by orbit distance from the window centre, +so whatever still comes along is opened last and `MAX_TRACKS_PER_SLICE` usually stops the job first. + +Neither job script does any time arithmetic: both read the run and the window straight off the same +manifest line, so they cannot disagree about a slot's identity. + +**Previewing the split.** The windows are decided at submission time, not at discovery, so `./lists/` +holds one file per *run*, not per window. To see how a given `SLOT_LENGTH_MIN` actually slices your +runs, run the manifest builder in preview mode — it skips the AliEn upload, so it needs no token and +re-queries nothing; add `--window-lists` to also write one plain file list per window: + +```bash +./stageSlotsForGrid.py --lists-dir ./lists --manifest-name mapSlots.preview.tsv \ + --slot-length-min 5 --preview --window-lists +ls mapSlots.preview.windows/ # one .TDof._.txt per window +``` + +`--preview` skips the upload, so it needs no token and re-queries nothing; `--window-lists` additionally +writes the per-window lists. See `./stageSlotsForGrid.py --help` for the full option list. + +The window boundaries and file counts are printed as it goes, which is all you usually need. To see +*which* files a window got, add `--window-lists`. That is off by default because on a parallel +filesystem each list is a metadata round trip: on Lustre, 120 of them measured **32.8 s**, against +0.03 s for all the actual work. Pair it with `--window-dir /tmp/windows` to keep them on local disk. +`--timing` prints the read/compute/write breakdown. + +The manifest itself is always written locally — one file, and the complete record of the split (one +line per window, sixth column being the comma-separated file list). + +Cheap enough to compare several lengths back to back. For run 572266 (20 min): + +| `SLOT_LENGTH_MIN` | windows | files per window | +|---|---|---| +| `10` | 2 | 63, 47 | +| `5` | 4 | 57, 41, 47, 45 | +| `2` | 10 | 57, 57, 63, 41, 41, 47, 47, 47, 45, 45 | + +Note the counts stop falling below ~5 min: a coarse aggregation label spanning 10 minutes overlaps +*every* 2-minute window inside it, so slicing finer than the aggregator's own slot length buys time +resolution but not less I/O per job. Add `--window-lists` to a normal (non-preview) run to write the +same directory alongside the uploaded manifest, so you can check afterwards what a submission actually +used. + +**`./lists/` accumulates.** A second batch discovered into the same tree does not replace the first — +it adds to it, and Step 2 by default puts *everything* under `./lists/` into the manifest. That +silently recalibrates the earlier batch from scratch at hours per slot. Either use a fresh `listsDir` +per batch, or set `RUNS_FILTER` in the Step-2 wrapper (see below). + +### Step 2 — configure and submit stage 1 (map creation) + +Edit the config block at the top of **`submitMapCreation.sh`**: + +| Variable | Meaning | +|---|---| +| `JOBNAME` | Identifies this production. Tags the AliEn manifest (`mapSlots..tsv`) and the small `stage1Workdir..txt` pointer file (see Step 3) — **must be unique per submission** so concurrent/repeated productions don't clobber each other's manifest or pointer. Stage 2 needs to know this value (see Step 4). | +| `TOPWORKDIR` | Static top-level bucket under your GRID home for this job's working directory (replaces `grid_submit.sh`'s generic `selfjobs` default with something identifiable). | +| `SLOT_LENGTH_MIN` | Target length of **one map**, in minutes. Each run is cut into whole steps of this length; a trailing remainder shorter than half a step is absorbed into the last window rather than left as a runt that would fail `MIN_TRACKS_PER_SLICE`. `0` = one map per run. A 20 min run at `5` gives 4 maps, a 60 min run gives 12 — the count follows the run length instead of being fixed. | +| `PACKAGESPEC` | O2/O2DPG software tag to run under — set this to the async-reco tag matching your data. Also determines whether `MACRO_SOURCE=official` can find the macros (see "Where the macros come from" above). | +| `ASUSER` | AliEn account this job runs/is charged as, e.g. `pwg_pp` (your account must already be a member of that group) instead of your own personal account/quota. Determines both which quota pays for the job and where its `${ALIEN_JDL_OUTPUTDIR}` lands (see Step 3). Verify on alimonitor that the first job is really attributed correctly before relying on it for a big batch. | +| `MACRO_SOURCE` / `MACRO_LOCAL_PATH` | See "Where the macros come from" above. | +| `RUNS_FILTER` | Optional comma-separated run whitelist, e.g. `"572557,572558"`. Empty means every run found under `listsDir` — including runs left over from an earlier batch (see Step 1). Naming a run that has no lists is a hard error, not a silently smaller production. | +| `BAD_RANGES_NAME` | Filename of the bad-time-ranges list under `${MACRO_ALIEN_DIR}/lists/`. Period- and system-specific (the default is the PbPb26 list), so change it when the data changes. It is both the upload destination and the name each job downloads, so the two cannot drift apart. Set to `""` to disable bad-ranges filtering entirely for this submission — a deliberate opt-out (`staticMapCreatorCPM.C` treats an empty filename that way), not the same as forgetting to set it. | +| `BAD_RANGES_LOCAL` | Local path to the bad-time-ranges list, only needed if you want to (re-)upload a new one this submission. Leave empty to reuse whatever is already staged on AliEn under `BAD_RANGES_NAME` — the wrapper checks that it actually exists there before submitting anything, so a wrong `BAD_RANGES_NAME`/`ALIEN_USER` or a never-uploaded file fails fast, locally, once, instead of inside every GRID subjob. | +| `MACRO_ALIEN_DIR_BARE` | AliEn directory (no `alien://` prefix) used for staging the slot manifest and the small `stage1Workdir` pointer file (see Step 3) — and the macro too, when `MACRO_SOURCE=alien`. **Not** the actual map data, which goes to `${ALIEN_JDL_OUTPUTDIR}` instead. Only edit it here — the wrapper substitutes it into the payload script (`mapCreationJob.sh`) automatically before every submission, same mechanism as `JOBNAME_TAG`. Don't hand-edit `MACRO_ALIEN_DIR_BARE`/`MACRO_ALIEN_DIR` inside the payload scripts themselves — it's a placeholder line there, not a real per-user setting. | + +**Physics/processing parameters live in a different file** — edit the config block in +**`mapCreationJob.sh`** itself (not the wrapper; these are not part of the wrapper's +placeholder-substitution mechanism, so a hand-edit here is a normal, permanent change, not something +that gets overwritten per submission): + +| Variable | Meaning | +|---|---| +| `Z2XBINNING`, `Y2XBINNING` | Voxel binning along z/x and y/x. | +| `USE_SMOOTHED` | Passed through to the macro's `useSmoothed`. | +| `CREATE_SPLINE` | Leave `false` — `TPCFastTransformInitCPM.C` is invoked separately in stage 2, not from this macro. | +| `MAX_TRACKS_PER_SLICE` / `MIN_TRACKS_PER_SLICE` | Per-voxel-slice track count cap / floor. Falling short of `MIN_TRACKS_PER_SLICE` is what triggers the `badCalib_` marking (see Step 3). | +| `MAX_DEDX`, `MAX_DEDX_EXP`, `MAX_DEV_DEDX_OVER_EXP` | dE/dx-based track rejection cuts (`-1` = disabled). | +| `SKIP_EDGE_PADS` | Skip edge pads in the calibration. | +| `BAD_RANGE_SELECTION` | Which bad-time-range comment tag(s) to apply (`"ALL"` = every entry in the bad-ranges list). | +| `MAX_Z2X_CUT` | Track `tgl` cut (`SpacePointsCalibConfParam scdcalib.maxZ2X`) — real production value is `1.4`. | +| `MAX_TRACK_WORKERS` | **Set this to match your GRID job's actual allocated CPU core count** (i.e. whatever `--packagespec`/JDL gives you, commonly matching a `multicore_N` tag). Auto-detection via `hardware_concurrency()` does *not* reliably reflect the real GRID allocation — confirmed auto-detecting 32 threads on an 8-core job (4x oversubscription) once. Getting this wrong doesn't fail the job, just wastes/starves CPU. | + +Note: the macro no longer reads IDC scalers from CCDB directly (`useCTPLumi`/`nScalerRanges`/`useScalerRange` +are gone from its signature) — luminosity values are joined back offline by `SmoothingExtrapolate.C` in +stage 2 instead (its own `USE_CTP_LUMI`, see Step 4). + +Then: + +```bash +./submitMapCreation.sh [listsDir] # listsDir defaults to ./lists +``` + +This builds + uploads the slot manifest (and, if `MACRO_SOURCE=alien`, re-stages the macro) and submits +a SINGLE `grid_submit.sh --prodsplit N` production, where N is the total number of windows across all +selected runs. There is no longer one submission per time division: a subjob index identifies a +(run, window) pair on its own, because the windows are baked into the manifest. + +**Local single-slot test before submitting to the GRID** (recommended — much faster feedback loop): + +```bash +SUBJOBID=1 ./mapCreationJob.sh +``` + +Runs subjob 1's slot locally using whatever manifest/macro is already staged on AliEn. + +### Step 3 — monitor and wait + +Watch progress on alimonitor (job status, logs, quota). + +**Where the output actually goes** (per ALICE Grid support): each slot's map is staged +directly to *that job's own* JDL-assigned output directory (`${ALIEN_JDL_OUTPUTDIR}`), **not** a fixed +path under your own AliEn home — writing multi-GB production data into personal space would need its +own separate disk-quota request on top of the CPU one, so this pipeline deliberately doesn't do that. +Concretely, for subjob N: + +``` +/-//voxRes.__.TDof.it0.root +``` + +That parent directory is only known once the job actually runs and is otherwise unrecoverable later +(it includes the real submission timestamp) — so `submitMapCreation.sh` captures it the +moment it submits and uploads it as a small pointer file, `stage1Workdir..txt`, to +your own AliEn space (negligible size — a single line of text, not the actual data). Stage 2 downloads +this pointer and reconstructs each slot's exact output location from it (see Step 4) — you shouldn't +need to look at it directly, but it's what makes stage 2 able to find stage 1's output at all. + +If a slot didn't reach the minimum track-count threshold, its map is staged with a `badCalib_` prefix +instead of the normal name — that's the real production's own convention, replicated here so stage 2 +knows to skip it (see below). This is expected behavior for low-statistics slots, not a failure. + +You do **not** need to wait for every stage-1 subjob to finish before moving on to stage 2 — see +"Partial failure" below. + +**Note**: there's no clean, human-browsable `/...`-style directory tree here (unlike the real +input data's own `/alice/data////...` catalog convention) — that catalog is +populated by a centrally-run, formally-registered production system with dedicated write access, not +something reachable via a generic `grid_submit.sh` submission like this one. If you want a tidy, +reorganized view of a run's output later, that needs a separate finalize/merge step (copying out of +these per-job locations into wherever makes sense) — deliberately not built as part of this pipeline. + +### Step 4 — configure and submit stage 2 (smoothing + FastTransform) + +Edit the config block at the top of **`submitPostProcess.sh`**: + +| Variable | Meaning | +|---|---| +| `JOBNAME` | This stage's *own* job name (just for GRID job monitoring) — **not** the same thing as `STAGE1_JOBNAME_TAG` below. | +| `STAGE1_JOBNAME_TAG` | **Must equal the `JOBNAME` stage 1 was actually submitted with** (Step 2). This is what stage 2 uses to find both the manifest and the `stage1Workdir..txt` pointer file that tells it where stage 1's own jobs staged their maps. A mismatch here doesn't corrupt anything — the wrapper fails loudly up front if the pointer file can't be fetched (see Step 4's submission output), rather than silently producing zero output. | +| `TOPWORKDIR` | Same meaning as stage 1's. Independent value — stage 2 is its own GRID submission with its own working directory, not nested under stage 1's. | +| `PACKAGESPEC` | Same meaning as stage 1; keep in sync. Also determines whether `MACRO_SOURCE=official` can find the macros. | +| `ASUSER` | Same meaning as stage 1's — independent of what stage 1 used (stage 2's own submission). | +| `MACRO_SOURCE` / `MACRO_LOCAL_DIR` | See "Where the macros come from" above (this stage uploads all three macros at once when `MACRO_SOURCE=alien`, via `MACRO_LOCAL_DIR` rather than a single-file path). | + +**Physics/processing parameters**, same as stage 1, live in the payload script +(**`postProcessJob.sh`**), not the wrapper: + +| Variable | Meaning | +|---|---| +| `USE_SMOOTHED` | Should match stage 1's `USE_SMOOTHED` — selects `DO_GAUS_SMOOTH` below. | +| `DO_GAUS_SMOOTH` | Whether `SmoothingExtrapolate.C` applies Gaussian smoothing. | +| `DO_EXTRAPOLATION` | `2` = extrapolate both smoothed and raw values. | +| `A11_MAX_Z2X` | `-1` = disabled. | +| `MASK_IA11` | `0` = disabled. | +| `USE_CTP_LUMI` | Selects `TPCFastTransformInitCPM.C`'s luminosity source. `2` = CTP (matches stage 1's macro not accessing IDC scalers directly at all -- see the note at the end of Step 2). | +| `QA_DRAW_ERRORS` | `voxResQA.C` option — draw error bars. | +| `QA_USE_SMOOTHED` | `voxResQA.C` option — `true` draws the smoothed dXS/dYS/dZS (needs `SmoothingExtrapolate.C`'s output, which is exactly what it runs on); `false` draws raw dX/dY/dZ instead. | +| `QA_Z2X_BIN_SEL` | `voxResQA.C` option — `-1` = all z2x bins. | + +**QA plots**: stage 2 also runs `voxResQA.C` on each slot's smoothed map, producing +`voxResQA_1D.root`/`_2D.root` (canvases) plus individual `.png` quick-look images. QA failure +is non-fatal — it won't fail an otherwise-good slot. + +Then: + +```bash +./submitPostProcess.sh +``` + +This downloads the *existing* manifest stage 1 already uploaded (does not rebuild it — see the comment in +the script for why that matters), re-stages the three macros if `MACRO_SOURCE=alien`, and submits one +production, same slot granularity as stage 1. It also captures and uploads its own workdir pointer, +`stage2Workdir..txt`, to `MACRO_ALIEN_DIR` — the same mechanism stage 1 uses for its own output +(see Step 3), so an external consumer that needs to locate a specific slot's final `FT_*.root`/smoothed +map (e.g. a downstream pipeline using this stage's output as an input) can reconstruct the path the same +way stage 2 itself reconstructs stage 1's — read `stage2Workdir..txt` for the base, recompute +`COUNTERWIDTH` from this submission's manifest line count, join with the zero-padded subjob index. + +**Local single-slot test:** + +```bash +SUBJOBID=1 ./postProcessJob.sh +``` + +Stage 2's final output (the smoothed map, `FT_*.root` FastTransform file, and the two `voxResQA*.root` +QA files) is staged the same way as stage 1's — directly to *this job's own* `${ALIEN_JDL_OUTPUTDIR}`, +not stage 1's. Since stage 2 is a completely separate GRID submission (its own `JOBNAME`, its own +submission timestamp), this is a **different directory tree** from stage 1's, even for the exact same +logical slot: + +``` +/-// + voxRes.__.TDof.it0.GausSmooth.Extrapolation.root + FT_voxRes.__.TDof.it0.GausSmooth.Extrapolation.root + voxResQA.voxRes.__.TDof.it0.GausSmooth.Extrapolation_1D.root + voxResQA.voxRes.__.TDof.it0.GausSmooth.Extrapolation_2D.root +``` + +Stage 1's and stage 2's output for the same slot are *not* grouped under one tidy parent directory — +that's the direct cost of letting the JDL own output placement instead of managing it ourselves (see +the note at the end of Step 3). Same disk-quota reasoning as stage 1: explicit, synchronous `alien.py cp` +straight there, not the passive `#JDL_OUTPUT@disk=N` auto-upload (which only happens during the job's +asynchronous SAVING phase, after this script has already exited). + +## Partial failure — this is by design, not a workaround + +- **Some stage-1 slots fail or haven't finished**: stage 2, for that slot, finds nothing at the + reconstructed `${ALIEN_JDL_OUTPUTDIR}` location, logs a warning, and exits 0 (success/no-op) — it does + not fail the whole production. Resubmit just the missing stage-1 slot(s) later, then resubmit the + corresponding stage-2 subjob(s). +- **A stage-1 slot is marked `badCalib_`** (too few tracks — see Step 3): stage 2 recognizes the + `badCalib_` prefix specifically and logs that the calibration was intentionally marked bad, rather + than the generic "not found" message. Also a no-op, not a failure. +- **Some stage-2 subjobs fail** (e.g. a transient GRID/network issue): only those need resubmitting — + stage 1's expensive output is untouched and doesn't need to be redone. + +## Known gotchas + +- **`STAGE1_JOBNAME_TAG` is the only thing stage 2 has to get right.** It selects both the manifest and the `stage1Workdir` pointer. There is no time-window arithmetic to keep in sync any more — both stages read each slot's window straight off the same manifest line. A wrong tag fails loudly at the pointer fetch; a *stale* pointer under a reused tag would still no-op silently, so keep `JOBNAME` unique per submission. +- **Don't hand-edit `STAGE1_OUTPUTDIR_BASE`/`COUNTERWIDTH`/`MACRO_ALIEN_DIR_BARE`/`BAD_RANGES_NAME` inside the payload scripts** — like `JOBNAME_TAG`, these are placeholder lines the wrapper substitutes automatically per submission, not real per-user settings. +- **A window split can, rarely, produce a half with zero real data** if the split point lands past the run's actual end-of-run (a residual file's nominal timestamp range can pad past the true last data). That slot correctly gets marked `badCalib_` — it's an expected edge case, not something to debug further; the macro now also fails fast on it instead of wasting the CPU-time quota downloading data it will discard. +- **AliEn `wc -l`-derived counts can come back whitespace-padded**, which `grid_submit.sh --prodsplit` rejects outright (`Production split must be a positive integer (got ' 70')`) — already fixed in the checked-in scripts, just noting it in case you copy this pattern elsewhere. +- **Job success rate around 80-90% is normal** for a first production pass — check individual failed-job logs on alimonitor before assuming something is systemically broken. diff --git a/DATA/production/configurations/mapCreationGrid/discoverResiduals.sh b/DATA/production/configurations/mapCreationGrid/discoverResiduals.sh new file mode 100755 index 000000000..b2d714c4b --- /dev/null +++ b/DATA/production/configurations/mapCreationGrid/discoverResiduals.sh @@ -0,0 +1,201 @@ +#!/usr/bin/env bash +# discoverResiduals.sh +# +# Runs locally. For each run in a batch file, calls makeResidualLists.sh +# with --alien to query AliEn for o2tpc_residuals*.root files and build +# per-period residual lists under lists//. +# +# Runs are processed in parallel using GNU parallel. +# +# Usage: +# ./discoverResiduals.sh [--calib-pass ] +# +# Batch file format (one run per line, whitespace-separated): +# +# e.g.: LHC26ak 572557 +# +# PERIOD is derived from the first field by stripping from the first '.' onward +# (e.g. LHC26ab.b5p → LHC26ab — a trailing '.xyz' suffix is tolerated but not +# required). +# YEAR is derived from the two-digit year in the period token +# (e.g. LHC26ab → 2026). +# +# Dependencies: alien.py, parallel, makeResidualLists.sh + +set -euo pipefail + +# --------------------------------------------------------------------------- +# Parse arguments +# --------------------------------------------------------------------------- +BATCH_FILE="" +CALIB_PASS="cpass2_residuals" + +while [[ $# -gt 0 ]]; do + case "$1" in + --calib-pass) + [[ $# -lt 2 ]] && { echo "ERROR: --calib-pass requires an argument"; exit 1; } + CALIB_PASS="$2"; shift 2 ;; + -*) + echo "ERROR: Unknown option: $1"; exit 1 ;; + *) + [[ -n "${BATCH_FILE}" ]] && { echo "ERROR: unexpected extra argument: $1"; exit 1; } + BATCH_FILE="$1"; shift ;; + esac +done + +[[ -z "${BATCH_FILE}" ]] && { echo "Usage: $0 [--calib-pass ]"; exit 1; } +[[ -f "${BATCH_FILE}" ]] || { echo "ERROR: batch file not found: ${BATCH_FILE}"; exit 1; } + +# --------------------------------------------------------------------------- +# Parse the batch file into one " " entry per line +# --------------------------------------------------------------------------- +# The period is resolved PER LINE, not once from the first line: the batch format carries a period +# token on every row, so a batch legitimately spanning two periods must not silently inherit the +# first row's period for all of its runs (that produced a valid-looking /alice/data//// query, which just finds nothing and degrades into a per-run warning). +mapfile -t ENTRIES < <(awk 'NF > 0 && !/^[[:space:]]*#/ {print $1, $2}' "${BATCH_FILE}") +[[ ${#ENTRIES[@]} -gt 0 ]] || { echo "ERROR: no runs found in ${BATCH_FILE}"; exit 1; } + +# Validate every entry up front -- a malformed row should stop the batch here, not surface as one +# failed run out of fifty after the parallel fan-out has already started. +RUNS=() +PERIODS_SEEN=() +for entry in "${ENTRIES[@]}"; do + read -r _tok _run <<< "${entry}" + [[ -n "${_tok}" && -n "${_run}" ]] \ + || { echo "ERROR: malformed line in ${BATCH_FILE} (need ' '): '${entry}'"; exit 1; } + [[ "${_run}" =~ ^[0-9]+$ ]] \ + || { echo "ERROR: run number is not numeric in ${BATCH_FILE}: '${entry}'"; exit 1; } + # Field 1 example: LHC26ab.b5p → PERIOD=LHC26ab, YEAR=2026 + [[ "${_tok%%.*}" =~ LHC([0-9]{2}) ]] \ + || { echo "ERROR: cannot extract year from period token '${_tok}' (line: '${entry}')"; exit 1; } + RUNS+=("${_run}") + PERIODS_SEEN+=("${_tok%%.*}") +done + +mapfile -t PERIODS_UNIQ < <(printf '%s\n' "${PERIODS_SEEN[@]}" | sort -u) + +# --------------------------------------------------------------------------- +# Fixed configuration +# --------------------------------------------------------------------------- +# Maximum number of runs to process in parallel (0 = one per CPU core) +N_PARALLEL=8 + +# Root of the output tree (relative or absolute) +WORK_DIR="./lists" + +# Directory containing this script +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +# Path to makeResidualLists.sh (co-located here) +MAKE_LISTS_SCRIPT="${SCRIPT_DIR}/makeResidualLists.sh" + +# --------------------------------------------------------------------------- + +echo "Batch file : ${BATCH_FILE}" +echo "Period(s) : ${PERIODS_UNIQ[*]}" +echo "Calib pass : ${CALIB_PASS}" +echo "Runs (${#RUNS[@]}): ${RUNS[*]}" +echo + +mkdir -p "${WORK_DIR}" + +# --------------------------------------------------------------------------- +# per_run — all work for a single run, called by parallel +# +# On success writes /run_completed.txt containing the absolute +# path of RUN_DIR so the parent shell can collect completed runs after +# parallel finishes. +# --------------------------------------------------------------------------- +per_run() { + local PERIOD_TOKEN="$1" # field 1 of this batch-file row + local RUN="$2" # field 2 of this batch-file row + local CALIB_PASS="$3" + local WORK_DIR="$4" + local MAKE_LISTS_SCRIPT="$5" + + # Period/year come from THIS row, so a multi-period batch resolves each run against its own period. + # Already validated in the caller's pre-flight loop, so a parse failure here would be a bug. + local PERIOD YEAR + PERIOD="${PERIOD_TOKEN%%.*}" # strip suffix after first '.' + [[ "${PERIOD}" =~ LHC([0-9]{2}) ]] + YEAR="20${BASH_REMATCH[1]}" + + local RUN_DIR="${WORK_DIR}/${RUN}" + mkdir -p "${RUN_DIR}" + + local ALIEN_PATH="/alice/data/${YEAR}/${PERIOD}/${RUN}/${CALIB_PASS}/" + + echo "========================================" + echo "Processing run ${RUN} (period ${PERIOD}, year ${YEAR})" + echo "========================================" + + echo " [${RUN}] Calling makeResidualLists.sh (alien path: ${ALIEN_PATH})..." + ( + cd "${RUN_DIR}" + run="${RUN}" "${MAKE_LISTS_SCRIPT}" --subdir "${CALIB_PASS}" --alien --alien-path "${ALIEN_PATH}" + ) || { + echo " [${RUN}] WARNING: makeResidualLists.sh failed, skipping." + return 0 + } + + echo " [${RUN}] Done." + + # Signal success to the parent by writing the absolute run dir path + realpath "${RUN_DIR}" > "${RUN_DIR}/run_completed.txt" +} + +# Export everything parallel needs to call per_run in a subshell +export -f per_run + +# --------------------------------------------------------------------------- +# Run all runs in parallel +# --------------------------------------------------------------------------- +echo "========================================" +echo "Launching ${#RUNS[@]} runs with N_PARALLEL=${N_PARALLEL}..." +echo "========================================" + +# Fan out over the full " " entries, not bare run numbers, so each job carries its +# own period. --colsep splits each entry into {1}=period token and {2}=run; note that under --colsep +# a bare {} means column 1 ONLY, so both columns must be passed explicitly. --tagstring uses {2} to +# keep the log prefix to just the run number. +# shellcheck disable=SC1083 # {1}/{2} are GNU parallel replacement strings, not brace expansions +parallel \ + --jobs "${N_PARALLEL}" \ + --line-buffer \ + --colsep ' ' \ + --tagstring "[{2}]" \ + per_run {1} {2} "${CALIB_PASS}" "${WORK_DIR}" "${MAKE_LISTS_SCRIPT}" \ + ::: "${ENTRIES[@]}" + +# --------------------------------------------------------------------------- +# Collect completed runs (sentinel files written by per_run on success) +# --------------------------------------------------------------------------- +COMPLETED_RUNS=() +for RUN in "${RUNS[@]}"; do + SENTINEL="${WORK_DIR}/${RUN}/run_completed.txt" + if [[ -f "${SENTINEL}" ]]; then + COMPLETED_RUNS+=("$(cat "${SENTINEL}")") + rm -f "${SENTINEL}" + fi +done + +echo "========================================" +echo "All runs processed. ${#COMPLETED_RUNS[@]}/${#RUNS[@]} succeeded." +echo "========================================" + +if (( ${#COMPLETED_RUNS[@]} == 0 )); then + echo "No runs completed successfully." + exit 0 +fi + +# Sort for deterministic output order. Must come AFTER the empty check: expanding "${arr[@]}" on an +# empty array is an "unbound variable" error under `set -u` in bash <= 4.3. +IFS=$'\n' COMPLETED_RUNS=($(printf '%s\n' "${COMPLETED_RUNS[@]}" | sort)) +unset IFS + +echo "Per-run slot-file indexes (input for stageSlotsForGrid.py):" +for RUN_DIR in "${COMPLETED_RUNS[@]}"; do + RUN="$(basename "${RUN_DIR}")" + echo " ${RUN_DIR}/${RUN}.residuals_lists.txt" +done diff --git a/DATA/production/configurations/mapCreationGrid/makeResidualLists.sh b/DATA/production/configurations/mapCreationGrid/makeResidualLists.sh new file mode 100755 index 000000000..cbdb6fd51 --- /dev/null +++ b/DATA/production/configurations/mapCreationGrid/makeResidualLists.sh @@ -0,0 +1,280 @@ +#!/usr/bin/env bash +# makeResidualLists.sh +# Find o2tpc_residuals*.root files (local or alien) and split them into +# per-period lists plus a combined all-runs list. +# +# Single-run usage: +# run= ./makeResidualLists.sh [--subdir ] [--alien] [--dry-run] +# +# Multi-run usage (loops over all run directories automatically): +# ./makeResidualLists.sh --all [--subdir ] [--alien] [--dry-run] +# +# Arguments: +# --all loop over all NNN*/ subdirs in the current directory +# --subdir subdirectory within each run dir to search in (default: cpass1_residuals) +# --alien use alien.py instead of local find +# --alien-path

explicit AliEn directory to search (overrides --year/--period/--subdir) +# --year year component of the AliEn path (required with --alien unless --alien-path) +# --period period component of the AliEn path (required with --alien unless --alien-path) +# --dry-run print what would be done without writing files +# In the normal chain you never pass --year/--period by hand: discoverResiduals.sh derives them per +# run from the batch file and passes a fully-formed --alien-path instead. +# +# Environment (single-run mode): +# run : run number (optional; used in output filenames) + +set -euo pipefail + +# ── colours ─────────────────────────────────────────────────────────────────── +RED='\033[0;31m'; YELLOW='\033[1;33m'; GREEN='\033[0;32m' +CYAN='\033[0;36m'; BOLD='\033[1m'; RESET='\033[0m' + + +# Safe exit: use return when sourced, exit when run directly +_finish() { local code=${1:-0}; [[ "${BASH_SOURCE[0]}" != "${0}" ]] && return "$code" || exit "$code"; } +error() { echo -e "${RED}[ERROR]${RESET} $*" >&2; _finish 1; } +warn() { echo -e "${YELLOW}[WARN]${RESET} $*"; } +info() { echo -e "${CYAN}[INFO]${RESET} $*"; } +ok() { echo -e "${GREEN}[OK]${RESET} $*"; } + +# ── settings ────────────────────────────────────────────────────────────────── +: ${run:=""} +USE_ALIEN=0 +DRY_RUN=0 +ALL_MODE=0 +SUBDIR="cpass1_residuals" +ALIEN_PATH_OVERRIDE="" +YEAR="" +PERIOD="" + +# ── parse arguments ─────────────────────────────────────────────────────────── +while [[ $# -gt 0 ]]; do + case "$1" in + --alien) USE_ALIEN=1 ; shift ;; + --dry-run) DRY_RUN=1 ; shift ;; + --all) ALL_MODE=1 ; shift ;; + --subdir) + [[ $# -lt 2 ]] && error "--subdir requires an argument" + SUBDIR="$2" ; shift 2 ;; + --alien-path) + [[ $# -lt 2 ]] && error "--alien-path requires an argument" + ALIEN_PATH_OVERRIDE="$2" ; shift 2 ;; + --year) + [[ $# -lt 2 ]] && error "--year requires an argument" + YEAR="$2" ; shift 2 ;; + --period) + [[ $# -lt 2 ]] && error "--period requires an argument" + PERIOD="$2" ; shift 2 ;; + *) error "Unknown argument: $1" ;; + esac +done + +# AliEn search path, e.g. /alice/data/2024/LHC24as/560334/cpass4_residuals/ +# +# There is deliberately NO default year/period here any more. There used to be a hardcoded +# YEAR=2026 / PERIOD=LHC26ab pair, which was harmless in the normal chain (discoverResiduals.sh always +# passes --alien-path, which overrides it) but became live in --all mode, where it silently queried +# LHC26ab for every run regardless of what the caller actually asked for. Requiring the caller to be +# explicit turns that into an up-front error instead of an empty result set. +ALIEN_PATH="" +if [[ -n "${ALIEN_PATH_OVERRIDE}" ]]; then + ALIEN_PATH="${ALIEN_PATH_OVERRIDE}" +elif [[ $USE_ALIEN -eq 1 ]]; then + [[ -n "$YEAR" && -n "$PERIOD" ]] \ + || error "--alien needs either --alien-path , or both --year and --period " + ALIEN_PATH="/alice/data/${YEAR}/${PERIOD}/${run}/${SUBDIR}/" +fi + +# ── multi-run mode ──────────────────────────────────────────────────────────── +# Re-invokes this script once per run directory, then collects all_list files. +if [[ $ALL_MODE -eq 1 ]]; then + SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" + TOP="$PWD" + + echo + echo -e "${BOLD}╔══════════════════════════════════════════════════════════════╗${RESET}" + echo -e "${BOLD}║ TPC Residual List Builder [multi-run mode] ║${RESET}" + echo -e "${BOLD}╚══════════════════════════════════════════════════════════════╝${RESET}" + echo + info "Base directory : $TOP" + info "Subdir : $SUBDIR" + [[ $DRY_RUN -eq 1 ]] && warn "DRY-RUN mode — no files will be written" + echo + + RUNS=( $(ls -d [0-9]*/ 2>/dev/null | sed 's|/$||') ) + [[ ${#RUNS[@]} -eq 0 ]] && error "No run directories (NNN*/) found in $TOP" + info "Found ${#RUNS[@]} run director(ies): ${RUNS[*]}" + echo + + FAILED=() + for r in "${RUNS[@]}"; do + WORK_DIR="$TOP/$r/$SUBDIR" + if [[ ! -d "$WORK_DIR" ]]; then + warn "Skipping run $r — subdir not found: $WORK_DIR" + FAILED+=("$r") + continue + fi + echo -e "${BOLD}════ Run $r ════${RESET}" + ( + cd "$WORK_DIR" + # Propagate every option that affects WHERE we look, not just the two mode flags. --subdir + # was previously dropped here: the parent used it to build WORK_DIR while the child fell + # back to the "cpass1_residuals" default, so the two disagreed about which calib pass was + # being listed. Array, not a string, so a value containing spaces cannot re-split. + EXTRA_FLAGS=("--subdir" "$SUBDIR") + [[ $USE_ALIEN -eq 1 ]] && EXTRA_FLAGS+=("--alien") + [[ $DRY_RUN -eq 1 ]] && EXTRA_FLAGS+=("--dry-run") + [[ -n "$YEAR" ]] && EXTRA_FLAGS+=("--year" "$YEAR") + [[ -n "$PERIOD" ]] && EXTRA_FLAGS+=("--period" "$PERIOD") + run="$r" bash "$SCRIPT" "${EXTRA_FLAGS[@]}" + ) || { warn "Run $r failed — continuing"; FAILED+=("$r"); } + echo + done + + # Collect the per-run index files back to the top directory + if [[ $DRY_RUN -eq 0 ]]; then + info "Collecting *.residuals_lists.txt → $TOP" + find "$TOP" -mindepth 3 -maxdepth 3 -name "*.residuals_lists.txt" \ + -exec cp {} "$TOP/" \; + else + info "[DRY-RUN] Would copy *.residuals_lists.txt to $TOP" + fi + + echo + if [[ ${#FAILED[@]} -gt 0 ]]; then + warn "Finished with ${#FAILED[@]} skipped/failed run(s): ${FAILED[*]}" + else + ok "All ${#RUNS[@]} run(s) processed successfully." + fi + _finish 0 +fi + +# ── single-run mode ─────────────────────────────────────────────────────────── +# Safety check: refuse to run in single-run mode from a directory that contains +# numeric run subdirectories — that almost certainly means --all was forgotten. +if [[ -z "$run" ]] && ls -d [0-9][0-9][0-9][0-9][0-9][0-9]*/ &>/dev/null; then + error "Current directory contains run subdirectories but no run number is set.\n" \ + " Did you mean: $0 --all\n" \ + " Or: run= $0" +fi + +FILE_ALL="residuals.all.txt" +RUN_PREFIX="${run:+${run}_}" + +echo +echo -e "${BOLD}╔══════════════════════════════════════════════════════════════╗${RESET}" +echo -e "${BOLD}║ TPC Residual List Builder ║${RESET}" +echo -e "${BOLD}╚══════════════════════════════════════════════════════════════╝${RESET}" +echo +[[ -n "$run" ]] && info "Run number : ${run}" || warn "No run number set — output filenames will omit run" +info "Working dir: $PWD" +info "Source : $([ $USE_ALIEN -eq 1 ] && echo "alien ($ALIEN_PATH)" || echo "local")" +[[ $DRY_RUN -eq 1 ]] && warn "DRY-RUN mode — no files will be written" +echo + +# ── clean up stale output files from previous run ───────────────────────────── +mapfile -t STALE < <(ls "$FILE_ALL" "residuals.${RUN_PREFIX}"*.txt \ + "${run:-NOMATCH}.residuals_lists.txt" \ + "${run:-NOMATCH}.residuals_all_list.txt" 2>/dev/null || true) + +if [[ ${#STALE[@]} -gt 0 ]]; then + warn "Removing ${#STALE[@]} stale file(s) from previous run:" + for f in "${STALE[@]}"; do + echo " rm $f" + [[ $DRY_RUN -eq 0 ]] && rm -f "$f" + done + echo +fi + +# ── collect residual file list ──────────────────────────────────────────────── +info "Searching for o2tpc_residuals*.root files..." + +if [[ $DRY_RUN -eq 1 ]]; then + if [[ $USE_ALIEN -eq 1 ]]; then + info "[DRY-RUN] Would run: alien.py find $ALIEN_PATH o2tpc_residuals*.root | sed 's|^|alien://|' > $FILE_ALL" + else + COUNT=$(find "$PWD" -name "o2tpc_residuals*.root" | wc -l) + info "[DRY-RUN] Would write $COUNT file path(s) to: $FILE_ALL" + find "$PWD" -name "o2tpc_residuals*.root" \ + | awk -F'/' '{print $NF";"$0}' | sort | awk -F';' '{print $NF}' \ + | head -5 | sed 's/^/ /' || true # `|| true`: head -5 SIGPIPEs the upstream find, + # which under pipefail would abort this dry run + [[ $COUNT -gt 5 ]] && echo " ... ($COUNT total)" + fi + echo + info "[DRY-RUN] Would rename to residuals.all.${RUN_PREFIX}___.txt" + info "[DRY-RUN] Would split into per-period lists residuals.${RUN_PREFIX}.txt" + info "[DRY-RUN] Would write index file ${run:-norunnumber}.residuals_lists.txt" + echo + ok "Dry-run complete — no files written." + _finish 0 +fi + +# ── real mode from here ─────────────────────────────────────────────────────── +if [[ $USE_ALIEN -eq 1 ]]; then + alien.py find "$ALIEN_PATH" o2tpc_residuals*.root \ + | sed 's|^|alien://|' > "$FILE_ALL" +else + find "$PWD" -name "o2tpc_residuals*.root" \ + | awk -F'/' '{print $NF";"$0}' \ + | sort \ + | awk -F';' '{print $NF}' \ + > "$FILE_ALL" +fi + +if [[ ! -s "$FILE_ALL" ]]; then + warn "No o2tpc_residuals*.root files found — all output lists will be empty." + _finish 0 +fi +ok "Found $(wc -l < "$FILE_ALL" | tr -d '[:space:]') file(s)." + +# ── extract timestamp range ─────────────────────────────────────────────────── +read -r firstTS1 lastTS1 firstTF1 lastTF1 <<< \ + "$(head -1 "$FILE_ALL" | sed -rn 's|.*_([0-9]{13})_([0-9]{13})_([0-9]+)_([0-9]+).*|\1 \2 \3 \4|p')" +read -r firstTS2 lastTS2 firstTF2 lastTF2 <<< \ + "$(tail -1 "$FILE_ALL" | sed -rn 's|.*_([0-9]{13})_([0-9]{13})_([0-9]+)_([0-9]+).*|\1 \2 \3 \4|p')" + +# ── MC fallback (no timestamps) ─────────────────────────────────────────────── +if [[ -z "${firstTS1:-}" || -z "${lastTS2:-}" ]]; then + warn "No timestamp info found — assuming MC input." + FILE_ALL_RANGE="residuals.all.${run}.txt" + mv "$FILE_ALL" "$FILE_ALL_RANGE" + ls "$PWD/${FILE_ALL_RANGE}" > "${run}.residuals_lists.txt" + ok "Done (MC mode)." + _finish 0 +fi + +# ── rename with timestamp range ─────────────────────────────────────────────── +FILE_ALL_RANGE="residuals.all.${RUN_PREFIX}${firstTS1}_${lastTS2}_${firstTF1}_${lastTF2}.txt" +info "Renaming: $FILE_ALL → $FILE_ALL_RANGE" +mv "$FILE_ALL" "$FILE_ALL_RANGE" + +# ── write the run's slot list ───────────────────────────────────────────────── +# ONE list per run, holding every residual file found. Slot granularity is deliberately NOT taken from +# the aggregation labels in the filenames -- it is chosen at submission time via SLOT_LENGTH_MIN, and +# mapCreationJob.sh prefilters each subjob's copy of this list down to the files that can actually +# contribute to its own time window. +# +# Why not group by label: each residual filename carries a slot label +# ___, and those labels are NOT a uniform grid -- same-length slots +# are not guaranteed, and a run's catalog can hold overlapping/nested labels at different granularities +# (why doesn't matter here, just that it happens), e.g. run 572266: +# 0_105425 (22 files) 105426_210851 (6) 0_210851 (35) +# 210852_316277 (2) 210852_421703 (45) +# Slicing per distinct label would give overlapping slots with wildly uneven statistics -- the 2- and +# 6-file ones trip minTracksPerSlice and come back badCalib_ while the data that would have fixed them +# sits in a sibling list. Keeping one list per run makes granularity a deliberate choice instead of an +# artefact of the upstream catalog's granularity, and keeps it uniform across runs. +# The run's slot list IS the all-files list -- there is exactly one per run, so it is not copied to a +# second name. Both index files below therefore point at this one file. +info "Slot list: $FILE_ALL_RANGE ($(wc -l < "$FILE_ALL_RANGE" | tr -d '[:space:]') file(s))" +info "Windowing happens at submission time (SLOT_LENGTH_MIN), not here -- this is the whole run." + +# ── write index files ───────────────────────────────────────────────────────── +# One index per run, holding the single path to that run's slot list. +LISTS_INDEX="${run:-norunnumber}.residuals_lists.txt" +ls "$PWD/${FILE_ALL_RANGE}" > "$LISTS_INDEX" + +echo +ok "Done. 1 slot list written." +ok "Slot list index : $LISTS_INDEX" \ No newline at end of file diff --git a/DATA/production/configurations/mapCreationGrid/mapCreationJob.sh b/DATA/production/configurations/mapCreationGrid/mapCreationJob.sh new file mode 100755 index 000000000..e5bae7188 --- /dev/null +++ b/DATA/production/configurations/mapCreationGrid/mapCreationJob.sh @@ -0,0 +1,296 @@ +#!/usr/bin/env bash +# mapCreationJob.sh — TPC space-charge map creation, one time slot per GRID job. +# +# This is STAGE 1 of a two-stage pipeline: runs staticMapCreatorCPM.C directly to produce the raw +# per-slot voxRes.*.it0.root -- the heavy step, hours per slot. Stages its output to this job's own +# JDL-assigned ${ALIEN_JDL_OUTPUTDIR} (see below) so STAGE 2 (postProcessJob.sh: +# SmoothingExtrapolate.C + TPCFastTransformInitCPM.C, minutes per slot) can find it independently and +# be resubmitted on its own if it fails, without re-running this stage. Everything further downstream +# (iteration chains beyond it0, finalize/QA) stays local for now -- deliberately out of scope here. +# +# Submitted via: +# ${O2DPG_ROOT}/GRID/utils/grid_submit.sh --script mapCreationJob.sh \ +# --prodsplit --packagespec O2PDPSuite:: ... +# +# --prodsplit gives every subjob nothing but a plain integer index +# (ALIEN_O2DPG_GRIDSUBMIT_SUBJOBID, exported by grid_submit.sh -- real GRID only). This script +# downloads the mapSlots..tsv manifest that stageSlotsForGrid.py uploaded (one line +# per slot: "\t") -- tagged by JOBNAME_TAG +# so a different-named submission's manifest doesn't overwrite this one's (see JOBNAME_TAG below) -- +# picks its own line by index, and reconstructs a local slot-list file. --prodsplit is used instead of +# --split-on-collection: the latter needs one already-registered AliEn catalog file per split unit +# (N slow alien.py round trips, N tiny objects cluttering one AliEn directory), while a single manifest +# upload avoids both. +# +# Slot windowing: this script does NO time arithmetic. stageSlotsForGrid.py decides the windows at +# submission time (from SLOT_LENGTH_MIN) and writes one manifest line per window, carrying the window's +# bounds and exactly the files that can contribute to it. Stage 2 reads the same line, so the two +# stages cannot disagree about a slot's identity -- they used to each recompute it from +# TIME_DIVISION/TIME_DIVISION_INDEX, and any mismatch silently produced zero output. +# +# JOBNAME_TAG: tags the slot manifest name (mapSlots..tsv) so a different-named submission's +# manifest doesn't silently overwrite this one's. Substituted from the wrapper's own JOBNAME. +# +# MACRO_SOURCE: "official" (default) runs the macro straight from the O2 package already loaded by this +# job's --packagespec ($O2_ROOT/share/macro/staticMapCreatorCPM.C -- the same idiom +# DATA/production/configurations/asyncCalib/createCorrectionMap.sh already uses for staticMapCreator.C), +# no staging/download needed. Requires a packagespec built from an O2 tag that includes +# staticMapCreatorCPM.C (Detectors/TPC/calibration/SpacePoints/macro/) -- until such a tag is published, +# use MACRO_SOURCE=alien instead, which downloads a copy staged ahead of time under MACRO_ALIEN_DIR (see +# submitMapCreation.sh's MACRO_LOCAL_PATH). +# +# MACRO_ALIEN_DIR_BARE: same placeholder mechanism, substituted from the wrapper's own +# MACRO_ALIEN_DIR_BARE -- if you change that in submitMapCreation.sh (e.g. to point at a +# different AliEn user/directory), it now propagates here automatically instead of needing a second, +# easy-to-forget manual edit of this file. Used for the bad-time-ranges list and the slot manifest +# regardless of MACRO_SOURCE; only used for the macro itself when MACRO_SOURCE=alien. +JOBNAME_TAG=default +MACRO_SOURCE=official +MACRO_ALIEN_DIR_BARE=${MACRO_ALIEN_DIR_BARE:-} +# +# BAD_RANGES_NAME: filename of the bad-time-ranges list to pull from ${MACRO_ALIEN_DIR}/lists/. Same +# placeholder mechanism -- substituted from the wrapper's own BAD_RANGES_NAME, which is also what the +# wrapper uploads to when BAD_RANGES_LOCAL is set, so the uploaded name and the downloaded name are the +# same value by construction. The list is period/system-specific (the default below is PbPb26), so +# expect to change it in the wrapper when the data changes -- not here. Empty ("") is a supported value +# meaning "skip bad-ranges filtering entirely for this submission" -- see BAD_RANGES_ARG below. +BAD_RANGES_NAME=${BAD_RANGES_NAME:-Merge_BadIntervals_LHC26_PbPb_DCA-0P300.txt} +#JDL_OUTPUT=*.log@disk=2,*.txt@disk=2,*.info@disk=2 +#JDL_REQUIRE={member(other.GridPartitions,"multicore_8") && (other.CE != "ALICE::NIHAM::PBS64") && (other.CE != "ALICE::KISTI_GSDC::LCG") && (other.CE != "ALICE::SaoPaulo::LCG")}; +# Site blacklist: these three CEs excluded from this pipeline's jobs. Setting #JDL_REQUIRE overrides +# grid_submit.sh's own default Requirements entirely (it's read verbatim, not merged -- see +# grid_submit.sh's REQUIRESPEC handling), so the multicore_8 GridPartitions clause is repeated here +# rather than dropped. +# voxRes.*.root deliberately NOT in this pattern -- it's staged explicitly, once, via the synchronous +# alien.py cp below (to a fixed, predictable location stage 2 can find). Having it ALSO match here would +# make the automatic per-job upload duplicate the same (100+ MB) file a second time, to the ephemeral +# per-job dir, for no benefit now that the fixed-location copy is the reliable one. See "Two-stage +# pipeline" in README.md. + +set -e + +# xrootd client tuning -- retry/timeout behavior for alien://xrootd reads. Set before any AliEn +# transfer below (both our own alien.py cp calls and the macro's own residual-file reads go through +# this same client). Without it, a stalled read can hang until AliEn's own watchdog kills the payload +# for staying below its CPU-usage threshold too long (TJAlienFile "Operation expired"). +# +# CONNECTIONRETRY=2, CONNECTIONWINDOW=30: patiently retrying a sick storage server for many minutes is +# fine on a machine you own, but fatal on the GRID -- AliEn's job agent kills any payload whose CPU +# stays below threshold for 15 minutes, and a stalled bulk read burns that budget at ~0% CPU. A higher +# CONNECTIONRETRY would allow correspondingly longer silent reconnection per request before an error is +# ever raised; at 2 x 30s the worst case is 60 s, so the macro's own "could not load entry -> skip file" +# path (which handles a FAILED read perfectly well -- the problem is only ever that xrootd does not +# fail, it waits) gets a chance to run before the watchdog does. +# +# Note this is a mitigation, not a guarantee: none of these knobs bound a transfer that is trickling +# rather than dead, since each sub-request completes before its own timeout while the overall read +# crawls. That case is caught earlier instead, by the unhealthy-replica open-time gate in +# staticMapCreatorCPM.C (SCDCALIB_MAX_FILE_OPEN_SEC). +export XRD_TIMEOUTRESOLUTION=15 +export XRD_CONNECTIONWINDOW=30 +export XRD_CONNECTIONRETRY=2 +export XRD_REQUESTTIMEOUT=120 +export XRD_STREAMTIMEOUT=120 +export XRD_STREAMERRORWINDOW=0 + +# --- AliEn path holding the bad-time-ranges list / mapSlots.tsv (and staticMapCreatorCPM.C too, when +# MACRO_SOURCE=alien). grid_submit.sh only ships this one script; everything else is staged here once +# and pulled down per job. +# +# Normally substituted above by submitMapCreation.sh (which derives it from your AliEn +# account), so it is empty only when this script is run by hand -- in which case say so explicitly +# rather than building "alien:///mapSlots...tsv" and failing later with a confusing catalog error. +: "${MACRO_ALIEN_DIR_BARE:?MACRO_ALIEN_DIR_BARE is empty -- the submission wrapper normally substitutes it. For a standalone/local run, export it first, e.g. MACRO_ALIEN_DIR_BARE=/alice/cern.ch/user///MapCreation}" +MACRO_ALIEN_DIR="alien://${MACRO_ALIEN_DIR_BARE}" + +if [[ "${MACRO_SOURCE}" == "official" ]]; then + : "${O2_ROOT:?O2_ROOT is not set -- MACRO_SOURCE=official needs the O2 package loaded via --packagespec}" + MACRO_PATH="${O2_ROOT}/share/macro/staticMapCreatorCPM.C" + if [[ ! -f "${MACRO_PATH}" ]]; then + echo "ERROR: MACRO_SOURCE=official but ${MACRO_PATH} does not exist -- this job's --packagespec does not include staticMapCreatorCPM.C yet. Use MACRO_SOURCE=alien until a packagespec with it is published." >&2 + exit 1 + fi +else + alien.py cp -f "${MACRO_ALIEN_DIR}/staticMapCreatorCPM.C" file:./staticMapCreatorCPM.C + MACRO_PATH="./staticMapCreatorCPM.C" +fi +# BAD_RANGES_NAME="" is a deliberate, supported value -- see the wrapper's own comment on it -- meaning +# "skip bad-ranges filtering entirely" (staticMapCreatorCPM.C treats an empty badRangeList argument that +# way, see its `if (badRangeList.length() > 0)` guard). BAD_RANGES_ARG is what actually gets passed to +# the macro below; empty there, not "badRanges.dat", is what selects that mode. +BAD_RANGES_ARG="" +if [[ -n "${BAD_RANGES_NAME}" ]]; then + alien.py cp -f "${MACRO_ALIEN_DIR}/lists/${BAD_RANGES_NAME}" file:./badRanges.dat + BAD_RANGES_ARG="badRanges.dat" +fi +alien.py cp -f "${MACRO_ALIEN_DIR}/mapSlots.${JOBNAME_TAG}.tsv" file:./mapSlots.tsv + +# --- Determine this subjob's index into mapSlots.tsv --- +# ALIEN_O2DPG_GRIDSUBMIT_SUBJOBID is set by grid_submit.sh on the real GRID (--prodsplit mode) before +# it runs this script. For --local testing without it, pass SUBJOBID explicitly, e.g.: +# SUBJOBID=3 grid_submit.sh --local ... +SUBJOBID="${ALIEN_O2DPG_GRIDSUBMIT_SUBJOBID:-${SUBJOBID:-}}" +: "${SUBJOBID:?SUBJOBID must be set (via ALIEN_O2DPG_GRIDSUBMIT_SUBJOBID on GRID, or the SUBJOBID env var for --local testing)}" + +LINE=$(sed -n "${SUBJOBID}p" mapSlots.tsv) +if [[ -z "${LINE}" ]]; then + echo "ERROR: mapSlots.tsv has no line ${SUBJOBID}" >&2 + exit 1 +fi + +# Manifest line: \t \t \t \t \t +# The window bounds and the file selection are decided once, at submission time, by +# stageSlotsForGrid.py -- this script does no time arithmetic of its own. That is deliberate: stage 2 +# reads the SAME manifest line, so the two stages cannot disagree about a slot's identity (they +# previously each recomputed it and a mismatch silently produced zero output). +IFS=$'\t' read -r RUN WIN_IDX WIN_COUNT firstTSIter lastTSIter PATHS <<< "${LINE}" +for v in RUN WIN_IDX WIN_COUNT firstTSIter lastTSIter; do + if [[ ! "${!v}" =~ ^[0-9]+$ ]]; then + echo "ERROR: manifest line ${SUBJOBID} field ${v}='${!v}' is not numeric -- expected 6 tab-separated fields " >&2 + exit 1 + fi +done +[[ -n "${PATHS}" ]] || { echo "ERROR: manifest line ${SUBJOBID} carries no file paths" >&2; exit 1; } + +SLOT_LIST="slotlist_${SUBJOBID}.txt" +tr ',' '\n' <<< "${PATHS}" > "${SLOT_LIST}" +nSlotFiles=$(wc -l < "${SLOT_LIST}" | tr -d '[:space:]') # tr: wc right-justifies its count +echo "Processing slot ${SUBJOBID}: run ${RUN}, window ${WIN_IDX}/${WIN_COUNT} [${firstTSIter},${lastTSIter}] ($(( (lastTSIter - firstTSIter) / 1000 ))s), ${nSlotFiles} file(s)" + +# --- Process the files in the order that reaches this window's data soonest --- +# WHY: the macro's per-file skip still has to OPEN every out-of-window file to read its records tree -- +# ~7 s each over alien://, at ~zero CPU -- and AliEn's job agent kills any payload whose CPU stays +# below threshold for 15 minutes (observed for real, run 572246: 47 files skipped in 6 min, killed +# before reaching in-window data). +# +# stageSlotsForGrid.py already dropped every file whose aggregation label cannot overlap this window, +# but that filter can only be as sharp as the labels are. A coarse label produced by the aggregator +# merging an underpopulated slot forward can span several windows, so overlapping this window by as +# little as 1 ms pulls in all of its files. Ordering fixes what filtering cannot: each file's CTF +# directory carries an _orbit field, which IS that file's true position in time, so we sort by +# distance from this window's centre. The files that can actually contribute are opened first and +# maxTracksPerSlice usually stops the job before it ever reaches the far ones. +# +# Only the ORDER depends on this, never which files are considered, so the orbit->time estimate below +# is allowed to be approximate: it is interpolated from the list's own extremes. +if awk -F'_orbit' 'NF < 2 {exit 1}' "${SLOT_LIST}"; then + awk -F'_orbit' -v lo="${firstTSIter}" -v hi="${lastTSIter}" ' + { + line[NR] = $0 + # orbit from the CTF directory name + split($2, o, "_"); orb[NR] = o[1] + 0 + # Time anchor: the EXTREMES of the aggregation labels present, not their midpoints. In nearly + # every window all files carry a single label (one aggregator slot spans several windows), and + # identical midpoints collapse the interpolation to frac=0 -- which sorts ascending no matter + # where the window actually sits, the exact order that gets a late window killed by the CPU + # watchdog. Extremes stay meaningful with one label: [labelFirst,labelLast] maps onto + # [oMin,oMax]. + if (match($0, /o2tpc_residuals_[0-9]{13}_[0-9]{13}_/)) { + split(substr($0, RSTART + 16, 27), t, "_") + if (!seen || (t[1] + 0) < mMin) mMin = t[1] + 0 + if (!seen || (t[2] + 0) > mMax) mMax = t[2] + 0 + seen = 1 + } + if (n == 0 || orb[NR] < oMin) oMin = orb[NR] + if (n == 0 || orb[NR] > oMax) oMax = orb[NR] + n = NR + } + END { + centre = (lo + hi) / 2 + # map the window centre onto the orbit axis using this list own extremes + frac = (seen && mMax > mMin) ? (centre - mMin) / (mMax - mMin) : 0 + if (frac < 0) frac = 0 + if (frac > 1) frac = 1 + target = oMin + frac * (oMax - oMin) + for (i = 1; i <= n; i++) { + d = orb[i] - target; if (d < 0) d = -d + printf "%d\t%s\n", d, line[i] + } + }' "${SLOT_LIST}" \ + | sort -n -k1,1 | cut -f2- > "${SLOT_LIST}.ord" + if [[ -s "${SLOT_LIST}.ord" ]]; then + mv "${SLOT_LIST}.ord" "${SLOT_LIST}" + echo "File order: sorted by orbit distance from the window centre (closest first)" + else + rm -f "${SLOT_LIST}.ord" + echo "WARNING: could not order ${SLOT_LIST} by orbit -- leaving the file order untouched" >&2 + fi +else + # No _orbit field in at least one path -- cannot establish a time order, so leave the list alone + # rather than reorder it on a key that is not there. Costs the optimization, never correctness. + echo "WARNING: not every path in ${SLOT_LIST} carries an _orbit field -- leaving the file order untouched (no time-ordering possible)" >&2 +fi + +# --- Config (production values; createSpline is the one deliberate exception, see CREATE_SPLINE +# below: TPCFastTransformInitCPM.C runs separately in stage 2 here instead) --- +# NOTE: nScalerRanges/useScalerRange/useCTPLumi are gone from the macro's signature. It no longer reads +# IDC scalers from CCDB at all; the values are joined in offline by SmoothingExtrapolate.C in stage 2. +Z2XBINNING="28" +Y2XBINNING="20" +USE_SMOOTHED=2 +CREATE_SPLINE=false # TPCFastTransformInitCPM.C runs in stage 2 instead; leave false here +MAX_TRACKS_PER_SLICE=10000 +MIN_TRACKS_PER_SLICE=3000 +MAX_DEDX=-1 +MAX_DEDX_EXP=-1 +MAX_DEV_DEDX_OVER_EXP=-1 +SKIP_EDGE_PADS=1 +BAD_RANGE_SELECTION="ALL" +MAX_Z2X_CUT=1.4 # SpacePointsCalibConfParam scdcalib.maxZ2X override -- real + # production value, vs. the struct's own compiled-in default of 1.0 +MAX_TRACK_WORKERS=8 # matches the JDL's CPUCores/multicore_8 allocation (grid_submit.sh's + # default) -- hardware_concurrency() does NOT reliably reflect the + # real GRID allocation (confirmed 2026-07-25: auto-detected 32 on an + # 8-core job, 4x oversubscription), so pass the known real value. + +OUTPUT_STEM="voxRes.${RUN}_${firstTSIter}_${lastTSIter}.TD${WIN_IDX}of${WIN_COUNT}.it0" +OUTPUT_FILE="${OUTPUT_STEM}.root" + +export alien_CLOSE_SE="ALICE::CERN::EOS" +cmd="(time root.exe -b -q -l -x '${MACRO_PATH}+O(\"${SLOT_LIST}\", ${RUN}, \"${OUTPUT_FILE}\", \"\", \"ITS-TPC\", \"${Z2XBINNING}\", \"${Y2XBINNING}\", ${USE_SMOOTHED}, ${CREATE_SPLINE}, ${MAX_TRACKS_PER_SLICE}, ${MIN_TRACKS_PER_SLICE}, \"${BAD_RANGES_ARG}\", ${firstTSIter}, ${lastTSIter}, ${MAX_DEDX}, ${MAX_DEDX_EXP}, ${MAX_DEV_DEDX_OVER_EXP}, ${SKIP_EDGE_PADS}, \"${BAD_RANGE_SELECTION}\", ${MAX_Z2X_CUT}, ${MAX_TRACK_WORKERS})')" +echo "$(date) ${cmd}" +eval "${cmd}" + +if [[ ! -s "${OUTPUT_FILE}" ]]; then + echo "ERROR: ${OUTPUT_FILE} was not produced -- this slot's map creation failed" >&2 + exit 1 +fi + +# Bad-calib handling: the macro creates an extensionless badCalib. marker file when +# minTracksPerSlice wasn't reached. Stage under a badCalib_-prefixed +# name instead of the normal one -- stage 2, looking for the normal (unprefixed) name, simply won't +# find it and will hit the same graceful-skip path already used for "stage 1 hasn't produced this yet", +# with no extra logic needed there. The marker file itself has no extension and matches none of our +# JDL output patterns, so it doesn't survive on its own -- this rename is what actually carries the +# bad-calib status forward. +STAGE_NAME="${OUTPUT_FILE}" +if [[ -f "badCalib.${OUTPUT_STEM}" ]]; then + STAGE_NAME="badCalib_${OUTPUT_FILE}" + echo "Calibration marked bad for this slot (found badCalib.${OUTPUT_STEM}) -- staging as ${STAGE_NAME} instead of ${OUTPUT_FILE}" +fi + +# --- Two-stage pipeline: stage the produced map to THIS job's own JDL-assigned output directory +# (per ALICE Grid support -- do not write production data to a personal-space path; that needs its own +# disk-quota request on top of the CPU one, unlike ${ALIEN_JDL_OUTPUTDIR}, which is already covered by +# however this job's quota/ownership is set up). ALIEN_JDL_OUTPUTDIR is JAliEn's own per-JDL-field env +# var (case-sensitive, same convention as ALIEN_JDL_CPUCORES etc.); fall back to ALIEN_JOB_OUTPUTDIR +# (grid_submit.sh's own equivalent, computed the same way from the same JDL field -- see that script) in +# case the former isn't exported in some execution context. Explicit, synchronous cp rather than relying +# on the passive #JDL_OUTPUT@disk=N auto-upload -- that only happens during the job's asynchronous +# SAVING phase after this script exits, which can take minutes and would race against a later reader +# (stage 2 doesn't try to read this immediately, but no reason to introduce that fragility when an +# explicit cp is just as easy and finishes before this script does). +# +# NOTE: this directory is only known once the job actually runs -- see +# submitMapCreation.sh for how the post-processing stage (postProcessJob.sh, +# SmoothingExtrapolate.C + TPCFastTransformInitCPM.C) finds it later without needing an in-job breadcrumb. +ALIEN_OUTPUT_DIR="alien://${ALIEN_JDL_OUTPUTDIR:-${ALIEN_JOB_OUTPUTDIR:-}}" +if [[ "${ALIEN_OUTPUT_DIR}" == "alien://" ]]; then + echo "ERROR: neither ALIEN_JDL_OUTPUTDIR nor ALIEN_JOB_OUTPUTDIR is set -- don't know where to stage ${OUTPUT_FILE}" >&2 + exit 1 +fi +if ! alien.py cp -f "file:${OUTPUT_FILE}" "${ALIEN_OUTPUT_DIR}/${STAGE_NAME}"; then + echo "ERROR: failed to stage ${OUTPUT_FILE} to ${ALIEN_OUTPUT_DIR} -- the post-processing stage won't be able to find this slot's map" >&2 + exit 1 +fi +echo "Staged ${OUTPUT_FILE} to ${ALIEN_OUTPUT_DIR}/${STAGE_NAME}" diff --git a/DATA/production/configurations/mapCreationGrid/postProcessJob.sh b/DATA/production/configurations/mapCreationGrid/postProcessJob.sh new file mode 100755 index 000000000..e555d09cc --- /dev/null +++ b/DATA/production/configurations/mapCreationGrid/postProcessJob.sh @@ -0,0 +1,286 @@ +#!/usr/bin/env bash +# postProcessJob.sh — STAGE 2 of the two-stage pipeline. +# +# Runs the fast (minutes, not hours) part of the chain on an already-produced map: +# SmoothingExtrapolate.C -> TPCFastTransformInitCPM.C +# +# Independent of stage 1 (mapCreationJob.sh): does NOT re-run map creation, does NOT even download the +# (multi-GB) residual files. It only needs the small manifest (mapSlots.tsv, reused as-is from stage 1, +# NOT rebuilt) to work out which map stage 1 should have produced for this same subjob index. Both +# stages read the run/window straight off the SAME manifest line, so they cannot disagree about a +# slot's identity. +# +# If stage 1 hasn't produced (or failed to produce) this slot's map yet, this script logs a warning +# and exits 0 (success/no-op) rather than failing -- so a partial stage-1 completion (some slots ok, +# some failed) doesn't turn every stage-2 subjob into a failure. Once stage 1 is resubmitted for the +# missing slots, just resubmit the corresponding stage-2 subjob(s). +# +# Slot windowing: none happens here. stageSlotsForGrid.py fixed the windows at submission time and +# wrote them into the manifest; this script just reads its line. +# +# STAGE1_JOBNAME_TAG: which stage-1 production's manifest/output to look for -- NOT this stage's own job +# name, but stage 1's JOBNAME_TAG (mapCreationJob.sh), since that's what determined the manifest +# name AND (via the STAGE1_OUTPUTDIR_BASE pointer file it uploaded) where stage 1's own jobs staged their +# maps. Must match exactly, or this will always hit the graceful-skip path even though stage 1 succeeded. +# +# STAGE1_OUTPUTDIR_BASE / COUNTERWIDTH (per ALICE Grid support): stage 1 stages its map to its own +# ${ALIEN_JDL_OUTPUTDIR} rather than a fixed personal-space path (avoids a separate disk-quota request), +# so there's no static location to just look up. STAGE1_OUTPUTDIR_BASE is stage 1's MY_JOBWORKDIR (that +# submission's timestamped parent directory, captured by submitMapCreation.sh right when it +# ran grid_submit.sh -- otherwise unrecoverable later) and COUNTERWIDTH is grid_submit.sh's own +# alien_counter zero-padding width -- together they let this script reconstruct stage 1's exact per-slot +# ${ALIEN_JDL_OUTPUTDIR} the same way grid_submit.sh itself derives OutputDir (see below), without any +# in-job breadcrumb from stage 1 or an extra AliEn round trip. +# +# MACRO_SOURCE: same meaning and default ("official") as stage 1 -- see mapCreationJob.sh. Applies +# to all three macros used here (SmoothingExtrapolate.C, TPCFastTransformInitCPM.C, voxResQA.C), all +# installed to the same $O2_ROOT/share/macro/ once the O2 PR is merged into this job's packagespec. +# +# MACRO_ALIEN_DIR_BARE: same placeholder mechanism, substituted from the wrapper's own +# MACRO_ALIEN_DIR_BARE -- must match stage 1's. Used for the manifest/stage1Workdir pointer regardless of +# MACRO_SOURCE; only used for the macros themselves when MACRO_SOURCE=alien. +STAGE1_JOBNAME_TAG=default +STAGE1_OUTPUTDIR_BASE= +COUNTERWIDTH=3 +MACRO_SOURCE=official +MACRO_ALIEN_DIR_BARE=${MACRO_ALIEN_DIR_BARE:-} +#JDL_OUTPUT=*.log@disk=2,*.txt@disk=2,*.png@disk=2 +#JDL_REQUIRE={member(other.GridPartitions,"multicore_8") && (other.CE != "ALICE::NIHAM::PBS64") && (other.CE != "ALICE::KISTI_GSDC::LCG") && (other.CE != "ALICE::SaoPaulo::LCG")}; +# Site blacklist -- same three CEs excluded as stage 1, see that script's comment for why the +# GridPartitions clause has to be repeated here (grid_submit.sh replaces its whole default Requirements, +# doesn't merge with it). +# voxRes.*.root/FT_*.root/voxResQA*.root deliberately NOT in this pattern -- same reasoning as stage 1 +# (mapCreationJob.sh): they're staged explicitly, once, to this job's own ${ALIEN_JDL_OUTPUTDIR} +# below, so having them ALSO match here would duplicate the upload to the same place for no benefit. +# The QA step's individual *.png canvases ARE left in this pattern -- they're small, convenience-only +# quick-look images, not worth a second explicit-staging code path for. + +set -e + +# xrootd client tuning -- see mapCreationJob.sh for the reasoning; same values, same rationale, +# applies equally here since this script does just as much alien://xrootd transfer (macros, manifest, +# the fixed-location map download) even though it doesn't touch the multi-GB residual files. +export XRD_TIMEOUTRESOLUTION=15 +export XRD_CONNECTIONWINDOW=30 +export XRD_CONNECTIONRETRY=4 +export XRD_REQUESTTIMEOUT=120 +export XRD_STREAMTIMEOUT=120 +export XRD_STREAMERRORWINDOW=0 + +# --- AliEn path holding mapSlots.tsv / the stage1Workdir pointer file (and the three macros too, when +# MACRO_SOURCE=alien). Same root dir as stage 1's MACRO_ALIEN_DIR. +# +# Normally substituted above by submitPostProcess.sh; empty only on a standalone/local run -- see the +# same guard in mapCreationJob.sh. +: "${MACRO_ALIEN_DIR_BARE:?MACRO_ALIEN_DIR_BARE is empty -- the submission wrapper normally substitutes it. For a standalone/local run, export it first, e.g. MACRO_ALIEN_DIR_BARE=/alice/cern.ch/user///MapCreation}" +MACRO_ALIEN_DIR="alien://${MACRO_ALIEN_DIR_BARE}" + +if [[ "${MACRO_SOURCE}" == "official" ]]; then + : "${O2_ROOT:?O2_ROOT is not set -- MACRO_SOURCE=official needs the O2 package loaded via --packagespec}" + SMOOTHING_MACRO_PATH="${O2_ROOT}/share/macro/SmoothingExtrapolate.C" + FASTTRANSFORM_MACRO_PATH="${O2_ROOT}/share/macro/TPCFastTransformInitCPM.C" + QA_MACRO_PATH="${O2_ROOT}/share/macro/voxResQA.C" + for f in "${SMOOTHING_MACRO_PATH}" "${FASTTRANSFORM_MACRO_PATH}" "${QA_MACRO_PATH}"; do + if [[ ! -f "${f}" ]]; then + echo "ERROR: MACRO_SOURCE=official but ${f} does not exist -- this job's --packagespec does not include it yet. Use MACRO_SOURCE=alien until a packagespec with it is published." >&2 + exit 1 + fi + done +else + alien.py cp -f "${MACRO_ALIEN_DIR}/SmoothingExtrapolate.C" file:./SmoothingExtrapolate.C + alien.py cp -f "${MACRO_ALIEN_DIR}/TPCFastTransformInitCPM.C" file:./TPCFastTransformInitCPM.C + alien.py cp -f "${MACRO_ALIEN_DIR}/voxResQA.C" file:./voxResQA.C + SMOOTHING_MACRO_PATH="./SmoothingExtrapolate.C" + FASTTRANSFORM_MACRO_PATH="./TPCFastTransformInitCPM.C" + QA_MACRO_PATH="./voxResQA.C" +fi +# Tagged manifest name -- must match whatever stage 1 actually uploaded (mapSlots..tsv). +alien.py cp -f "${MACRO_ALIEN_DIR}/mapSlots.${STAGE1_JOBNAME_TAG}.tsv" file:./mapSlots.tsv + +# --- Determine this subjob's index into mapSlots.tsv (same convention as stage 1) --- +SUBJOBID="${ALIEN_O2DPG_GRIDSUBMIT_SUBJOBID:-${SUBJOBID:-}}" +: "${SUBJOBID:?SUBJOBID must be set (via ALIEN_O2DPG_GRIDSUBMIT_SUBJOBID on GRID, or the SUBJOBID env var for --local testing)}" + +LINE=$(sed -n "${SUBJOBID}p" mapSlots.tsv) +if [[ -z "${LINE}" ]]; then + echo "ERROR: mapSlots.tsv has no line ${SUBJOBID}" >&2 + exit 1 +fi + +# Manifest line: \t \t \t \t \t +# Stage 2 does NO time arithmetic: it reads the window straight off the same manifest line stage 1 +# used, so OUTPUT_STEM below is identical to stage 1's by construction. Previously both stages +# recomputed the window from TIME_DIVISION/TIME_DIVISION_INDEX and any disagreement meant stage 2 +# silently found nothing and no-opped for every slot. +# The 6th field (the file list) is deliberately discarded: stage 2 never touches the residual +# files, it only needs the run and the window. It still has to be READ, or lastTSIter would +# swallow the rest of the line. +IFS=$'\t' read -r RUN WIN_IDX WIN_COUNT firstTSIter lastTSIter _ <<< "${LINE}" +for v in RUN WIN_IDX WIN_COUNT firstTSIter lastTSIter; do + if [[ ! "${!v}" =~ ^[0-9]+$ ]]; then + echo "ERROR: manifest line ${SUBJOBID} field ${v}='${!v}' is not numeric -- expected 6 tab-separated fields " >&2 + exit 1 + fi +done +echo "Slot ${SUBJOBID}: run ${RUN}, window ${WIN_IDX}/${WIN_COUNT} [${firstTSIter},${lastTSIter}]" + +# --- Config (production defaults) --- +USE_SMOOTHED=2 # matches stage 1's USE_SMOOTHED -- selects doGausSmooth=true below +DO_GAUS_SMOOTH=true +DO_EXTRAPOLATION=2 # smoothed AND raw values extrapolated +A11_MAX_Z2X=-1 # disabled +MASK_IA11=0 # disabled +USE_CTP_LUMI=2 # matches stage 1's USE_CTP_LUMI + +# voxResQA.C config -- needs SmoothingExtrapolate.C's DS[] (smoothed dX/dY/dZ) already filled in, +# hence run from here, on OUTPUT_MAP_SMOOTH, not the raw stage-1 map. +QA_DRAW_ERRORS=false +QA_USE_SMOOTHED=true # true: draw dXS/dYS/dZS (needs SmoothingExtrapolate.C's output); false: raw dX/dY/dZ +QA_Z2X_BIN_SEL=-1 # -1: all z2x bins + +# --- Expected stage-1 output filename (must match mapCreationJob.sh's OUTPUT_STEM exactly) --- +OUTPUT_STEM="voxRes.${RUN}_${firstTSIter}_${lastTSIter}.TD${WIN_IDX}of${WIN_COUNT}.it0" +STAGE1_OUTPUT_FILE="${OUTPUT_STEM}.root" + +# Reconstruct stage 1's exact per-slot ${ALIEN_JDL_OUTPUTDIR} -- same formula grid_submit.sh itself uses +# to derive OutputDir (MY_JOBWORKDIR/#alien_counter_0Wi#): STAGE1_OUTPUTDIR_BASE is stage 1's +# MY_JOBWORKDIR, and this slot's own subjob index (SUBJOBID, same manifest line/counter convention as +# stage 1) zero-padded to COUNTERWIDTH gives the exact subdirectory stage 1's own job for this slot wrote +# its output to. +SUBJOB_COUNTER_DIR=$(printf "%0${COUNTERWIDTH}d" "${SUBJOBID}") +STAGE1_OUTPUT_LOCATION="alien://${STAGE1_OUTPUTDIR_BASE}/${SUBJOB_COUNTER_DIR}" + +# Local name deliberately does NOT look like our own output (voxRes.*.root/FT_*.root) -- both are now +# staged explicitly below rather than picked up via a JDL_OUTPUT glob, but naming this distinctly still +# avoids any confusion between "the map we downloaded as input" and "the map we produced" (the exact bug +# found+fixed earlier for badRanges.txt/badRanges.dat). +INPUT_MAP="input_map.root" + +echo "Looking for stage-1 output at ${STAGE1_OUTPUT_LOCATION}/${STAGE1_OUTPUT_FILE}" +if ! alien.py cp -f "${STAGE1_OUTPUT_LOCATION}/${STAGE1_OUTPUT_FILE}" "file:${INPUT_MAP}" || [[ ! -s "${INPUT_MAP}" ]]; then + rm -f "${INPUT_MAP}" + # Not there under the normal name -- check whether stage 1 actually ran but renamed it away because + # the calibration was marked bad (same convention as the real production, see mapCreationJob.sh), + # rather than assuming it just hasn't run yet. + if alien.py cp -f "${STAGE1_OUTPUT_LOCATION}/badCalib_${STAGE1_OUTPUT_FILE}" "file:${INPUT_MAP}" 2>/dev/null && [[ -s "${INPUT_MAP}" ]]; then + echo "Stage 1 marked this slot's calibration as bad (found badCalib_${STAGE1_OUTPUT_FILE} instead of ${STAGE1_OUTPUT_FILE}) -- skipping post-processing on purpose, not an error." >&2 + else + echo "WARNING: stage-1 output '${STAGE1_OUTPUT_FILE}' not found at ${STAGE1_OUTPUT_LOCATION} -- this slot's map creation likely hasn't run yet or failed. Skipping (not an error) -- resubmit this subjob once stage 1 has produced it." >&2 + fi + rm -f "${INPUT_MAP}" + exit 0 +fi + +# --- [1/2] Gaussian smoothing + extrapolation --- +GAUSSMOOTH_ADD="" +[[ "${DO_GAUS_SMOOTH}" == "true" ]] && GAUSSMOOTH_ADD+=".GausSmooth" +[[ "${DO_EXTRAPOLATION}" -gt 0 ]] && GAUSSMOOTH_ADD+=".Extrapolation" +OUTPUT_MAP_SMOOTH="${OUTPUT_STEM}${GAUSSMOOTH_ADD}.root" + +cmd="(time root.exe -b -q -l -x '${SMOOTHING_MACRO_PATH}+Og(\"${INPUT_MAP}\", \"${OUTPUT_MAP_SMOOTH}\", ${DO_GAUS_SMOOTH}, ${DO_EXTRAPOLATION}, ${A11_MAX_Z2X}, ${MASK_IA11})')" +echo "$(date) ${cmd}" +eval "${cmd}" + +if [[ ! -s "${OUTPUT_MAP_SMOOTH}" ]]; then + echo "ERROR: ${OUTPUT_MAP_SMOOTH} was not produced -- SmoothingExtrapolate.C failed" >&2 + exit 1 +fi + +# --- Resolve this job's own JDL-assigned output directory (per ALICE Grid support -- see +# mapCreationJob.sh for the full reasoning: production data belongs in ${ALIEN_JDL_OUTPUTDIR}, +# not a personal-space path). ALIEN_JDL_OUTPUTDIR is JAliEn's own per-JDL-field env var; fall back to +# ALIEN_JOB_OUTPUTDIR (grid_submit.sh's equivalent, computed from the same JDL field). +ALIEN_OUTPUT_DIR="alien://${ALIEN_JDL_OUTPUTDIR:-${ALIEN_JOB_OUTPUTDIR:-}}" +if [[ "${ALIEN_OUTPUT_DIR}" == "alien://" ]]; then + echo "ERROR: neither ALIEN_JDL_OUTPUTDIR nor ALIEN_JOB_OUTPUTDIR is set -- don't know where to stage this slot's output" >&2 + exit 1 +fi + +# Stage the smoothed map NOW, before the QA and FastTransform steps rather than after them. Those steps +# can fail hard on input this script cannot control -- notably TPCFastTransformInitCPM.C's +# LOGP(fatal, "meanCTP ... not set!"), which fires for any slot whose stage-1 map carries meanCTP=0 in +# its UserInfo (useCTPLumi=2 exempts meanIDC from that check, but NOT meanCTP). Staging last meant such +# a slot lost its perfectly good smoothed map as well, forcing a resubmit to redo the smoothing. +if ! alien.py cp -f "file:${OUTPUT_MAP_SMOOTH}" "${ALIEN_OUTPUT_DIR}/${OUTPUT_MAP_SMOOTH}"; then + echo "ERROR: failed to stage ${OUTPUT_MAP_SMOOTH} to ${ALIEN_OUTPUT_DIR}" >&2 + exit 1 +fi +echo "Staged ${OUTPUT_MAP_SMOOTH} to ${ALIEN_OUTPUT_DIR}" + +# --- Provenance pointer: unlike stage 1 -> stage 2 (which has the stage1Workdir..txt pointer), +# stage 2's own output directory otherwise carries no trace of which exact stage-1 output it was built +# from. Record it as a one-line pointer, same idea, staged right alongside the smoothed map. +STAGE1_SOURCE_FILE="stage1Source.txt" +echo "${STAGE1_OUTPUT_LOCATION}/${STAGE1_OUTPUT_FILE}" > "${STAGE1_SOURCE_FILE}" +if ! alien.py cp -f "file:${STAGE1_SOURCE_FILE}" "${ALIEN_OUTPUT_DIR}/${STAGE1_SOURCE_FILE}"; then + echo "WARNING: failed to stage ${STAGE1_SOURCE_FILE} -- provenance pointer only, not fatal" >&2 +fi +# Remove the local copy now that it's staged: it matches the #JDL_OUTPUT *.txt glob above, and the +# JobAgent's own passive upload of that glob (run after this script exits) does a plain, non-forced +# cp -- it would otherwise collide with the LFN this explicit -f upload just created and put the +# whole subjob into ERROR_SV, even though the actual deliverable upload above succeeded. +rm -f "${STAGE1_SOURCE_FILE}" + +# --- QA plots (voxResQA.C) -- runs on just this one slot's smoothed map, same per-slot granularity as +# everything else in this pipeline. Non-fatal on failure: QA is a diagnostic nice-to-have, not a blocking +# deliverable like the smoothed map/FastTransform output -- a QA hiccup shouldn't fail an otherwise-good +# slot. +QA_SUFFIX=".${OUTPUT_STEM}${GAUSSMOOTH_ADD}" +cmd="(time root.exe -b -q -l -x '${QA_MACRO_PATH}+Og(\"ls ${OUTPUT_MAP_SMOOTH}\", \"${QA_SUFFIX}\", ${QA_DRAW_ERRORS}, ${QA_USE_SMOOTHED}, ${QA_Z2X_BIN_SEL})')" +echo "$(date) ${cmd}" +eval "${cmd}" || echo "WARNING: voxResQA.C failed for this slot -- QA plots will be missing, but this doesn't affect ${OUTPUT_MAP_SMOOTH} itself" >&2 + +QA_FILE_1D="voxResQA${QA_SUFFIX}_1D.root" +QA_FILE_2D="voxResQA${QA_SUFFIX}_2D.root" + +# --- [2/2] Final O2 FastTransform --- +OUTPUT_FT="FT_${OUTPUT_MAP_SMOOTH}" + +cmd="(time root.exe -b -q -l -x '${FASTTRANSFORM_MACRO_PATH}+Og(\"${OUTPUT_MAP_SMOOTH}\", \"${OUTPUT_FT}\", ${USE_SMOOTHED}, ${USE_CTP_LUMI})')" +echo "$(date) ${cmd}" +eval "${cmd}" + +if [[ ! -s "${OUTPUT_FT}" ]]; then + echo "ERROR: ${OUTPUT_FT} was not produced -- TPCFastTransformInitCPM.C failed" >&2 + exit 1 +fi + +# --- Stage the FastTransform output to this job's own JDL-assigned output directory (the smoothed map +# was already staged above, right after it was produced). Explicit, synchronous cp rather than relying on +# the passive #JDL_OUTPUT@disk=N auto-upload, which only happens during the job's asynchronous SAVING +# phase after this script exits. +if ! alien.py cp -f "file:${OUTPUT_FT}" "${ALIEN_OUTPUT_DIR}/${OUTPUT_FT}"; then + echo "ERROR: failed to stage ${OUTPUT_FT} to ${ALIEN_OUTPUT_DIR}" >&2 + exit 1 +fi +echo "Staged ${OUTPUT_FT} to ${ALIEN_OUTPUT_DIR}" + +# QA plots -- non-fatal staging, same reasoning as the non-fatal QA run itself above: if voxResQA.C +# failed or produced nothing, there's nothing to stage, and that shouldn't fail this otherwise-good slot. +for qaFile in "${QA_FILE_1D}" "${QA_FILE_2D}"; do + if [[ -s "${qaFile}" ]]; then + alien.py cp -f "file:${qaFile}" "${ALIEN_OUTPUT_DIR}/${qaFile}" || \ + echo "WARNING: failed to stage ${qaFile} -- non-fatal, just loses this slot's QA plots" >&2 + else + echo "WARNING: ${qaFile} was not produced -- voxResQA.C likely failed above, skipping its staging" >&2 + fi +done + +# TPCFastTransformInitCPM.C's own debug/summary trees (produced unconditionally alongside OUTPUT_FT, +# named after its stem -- see createFastTransform() in that macro) were never staged anywhere: the +# #JDL_OUTPUT pattern above only matches *.log/*.txt/*.png (no *.root), and the explicit staging block +# above only names OUTPUT_MAP_SMOOTH/OUTPUT_FT -- so these were produced on the worker and then silently +# discarded when the job finished. Non-fatal staging, same reasoning as the QA plots: diagnostic, not a +# blocking deliverable. +OUTPUT_FT_DEBUG="${OUTPUT_FT%.root}.debug.root" +OUTPUT_FT_SUMMARY="${OUTPUT_FT%.root}.summary.root" +for ftDebugFile in "${OUTPUT_FT_DEBUG}" "${OUTPUT_FT_SUMMARY}"; do + if [[ -s "${ftDebugFile}" ]]; then + alien.py cp -f "file:${ftDebugFile}" "${ALIEN_OUTPUT_DIR}/${ftDebugFile}" || \ + echo "WARNING: failed to stage ${ftDebugFile} -- non-fatal, just loses this slot's FT debug/summary tree" >&2 + else + echo "WARNING: ${ftDebugFile} was not produced -- skipping its staging" >&2 + fi +done + +echo "Stage 2 complete: ${OUTPUT_MAP_SMOOTH}, ${OUTPUT_FT}" diff --git a/DATA/production/configurations/mapCreationGrid/stageSlotsForGrid.py b/DATA/production/configurations/mapCreationGrid/stageSlotsForGrid.py new file mode 100755 index 000000000..eae34838d --- /dev/null +++ b/DATA/production/configurations/mapCreationGrid/stageSlotsForGrid.py @@ -0,0 +1,340 @@ +#!/usr/bin/env python3 +"""Build and upload the slot manifest for the TPC map-creation GRID pipeline. + +Bridge between the local Step-1 list discovery (discoverResiduals.sh) and GRID submission. + +Cuts each run into time windows of ~--slot-length-min and writes ONE manifest line per window: + + \t \t \t \t \t + +paired with `grid_submit.sh --prodsplit `. Every subjob gets nothing but a plain integer +index (ALIEN_O2DPG_GRIDSUBMIT_SUBJOBID), downloads this one manifest and picks its own line, so a +subjob index identifies a (run, window) pair on its own. Both job scripts read the window straight off +that line, which is why they cannot disagree about a slot's identity. + +Why the windows are not simply the aggregation labels +----------------------------------------------------- +Each residual filename carries the slot label ___ that its reco +job assigned, and those labels are NOT a uniform grid -- same-length slots are not guaranteed, and a +run's catalog can hold overlapping/nested labels at different granularities (why doesn't matter here, +just that it happens). Slicing per label gives overlapping slots with wildly uneven statistics. +Choosing the window length here instead makes granularity a deliberate decision, uniform across runs. + +A file is assigned to a window when its label overlaps it by MORE than --overlap-margin-ms. A bare +overlap test is too generous at the edges: coarse labels routinely begin 1-2 ms before a window ends, +which would drag all of their files into a window whose map they contribute nothing to. The bound is +exact -- if a label overlaps by X ms then at most X ms of that file's data can belong to the window, +and the macro's own per-TF cut discards the rest regardless -- so skipping at X <= margin costs at +most `margin` ms of coverage per window edge. + +This was a bash+awk script. It moved to Python because the work is arithmetic over 13-digit +millisecond timestamps and every bug it produced was a property of the shell rather than of the +problem: `(( x += 0 ))` returning 1 under `set -e`, `sed | sort | head` aborting the script by SIGPIPE +under `pipefail`, `<<<` herestrings writing temp files onto Lustre, awk's %d truncating a millisecond +timestamp through a 32-bit int on mawk, and awk holding one output file open per window. Python has +arbitrary-precision ints and none of those failure modes. + +Prints the number of manifest lines (windows) to stdout on its own -- pass that as --prodsplit N to +grid_submit.sh. All other output goes to stderr, so `N=$(./stageSlotsForGrid.py ...)` captures just the +count. +""" + +import argparse +import os +import re +import shutil +import subprocess +import sys +import tempfile +import time +from pathlib import Path + +# o2tpc_residuals____.root, anchored: the timestamps are always +# exactly 13 digits (ResidualAggregator.cxx formats them from the slot's start/end in ms). +SIG_RE = re.compile(r"o2tpc_residuals_(\d{13})_(\d{13})_\d+_\d+\.root$") + + +def log(msg): + print(msg, file=sys.stderr) + + +def parse_args(argv): + p = argparse.ArgumentParser( + description="Build and upload the TPC map-creation slot manifest.", + formatter_class=argparse.ArgumentDefaultsHelpFormatter, + ) + p.add_argument("--lists-dir", default="./lists", + help="root lists dir (discoverResiduals.sh's WORK_DIR), containing one / " + "subdir per run, each with a .residuals_lists.txt index") + p.add_argument("--alien-dir", default="", + help="AliEn directory to upload the manifest into. Ignored with --preview.") + p.add_argument("--manifest-name", default="mapSlots.tsv", + help="manifest filename. Tag it per submission (mapSlots..tsv) so " + "concurrent productions do not overwrite each other's.") + p.add_argument("--runs", default="", + help="comma-separated run whitelist. Empty means EVERY run under --lists-dir, " + "including ones left from an earlier batch, which would be recalibrated from " + "scratch at hours per slot. Naming a run that has no lists is an error.") + p.add_argument("--slot-length-min", type=float, default=0, + help="target length of ONE map, in minutes. 0 = one window per run.") + p.add_argument("--overlap-margin-ms", type=int, default=1000, + help="a file whose label overlaps a window by <= this is not assigned to it") + p.add_argument("--window-lists", action="store_true", + help="also write one plain file list per window, for inspecting which files a " + "window got. OFF by default: on Lustre each is a metadata round trip and 120 " + "of them cost ~33 s, against ~0.03 s for all the actual work. The manifest " + "already holds every window in full, one line each.") + p.add_argument("--window-dir", default="", + help="where --window-lists writes. Default: .windows. Point it at " + "local disk (e.g. /tmp/windows) on a parallel filesystem.") + p.add_argument("--timing", action="store_true", default=bool(os.environ.get("STAGE_TIMING")), + help="print a read/compute/write breakdown to stderr (also via STAGE_TIMING=1)") + p.add_argument("--preview", action="store_true", + default=bool(os.environ.get("PREVIEW_ONLY")), + help="write the per-window lists but do not upload (also via PREVIEW_ONLY=1)") + return p.parse_args(argv) + + +def read_slot_files(index_path): + """The per-run index lists the file(s) holding that run's residual paths (normally exactly one).""" + out = [] + for line in index_path.read_text().splitlines(): + line = line.strip() + if line: + out.append(Path(line)) + return out + + +def plan_windows(run_first, run_last, slot_ms, margin_ms): + """Whole steps of slot_ms across [run_first, run_last]. + + A trailing remainder shorter than half a step is absorbed into the last window rather than left as + a runt that would fail minTracksPerSlice. Returns [(winFirst, winLast, effectiveMargin)]. + """ + span = run_last - run_first + if slot_ms <= 0 or span <= 0: + n_win = 1 + else: + n_win = max(1, span // slot_ms) + if (span - n_win * slot_ms) * 2 >= slot_ms: + n_win += 1 + + wins = [] + for w in range(n_win): + if n_win == 1: + ws, we = run_first, run_last + else: + ws = run_first + w * slot_ms + we = run_last if w + 1 == n_win else run_first + (w + 1) * slot_ms - 1 + # Clamp the margin to a tenth of the window so a short window can never be emptied by it. + wins.append((ws, we, min(margin_ms, (we - ws) // 10))) + return wins + + +def assign(paths, wins, run_first, slot_ms): + """Fan every file out to the windows its label overlaps by more than that window's margin. + + Only the windows a label can possibly touch are examined, derived directly from the label bounds, + rather than scanning all of them per file: with 10-minute labels and 5-minute windows that is 2-3 + candidates instead of the whole run's worth. Returns (buckets, grazed, orphans). + """ + n_win = len(wins) + buckets = [[] for _ in range(n_win)] + grazed = [0] * n_win + orphans = [] + + for lo, hi, line in paths: + if slot_ms > 0 and n_win > 1: + # Windows are contiguous steps of slot_ms from run_first, so a label maps to an index + # range. +/-1 guards the boundaries and the last window, which extends to run_last. + first = max(0, (lo - run_first) // slot_ms - 1) + last = min(n_win - 1, (hi - run_first) // slot_ms + 1) + else: + first, last = 0, n_win - 1 + + hit = False + for i in range(first, last + 1): + ws, we, margin = wins[i] + if hi < ws or lo > we: + continue + if min(hi, we) - max(lo, ws) > margin: + buckets[i].append(line) + hit = True + else: + grazed[i] += 1 + if not hit: + orphans.append(line) + + return buckets, grazed, orphans + + +def main(argv): + args = parse_args(argv) + + if not args.preview and not args.alien_dir: + log("ERROR: --alien-dir is required unless --preview is given") + return 1 + + lists_dir = Path(args.lists_dir) + index_files = sorted(lists_dir.glob("*/*.residuals_lists.txt")) + if not index_files: + log(f"ERROR: no *.residuals_lists.txt files found under {lists_dir}") + return 1 + + slot_ms = int(round(args.slot_length_min * 60_000)) + wanted = [r for r in args.runs.split(",") if r] if args.runs else [] + + # Rebuilt from scratch each time so it always reflects THIS invocation's slot length and margin + # rather than a stale earlier split. + window_dir = Path(args.window_dir) if args.window_dir else \ + Path(Path(args.manifest_name).with_suffix("").name + ".windows") + t_rm = time.monotonic() + if args.window_lists: + if window_dir.exists(): + shutil.rmtree(window_dir) + window_dir.mkdir(parents=True) + t_rm = time.monotonic() - t_rm + t_read = t_calc = t_write = 0.0 + + manifest_lines = [] + runs_included = [] + empty_runs = [] + n_missing = 0 + n_orphan_total = 0 + + for idx in index_files: + run = idx.name[: -len(".residuals_lists.txt")] + if wanted and run not in wanted: + log(f"Run {run}: not in runs filter, skipping") + continue + runs_included.append(run) + log(f"Run {run}: adding slots from {idx.name}") + + n_slots_run = 0 + for slot_file in read_slot_files(idx): + if not slot_file.is_file(): + log(f" WARNING: missing local file {slot_file}, skipping") + n_missing += 1 + continue + + _t = time.monotonic() + _text = slot_file.read_text() + t_read += time.monotonic() - _t + _t = time.monotonic() + parsed = [] + for line in _text.splitlines(): + m = SIG_RE.search(line) + if m: + parsed.append((int(m.group(1)), int(m.group(2)), line)) + if not parsed: + log(f" WARNING: no parseable residual signature in {slot_file}, skipping") + continue + + t_calc += time.monotonic() - _t + _t = time.monotonic() + run_first = min(p[0] for p in parsed) + run_last = max(p[1] for p in parsed) + wins = plan_windows(run_first, run_last, slot_ms, args.overlap_margin_ms) + buckets, grazed, orphans = assign(parsed, wins, run_first, slot_ms) + n_win = len(wins) + t_calc += time.monotonic() - _t + + for w, ((ws, we, _), files) in enumerate(zip(wins, buckets)): + if grazed[w]: + log(f" (skipped {grazed[w]} file(s) grazing this window)") + if not files: + log(f" WARNING: run {run} window {w}/{n_win} [{ws},{we}] has no files, skipping") + continue + if args.window_lists: + _t = time.monotonic() + out = window_dir / f"{run}.TD{w}of{n_win}.{ws}_{we}.txt" + out.write_text("\n".join(files) + "\n") + t_write += time.monotonic() - _t + manifest_lines.append(f"{run}\t{w}\t{n_win}\t{ws}\t{we}\t{','.join(files)}") + log(f" window {w}/{n_win} [{ws},{we}] {(we - ws) // 1000}s {len(files)} file(s)") + n_slots_run += 1 + + # Safety net for the overlap margin: every file must still land in at least one window. It + # always should -- an aggregation label is minutes long, so it overlaps some window by far + # more than the margin -- but a file in no window at all is silent data loss. + if orphans: + log(f" WARNING: {len(orphans)} file(s) of run {run} landed in NO window -- " + f"their data is not in any map.") + log(f" --overlap-margin-ms ({args.overlap_margin_ms}) is too large for a " + f"slot length of {args.slot_length_min} min.") + for o in orphans[:3]: + log(f" {o}") + n_orphan_total += len(orphans) + + log(f" {n_slots_run} slot(s) added for run {run}") + if n_slots_run == 0: + empty_runs.append(run) + + # A run named in --runs but absent from --lists-dir is a typo or a missed discovery step, not a + # request for a smaller production. + missing_runs = [r for r in wanted if r not in runs_included] + if missing_runs: + log(f"ERROR: run(s) requested in --runs have no *.residuals_lists.txt under {lists_dir}: " + f"{' '.join(missing_runs)}") + log(" Run discoverResiduals.sh for them first, or drop them from --runs.") + return 1 + + if n_missing or empty_runs or n_orphan_total: + log("") + log(" ****************************************************************") + log(" ** INCOMPLETE INPUT -- this manifest is smaller than expected **") + if n_missing: + log(f" ** {n_missing} slot file(s) referenced by an index but missing on disk") + if empty_runs: + log(f" ** run(s) contributing 0 slots: {' '.join(empty_runs)}") + if n_orphan_total: + log(f" ** {n_orphan_total} file(s) landed in no window -- lower --overlap-margin-ms") + log(" ** -> re-run discoverResiduals.sh for those runs, or accept") + log(" ** that they are simply absent from this production.") + log(" ****************************************************************") + log("") + + manifest_text = "".join(l + "\n" for l in manifest_lines) + # The manifest is always written locally, as a single file -- it is the complete record of the + # split and costs one create. + _t = time.monotonic() + Path(args.manifest_name).write_text(manifest_text) + t_write += time.monotonic() - _t + n_total = len(manifest_lines) + + if args.timing: + log(f"timing: clear={t_rm:.2f}s read={t_read:.2f}s compute={t_calc:.2f}s " + f"write={t_write:.2f}s ({n_total} window file(s))") + + if args.preview: + log("PREVIEW_ONLY set -- manifest NOT uploaded.") + log(f"Manifest written to: {args.manifest_name} ({n_total} window(s))") + if args.window_lists: + log(f"Per-window file lists in: {window_dir}/") + else: + log("Pass --window-lists to also dump one file list per window " + "(slow on Lustre; add --window-dir /tmp/... )") + print(n_total) + return 0 + + with tempfile.NamedTemporaryFile("w", suffix=".tsv", delete=False) as fh: + fh.write(manifest_text) + tmp_manifest = fh.name + try: + dest = f"alien://{args.alien_dir.rstrip('/')}/{args.manifest_name}" + subprocess.run(["alien.py", "cp", "-f", f"file:{tmp_manifest}", dest], + check=True, stdout=sys.stderr.fileno()) + log(f"Manifest uploaded: {dest} ({n_total} slot(s) across {len(runs_included)} run(s))") + finally: + os.unlink(tmp_manifest) + + log(f"Manifest also written locally to: {args.manifest_name}") + if args.window_lists: + log(f"Per-window file lists in: {window_dir}/") + log(f"Runs in this manifest: {' '.join(runs_included)}") + print(n_total) + return 0 + + +if __name__ == "__main__": + sys.exit(main(sys.argv[1:])) diff --git a/DATA/production/configurations/mapCreationGrid/submitMapCreation.sh b/DATA/production/configurations/mapCreationGrid/submitMapCreation.sh new file mode 100755 index 000000000..f63b5cd7b --- /dev/null +++ b/DATA/production/configurations/mapCreationGrid/submitMapCreation.sh @@ -0,0 +1,222 @@ +#!/usr/bin/env bash +# submitMapCreation.sh +# +# Submits one grid_submit.sh production-split job, one subjob per time-window slot found in +# (built beforehand by discoverResiduals.sh -- run that first). +# +# Before each submission, this re-stages the slot manifest (mapSlots.tsv, built here via +# stageSlotsForGrid.py) -- and, when MACRO_SOURCE=alien, the macro (staticMapCreatorCPM.C) too -- to +# the same AliEn dir mapCreationJob.sh downloads from. Both must be fresh: a stale macro, or +# stageSlotsForGrid.py pointed at a different destination than the job script expects, would silently +# desync what a running job downloads from what this wrapper thinks it uploaded. The bad-time-ranges list +# is treated differently -- see BAD_RANGES_LOCAL below -- it's occasionally-updated reference data, not +# something edited every session like the macro, so forcing a fresh re-upload on every submission is +# unnecessary friction. +# +# Slot windowing: stageSlotsForGrid.py cuts each run into windows of ~SLOT_LENGTH_MIN and writes ONE +# manifest line per window, carrying the window bounds and exactly the files that can contribute to it. +# A subjob index therefore identifies a (run, window) pair on its own, so this is a SINGLE submission -- +# no loop over TimeDivisionIndex, no TIME_DIVISION placeholders substituted into the payload, and one +# stage1Workdir pointer instead of one per index. (With a fixed window length, runs of different length +# produce different window counts, so the old "one production per index x prodsplit runs" rectangle +# does not exist any more.) Both stages read the window off the same manifest line, which removes the +# old failure mode where stage 2 recomputed it and any mismatch silently produced zero output. +# +# Output location (2026-07-26, per ALICE Grid support): the actual map data (voxRes.*.root) is staged by +# mapCreationJob.sh directly to ${ALIEN_JDL_OUTPUTDIR} (the JDL-assigned per-subjob output dir -- +# see that script), NOT to a fixed personal-space path -- avoids a separate disk-quota request on top of +# the CPU one, and is the standard mechanism support asked us to use. That directory is only known once +# the job actually runs, and its parent (MY_JOBWORKDIR, timestamped at THIS submission) is otherwise +# unrecoverable later -- grid_submit.sh prints it once, right here, before any GRID execution starts: +# pok "Your job's working directory will be $MY_JOBWORKDIR" +# So this wrapper captures that one line, once per submission, and uploads it as a small pointer file +# (stage1Workdir..txt, negligible size, fine to keep in personal space) -- submitPostProcess.sh +# downloads it and reconstructs each slot's exact +# ${ALIEN_JDL_OUTPUTDIR} from MY_JOBWORKDIR + the same alien_counter formula grid_submit.sh itself uses +# (see that script), without needing any in-job breadcrumb or querying AliEn after the fact. +# +# ASUSER / quota: setting ASUSER="pwg_pp" charges this job's CPU usage to that working group instead of +# the personal account, per ALICE Grid support's instruction (the account must already be a member of +# that group). grid_submit.sh's --asuser sets MY_USER=${ASUSER}, which determines BOTH the role active +# when its own internal transaction issues `submit` (so JAliEn attributes the job to that role) AND +# MY_HOMEDIR="/alice/cern.ch/user/${MY_USER:0:1}/${MY_USER}" -- so OutputDir automatically lands under +# that group's own writable space, with zero extra redirection needed. MACRO_ALIEN_DIR_BARE (the small +# manifest/macro staging area this wrapper itself uploads to, read-only from the job's point of view) +# stays fixed under the submitting account regardless of ASUSER -- read access there is unaffected by +# role. +# +# Usage: +# ./submitMapCreation.sh [listsDir] +# (listsDir defaults to ./lists -- discoverResiduals.sh's WORK_DIR, run from wherever you ran that +# script) + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +LISTS_DIR="${1:-./lists}" + +# --------------------------------------------------------------------------- +# Config -- edit per submission +# --------------------------------------------------------------------------- +JOBNAME="mapcreation-v1" # must be unique per submission -- bump the suffix for each new production +TOPWORKDIR="MapCreation" # replaces grid_submit.sh's generic default "selfjobs" bucket (on top of $HOME) + # with something identifiable -- matches MACRO_ALIEN_DIR_BARE's own naming + # below. Still just a static string per submission, not a per-run/period path + # like the real input data's own convention (.../2026/LHC26ak/572244/...) -- + # that needs a real post-processing/organize step, deliberately deferred to a + # later iteration of this pipeline. +PACKAGESPEC="O2::daily-20260730-0000-1" +ASUSER="pwg_pp" # your own account = personal quota. "pwg_pp" = charged to that working group's quota AND + # moves MY_JOBWORKDIR/OutputDir to pwg_pp's own space automatically -- see the + # long comment above for why this one value now does everything. NOTE support + # hedged on whether pwg_pp behaves like the other major accounts -- confirm on + # alimonitor that usage is really charged to the group before a large batch. +MACRO_SOURCE="official" # "official" (default): GRID jobs run staticMapCreatorCPM.C straight from the + # O2 package loaded via PACKAGESPEC ($O2_ROOT/share/macro/), no macro upload + # needed here at all -- requires PACKAGESPEC to resolve to a tag built from an O2 + # version that includes it (Detectors/TPC/calibration/SpacePoints/macro/). "alien": + # stage MACRO_LOCAL_PATH to AliEn and have jobs download it instead -- use this + # until such a tag is published. +MACRO_LOCAL_PATH="" # only used when MACRO_SOURCE=alien: local path to staticMapCreatorCPM.C to + # upload, e.g. your O2 checkout's + # Detectors/TPC/calibration/SpacePoints/macro/staticMapCreatorCPM.C. + # Staging area for the manifest + stage1Workdir pointer (and the macro, when + # MACRO_SOURCE=alien). No alien:// prefix. Derived from your own AliEn account the + # same way grid_submit.sh derives MY_HOMEDIR (ALIEN_USER, else the local + # username), so this file needs no per-user edit. Stays under the SUBMITTING + # account regardless of ASUSER -- read access there is unaffected by role, see the + # comment above. If your laptop's local username differs from your AliEn account + # (common), export ALIEN_USER, or just set MAPCREATION_ALIEN_DIR outright. +ALIEN_USER_NAME="${ALIEN_USER:-$(whoami)}" +MACRO_ALIEN_DIR_BARE="${MAPCREATION_ALIEN_DIR:-/alice/cern.ch/user/${ALIEN_USER_NAME:0:1}/${ALIEN_USER_NAME}/MapCreation}" +BAD_RANGES_NAME="Merge_BadIntervals_LHC26_PbPb_DCA-0P300.txt" + # Filename of the bad-time-ranges list, under ${MACRO_ALIEN_DIR}/lists/. Period- + # and system-specific (this one is PbPb26), so it changes when the data does -- + # hence a variable rather than a constant baked into three places. Substituted + # into mapCreationJob.sh the same way as JOBNAME_TAG, so + # set it only here. Used both as the upload destination (when BAD_RANGES_LOCAL is + # set) and as the name each job downloads -- the two cannot drift apart. + # Set to "" to disable bad-ranges filtering entirely for this submission + # (staticMapCreatorCPM.C treats an empty badRangeList argument that way) -- a + # deliberate opt-out, not the same as forgetting to set it. +BAD_RANGES_LOCAL="" # optional: local path to the bad-time-ranges file to (re-)upload this run (real + # source for the PbPb26 list above: + # /lustre/alice/tpcdata/Run3/SCDprodTests/lists/badRange/PbPb26/). Leave empty to + # skip the upload and just use the copy already at + # ${MACRO_ALIEN_DIR}/lists/${BAD_RANGES_NAME}. +RUNS_FILTER="" # optional comma-separated run whitelist, e.g. "572557,572558". Empty = every run + # found under listsDir. discoverResiduals.sh appends to the same ./lists tree + # across batches, so leaving this empty after a second discovery round silently + # folds the EARLIER batch's runs into this submission too -- recalibrating them + # from scratch at hours per slot. Set it when ./lists holds more than you want. + # A run named here with no lists under listsDir is a hard error. +OVERLAP_MARGIN_MS=1000 # a file is assigned to a window only if its aggregation label overlaps that + # window by MORE than this. Coarse labels routinely begin 1-2 ms before a window + # ends, and a bare overlap test drags all of their files into a window whose map + # they contribute nothing to. Bounded cost: at most this many ms of coverage per + # window edge, since the macro's per-TF cut discards the rest anyway. +SLOT_LENGTH_MIN=5 # target length of ONE map, in minutes. Each run is cut into whole steps of this + # length; a trailing remainder shorter than half a step is absorbed into the last + # window rather than left as a runt that would fail MIN_TRACKS_PER_SLICE. 0 means + # one map per run (no splitting). This replaces the old TIME_DIVISION count: with + # a fixed length, a 20 min run gives 4 maps and a 60 min run gives 12, instead of + # the same count regardless of run length. Stage 2 does not need this value -- it + # reuses stage 1's manifest, which already carries the windows. +# --------------------------------------------------------------------------- + +MACRO_ALIEN_DIR="alien://${MACRO_ALIEN_DIR_BARE}" +echo "== Staging area (manifest + stage1Workdir pointer): ${MACRO_ALIEN_DIR_BARE} ==" +echo " (derived from AliEn user '${ALIEN_USER_NAME}' -- export ALIEN_USER or MAPCREATION_ALIEN_DIR if that is wrong)" + +if [[ "${MACRO_SOURCE}" == "alien" ]]; then + [[ -n "${MACRO_LOCAL_PATH}" && -f "${MACRO_LOCAL_PATH}" ]] || { echo "ERROR: MACRO_SOURCE=alien needs MACRO_LOCAL_PATH set to a real staticMapCreatorCPM.C (got '${MACRO_LOCAL_PATH}')" >&2; exit 1; } + echo "== Re-staging macro from ${MACRO_LOCAL_PATH} to ${MACRO_ALIEN_DIR} ==" + alien.py cp -f "file:${MACRO_LOCAL_PATH}" "${MACRO_ALIEN_DIR}/staticMapCreatorCPM.C" +else + echo "== MACRO_SOURCE=official -- skipping macro upload, jobs will use \${O2_ROOT}/share/macro/staticMapCreatorCPM.C from PACKAGESPEC ==" +fi + +if [[ -z "${BAD_RANGES_NAME}" ]]; then + # Deliberate opt-out, not a mistake: an empty BAD_RANGES_NAME propagates through to + # mapCreationJob.sh as BAD_RANGES_ARG="" -- staticMapCreatorCPM.C's own + # `if (badRangeList.length() > 0)` guard treats that as "skip bad-ranges filtering entirely" for + # this submission. Nothing to upload or verify. + echo "== BAD_RANGES_NAME is empty -- bad-time-ranges filtering disabled for this submission ==" +elif [[ -n "${BAD_RANGES_LOCAL}" ]]; then + [[ -f "${BAD_RANGES_LOCAL}" ]] || { echo "ERROR: BAD_RANGES_LOCAL not found: ${BAD_RANGES_LOCAL}" >&2; exit 1; } + echo "== Re-staging bad-ranges list from ${BAD_RANGES_LOCAL} to ${MACRO_ALIEN_DIR}/lists/${BAD_RANGES_NAME} ==" + alien.py cp -f "file:${BAD_RANGES_LOCAL}" "${MACRO_ALIEN_DIR}/lists/${BAD_RANGES_NAME}" +else + # No local file to (re-)upload -- the job will download whatever is already staged under + # BAD_RANGES_NAME, so check now, once, locally, that it actually exists. Left unchecked, a wrong + # BAD_RANGES_NAME/ALIEN_USER or a never-uploaded file would only surface inside + # mapCreationJob.sh's own alien.py cp, in every single subjob, with a raw/confusing AliEn error + # instead of one clear message before anything is submitted. + echo "== BAD_RANGES_LOCAL not set -- checking ${MACRO_ALIEN_DIR}/lists/${BAD_RANGES_NAME} already exists on AliEn ==" + if ! alien.py stat "${MACRO_ALIEN_DIR}/lists/${BAD_RANGES_NAME}" &>/dev/null; then + echo "ERROR: ${MACRO_ALIEN_DIR}/lists/${BAD_RANGES_NAME} not found on AliEn, and BAD_RANGES_LOCAL is not set to (re-)upload one." >&2 + echo " Either set BAD_RANGES_LOCAL to a local file to upload, set BAD_RANGES_NAME=\"\" to disable bad-ranges filtering, or upload the file to that path by hand." >&2 + exit 1 + fi + echo "== Found existing ${MACRO_ALIEN_DIR}/lists/${BAD_RANGES_NAME} ==" +fi + +# Tagged by JOBNAME: a shared, untagged manifest name would let a second submission (different +# lists/config) silently overwrite the first's manifest, breaking any already-running job that re-reads +# it and desyncing stage 2's later subjob-index -> slot lookup. +MANIFEST_NAME="mapSlots.${JOBNAME}.tsv" +echo "== Building + uploading slot manifest from ${LISTS_DIR} (as ${MANIFEST_NAME}) ==" +N=$("${SCRIPT_DIR}/stageSlotsForGrid.py" \ + --lists-dir "${LISTS_DIR}" \ + --alien-dir "${MACRO_ALIEN_DIR_BARE}" \ + --manifest-name "${MANIFEST_NAME}" \ + --runs "${RUNS_FILTER}" \ + --slot-length-min "${SLOT_LENGTH_MIN}" \ + --overlap-margin-ms "${OVERLAP_MARGIN_MS}") +echo "N=${N} slot(s) == map(s) to be produced" +[[ "${N}" -gt 0 ]] || { echo "ERROR: no slots found under ${LISTS_DIR}" >&2; exit 1; } + +TMP_SCRIPT="$(mktemp)" +trap 'rm -f "${TMP_SCRIPT}"' EXIT + +# ONE submission, one subjob per manifest line. There is no longer a loop over TimeDivisionIndex: the +# windows are baked into the manifest by stageSlotsForGrid.py, so a subjob index alone identifies a +# (run, window) pair. That also means one stage1Workdir pointer instead of one per index, and no +# TIME_DIVISION/TIME_DIVISION_INDEX placeholders to substitute -- with a fixed SLOT_LENGTH_MIN, +# different-length runs yield different window counts, so the old rectangular +# "one production per index x prodsplit runs" grid does not exist any more. +sed \ + -e "s/^JOBNAME_TAG=.*/JOBNAME_TAG=${JOBNAME}/" \ + -e "s/^MACRO_SOURCE=.*/MACRO_SOURCE=${MACRO_SOURCE}/" \ + -e "s|^MACRO_ALIEN_DIR_BARE=.*|MACRO_ALIEN_DIR_BARE=${MACRO_ALIEN_DIR_BARE}|" \ + -e "s|^BAD_RANGES_NAME=.*|BAD_RANGES_NAME=${BAD_RANGES_NAME}|" \ + "${SCRIPT_DIR}/mapCreationJob.sh" > "${TMP_SCRIPT}" + +GRID_SUBMIT_ARGS=( + --script "${TMP_SCRIPT}" + --jobname "${JOBNAME}" + --topworkdir "${TOPWORKDIR}" + --prodsplit "${N}" + --packagespec "${PACKAGESPEC}" + --asuser "${ASUSER}" +) + +echo "== Submitting ${JOBNAME} (prodsplit=${N}, asuser=${ASUSER}) ==" +# Capture grid_submit.sh's own stderr (its "pok" status messages go there) while still showing it live, +# so we can pull out the one line that tells us where this submission's data will actually land. +SUBMIT_LOG=$("${O2DPG_ROOT}/GRID/utils/grid_submit.sh" "${GRID_SUBMIT_ARGS[@]}" 2>&1 | tee /dev/stderr) + +# Strip ANSI color codes (pok() wraps its output in \033[32m...\033[m) before parsing. +MY_JOBWORKDIR=$(echo "${SUBMIT_LOG}" | sed -E 's/\x1b\[[0-9;]*m//g' | sed -n "s/^Your job's working directory will be //p" | tail -1) +if [[ -z "${MY_JOBWORKDIR}" ]]; then + echo "ERROR: could not find \"Your job's working directory will be ...\" in grid_submit.sh's output -- stage 2 won't be able to find this submission's output. Aborting." >&2 + exit 1 +fi +echo "== MY_JOBWORKDIR = ${MY_JOBWORKDIR} ==" + +WORKDIR_POINTER_NAME="stage1Workdir.${JOBNAME}.txt" +WORKDIR_POINTER_TMP="$(mktemp)" +echo "${MY_JOBWORKDIR}" > "${WORKDIR_POINTER_TMP}" +alien.py cp -f "file:${WORKDIR_POINTER_TMP}" "${MACRO_ALIEN_DIR}/${WORKDIR_POINTER_NAME}" +rm -f "${WORKDIR_POINTER_TMP}" +echo "== Uploaded ${WORKDIR_POINTER_NAME} ==" diff --git a/DATA/production/configurations/mapCreationGrid/submitPostProcess.sh b/DATA/production/configurations/mapCreationGrid/submitPostProcess.sh new file mode 100755 index 000000000..4f34df062 --- /dev/null +++ b/DATA/production/configurations/mapCreationGrid/submitPostProcess.sh @@ -0,0 +1,171 @@ +#!/usr/bin/env bash +# submitPostProcess.sh +# +# STAGE 2 submission wrapper: submits one grid_submit.sh production-split job, one subjob per slot -- +# same granularity as stage 1 (submitMapCreation.sh), so a failed +# stage-2 subjob can be resubmitted on its own without touching stage 1's (hours-long) map creation. +# +# Unlike stage 1, this does NOT rebuild the slot manifest (mapSlots.tsv) -- it reuses the exact one +# stage 1 already uploaded (downloaded here only to count lines for --prodsplit N). Rebuilding it here +# would risk a mismatch if the local lists/ dir changed between the two submissions, which would +# silently desync stage 2's subjob-index -> slot mapping from stage 1's. +# +# IMPORTANT: STAGE1_JOBNAME_TAG must match whatever the stage-1 submission this is post-processing +# actually used -- it selects both the manifest and the stage1Workdir pointer. There is nothing else to +# keep in sync: the slot windows come from stage 1's own manifest, which this stage reuses verbatim, so +# the two stages cannot disagree about a slot's identity the way they could when each recomputed it. +# +# Output location (per ALICE Grid support -- see submitMapCreation.sh): stage 1 stages its map to its +# own ${ALIEN_JDL_OUTPUTDIR}, not a fixed personal-space path, so there's no static location +# to just look up. Stage 1's wrapper captured MY_JOBWORKDIR (that submission's timestamped parent +# directory) at submission time and uploaded it as a small pointer file -- +# downloaded below and substituted into the payload script (STAGE1_OUTPUTDIR_BASE) alongside COUNTERWIDTH +# (grid_submit.sh's own alien_counter zero-padding width -- deterministic from N, which is shared between +# both stages since this wrapper reuses stage 1's manifest, so it's recomputed here rather than needing +# its own pointer file). The payload script reconstructs each slot's exact ${ALIEN_JDL_OUTPUTDIR} from +# those two values the same way grid_submit.sh itself derives OutputDir. +# +# ASUSER / quota: see submitMapCreation.sh's own comment for the full story -- ASUSER +# determines both the role JAliEn attributes the job to and where MY_HOMEDIR (and therefore OutputDir) +# lands. Must match whatever STAGE1_JOBNAME_TAG's submission actually used, since STAGE1_OUTPUTDIR_BASE +# below (from stage 1's pointer file) already reflects wherever stage 1's own ASUSER put it -- this +# script doesn't need its own OutputDir logic, it just needs to read/write consistently with whatever +# stage 1 did. +# +# This wrapper also captures and uploads ITS OWN workdir pointer (stage2Workdir..txt), the same +# way stage 1 does for its own output -- nothing inside this pipeline needed that before (stage 2 is the +# end of the line here), but an external consumer that needs to locate a specific slot's final +# FT_*.root/smoothed map (e.g. a downstream pipeline using it as an input) has no other way to find it, +# since it also lands in an ephemeral ${ALIEN_JDL_OUTPUTDIR}, not a fixed path. Purely additive: this +# stage's own control flow and outputs are unchanged, it just also records where they went. +# +# Usage: +# ./submitPostProcess.sh + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +# --------------------------------------------------------------------------- +# Config -- edit per submission. STAGE1_JOBNAME_TAG must name the stage-1 submission this +# post-processes; everything else about the slot layout comes from that submission's manifest. +# --------------------------------------------------------------------------- +JOBNAME="mapcreation-postprocess-v1" # this stage's OWN job name (GRID job monitoring identity) -- + # NOT the same thing as STAGE1_JOBNAME_TAG below. +STAGE1_JOBNAME_TAG="mapcreation-v1" # must equal the JOBNAME stage 1 (submitMapCreation.sh) was + # actually submitted with -- that's what determined where + # stage 1 staged its map. +TOPWORKDIR="MapCreation" +PACKAGESPEC="O2::daily-20260730-0000-1" +ASUSER="pwg_pp" # see the long comment above -- your own account for personal quota, "pwg_pp" for group + # quota (this stage's own submission; independent of what stage 1 used). +MACRO_SOURCE="official" # same meaning/default as stage 1 (submitMapCreation.sh) -- applies + # to all three macros used here. +MACRO_LOCAL_DIR="" # only used when MACRO_SOURCE=alien: local directory containing + # SmoothingExtrapolate.C, TPCFastTransformInitCPM.C, voxResQA.C to upload, e.g. + # your O2 checkout's Detectors/TPC/calibration/SpacePoints/macro/ + + # GPU/TPCFastTransformation/macro/ (voxResQA.C and SmoothingExtrapolate.C live in + # the former, TPCFastTransformInitCPM.C in the latter -- symlink/copy them into one + # local dir first if they're not already colocated). + # Same staging root as stage 1, derived the same way (see that script's comment): + # ALIEN_USER, else the local username; override with MAPCREATION_ALIEN_DIR. Must + # resolve to whatever stage 1 actually used, since that's where its manifest and + # stage1Workdir pointer live. +ALIEN_USER_NAME="${ALIEN_USER:-$(whoami)}" +MACRO_ALIEN_DIR_BARE="${MAPCREATION_ALIEN_DIR:-/alice/cern.ch/user/${ALIEN_USER_NAME:0:1}/${ALIEN_USER_NAME}/MapCreation}" +# --------------------------------------------------------------------------- + +MACRO_ALIEN_DIR="alien://${MACRO_ALIEN_DIR_BARE}" +echo "== Staging area (must match stage 1's): ${MACRO_ALIEN_DIR_BARE} ==" +echo " (derived from AliEn user '${ALIEN_USER_NAME}' -- export ALIEN_USER or MAPCREATION_ALIEN_DIR if that is wrong)" + +if [[ "${MACRO_SOURCE}" == "alien" ]]; then + [[ -n "${MACRO_LOCAL_DIR}" && -f "${MACRO_LOCAL_DIR}/SmoothingExtrapolate.C" && -f "${MACRO_LOCAL_DIR}/TPCFastTransformInitCPM.C" && -f "${MACRO_LOCAL_DIR}/voxResQA.C" ]] || { echo "ERROR: MACRO_SOURCE=alien needs MACRO_LOCAL_DIR set to a directory containing all three macros (got '${MACRO_LOCAL_DIR}')" >&2; exit 1; } + echo "== Re-staging SmoothingExtrapolate.C / TPCFastTransformInitCPM.C / voxResQA.C from ${MACRO_LOCAL_DIR} to ${MACRO_ALIEN_DIR} ==" + alien.py cp -f "file:${MACRO_LOCAL_DIR}/SmoothingExtrapolate.C" "${MACRO_ALIEN_DIR}/SmoothingExtrapolate.C" + alien.py cp -f "file:${MACRO_LOCAL_DIR}/TPCFastTransformInitCPM.C" "${MACRO_ALIEN_DIR}/TPCFastTransformInitCPM.C" + alien.py cp -f "file:${MACRO_LOCAL_DIR}/voxResQA.C" "${MACRO_ALIEN_DIR}/voxResQA.C" +else + echo "== MACRO_SOURCE=official -- skipping macro upload, jobs will use \${O2_ROOT}/share/macro/ from PACKAGESPEC ==" +fi + +# Tagged manifest name -- must match whatever stage 1 actually uploaded (mapSlots..tsv), +# hence STAGE1_JOBNAME_TAG rather than this stage's own JOBNAME. +MANIFEST_NAME="mapSlots.${STAGE1_JOBNAME_TAG}.tsv" +echo "== Fetching existing slot manifest from ${MACRO_ALIEN_DIR}/${MANIFEST_NAME} (NOT rebuilt) ==" +TMP_MANIFEST="$(mktemp)" +alien.py cp -f "${MACRO_ALIEN_DIR}/${MANIFEST_NAME}" "file:${TMP_MANIFEST}" +N=$(wc -l < "${TMP_MANIFEST}" | tr -d '[:space:]') # wc -l right-justifies its count with leading + # whitespace even without a filename arg; grid_submit.sh's + # --prodsplit does a strict positive-integer check that + # rejects that -- confirmed real, 2026-07-26 ("Production + # split must be a positive integer (got ' 70')"). +echo "N=${N} slot(s)" +[[ "${N}" -gt 0 ]] || { echo "ERROR: ${MANIFEST_NAME} has no lines -- was stage 1 ever submitted with this JOBNAME?" >&2; exit 1; } + +# Same formula grid_submit.sh itself uses to derive its alien_counter zero-padding width -- deterministic +# from N, which is identical between stage 1 and stage 2 (same manifest, same line count), so this will +# match whatever stage 1's own submission actually used without needing to record it separately. +COUNTERWIDTH=${#N} +[[ "${COUNTERWIDTH}" -lt 3 ]] && COUNTERWIDTH=3 + +TMP_SCRIPT="$(mktemp)" +TMP_WORKDIR_POINTER="$(mktemp)" +TMP_STAGE2_WORKDIR_POINTER="$(mktemp)" +trap 'rm -f "${TMP_MANIFEST}" "${TMP_SCRIPT:-}" "${TMP_WORKDIR_POINTER}" "${TMP_STAGE2_WORKDIR_POINTER}"' EXIT + +# ONE submission, mirroring stage 1: the windows live in the manifest, so a subjob index identifies a +# (run, window) pair by itself and there is nothing to loop over. +WORKDIR_POINTER_NAME="stage1Workdir.${STAGE1_JOBNAME_TAG}.txt" +echo "== Fetching ${WORKDIR_POINTER_NAME} ==" +# Truncate first: an alien.py cp that returns 0 without writing would otherwise leave stale content +# here and point this whole submission at the wrong stage-1 workdir, silently no-opping every slot. +: > "${TMP_WORKDIR_POINTER}" +if ! alien.py cp -f "${MACRO_ALIEN_DIR}/${WORKDIR_POINTER_NAME}" "file:${TMP_WORKDIR_POINTER}" || [[ ! -s "${TMP_WORKDIR_POINTER}" ]]; then + echo "ERROR: could not fetch ${WORKDIR_POINTER_NAME} -- was stage 1 actually submitted under STAGE1_JOBNAME_TAG=${STAGE1_JOBNAME_TAG}?" >&2 + exit 1 +fi +STAGE1_OUTPUTDIR_BASE=$(head -1 "${TMP_WORKDIR_POINTER}") +if [[ "${STAGE1_OUTPUTDIR_BASE}" != /alice/* ]]; then + echo "ERROR: ${WORKDIR_POINTER_NAME} does not contain an absolute /alice/... path: '${STAGE1_OUTPUTDIR_BASE}'" >&2 + exit 1 +fi +echo "== STAGE1_OUTPUTDIR_BASE = ${STAGE1_OUTPUTDIR_BASE} ==" + +sed \ + -e "s/^STAGE1_JOBNAME_TAG=.*/STAGE1_JOBNAME_TAG=${STAGE1_JOBNAME_TAG}/" \ + -e "s/^MACRO_SOURCE=.*/MACRO_SOURCE=${MACRO_SOURCE}/" \ + -e "s|^MACRO_ALIEN_DIR_BARE=.*|MACRO_ALIEN_DIR_BARE=${MACRO_ALIEN_DIR_BARE}|" \ + -e "s|^STAGE1_OUTPUTDIR_BASE=.*|STAGE1_OUTPUTDIR_BASE=${STAGE1_OUTPUTDIR_BASE}|" \ + -e "s/^COUNTERWIDTH=.*/COUNTERWIDTH=${COUNTERWIDTH}/" \ + "${SCRIPT_DIR}/postProcessJob.sh" > "${TMP_SCRIPT}" + +GRID_SUBMIT_ARGS=( + --script "${TMP_SCRIPT}" + --jobname "${JOBNAME}" + --topworkdir "${TOPWORKDIR}" + --prodsplit "${N}" + --packagespec "${PACKAGESPEC}" + --asuser "${ASUSER}" +) + +echo "== Submitting ${JOBNAME} (prodsplit=${N}, asuser=${ASUSER}) ==" +# Capture grid_submit.sh's own stderr (its "pok" status messages go there) while still showing it live, +# so we can pull out the one line that tells us where this submission's data will actually land -- same +# mechanism submitMapCreation.sh uses for its own stage1Workdir pointer. Nothing downstream needed +# stage 2's own output location before now; this exists so a future consumer (e.g. a pipeline that reads +# stage 2's FT_*.root as an input) can find it without guessing. +SUBMIT_LOG=$("${O2DPG_ROOT}/GRID/utils/grid_submit.sh" "${GRID_SUBMIT_ARGS[@]}" 2>&1 | tee /dev/stderr) + +# Strip ANSI color codes (pok() wraps its output in \033[32m...\033[m) before parsing. +MY_JOBWORKDIR=$(echo "${SUBMIT_LOG}" | sed -E 's/\x1b\[[0-9;]*m//g' | sed -n "s/^Your job's working directory will be //p" | tail -1) +if [[ -z "${MY_JOBWORKDIR}" ]]; then + echo "ERROR: could not find \"Your job's working directory will be ...\" in grid_submit.sh's output -- a future consumer of this stage's output won't be able to find it. Aborting." >&2 + exit 1 +fi +echo "== MY_JOBWORKDIR (stage 2's own) = ${MY_JOBWORKDIR} ==" + +STAGE2_WORKDIR_POINTER_NAME="stage2Workdir.${JOBNAME}.txt" +echo "${MY_JOBWORKDIR}" > "${TMP_STAGE2_WORKDIR_POINTER}" +alien.py cp -f "file:${TMP_STAGE2_WORKDIR_POINTER}" "${MACRO_ALIEN_DIR}/${STAGE2_WORKDIR_POINTER_NAME}" +echo "== Uploaded ${STAGE2_WORKDIR_POINTER_NAME} ==" diff --git a/DATA/production/o2dpg_qc_postproc_workflow.py b/DATA/production/o2dpg_qc_postproc_workflow.py index 89b958a12..0c7402936 100755 --- a/DATA/production/o2dpg_qc_postproc_workflow.py +++ b/DATA/production/o2dpg_qc_postproc_workflow.py @@ -69,6 +69,7 @@ def add_QC_postprocessing(taskName, qcConfigPath, needs, runSpecific, periodSpec task = createTask(name=taskName, needs=needs, cwd=qcdir, lab=["QC"], cpu=1, mem='2000') overrideValues = '--override-values "' overrideValues += f'qc.config.database.host={qcdbUrl};' + overrideValues += f'qc.config.Activity.type=PHYSICS;' overrideValues += f'qc.config.Activity.number={runNumber};' if runSpecific else 'qc.config.Activity.number=0;' overrideValues += f'qc.config.Activity.periodName={periodName};' if periodSpecific else 'qc.config.Activity.periodName=;' overrideValues += f'qc.config.Activity.passName={passName};' if passSpecific else 'qc.config.Activity.passName=;' @@ -77,7 +78,7 @@ def add_QC_postprocessing(taskName, qcConfigPath, needs, runSpecific, periodSpec stages.append(task) ## The list of QC Post-processing workflows, add the new ones below - add_QC_postprocessing('example', 'json://${O2DPG_ROOT}/DATA/production/qc-postproc-async/example.json', needs=[], runSpecific=False, periodSpecific=False, passSpecific=True) + add_QC_postprocessing('example', 'json://${O2DPG_ROOT}/DATA/production/qc-postproc-async/example.json', needs=[], runSpecific=False, periodSpecific=True, passSpecific=True) add_QC_postprocessing('EMC', 'json://${O2DPG_ROOT}/DATA/production/qc-postproc-async/emc.json', needs=[], runSpecific=False, periodSpecific=True, passSpecific=True) add_QC_postprocessing('MCH', 'json://${O2DPG_ROOT}/DATA/production/qc-postproc-async/mch.json', needs=[], runSpecific=True, periodSpecific=True, passSpecific=True) add_QC_postprocessing('ZDC', 'json://${O2DPG_ROOT}/DATA/production/qc-postproc-async/zdc.json', needs=[], runSpecific=True, periodSpecific=True, passSpecific=True) diff --git a/DATA/production/production.desc b/DATA/production/production.desc index 887645e8a..0691ccdeb 100644 --- a/DATA/production/production.desc +++ b/DATA/production/production.desc @@ -1,3 +1,3 @@ synchronous-workflow: "O2PDPSuite" reco,128,126,"GPU_NUM_MEM_REG_CALLBACKS=5 SYNCMODE=1 NUMAGPUIDS=1 NUMAID=0 production/dpl-workflow.sh" reco,128,126,"GPU_NUM_MEM_REG_CALLBACKS=5 SYNCMODE=1 NUMAGPUIDS=1 NUMAID=1 production/dpl-workflow.sh" synchronous-workflow-1numa: "O2PDPSuite" reco,128,126,"SYNCMODE=1 production/dpl-workflow.sh" -synchronous-workflow-calib: "O2PDPSuite" reco,128,126,"GPU_NUM_MEM_REG_CALLBACKS=5 SYNCMODE=1 NUMAGPUIDS=1 NUMAID=0 WORKFLOW_PARAMETERS+=,CALIB_PROXIES production/dpl-workflow.sh" reco,128,126,"GPU_NUM_MEM_REG_CALLBACKS=5 SYNCMODE=1 NUMAGPUIDS=1 NUMAID=1 WORKFLOW_PARAMETERS+=,CALIB_PROXIES production/dpl-workflow.sh" calib,32,"AGGREGATOR_TASKS=BARREL_TF SHMSIZE=68719476736 WORKFLOW_PARAMETERS+=,CALIB_PROXIES production/aggregator-workflow.sh" calib,32,"AGGREGATOR_TASKS=BARREL_SPORADIC SHMSIZE=68719476736 WORKFLOW_PARAMETERS+=,CALIB_PROXIES production/aggregator-workflow.sh" calib,16,"AGGREGATOR_TASKS=CALO_TF SHMSIZE=34359738368 WORKFLOW_PARAMETERS+=,CALIB_PROXIES production/aggregator-workflow.sh" calib,16,"AGGREGATOR_TASKS=CALO_SPORADIC SHMSIZE=34359738368 WORKFLOW_PARAMETERS+=,CALIB_PROXIES production/aggregator-workflow.sh" calib,16,"AGGREGATOR_TASKS=MUON_TF SHMSIZE=34359738368 WORKFLOW_PARAMETERS+=,CALIB_PROXIES production/aggregator-workflow.sh" calib,16,"AGGREGATOR_TASKS=MUON_SPORADIC SHMSIZE=34359738368 WORKFLOW_PARAMETERS+=,CALIB_PROXIES production/aggregator-workflow.sh" calib,16,"AGGREGATOR_TASKS=FORWARD_TF SHMSIZE=34359738368 WORKFLOW_PARAMETERS+=,CALIB_PROXIES production/aggregator-workflow.sh" calib,16,"AGGREGATOR_TASKS=FORWARD_SPORADIC SHMSIZE=34359738368 WORKFLOW_PARAMETERS+=,CALIB_PROXIES production/aggregator-workflow.sh" calib,128,"AGGREGATOR_TASKS=TPC_IDCBOTH_SAC SHMSIZE=137438953472 WORKFLOW_PARAMETERS+=,CALIB_PROXIES production/aggregator-workflow.sh" +synchronous-workflow-calib: "O2PDPSuite" reco,128,126,"GPU_NUM_MEM_REG_CALLBACKS=5 SYNCMODE=1 NUMAGPUIDS=1 NUMAID=0 WORKFLOW_PARAMETERS+=,CALIB_PROXIES production/dpl-workflow.sh" reco,128,126,"GPU_NUM_MEM_REG_CALLBACKS=5 SYNCMODE=1 NUMAGPUIDS=1 NUMAID=1 WORKFLOW_PARAMETERS+=,CALIB_PROXIES production/dpl-workflow.sh" calib,32,"AGGREGATOR_TASKS=BARREL_TF SYNCMODE=1 SHMSIZE=68719476736 WORKFLOW_PARAMETERS+=,CALIB_PROXIES production/aggregator-workflow.sh" calib,32,"AGGREGATOR_TASKS=BARREL_SPORADIC SYNCMODE=1 SHMSIZE=68719476736 WORKFLOW_PARAMETERS+=,CALIB_PROXIES production/aggregator-workflow.sh" calib,16,"AGGREGATOR_TASKS=CALO_TF SYNCMODE=1 SHMSIZE=34359738368 WORKFLOW_PARAMETERS+=,CALIB_PROXIES production/aggregator-workflow.sh" calib,16,"AGGREGATOR_TASKS=CALO_SPORADIC SYNCMODE=1 SHMSIZE=34359738368 WORKFLOW_PARAMETERS+=,CALIB_PROXIES production/aggregator-workflow.sh" calib,16,"AGGREGATOR_TASKS=MUON_TF SYNCMODE=1 SHMSIZE=34359738368 WORKFLOW_PARAMETERS+=,CALIB_PROXIES production/aggregator-workflow.sh" calib,16,"AGGREGATOR_TASKS=MUON_SPORADIC SYNCMODE=1 SHMSIZE=34359738368 WORKFLOW_PARAMETERS+=,CALIB_PROXIES production/aggregator-workflow.sh" calib,16,"AGGREGATOR_TASKS=FORWARD_TF SYNCMODE=1 SHMSIZE=34359738368 WORKFLOW_PARAMETERS+=,CALIB_PROXIES production/aggregator-workflow.sh" calib,16,"AGGREGATOR_TASKS=FORWARD_SPORADIC SYNCMODE=1 SHMSIZE=34359738368 WORKFLOW_PARAMETERS+=,CALIB_PROXIES production/aggregator-workflow.sh" calib,128,"AGGREGATOR_TASKS=TPC_IDCBOTH_SAC SHMSIZE=137438953472 WORKFLOW_PARAMETERS+=,CALIB_PROXIES production/aggregator-workflow.sh" calib,128,"AGGREGATOR_TASKS=TPC_CMV SHMSIZE=137438953472 WORKFLOW_PARAMETERS+=,CALIB_PROXIES production/aggregator-workflow.sh" diff --git a/DATA/production/qc-async/cpv.json b/DATA/production/qc-async/cpv.json index 71f02f0c9..f7130b5c1 100644 --- a/DATA/production/qc-async/cpv.json +++ b/DATA/production/qc-async/cpv.json @@ -3,23 +3,14 @@ "config": { "database": { "implementation": "CCDB", - "host": "alio2-cr1-hv-qcdb1.cern.ch:8083", - "username": "not_applicable", - "password": "not_applicable", - "name": "not_applicable" - }, - "Activity": { - "number": "42", - "type": "2" + "host": "ccdb-test.cern.ch:8080" }, + "Activity": {}, "monitoring": { - "url": "influxdb-unix:///tmp/telegraf.sock" - }, - "consul": { - "url": "alio2-cr1-hv-aliecs:8500" + "url": "infologger:///debug?qc" }, "conditionDB": { - "url": "http://localhost:8084/" + "url": "ccdb-test.cern.ch:8080" } }, "tasks": { @@ -30,7 +21,6 @@ "moduleName": "QcCPV", "detectorName": "CPV", "cycleDurationSeconds": "120", - "maxNumberCycles": "-1", "dataSource": { "type": "direct", "query": "clusters:CPV/CLUSTERS/0;ctrigrec:CPV/CLUSTERTRIGRECS/0" @@ -40,9 +30,7 @@ "ccdbCheckInterval": "1000" } } - }, - "dataSamplingPolicies": [ - - ] - } -} + } + }, + "dataSamplingPolicies": [] +} \ No newline at end of file diff --git a/DATA/production/qc-async/emc.json b/DATA/production/qc-async/emc.json index 3a7d6fe8e..1eb272482 100644 --- a/DATA/production/qc-async/emc.json +++ b/DATA/production/qc-async/emc.json @@ -3,20 +3,12 @@ "config": { "database": { "implementation": "CCDB", - "host": "qcdb.cern.ch:8083", - "username": "not_applicable", - "password": "not_applicable", - "name": "not_applicable" + "host": "ccdb-test.cern.ch:8080" }, "Activity": { - "number": "42", - "type": "2" }, "monitoring": { - "url": "influxdb-unix:///tmp/telegraf.sock" - }, - "consul": { - "url": "alio2-cr1-hv-con01.cern.ch:8500" + "url": "infologger:///debug?qc" }, "conditionDB": { "url": "qcdb.cern.ch:8083" @@ -29,12 +21,18 @@ "className": "o2::quality_control_modules::emcal::CellTask", "moduleName": "QcEMCAL", "detectorName": "EMC", - "cycleDurationSeconds": "60", - "maxNumberCycles": "-1", + "cycleDurationSeconds": "300", "dataSource": { "type": "direct", "query": "emcal-cells:EMC/CELLS/0;emcal-triggerecords:EMC/CELLSTRGR/0" - } + }, + "movingWindows": [ + "averageCellTime_PHYS", + "cellAmplitudeSupermodule_PHYS", + "averageCellEnergy_PHYS", + "averageCellTime_PHYS", + "cellOccupancyEMCwThr_PHYS" + ] }, "ClusterTaskEMCAL": { "active": "true", @@ -43,7 +41,6 @@ "moduleName": "QcEMCAL", "detectorName": "EMC", "cycleDurationSeconds": "60", - "maxNumberCycles": "-1", "dataSource": { "type": "direct", "query": "emcal-cells:EMC/CELLS/0;emcal-triggerecords:EMC/CELLSTRGR/0" @@ -61,6 +58,16 @@ "clusterizerMaxTimeDelta": "1000.", "hasInvMassMesons": "true", "mesonClustersRejectExotics": 1 + }, + "grpGeomRequest" : { + "geomRequest": "Ideal", + "askGRPECS": "false", + "askGRPLHCIF": "false", + "askGRPMagField": "true", + "askMatLUT": "false", + "askTime": "false", + "askOnceAllButField": "true", + "needPropagatorD": "false" } }, "BCTaskEMCAL": { @@ -70,10 +77,9 @@ "moduleName": "QcEMCAL", "detectorName": "EMC", "cycleDurationSeconds": "60", - "maxNumberCycles": "-1", "dataSource": { "type": "direct", - "query": "emcal-triggers:EMC/CELLSTRGR/0;ctp-digits:CTP/DIGITS" + "query": "emcal-triggers:EMC/CELLSTRGR/0;ctp-digits:CTP/DIGITS;ctp-config:CTP/CONFIG/0?lifetime=condition&ccdb-path=CTP/Config/Config&ccdb-run-dependent=1" }, "taskParameters": { } diff --git a/DATA/production/qc-async/emc_PbPb.json b/DATA/production/qc-async/emc_PbPb.json index 8706a1f89..e9328d890 100644 --- a/DATA/production/qc-async/emc_PbPb.json +++ b/DATA/production/qc-async/emc_PbPb.json @@ -3,20 +3,12 @@ "config": { "database": { "implementation": "CCDB", - "host": "qcdb.cern.ch:8083", - "username": "not_applicable", - "password": "not_applicable", - "name": "not_applicable" + "host": "ccdb-test.cern.ch:8080" }, "Activity": { - "number": "42", - "type": "2" }, "monitoring": { - "url": "influxdb-unix:///tmp/telegraf.sock" - }, - "consul": { - "url": "alio2-cr1-hv-con01.cern.ch:8500" + "url": "infologger:///debug?qc" }, "conditionDB": { "url": "qcdb.cern.ch:8083" @@ -29,8 +21,7 @@ "className": "o2::quality_control_modules::emcal::CellTask", "moduleName": "QcEMCAL", "detectorName": "EMC", - "cycleDurationSeconds": "60", - "maxNumberCycles": "-1", + "cycleDurationSeconds": "300", "dataSource": { "type": "direct", "query": "emcal-cells:EMC/CELLS/0;emcal-triggerecords:EMC/CELLSTRGR/0" @@ -44,7 +35,14 @@ "TotalEnergyRangeSM": "200", "TotalEnergyRange": "2000", "TotalEnergyMaxCellTime": "25" - } + }, + "movingWindows": [ + "averageCellTime_PHYS", + "cellAmplitudeSupermodule_PHYS", + "averageCellEnergy_PHYS", + "averageCellTime_PHYS", + "cellOccupancyEMCwThr_PHYS" + ] }, "ClusterTaskEMCAL": { "active": "true", @@ -53,7 +51,6 @@ "moduleName": "QcEMCAL", "detectorName": "EMC", "cycleDurationSeconds": "60", - "maxNumberCycles": "-1", "dataSource": { "type": "direct", "query": "emcal-cells:EMC/CELLS/0;emcal-triggerecords:EMC/CELLSTRGR/0" @@ -75,6 +72,16 @@ "mesonClusterMaxTime": "50.", "MultiplicityRange":"400", "mesonMinPt": "7." + }, + "grpGeomRequest" : { + "geomRequest": "Ideal", + "askGRPECS": "false", + "askGRPLHCIF": "false", + "askGRPMagField": "true", + "askMatLUT": "false", + "askTime": "false", + "askOnceAllButField": "true", + "needPropagatorD": "false" } }, "BCTaskEMCAL": { @@ -84,13 +91,12 @@ "moduleName": "QcEMCAL", "detectorName": "EMC", "cycleDurationSeconds": "60", - "maxNumberCycles": "-1", "dataSource": { "type": "direct", - "query": "emcal-triggers:EMC/CELLSTRGR;ctp-digits:CTP/DIGITS" + "query": "emcal-triggers:EMC/CELLSTRGR/0;ctp-digits:CTP/DIGITS;ctp-config:CTP/CONFIG/0?lifetime=condition&ccdb-path=CTP/Config/Config&ccdb-run-dependent=1" }, "taskParameters": { - "AliasMB" : "CMTVXTSC" + "AliasMB" : "CMTVXTSC,C0TVXTSC,C0TVXTCE" } } } diff --git a/DATA/production/qc-async/ft0.json b/DATA/production/qc-async/ft0.json index efb6516ab..f06d73d1c 100644 --- a/DATA/production/qc-async/ft0.json +++ b/DATA/production/qc-async/ft0.json @@ -35,7 +35,17 @@ "type": "direct", "query": "recpoints:FT0/RECPOINTS/0;channels:FT0/RECCHDATA/0" }, - "saveObjectsToFile":"QC_FT0.root" + "saveObjectsToFile":"QC_FT0.root", + "grpGeomRequest" : { + "geomRequest": "None", + "askGRPECS": "false", + "askGRPLHCIF": "true", + "askGRPMagField": "false", + "askMatLUT": "false", + "askTime": "false", + "askOnceAllButField": "false", + "needPropagatorD": "false" + } } } } diff --git a/DATA/production/qc-async/hmp.json b/DATA/production/qc-async/hmp.json new file mode 100644 index 000000000..03915cb13 --- /dev/null +++ b/DATA/production/qc-async/hmp.json @@ -0,0 +1,54 @@ +{ + "qc":{ + "config":{ + "database":{ + "implementation":"CCDB", + "host":"ccdb-test.cern.ch:8080", + "username":"not_applicable", + "password":"not_applicable", + "name":"not_applicable" + }, + "Activity":{ + "number":"42", + "type":"2" + }, + "monitoring":{ + "url":"infologger:///debug?qc" + }, + "consul":{ + "url":"http://consul-test.cern.ch:8500" + }, + "conditionDB":{ + "url":"ccdb-test.cern.ch:8080" + } + }, + "tasks": { + "HMPClusters":{ + "active":"true", + "taskName" : "Clusters", + "className":"o2::quality_control_modules::hmpid::HmpidTaskClusters", + "moduleName":"QcHMPID", + "detectorName":"HMP", + "cycleDurationSeconds":"60", + "maxNumberCycles":"-1", + "dataSource": { + "type":"direct", + "query":"intrecord:HMP/INTRECORDS1/0;clusters:HMP/CLUSTERS/0" + } + }, + "HMPMatches":{ + "active":"true", + "taskName" : "Matches", + "className":"o2::quality_control_modules::hmpid::HmpidTaskMatches", + "moduleName":"QcHMPID", + "detectorName":"HMP", + "cycleDurationSeconds":"60", + "maxNumberCycles":"-1", + "dataSource": { + "type":"direct", + "query":"matchHMP:HMP/MATCHES/0;hmpidcluster:HMP/CLUSTERS/0;hmpidtriggers:HMP/INTRECORDS1/0" + } + } + } + } +} diff --git a/DATA/production/qc-async/its.json b/DATA/production/qc-async/its.json index 82a5c0345..4e1280af2 100644 --- a/DATA/production/qc-async/its.json +++ b/DATA/production/qc-async/its.json @@ -31,6 +31,15 @@ "detectorName": "ITS", "cycleDurationSeconds": "180", "maxNumberCycles": "-1", + "movingWindows": [ + "Layer0/ClusterOccupation", + "Layer1/ClusterOccupation", + "Layer2/ClusterOccupation", + "Layer3/ClusterOccupation", + "Layer4/ClusterOccupation", + "Layer5/ClusterOccupation", + "Layer6/ClusterOccupation" + ], "dataSource_comment": "The other type of dataSource is \"direct\", see basic-no-sampling.json.", "dataSource": { "type": "direct", @@ -44,17 +53,7 @@ "dicttimestamp" : "0", "publishDetailedSummary": "1", "publishSummary1D": "0" - }, - "grpGeomRequest" : { - "geomRequest": "Aligned", - "askGRPECS": "false", - "askGRPLHCIF": "false", - "askGRPMagField": "false", - "askMatLUT": "false", - "askTime": "false", - "askOnceAllButField": "true", - "needPropagatorD": "false" - } + } }, "ITSTracks": { "active": "true", @@ -62,8 +61,13 @@ "className": "o2::quality_control_modules::its::ITSTrackTask", "moduleName": "QcITS", "detectorName": "ITS", - "cycleDurationSeconds": "30", + "cycleDurationSeconds": "180", "maxNumberCycles": "-1", + "movingWindows": [ + "Ntracks", + "NClusters", + "AngularDistribution" + ], "dataSource_comment": "The other type of dataSource is \"direct\", see basic-no-sampling.json.", "dataSource": { "type": "direct", @@ -79,7 +83,9 @@ "doTTree": "0", "nBCbins" : "103", "dicttimestamp" : 0, - "doNorm": "1" + "doNorm": "1", + "InvMasses" : "0", + "publishMore": "0" } } }, diff --git a/DATA/production/qc-async/itstpc.json b/DATA/production/qc-async/itstpc.json index 9abcd46c7..d0ca44d88 100644 --- a/DATA/production/qc-async/itstpc.json +++ b/DATA/production/qc-async/itstpc.json @@ -39,28 +39,39 @@ "dataSource" : { "type" : "direct", "query_comment" : "checking every matched track", - "query" : "trackITSTPC:GLO/TPCITS/0;trackITSTPCABREFS:GLO/TPCITSAB_REFS/0;trackITSTPCABCLID:GLO/TPCITSAB_CLID/0;trackTPC:TPC/TRACKS;trackTPCClRefs:TPC/CLUSREFS;trackITS:ITS/TRACKS/0;trackITSROF:ITS/ITSTrackROF/0;trackITSClIdx:ITS/TRACKCLSID/0;alpparITS:ITS/ALPIDEPARAM/0?lifetime=condition&ccdb-path=ITS/Config/AlpideParam" + "query" : "" }, "taskParameters" : { "GID" : "ITS-TPC,ITS", "verbose" : "false", - "minPtCut" : "0.1f", - "etaCut" : "1.4f", - "minNTPCClustersCut" : "60", - "minDCACut" : "100.f", - "minDCACutY" : "10.f" + "minPtITSCut": "0.1f", + "etaITSCut": "1e10f", + "minNITSClustersCut": "0", + "maxChi2PerClusterITS": "100000", + "minPtTPCCut": "0.1f", + "etaTPCCut": "0.9f", + "minNTPCClustersCut": "60", + "minDCACut": "100.f", + "minDCACutY": "10.f", + "minPtCut": "0.1f", + "maxPtCut": "20.f", + "etaCut": "1e10f", + "cutK0Mass": "0.05f", + "maxEtaK0": "0.8f", + "doK0QC": "true", + "trackSourcesK0": "" }, "grpGeomRequest" : { - "geomRequest": "Aligned", - "askGRPECS": "false", - "askGRPLHCIF": "false", + "geomRequest": "None", + "askGRPECS": "true", + "askGRPLHCIF": "true", "askGRPMagField": "true", "askMatLUT": "false", "askTime": "false", "askOnceAllButField": "true", "needPropagatorD": "false" }, - "saveObjectsToFile" : "ITSTPCmatched.root", + "saveObjectsToFile" : "ITSTPCmatched_allTracks_enabledK0.root", "" : "For debugging, path to the file where to save. If empty or missing it won't save." } } diff --git a/DATA/production/qc-async/itstpctof.json b/DATA/production/qc-async/itstpctof.json index 8b82d6f91..a259db1c4 100644 --- a/DATA/production/qc-async/itstpctof.json +++ b/DATA/production/qc-async/itstpctof.json @@ -29,24 +29,25 @@ "className" : "o2::quality_control_modules::tof::TOFMatchedTracks", "moduleName" : "QcTOF", "detectorName" : "TOF", - "cycleDurationSeconds" : "60", + "cycleDurationSeconds" : "300", "maxNumberCycles" : "-1", "dataSource" : { "type" : "direct", "query_comment" : "checking every matched track", "query" : "matchITSTPCTOF:TOF/MTC_ITSTPC/0;matchTPCTOF:TOF/MTC_TPC/0;trackTPCTOF:TOF/TOFTRACKS_TPC/0;trackITSTPC:GLO/TPCITS/0;trackITSTPCABREFS:GLO/TPCITSAB_REFS/0;trackITSTPCABCLID:GLO/TPCITSAB_CLID/0;trackTPC:TPC/TRACKS/0;trackTPCClRefs:TPC/CLUSREFS/0;tofcluster:TOF/CLUSTERS/0" }, + "movingWindows" : [ "mEffPt_ITSTPC-ITSTPCTRD" ], "taskParameters" : { "GID" : "ITS-TPC,TPC,ITS-TPC-TOF,TPC-TOF", "verbose" : "false", - "minPtCut" : "0.3f", + "minPtCut" : "0.1f", "etaCut" : "0.8f", "minNTPCClustersCut" : "60", "minDCACut" : "100.f", "minDCACutY" : "10.f" }, "grpGeomRequest" : { - "geomRequest": "Aligned", + "geomRequest": "None", "askGRPECS": "false", "askGRPLHCIF": "false", "askGRPMagField": "true", diff --git a/DATA/production/qc-async/itstpctofwtrd.json b/DATA/production/qc-async/itstpctofwtrd.json index b107730c7..bb9a8765b 100644 --- a/DATA/production/qc-async/itstpctofwtrd.json +++ b/DATA/production/qc-async/itstpctofwtrd.json @@ -29,24 +29,25 @@ "className" : "o2::quality_control_modules::tof::TOFMatchedTracks", "moduleName" : "QcTOF", "detectorName" : "TOF", - "cycleDurationSeconds" : "60", + "cycleDurationSeconds" : "300", "maxNumberCycles" : "-1", "dataSource" : { "type" : "direct", "query_comment" : "checking every matched track", "query" : "tofcluster:TOF/CLUSTERS/0;trackTPC:TPC/TRACKS/0;trackTPCClRefs:TPC/CLUSREFS/0;matchTPCTOF:TOF/MTC_TPC/0;trackTPCTOF:TOF/TOFTRACKS_TPC/0;trackITSTPC:GLO/TPCITS/0;trackITSTPCABREFS:GLO/TPCITSAB_REFS/0;trackITSTPCABCLID:GLO/TPCITSAB_CLID/0;matchITSTPCTOF:TOF/MTC_ITSTPC/0;trackITSTPCTRD:TRD/MATCH_ITSTPC/0;trigITSTPCTRD:TRD/TRGREC_ITSTPC/0;matchITSTPCTRDTOF:TOF/MTC_ITSTPCTRD/0;trackTPCTRD:TRD/MATCH_TPC/0;trigTPCTRD:TRD/TRGREC_TPC/0;matchTPCTRDTOF:TOF/MTC_TPCTRD/0" }, - "taskParameters" : { + "movingWindows" : [ "mEffPt_ITSTPC-ITSTPCTRD" ], + "taskParameters" : { "GID" : "TPC,TPC-TOF,ITS-TPC,ITS-TPC-TOF,ITS-TPC-TRD,ITS-TPC-TRD-TOF,TPC-TRD,TPC-TRD-TOF", "verbose" : "false", - "minPtCut" : "0.3f", + "minPtCut" : "0.1f", "etaCut" : "0.8f", "minNTPCClustersCut" : "60", "minDCACut" : "100.f", "minDCACutY" : "10.f" }, "grpGeomRequest" : { - "geomRequest": "Aligned", + "geomRequest": "None", "askGRPECS": "false", "askGRPLHCIF": "false", "askGRPMagField": "true", @@ -55,7 +56,7 @@ "askOnceAllButField": "true", "needPropagatorD": "false" }, - "saveObjectsToFile" : "TOFmatchedITSTPCTOF_TPCTOF.root", + "saveObjectsToFile" : "TOFmatchedITSTPCTOF_TPCTOF_wTRD.root", "" : "For debugging, path to the file where to save. If empty or missing it won't save." } } diff --git a/DATA/production/qc-async/mch-digits.json b/DATA/production/qc-async/mch-digits.json index cf3594bc5..4b867fce6 100644 --- a/DATA/production/qc-async/mch-digits.json +++ b/DATA/production/qc-async/mch-digits.json @@ -36,7 +36,12 @@ "type": "direct", "query": "digits:MCH/DIGITS" }, + "movingWindows": [ + "RateSignalPerDualSampa" + ], "taskParameters": { + "Enable1DRateMaps": "true", + "Enable2DRateMaps": "false", "Diagnostic": "false" }, "grpGeomRequest": { diff --git a/DATA/production/qc-async/mch-reco.json b/DATA/production/qc-async/mch-reco.json index 55a18d32f..0fec68d6b 100644 --- a/DATA/production/qc-async/mch-reco.json +++ b/DATA/production/qc-async/mch-reco.json @@ -49,6 +49,13 @@ "dataSource": { "type": "direct", "query": "preclusters:MCH/PRECLUSTERS/0;preclusterdigits:MCH/PRECLUSTERDIGITS/0" + }, + "movingWindows": [ + "PseudoeffPerDualSampa" + ], + "taskParameters": { + "Enable1DPseudoeffMaps": "true", + "Enable2DPseudoeffMaps": "false" } }, "FRofs": { @@ -68,4 +75,4 @@ } } } -} \ No newline at end of file +} diff --git a/DATA/production/qc-async/mch-tracks.json b/DATA/production/qc-async/mch-tracks.json index e5c5d729f..ead617c0d 100644 --- a/DATA/production/qc-async/mch-tracks.json +++ b/DATA/production/qc-async/mch-tracks.json @@ -23,27 +23,68 @@ } }, "tasks": { + "MCHClusters": { + "active": "true", + "taskName": "Clusters", + "className": "o2::quality_control_modules::muonchambers::ClustersTask", + "moduleName": "QcMuonChambers", + "detectorName": "MCH", + "cycleDurationSeconds": "300", + "maxNumberCycles": "-1", + "dataSource": { + "type": "direct", + "query": "tracks:MCH/TRACKS;trackrofs:MCH/TRACKROFS;trackclusters:MCH/TRACKCLUSTERS" + }, + "movingWindows": [ + "ClusterSizePerChamber", + "ClustersPerDualSampa", + "ClustersPerChamber", + "ClustersPerTrack" + ], + "taskParameters": {}, + "grpGeomRequest": { + "geomRequest": "Aligned", + "askGRPECS": "false", + "askGRPLHCIF": "false", + "askGRPMagField": "false", + "askMatLUT": "false", + "askTime": "false", + "askOnceAllButField": "true", + "needPropagatorD": "false" + } + }, "MCHTracks": { "active": "true", "taskName": "Tracks", "className": "o2::quality_control_modules::muon::TracksTask", "moduleName": "QcMUONCommon", "detectorName": "MCH", - "cycleDurationSeconds": "180", + "cycleDurationSeconds": "300", "maxNumberCycles": "-1", "dataSource": { "type": "direct", "query": "trackMCH:MCH/TRACKS;trackMCHROF:MCH/TRACKROFS;trackMCHTRACKCLUSTERS:MCH/TRACKCLUSTERS;mchtrackdigits:MCH/CLUSTERDIGITS" }, + "movingWindows": [ + "TracksPerTF", + "TrackPt", + "TrackEta", + "TrackPhi", + "WithCuts/TracksPerTF", + "WithCuts/TrackPt", + "WithCuts/TrackEta", + "WithCuts/TrackPhi", + "WithCuts/Minv" + ], "taskParameters": { "maxTracksPerTF": "600", "GID": "MCH" }, "grpGeomRequest": { "geomRequest": "Aligned", - "askGRPECS": "false", + "askGRPECS": "true", "askGRPLHCIF": "false", - "askGRPMagField": "false", + "askGRPMagField": "true", "askMatLUT": "false", "askTime": "false", "askOnceAllButField": "true", @@ -52,4 +93,4 @@ } } } -} \ No newline at end of file +} diff --git a/DATA/production/qc-async/mchmid-tracks.json b/DATA/production/qc-async/mchmid-tracks.json new file mode 100644 index 000000000..f87a75ce4 --- /dev/null +++ b/DATA/production/qc-async/mchmid-tracks.json @@ -0,0 +1,46 @@ +{ + "qc": { + "tasks": { + "TaskMUONTracks": { + "active": "true", + "className": "o2::quality_control_modules::muon::TracksTask", + "moduleName": "QcMUONCommon", + "detectorName": "GLO", + "taskName": "MUONTracks", + "cycleDurationSeconds": "300", + "maxNumberCycles": "-1", + "dataSource": { + "type": "direct", + "query": "trackMCH:MCH/TRACKS;trackMCHROF:MCH/TRACKROFS;trackMCHTRACKCLUSTERS:MCH/TRACKCLUSTERS;mchtrackdigits:MCH/CLUSTERDIGITS;trackMID:MID/TRACKS/0;trackMIDROF:MID/TRACKROFS/0;trackMIDTRACKCLUSTERS:MID/TRACKCLUSTERS/0;trackClMIDROF:MID/TRCLUSROFS/0;matchMCHMID:GLO/MTC_MCHMID" + }, + "movingWindows": [ + "WithCuts/TracksPerTF", + "WithCuts/TrackPt", + "WithCuts/TrackEta", + "WithCuts/TrackPhi", + "WithCuts/Minv", + "MCH-MID/WithCuts/TracksPerTF", + "MCH-MID/WithCuts/TrackPt", + "MCH-MID/WithCuts/TrackEta", + "MCH-MID/WithCuts/TrackPhi", + "MCH-MID/WithCuts/Minv" + ], + "taskParameters": { + "maxTracksPerTF": "600", + "GID" : "MCH,MID,MCH-MID" + }, + "grpGeomRequest": { + "geomRequest": "Aligned", + "askGRPECS": "true", + "askGRPLHCIF": "false", + "askGRPMagField": "true", + "askMatLUT": "false", + "askTime": "false", + "askOnceAllButField": "false", + "needPropagatorD": "false" + }, + "location": "remote" + } + } + } +} diff --git a/DATA/production/qc-async/mft.json b/DATA/production/qc-async/mft.json index 7cbb78b8c..588d1e074 100644 --- a/DATA/production/qc-async/mft.json +++ b/DATA/production/qc-async/mft.json @@ -29,27 +29,29 @@ "className": "o2::quality_control_modules::mft::QcMFTClusterTask", "moduleName": "QcMFT", "detectorName": "MFT", - "cycleDurationSeconds": "60", + "cycleDurationSeconds": "300", "maxNumberCycles": "-1", "dataSource": { "type": "direct", "query": "randomcluster:MFT/COMPCLUSTERS/0;clustersrof:MFT/CLUSTERSROF/0;patterns:MFT/PATTERNS/0;cldict:MFT/CLUSDICT/0?lifetime=condition&ccdb-path=MFT/Calib/ClusterDictionary" }, + "movingWindows": [ "mClustersROFSize" ], "taskParameters": { + "onlineQC" : "0", "maxClusterROFSize" : "50000", "maxDuration" : "60000", "timeBinSize" : "0.1", "ROFLengthInBC" : "594" }, - "grpGeomRequest" : { - "geomRequest": "Aligned", - "askGRPECS": "false", + "grpGeomRequest" : { + "geomRequest": "None", + "askGRPECS": "true", "askGRPLHCIF": "false", - "askGRPMagField": "true", + "askGRPMagField": "false", "askMatLUT": "false", "askTime": "false", - "askOnceAllButField": "true", - "needPropagatorD": "false" + "askOnceAllButField": "false", + "needPropagatorD": "false" } }, "MFTTracks": { @@ -58,7 +60,7 @@ "className": "o2::quality_control_modules::mft::QcMFTTrackTask", "moduleName": "QcMFT", "detectorName": "MFT", - "cycleDurationSeconds": "60", + "cycleDurationSeconds": "300", "maxNumberCycles": "-1", "dataSource_comment": "The other type of dataSource is \"direct\", see basic-no-sampling.json.", "dataSource": { @@ -66,12 +68,23 @@ "query_comment" : "100% sampling", "query": "tracks:MFT/TRACKS/0;tracksrofs:MFT/MFTTrackROF/0;clustersrofs:MFT/CLUSTERSROF/0" }, + "movingWindows": [ "mMFTTrackROFSize", "mMFTTrackEta", "mMFTTrackPhi" ], "taskParameters": { "ROFLengthInBC": "594", "MaxTrackROFSize": "10000", "MaxClusterROFSize": "50000", "MaxDuration": "60000", "TimeBinSize": "0.1" + }, + "grpGeomRequest" : { + "geomRequest": "None", + "askGRPECS": "true", + "askGRPLHCIF": "false", + "askGRPMagField": "false", + "askMatLUT": "false", + "askTime": "false", + "askOnceAllButField": "false", + "needPropagatorD": "false" } } }, @@ -85,13 +98,29 @@ "MOs" : ["mClusterOccupancy","mClusterPatternIndex","mClusterSizeSummary","mGroupedClusterSizeSummary","mClusterOccupancySummary"] }], "checkParameters" : { - "ZoneThresholdMedium" : "2", - "ZoneThresholdBad" : "5" + "LadderThresholdMedium" : "1", + "LadderThresholdBad" : "2" }, "className": "o2::quality_control_modules::mft::QcMFTClusterCheck", "moduleName": "QcMFT", "detectorName": "MFT", "policy": "OnEachSeparately" + }, + "MFTTracks": { + "active": "true", + "checkName": "Tracks", + "dataSource": [{ + "type": "Task", + "name": "MFTTracks", + "MOs" : ["mClusterRatioVsBunchCrossing"] + }], + "checkParameters" : { + "onlineQC" : "0" + }, + "className": "o2::quality_control_modules::mft::QcMFTTrackCheck", + "moduleName": "QcMFT", + "detectorName": "MFT", + "policy": "OnEachSeparately" } } }, diff --git a/DATA/production/qc-async/mftmch-tracks.json b/DATA/production/qc-async/mftmch-tracks.json new file mode 100644 index 000000000..37b47b756 --- /dev/null +++ b/DATA/production/qc-async/mftmch-tracks.json @@ -0,0 +1,46 @@ +{ + "qc": { + "tasks": { + "TaskMUONTracks": { + "active": "true", + "className": "o2::quality_control_modules::muon::TracksTask", + "moduleName": "QcMUONCommon", + "detectorName": "GLO", + "taskName": "MUONTracks", + "cycleDurationSeconds": "300", + "maxNumberCycles": "-1", + "dataSource": { + "type": "direct", + "query": "trackMCH:MCH/TRACKS;trackMCHROF:MCH/TRACKROFS;trackMCHTRACKCLUSTERS:MCH/TRACKCLUSTERS;mchtrackdigits:MCH/CLUSTERDIGITS;trackMFT:MFT/TRACKS;trackMFTROF:MFT/MFTTrackROF;trackMFTClIdx:MFT/TRACKCLSID;alpparMFT:MFT/ALPIDEPARAM/0?lifetime=condition&ccdb-path=MFT/Config/AlpideParam;fwdtracks:GLO/GLFWD" + }, + "movingWindows": [ + "WithCuts/TracksPerTF", + "WithCuts/TrackPt", + "WithCuts/TrackEta", + "WithCuts/TrackPhi", + "WithCuts/Minv", + "MFT-MCH/WithCuts/TracksPerTF", + "MFT-MCH/WithCuts/TrackPt", + "MFT-MCH/WithCuts/TrackEta", + "MFT-MCH/WithCuts/TrackPhi", + "MFT-MCH/WithCuts/Minv" + ], + "taskParameters": { + "maxTracksPerTF": "600", + "GID" : "MCH,MFT,MFT-MCH" + }, + "grpGeomRequest": { + "geomRequest": "Aligned", + "askGRPECS": "true", + "askGRPLHCIF": "false", + "askGRPMagField": "true", + "askMatLUT": "false", + "askTime": "false", + "askOnceAllButField": "false", + "needPropagatorD": "false" + }, + "location": "remote" + } + } + } +} diff --git a/DATA/production/qc-async/mftmchmid-tracks.json b/DATA/production/qc-async/mftmchmid-tracks.json new file mode 100644 index 000000000..2b892e070 --- /dev/null +++ b/DATA/production/qc-async/mftmchmid-tracks.json @@ -0,0 +1,139 @@ +{ + "qc": { + "tasks": { + "TaskMUONTracks": { + "active": "true", + "className": "o2::quality_control_modules::muon::TracksTask", + "moduleName": "QcMUONCommon", + "detectorName": "GLO", + "taskName": "MUONTracks", + "cycleDurationSeconds": "300", + "maxNumberCycles": "-1", + "dataSource": { + "type": "direct", + "_type": "dataSamplingPolicy", + "name": "muon-tracks", + "query": "trackMCH:MCH/TRACKS;trackMCHROF:MCH/TRACKROFS;trackMCHTRACKCLUSTERS:MCH/TRACKCLUSTERS;mchtrackdigits:MCH/CLUSTERDIGITS;trackMFT:MFT/TRACKS;trackMFTROF:MFT/MFTTrackROF;trackMFTClIdx:MFT/TRACKCLSID;alpparMFT:MFT/ALPIDEPARAM/0?lifetime=condition&ccdb-path=MFT/Config/AlpideParam;fwdtracks:GLO/GLFWD;trackMID:MID/TRACKS/0;trackMIDROF:MID/TRACKROFS/0;trackMIDTRACKCLUSTERS:MID/TRACKCLUSTERS/0;trackClMIDROF:MID/TRCLUSROFS/0;matchMCHMID:GLO/MTC_MCHMID" + }, + "movingWindows": [ + "WithCuts/TracksPerTF", + "WithCuts/TrackPt", + "WithCuts/TrackEta", + "WithCuts/TrackPhi", + "WithCuts/Minv", + "MCH-MID/WithCuts/TracksPerTF", + "MCH-MID/WithCuts/TrackPt", + "MCH-MID/WithCuts/TrackEta", + "MCH-MID/WithCuts/TrackPhi", + "MCH-MID/WithCuts/Minv" + ], + "taskParameters": { + "maxTracksPerTF": "600", + "cutRAbsMin": "17.6", + "cutRAbsMax": "89.5", + "cutEtaMin": "-4.0", + "cutEtaMax": "-2.5", + "cutPtMin": "0.5", + "nSigmaPDCA": "6", + "cutChi2Max": "1000", + "diMuonTimeCut": "100", + "fullHistos": "0", + "GID" : "MCH,MCH-MID" + }, + "grpGeomRequest": { + "geomRequest": "Aligned", + "askGRPECS": "true", + "askGRPLHCIF": "false", + "askGRPMagField": "true", + "askMatLUT": "false", + "askTime": "false", + "askOnceAllButField": "false", + "needPropagatorD": "false" + }, + "location": "remote" + }, + "TaskMUONTracksMFT": { + "active": "true", + "className": "o2::quality_control_modules::muon::TracksTask", + "moduleName": "QcMUONCommon", + "detectorName": "GLO", + "taskName": "MUONTracksMFT", + "cycleDurationSeconds": "300", + "maxNumberCycles": "-1", + "dataSource": { + "_type": "direct", + "type": "dataSamplingPolicy", + "name": "glo-mu-tracks", + "query": "trackMCH:MCH/TRACKS;trackMCHROF:MCH/TRACKROFS;trackMCHTRACKCLUSTERS:MCH/TRACKCLUSTERS;mchtrackdigits:MCH/CLUSTERDIGITS;trackMFT:MFT/TRACKS;trackMFTROF:MFT/MFTTrackROF;trackMFTClIdx:MFT/TRACKCLSID;alpparMFT:MFT/ALPIDEPARAM/0?lifetime=condition&ccdb-path=MFT/Config/AlpideParam;fwdtracks:GLO/GLFWD;trackMID:MID/TRACKS/0;trackMIDROF:MID/TRACKROFS/0;trackMIDTRACKCLUSTERS:MID/TRACKCLUSTERS/0;trackClMIDROF:MID/TRCLUSROFS/0;matchMCHMID:GLO/MTC_MCHMID" + }, + "movingWindows": [ + "MCH-MID/WithCuts/TracksPerTF", + "MCH-MID/WithCuts/TrackPt", + "MCH-MID/WithCuts/TrackEta", + "MCH-MID/WithCuts/TrackPhi", + "MCH-MID/WithCuts/Minv", + "MFT-MCH-MID/WithCuts/TracksPerTF", + "MFT-MCH-MID/WithCuts/TrackPt", + "MFT-MCH-MID/WithCuts/TrackEta", + "MFT-MCH-MID/WithCuts/TrackPhi", + "MFT-MCH-MID/WithCuts/Minv" + ], + "taskParameters": { + "maxTracksPerTF": "600", + "cutRAbsMin": "26.5", + "cutRAbsMax": "89.5", + "cutEtaMin": "-3.3", + "cutEtaMax": "-2.5", + "cutPtMin": "0.5", + "nSigmaPDCA": "6", + "cutChi2Max": "1000", + "matchChi2MaxMFT": "45", + "diMuonTimeCut": "100", + "fullHistos": "0", + "GID" : "MCH-MID,MFT-MCH-MID" + }, + "grpGeomRequest": { + "geomRequest": "Aligned", + "askGRPECS": "true", + "askGRPLHCIF": "false", + "askGRPMagField": "true", + "askMatLUT": "false", + "askTime": "false", + "askOnceAllButField": "false", + "needPropagatorD": "false" + }, + "location": "remote" + } + } + }, + "dataSamplingPolicies": [ + { + "id": "muon-tracks", + "active": "true", + "machines": [], + "query": "trackMCH:MCH/TRACKS;trackMCHROF:MCH/TRACKROFS;trackMCHTRACKCLUSTERS:MCH/TRACKCLUSTERS;mchtrackdigits:MCH/CLUSTERDIGITS;trackMFT:MFT/TRACKS;trackMFTROF:MFT/MFTTrackROF;trackMFTClIdx:MFT/TRACKCLSID;alpparMFT:MFT/ALPIDEPARAM/0?lifetime=condition&ccdb-path=MFT/Config/AlpideParam;fwdtracks:GLO/GLFWD;trackMID:MID/TRACKS/0;trackMIDROF:MID/TRACKROFS/0;trackMIDTRACKCLUSTERS:MID/TRACKCLUSTERS/0;trackClMIDROF:MID/TRCLUSROFS/0;matchMCHMID:GLO/MTC_MCHMID", + "samplingConditions": [ + { + "condition": "random", + "fraction": "0.1", + "seed": "0" + } + ], + "blocking": "false" + }, + { + "id": "glo-mu-tracks", + "active": "true", + "machines": [], + "query": "trackMCH:MCH/TRACKS;trackMCHROF:MCH/TRACKROFS;trackMCHTRACKCLUSTERS:MCH/TRACKCLUSTERS;mchtrackdigits:MCH/CLUSTERDIGITS;trackMFT:MFT/TRACKS;trackMFTROF:MFT/MFTTrackROF;trackMFTClIdx:MFT/TRACKCLSID;alpparMFT:MFT/ALPIDEPARAM/0?lifetime=condition&ccdb-path=MFT/Config/AlpideParam;fwdtracks:GLO/GLFWD;trackMID:MID/TRACKS/0;trackMIDROF:MID/TRACKROFS/0;trackMIDTRACKCLUSTERS:MID/TRACKCLUSTERS/0;trackClMIDROF:MID/TRCLUSROFS/0;matchMCHMID:GLO/MTC_MCHMID", + "samplingConditions": [ + { + "condition": "random", + "fraction": "0.1", + "seed": "0" + } + ], + "blocking": "false" + } + ] +} diff --git a/DATA/production/qc-async/mid.json b/DATA/production/qc-async/mid.json index bbad331ab..2007b84bd 100644 --- a/DATA/production/qc-async/mid.json +++ b/DATA/production/qc-async/mid.json @@ -3,23 +3,17 @@ "config": { "database": { "implementation": "CCDB", - "host": "ali-qcdb.cern.ch:8083", - "username": "not_applicable", - "password": "not_applicable", - "name": "not_applicable" - }, - "Activity": { - "number": "42", - "type": "2" + "host": "ccdb-test.cern.ch:8080" }, + "Activity": {}, "monitoring": { - "url": "influxdb-unix:///tmp/telegraf.sock" + "url": "infologger:///debug?qc" }, "consul": { - "url": "null:8500" + "url": "" }, "conditionDB": { - "url": "null:8083" + "url": "ccdb-test.cern.ch:8080" } }, "tasks": { @@ -30,11 +24,25 @@ "moduleName": "QcMID", "detectorName": "MID", "cycleDurationSeconds": "60", - "maxNumberCycles": "-1", + "movingWindows": [ "mLocalBoardsMap", "mLocalBoardsMap11","mLocalBoardsMap12","mLocalBoardsMap21","mLocalBoardsMap22", + "BendHitsMap11", "BendHitsMap12", "BendHitsMap21", "BendHitsMap22", + "NBendHitsMap11", "NBendHitsMap12", "NBendHitsMap21", "NBendHitsMap22" + ], + "dataSource": { + "type": "direct", + "query": "digits:MID/DATA/0;digits_rof:MID/DATAROF/0" + } + }, + "MIDFilteredDigits": { + "active": "true", + "taskName": "FilteredDigits", + "className": "o2::quality_control_modules::mid::DigitsQcTask", + "moduleName": "QcMID", + "detectorName": "MID", + "cycleDurationSeconds": "60", "dataSource": { "type": "direct", - "query": "digits:MID/DATA;digits_rof:MID/DATAROF", - "query_comment" : "100% sampling" + "query": "digits:MID/FDATA/0;digits_rof:MID/FDATAROF/0" } }, "MIDClusters": { @@ -44,11 +52,9 @@ "moduleName": "QcMID", "detectorName": "MID", "cycleDurationSeconds": "60", - "maxNumberCycles": "-1", "dataSource": { "type": "direct", - "query": "clusters:MID/TRACKCLUSTERS;clusterrofs:MID/TRCLUSROFS", - "query_comment" : "100% sampling" + "query": "clusters:MID/TRACKCLUSTERS/0;clusterrofs:MID/TRCLUSROFS/0" } }, "MIDTracks": { @@ -58,40 +64,61 @@ "moduleName": "QcMID", "detectorName": "MID", "cycleDurationSeconds": "60", - "maxNumberCycles": "-1", "dataSource": { "type": "direct", - "query": "tracks:MID/TRACKS;trackrofs:MID/TRACKROFS", - "query_comment" : "100% sampling" + "query": "tracks:MID/TRACKS/0;trackrofs:MID/TRACKROFS/0" } } }, "checks": { "MIDDigits": { - "active": "false", + "active": "true", "checkName": "Digits", "className": "o2::quality_control_modules::mid::DigitsQcCheck", "moduleName": "QcMID", "detectorName": "MID", "policy": "OnAny", "checkParameters": { - "MeanMultThreshold": "100." + "MeanMultThreshold": "100.", + "MinMultThreshold": "0.0", + "NbOrbitPerTF": "32.", + "LocalBoardScale": "200.0", + "LocalBoardThreshold": "800.0", + "NbBadLocalBoard": "10.", + "NbEmptyLocalBoard": "117." + }, + "dataSource": [ + { + "type": "Task", + "name": "MIDDigits" + } + ] + }, + "MIDFilteredDigits": { + "active": "true", + "checkName": "FilteredDigits", + "className": "o2::quality_control_modules::mid::DigitsQcCheck", + "moduleName": "QcMID", + "detectorName": "MID", + "policy": "OnAny", + "checkParameters": { + "MeanMultThreshold": "100.", + "MinMultThreshold": "0.0", + "NbOrbitPerTF": "32.", + "LocalBoardScale": "200.0", + "LocalBoardThreshold": "800.0", + "NbBadLocalBoard": "10.", + "NbEmptyLocalBoard": "117." }, "dataSource": [ { "type": "Task", - "name": "MIDDigits", - "MOs": [ - "mMultHitMT11B", - "mMultHitMT12B", - "mMultHitMT21B", - "mMultHitMT22B" - ] + "name": "MIDFilteredDigits" } ] }, "MIDClusters": { - "active": "false", + "active": "true", "checkName": "Clusters", "className": "o2::quality_control_modules::mid::ClustQcCheck", "moduleName": "QcMID", @@ -100,28 +127,28 @@ "dataSource": [ { "type": "Task", - "name": "MIDClusters", - "MOs": [] + "name": "MIDClusters" } ] }, "MIDTracks": { - "active": "false", + "active": "true", "checkName": "Tracks", "className": "o2::quality_control_modules::mid::TracksQcCheck", "moduleName": "QcMID", "detectorName": "MID", "policy": "OnAny", + "checkParameters": { + "Ratio44Threshold": "0.1" + }, "dataSource": [ { "type": "Task", - "name": "MIDTracks", - "MOs": [] + "name": "MIDTracks" } ] } } }, - "dataSamplingPolicies": [ - ] + "dataSamplingPolicies": [] } diff --git a/DATA/production/qc-async/phs.json b/DATA/production/qc-async/phs.json index 63a7dd28d..37d57a4a6 100644 --- a/DATA/production/qc-async/phs.json +++ b/DATA/production/qc-async/phs.json @@ -3,23 +3,14 @@ "config": { "database": { "implementation": "CCDB", - "host": "ali-qcdb.cern.ch:8083", - "username": "not_applicable", - "password": "not_applicable", - "name": "not_applicable" - }, - "Activity": { - "number": "42", - "type": "2" + "host": "ccdb-test.cern.ch:8080" }, + "Activity": {}, "monitoring": { - "url": "influxdb-unix:///tmp/telegraf.sock" - }, - "consul": { - "url": "" + "url": "infologger:///debug?qc" }, "conditionDB": { - "url": "http://localhost:8084" + "url": "ccdb-test.cern.ch:8080" } }, "tasks": { @@ -30,18 +21,13 @@ "moduleName": "QcPHOS", "detectorName": "PHS", "cycleDurationSeconds": "100", - "maxNumberCycles": "-1", "dataSource": { "type": "direct", "query": "clusters:PHS/CLUSTERS/0;clustertr:PHS/CLUSTERTRIGREC/0" }, - "taskParameters": { - "": "" - } + "taskParameters": {} } - }, - "dataSamplingPolicies": [ - - ] - } -} + } + }, + "dataSamplingPolicies": [] +} \ No newline at end of file diff --git a/DATA/production/qc-async/pidft0tof.json b/DATA/production/qc-async/pidft0tof.json index bd490d249..a68b0a8ee 100644 --- a/DATA/production/qc-async/pidft0tof.json +++ b/DATA/production/qc-async/pidft0tof.json @@ -36,13 +36,14 @@ "taskName": "PID", "moduleName": "QcTOF", "detectorName": "TOF", - "cycleDurationSeconds": "10", + "cycleDurationSeconds": "300", "maxNumberCycles": "-1", "dataSource": { "type": "direct", "query_comment": "checking every matched track", "query": "matchITSTPCTOF:TOF/MTC_ITSTPC/0;matchTPCTOF:TOF/MTC_TPC/0;trackTPCTOF:TOF/TOFTRACKS_TPC/0;trackITSTPC:GLO/TPCITS/0;trackITSTPCABREFS:GLO/TPCITSAB_REFS/0;trackITSTPCABCLID:GLO/TPCITSAB_CLID/0;trackTPC:TPC/TRACKS/0;trackTPCClRefs:TPC/CLUSREFS/0;tofcluster:TOF/CLUSTERS/0;recpoints:FT0/RECPOINTS/0" }, + "movingWindows" : [ "EvTimeTOF", "DeltaEvTimeTOFVsFT0AC" ], "taskParameters": { "GID": "ITS-TPC,ITS-TPC-TOF,TPC,TPC-TOF", "verbose": "false", diff --git a/DATA/production/qc-async/pidft0tofwtrd.json b/DATA/production/qc-async/pidft0tofwtrd.json index 9529c6365..ec13d144d 100644 --- a/DATA/production/qc-async/pidft0tofwtrd.json +++ b/DATA/production/qc-async/pidft0tofwtrd.json @@ -36,13 +36,14 @@ "className": "o2::quality_control_modules::pid::TaskFT0TOF", "moduleName": "QcTOF", "detectorName": "TOF", - "cycleDurationSeconds": "10", + "cycleDurationSeconds": "300", "maxNumberCycles": "-1", "dataSource": { "type": "direct", "query_comment": "checking every matched track", "query" : "tofcluster:TOF/CLUSTERS/0;recpoints:FT0/RECPOINTS/0;trackTPC:TPC/TRACKS/0;trackTPCClRefs:TPC/CLUSREFS/0;matchTPCTOF:TOF/MTC_TPC/0;trackTPCTOF:TOF/TOFTRACKS_TPC/0;trackITSTPC:GLO/TPCITS/0;trackITSTPCABREFS:GLO/TPCITSAB_REFS/0;trackITSTPCABCLID:GLO/TPCITSAB_CLID/0;matchITSTPCTOF:TOF/MTC_ITSTPC/0;trackITSTPCTRD:TRD/MATCH_ITSTPC/0;trigITSTPCTRD:TRD/TRGREC_ITSTPC/0;matchITSTPCTRDTOF:TOF/MTC_ITSTPCTRD/0;trackTPCTRD:TRD/MATCH_TPC/0;trigTPCTRD:TRD/TRGREC_TPC/0;matchTPCTRDTOF:TOF/MTC_TPCTRD/0" }, + "movingWindows" : [ "EvTimeTOF", "DeltaEvTimeTOFVsFT0AC" ], "taskParameters": { "GID" : "TPC,TPC-TOF,ITS-TPC,ITS-TPC-TOF,ITS-TPC-TRD,ITS-TPC-TRD-TOF,TPC-TRD,TPC-TRD-TOF", "verbose": "false", diff --git a/DATA/production/qc-async/pidtof.json b/DATA/production/qc-async/pidtof.json index eddbc98af..4192765b6 100644 --- a/DATA/production/qc-async/pidtof.json +++ b/DATA/production/qc-async/pidtof.json @@ -36,14 +36,15 @@ "className": "o2::quality_control_modules::pid::TaskFT0TOF", "moduleName": "QcTOF", "detectorName": "TOF", - "cycleDurationSeconds": "10", + "cycleDurationSeconds": "300", "maxNumberCycles": "-1", "dataSource": { "type": "direct", "query_comment": "checking every matched track", "query": "matchITSTPCTOF:TOF/MTC_ITSTPC/0;matchTPCTOF:TOF/MTC_TPC/0;trackTPCTOF:TOF/TOFTRACKS_TPC/0;trackITSTPC:GLO/TPCITS/0;trackITSTPCABREFS:GLO/TPCITSAB_REFS/0;trackITSTPCABCLID:GLO/TPCITSAB_CLID/0;trackTPC:TPC/TRACKS/0;trackTPCClRefs:TPC/CLUSREFS/0;tofcluster:TOF/CLUSTERS/0" }, - "taskParameters": { + "movingWindows" : [ "EvTimeTOF", "DeltaEvTimeTOFVsFT0AC" ], + "taskParameters": { "GID": "ITS-TPC,ITS-TPC-TOF,TPC,TPC-TOF", "verbose": "false", "minPtCut": "0.3f", diff --git a/DATA/production/qc-async/qc-global.json b/DATA/production/qc-async/qc-global.json index c2bd64bb6..f456f1197 100644 --- a/DATA/production/qc-async/qc-global.json +++ b/DATA/production/qc-async/qc-global.json @@ -27,7 +27,7 @@ }, "infologger": { "filterDiscardDebug": "true", - "filterDiscardLevel": "1" + "filterDiscardLevel": "3" } } } diff --git a/DATA/production/qc-async/tof.json b/DATA/production/qc-async/tof.json index f8a259e15..9b796abc6 100644 --- a/DATA/production/qc-async/tof.json +++ b/DATA/production/qc-async/tof.json @@ -29,19 +29,20 @@ "className": "o2::quality_control_modules::tof::TaskDigits", "moduleName": "QcTOF", "detectorName": "TOF", - "cycleDurationSeconds": "60", + "cycleDurationSeconds": "300", "maxNumberCycles": "-1", "dataSource": { "type": "direct", - "query": "tofdigits:TOF/DIGITS/0;readoutwin:TOF/READOUTWINDOW/0;patterns:TOF/PATTERNS;diafreq:TOF/DIAFREQ/0", + "query": "tofdigits:TOF/DIGITS/0;readoutwin:TOF/READOUTWINDOW/0;patterns:TOF/PATTERNS;diafreq:TOF/DIAFREQ/0;channelCalib:TOF/CHCALIB/0?lifetime=condition&ccdb-path=TOF/Calib/ChannelCalib;lhcPhase:TOF/LHCP/0?lifetime=condition&ccdb-path=TOF/Calib/LHCphase", "query_comment" : "100% sampling" }, + "movingWindows" : [ "DecodingErrors", "HitMap", "OrbitVsCrate" ], "taskParameters": { "Diagnostic": "true", "PerChannel": "True", "RangeMaxMultiplicity": "15000", "NbinsMultiplicity": "15000", - "applyCalib": "True", + "applyCalib": "False", "NoiseClassSelection": "1" }, "grpGeomRequest" : { diff --git a/DATA/production/qc-async/tpc.json b/DATA/production/qc-async/tpc.json index 10351d4ed..f172cffe4 100644 --- a/DATA/production/qc-async/tpc.json +++ b/DATA/production/qc-async/tpc.json @@ -3,10 +3,7 @@ "config": { "database": { "implementation": "CCDB", - "host": "aaa", - "username": "not_applicable", - "password": "not_applicable", - "name": "not_applicable" + "host": "ccdb-test.cern.ch:8080" }, "Activity": { "number": "42", @@ -23,7 +20,7 @@ "url": "" }, "conditionDB": { - "url": "alice-ccdb.cern.ch" + "url": "ccdb-test.cern.ch:8080" } }, "tasks": { @@ -33,12 +30,11 @@ "className": "o2::quality_control_modules::tpc::Clusters", "moduleName": "QcTPC", "detectorName": "TPC", - "cycleDurationSeconds": "60", - "resetAfterCycles": "1", - "maxNumberCycles": "-1", + "cycleDurationSeconds": "300", "dataSource": { "type": "dataSamplingPolicy", - "name": "tpc-clusters" + "name": "tpc-clusters", + "query": "inputClusters:TPC/CLUSTERNATIVE" }, "taskParameters": { "mergeableOutput": "true", @@ -56,9 +52,8 @@ "className": "o2::quality_control_modules::tpc::PID", "moduleName": "QcTPC", "detectorName": "TPC", - "cycleDurationSeconds": "60", - "resetAfterCycles": "1", - "maxNumberCycles": "-1", + "cycleDurationSeconds": "300", + "movingWindows" : [ "hdEdxTotMIP_TPC" ], "dataSource": { "type": "direct", "query" : "inputTracks:TPC/TRACKS/0" @@ -71,26 +66,50 @@ "cutMinpTPC": "0.05", "cutMaxpTPC": "20.", "cutMinpTPCMIPs": "0.45", - "cutMaxpTPCMIPs": "0.55" + "cutMaxpTPCMIPs": "0.55", + "turnOffHistosForAsync": "true", + "getdEdxVspHypoHist": "true" } }, + "TPCTrackClusters": { + "active": "true", + "taskName": "TrackClusters", + "className": "o2::quality_control_modules::tpc::TrackClusters", + "moduleName": "QcTPC", + "detectorName": "TPC", + "cycleDurationSeconds": "300", + "dataSource": { + "type": "direct", + "query": "inputTracks:TPC/TRACKS/0;inputClusters:TPC/CLUSTERNATIVE;inputClusRefs:TPC/CLUSREFS/0" + }, + "taskParameters": { + "cutAbsEta": "1.5", + "cutMinNCluster": "60", + "cutMindEdxTot": "20.", + "seed": "0", + "samplingFraction": "0.1" + } + }, "TPCTracks": { "active": "true", "taskName": "Tracks", "className": "o2::quality_control_modules::tpc::Tracks", "moduleName": "QcTPC", "detectorName": "TPC", - "cycleDurationSeconds": "60", - "resetAfterCycles": "1", - "maxNumberCycles": "-1", + "cycleDurationSeconds": "300", + "movingWindows" : [ "h2DNClustersEta","hDCArVsEtaPos", "hDCArVsEtaNeg","hDCArVsEtaPos_pTmin","hDCArVsEtaNeg_pTmin" ], "dataSource": { "type": "dataSamplingPolicy", - "name": "tpc-tracks" + "name": "tpc-tracks", + "query": "inputTracks:TPC/TRACKS/0" }, "taskParameters": { "cutAbsEta": "1.", "cutMinNCluster": "60", - "cutMindEdxTot": "20." + "cutMindEdxTot": "20.", + "samplingFractionDCAr": "0.1", + "cutPtForDCAr": "1.5", + "turnOffHistosForAsync": "true" }, "grpGeomRequest" : { "geomRequest": "None", @@ -109,7 +128,7 @@ { "id": "tpc-clusters", "active": "true", - "machines": ["localhost"], + "machines": [], "query" : "inputClusters:TPC/CLUSTERNATIVE", "outputs": "sampled-clusters:DS/CLUSTERNATIVE", "samplingConditions": [ @@ -130,7 +149,7 @@ { "condition": "random", "fraction": "0.1", - "seed": "1234" + "seed": "0" } ], "blocking": "false" diff --git a/DATA/production/qc-async/trd.json b/DATA/production/qc-async/trd.json index bf81c26b8..967c5b85e 100644 --- a/DATA/production/qc-async/trd.json +++ b/DATA/production/qc-async/trd.json @@ -3,23 +3,17 @@ "config": { "database": { "implementation": "CCDB", - "host": "ccdb-test.cern.ch:8080", - "username": "not_applicable", - "password": "not_applicable", - "name": "not_applicable" + "host": "ccdb-test.cern.ch:8080" }, "Activity": { "type": "2", "number": "42" }, "monitoring": { - "url": "influxdb-unix:///tmp/telegraf.sock" - }, - "consul": { - "url": "alio2-cr1-hv-aliecs:8500" + "url": "infologger:///debug?qc" }, "conditionDB": { - "url": "alio2-cr1-hv-qcdb1.cern.ch:8083" + "url": "ccdb-test.cern.ch:8080" } }, "tasks": { @@ -29,10 +23,9 @@ "moduleName": "QcTRD", "detectorName": "TRD", "cycleDurationSeconds": "60", - "maxNumberCycles": "-1", "dataSource": { "type": "direct", - "query": "digits:TRD/DIGITS;triggers:TRD/TRKTRGRD" + "query": "digits:TRD/DIGITS;triggers:TRD/TRKTRGRD;noiseMap:TRD/NOISEMAP/0?lifetime=condition&ccdb-path=TRD/Calib/NoiseMapMCM;chamberStatus:TRD/CHSTATUS/0?lifetime=condition&ccdb-path=TRD/Calib/HalfChamberStatusQC;fedChamberStatus:TRD/FCHSTATUS/0?lifetime=condition&ccdb-path=TRD/Calib/DCSDPsFedChamberStatus" } }, "Tracklets": { @@ -41,10 +34,9 @@ "moduleName": "QcTRD", "detectorName": "TRD", "cycleDurationSeconds": "60", - "maxNumberCycles": "-1", "dataSource": { "type": "direct", - "query": "tracklets:TRD/TRACKLETS;triggers:TRD/TRKTRGRD" + "query": "tracklets:TRD/TRACKLETS;triggers:TRD/TRKTRGRD;noiseMap:TRD/NOISEMAP/0?lifetime=condition&ccdb-path=TRD/Calib/NoiseMapMCM;chamberStatus:TRD/CHSTATUS/0?lifetime=condition&ccdb-path=TRD/Calib/HalfChamberStatusQC;fedChamberStatus:TRD/FCHSTATUS/0?lifetime=condition&ccdb-path=TRD/Calib/DCSDPsFedChamberStatus" } }, "PHTrackMatch": { @@ -53,7 +45,6 @@ "moduleName": "QcTRD", "detectorName": "TRD", "cycleDurationSeconds": "60", - "maxNumberCycles": "-1", "dataSource": { "type": "direct", "query": "phValues:TRD/PULSEHEIGHT" @@ -65,7 +56,6 @@ "moduleName": "QcTRD", "detectorName": "TRD", "cycleDurationSeconds": "60", - "maxNumberCycles": "-1", "dataSource": { "type": "direct", "query": "trackITSTPCTRD:TRD/MATCH_ITSTPC;trigITSTPCTRD:TRD/TRGREC_ITSTPC" diff --git a/DATA/production/qc-async/zdc.json b/DATA/production/qc-async/zdc.json index 88ca06a3e..5059a95c9 100644 --- a/DATA/production/qc-async/zdc.json +++ b/DATA/production/qc-async/zdc.json @@ -37,19 +37,28 @@ }, "taskParameters": { "ADC": "3025;-100;12000", - "ADCH": "1100;-100;1000", - "TDCT": "2400;-25;25", + "ADCZEM": "3025;-100;12000", + "ADCH": "3100;-100;3000", + "TDCT": "2400;-13.5;11.45", "TDCA": "2050;-0.5;4099.5", - "TDCAH": "2000;-0.5;3999.5", - "ADCSUMvsTC": "605;-100;12000;605;-100;12000", - "ADCvsTDCT": "240;-25;25;605;-100;12000", - "TDCDIFF": "240;-25;25;240;-25;25", - "TDCAvsTDCT": "480;-25;25;500;-1;3999", - "TDCAvsTDCA": "500;-1;3999;500;-1;3999", + "TDCAZEM": "2050;-0.5;4099.5", + "TDCAH": "1010;-10;1000", + "ADCSUMvsTC": "121;-100;12000;121;-100;12000", + "ADCZEMvsADCZEM": "121;-100;12000;121;-100;12000", + "ADCZEMvsTC": "121;-100;12000;121;-100;12000", + "ADCvsTDCT": "120;-13.5;11.45;121;-100;12000", + "ADCZEMvsTDCT": "120;-13.5;11.45;121;-100;12000", + "TDCDIFF": "240;-27;22.90;240;-27;22.90", + "TDCAvsTDCT": "120;-13.5;11.45;125;-1;3999", + "TDCAZEMvsTDCT": "120;-13.5;11.45;125;-1;3999", + "TDCAvsTDCA": "125;-1;3999;125;-1;3999", + "TDCAZEMvsTDCAZEM": "125;-1;3999;125;-1;3999", + "TDCAZEMvsTDCA": "125;-1;3999;125;-1;3999", "CENTR_ZNA": "200;-2;2;200;-2;2", "CENTR_ZNC": "200;-2;2;200;-2;2", "CENTR_ZPA": "2240;0;22.4", - "CENTR_ZPC": "2240;-22.4;0" + "CENTR_ZPC": "2240;-22.4;0", + "CENTRAL_EVENT_CONFIG": "2.5;1" } } } diff --git a/DATA/production/qc-async/zdcPbPb.json b/DATA/production/qc-async/zdcPbPb.json new file mode 100644 index 000000000..0407e0d90 --- /dev/null +++ b/DATA/production/qc-async/zdcPbPb.json @@ -0,0 +1,66 @@ +{ + "qc": { + "config": { + "database": { + "implementation": "CCDB", + "host": "alio2-cr1-hv-qcdb1.cern.ch:8083", + "username": "not_applicable", + "password": "not_applicable", + "name": "not_applicable" + }, + "Activity": { + "number": "42", + "type": "2" + }, + "monitoring": { + "url": "influxdb-unix:///tmp/telegraf.sock" + }, + "consul": { + "url": "http://ali-consul.cern.ch:8500" + }, + "conditionDB": { + "url": "http://o2-ccdb.internal" + } + }, + "tasks": { + "ZDCRec": { + "active": "true", + "taskName": "Rec", + "className": "o2::quality_control_modules::zdc::ZDCRecDataTask", + "moduleName": "QcZDC", + "detectorName": "ZDC", + "cycleDurationSeconds": "60", + "maxNumberCycles": "-1", + "dataSource": { + "type": "direct", + "query": "zdc-bcrec:ZDC/BCREC/0;zdc-energyrec:ZDC/ENERGY/0;zdc-tdcrec:ZDC/TDCDATA/0;zdc-inforec:ZDC/INFO/0" + }, + "taskParameters": { + "ADC": "1440; -3;357", + "ADCZEM": "3025;-100;12000", + "ADCH": "115;-3;20", + "TDCT": "2400;-13.5;11.45", + "TDCA": "1440; -3;357", + "TDCAZEM": "2050;-0.5;4099.5", + "TDCAH": "230;-3;20", + "ADCSUMvsTC": "144;-3;357;144;-3;357", + "ADCZEMvsADCZEM": "121;-100;12000;121;-100;12000", + "ADCZEMvsTC": "121;-100;12000;144;-3;357", + "ADCvsTDCT": "120;-13.5;11.45;144;-3;357", + "ADCZEMvsTDCT": "120;-13.5;11.45;121;-100;12000", + "TDCDIFF": "240;-27;22.90;240;-27;22.90", + "TDCAvsTDCT": "120;-13.5;11.45;144;-3;357", + "TDCAZEMvsTDCT": "120;-13.5;11.45;125;-1;3999", + "TDCAvsTDCA": "144;-3;357;144;-3;357", + "TDCAZEMvsTDCAZEM": "125;-1;3999;125;-1;3999", + "TDCAZEMvsTDCA": "125;-1;3999;144;-3;357;", + "CENTR_ZNA": "200;-2;2;200;-2;2", + "CENTR_ZNC": "200;-2;2;200;-2;2", + "CENTR_ZPA": "2240;0;22.4", + "CENTR_ZPC": "2240;-22.4;0", + "CENTRAL_EVENT_CONFIG": "2.5;1" + } + } + } + } +} diff --git a/DATA/production/qc-postproc-async/emc.json b/DATA/production/qc-postproc-async/emc.json index 9908bb047..6342060ae 100644 --- a/DATA/production/qc-postproc-async/emc.json +++ b/DATA/production/qc-postproc-async/emc.json @@ -496,7 +496,7 @@ "moduleName": "QualityControl", "detectorName": "EMC", "resumeTrend": "false", - "producePlotsOnUpdate": "true", + "producePlotsOnUpdate": "false", "dataSources": [ { "type": "repository", diff --git a/DATA/production/qc-postproc-async/zdc.json b/DATA/production/qc-postproc-async/zdc.json index a744a53d1..986d0bd28 100644 --- a/DATA/production/qc-postproc-async/zdc.json +++ b/DATA/production/qc-postproc-async/zdc.json @@ -87,6 +87,48 @@ ] } ], + "dataSourcesTDCA": [ + { + "type": "repository", + "path": "ZDC/MO/Rec", + "names": [ + "ZNAC:h_TDC_ZNA_TC_A", + "ZNAS:h_TDC_ZNA_SUM_A", + "ZPAC:h_TDC_ZPA_TC_A", + "ZPAS:h_TDC_ZPA_SUM_A", + "ZEM1:h_TDC_ZEM1_A", + "ZEM2:h_TDC_ZEM2_A", + "ZNCC:h_TDC_ZNC_TC_A", + "ZNCS:h_TDC_ZNC_SUM_A", + "ZPCC:h_TDC_ZPC_TC_A", + "ZPCS:h_TDC_ZPC_SUM_A" + ] + } + ], + "dataSourcesPeak1n": [ + { + "type": "repository", + "path": "ZDC/MO/Rec", + "names": [ + "ZNAC:h_TDC_ZNA_TC_A", + "ZNAS:h_TDC_ZNA_SUM_A", + "ZNCC:h_TDC_ZNC_TC_A", + "ZNCS:h_TDC_ZNC_SUM_A" + ] + } + ], + "dataSourcesPeak1p": [ + { + "type": "repository", + "path": "ZDC/MO/Rec", + "names": [ + "ZPAC:h_TDC_ZPA_TC_A", + "ZPAS:h_TDC_ZPA_SUM_A", + "ZPCC:h_TDC_ZPC_TC_A", + "ZPCS:h_TDC_ZPC_SUM_A" + ] + } + ], "initTrigger": [ "userorcontrol" ], @@ -117,7 +159,7 @@ { "name": "ZDCQuality/ZDCQuality", "title": "ZDC Quality", - "messageBad": "Inform on-call immediately", + "messageBad": "Bad histograms", "messageMedium": "Add bookkeeping entry", "messageGood": "All checks are OK", "messageNull": "Some histograms are empty!!!" @@ -156,53 +198,77 @@ "detectorName": "ZDC", "policy": "OnAny", "checkParameters": { - "ADC_ZNAC": "700;200;400", - "ADC_ZNA1": "260;100;200", - "ADC_ZNA2": "300;100;200", - "ADC_ZNA3": "170;100;200", - "ADC_ZNA4": "170;100;200", - "ADC_ZNAS": "700;200;400", - "ADC_ZPAC": "700;200;400", - "ADC_ZPA1": "60;50;100", - "ADC_ZPA2": "80;50;100", - "ADC_ZPA3": "245;50;100", - "ADC_ZPA4": "700;200;400", - "ADC_ZPAS": "850;200;400", - "ADC_ZEM1": "1230;200;400", - "ADC_ZEM2": "1275;200;400", - "ADC_ZNCC": "700;200;400", - "ADC_ZNC1": "130;100;200", - "ADC_ZNC2": "280;100;200", - "ADC_ZNC3": "130;100;200", - "ADC_ZNC4": "260;100;200", - "ADC_ZNCS": "600;200;400", - "ADC_ZPCC": "790;200;400", - "ADC_ZPC1": "770;200;400", - "ADC_ZPC2": "350;100;200", - "ADC_ZPC3": "97;50;100", - "ADC_ZPC4": "67;50;67", - "ADC_ZPCS": "1044;200;400", - "ADC_POS_MSG_X": "0.15", - "ADC_POS_MSG_Y": "0.92", - "TDC_ZNAC": "0.0;2.0;4.0", - "TDC_ZNAS": "0.0;2.0;4.0", - "TDC_ZPAC": "0.0;2.0;4.0", - "TDC_ZPAS": "0.0;2.0;4.0", - "TDC_ZEM1": "0.0;2.0;4.0", - "TDC_ZEM2": "0.0;2.0;4.0", - "TDC_ZNCC": "0.0;2.0;4.0", - "TDC_ZNCS": "0.0;2.0;4.0", - "TDC_ZPCC": "0.0;2.0;4.0", - "TDC_ZPCS": "0.0;2.0;4.0", - "TDC_POS_MSG_X": "0.01", - "TDC_POS_MSG_Y": "0.92" + "ADC_ZNAC" : "40.2;10;20", + "ADC_ZNA1" : "10.6;1000;2000", + "ADC_ZNA2" : "11.8;1000;2000", + "ADC_ZNA3" : "8.6;1000;2000", + "ADC_ZNA4" : "6.9;1000;2000", + "ADC_ZNAS" : "41.7;1000;2000", + "ADC_ZPAC" : "17.7;1000;2000", + "ADC_ZPA1" : "1.63;1000;2000", + "ADC_ZPA2" : "2.86;1000;2000", + "ADC_ZPA3" : "5.30;1000;2000", + "ADC_ZPA4" : "12.25;1000;2000", + "ADC_ZPAS" : "20.2;1000;2000", + "ADC_ZEM1" : "803.2;1000;2000", + "ADC_ZEM2" : "891.2;1000;2000", + "ADC_ZNCC" : "40.7;1000;2000", + "ADC_ZNC1" : "9.7;1000;2000", + "ADC_ZNC2" : "15.8;1000;2000", + "ADC_ZNC3" : "6.2;1000;2000", + "ADC_ZNC4" : "8.6;1000;2000", + "ADC_ZNCS" : "42.3;1000;2000", + "ADC_ZPCC" : "19.6;1000;2000", + "ADC_ZPC1" : "14.19;1000;2000", + "ADC_ZPC2" : "7.63;1000;2000", + "ADC_ZPC3" : "2.96;1000;2000", + "ADC_ZPC4" : "1.21;1000;2000", + "ADC_ZPCS" : "20.9;1000;2000", + "ADC_POS_MSG_X": "0.15", + "ADC_POS_MSG_Y": "0.92", + "TDC_ZNAC" : "0.14;1000;2000", + "TDC_ZNAS" : "0.05;1000;2000", + "TDC_ZPAC" : "-0.39;1000;2000", + "TDC_ZPAS" : "-0.11;1000;2000", + "TDC_ZEM1" : "0.07;1000;2000", + "TDC_ZEM2" : "0.02;1000;2000", + "TDC_ZNCC" : "0.19;1000;2000", + "TDC_ZNCS" : "0.08;1000;2000", + "TDC_ZPCC" : "-0.29;1000;2000", + "TDC_ZPCS" : "-0.20;1000;2000", + "TDC_POS_MSG_X": "0.01", + "TDC_POS_MSG_Y": "0.92", + "TDCA_ZNAC" : "326;1000;2000", + "TDCA_ZNAS" : "331;1000;2000", + "TDCA_ZPAC" : "130;1000;2000", + "TDCA_ZPAS" : "146;1000;2000", + "TDCA_ZEM1" : "1203;1000;2000", + "TDCA_ZEM2" : "1265;1000;2000", + "TDCA_ZNCC" : "329;1000;2000", + "TDCA_ZNCS" : "339;1000;2000", + "TDCA_ZPCC" : "146;1000;2000", + "TDCA_ZPCS" : "150;1000;2000", + "TDCA_POS_MSG_X": "0.01", + "TDCA_POS_MSG_Y": "0.92", + "PEAK1N_ZNAC" : "40;1000;2000", + "PEAK1N_ZNAS" : "40;1000;2000", + "PEAK1N_ZNCC" : "40;1000;2000", + "PEAK1N_ZNCS" : "40;1000;2000", + "PEAK1N_POS_MSG_X": "0.01", + "PEAK1N_POS_MSG_Y": "0.92", + "PEAK1P_ZPAC" : "52;1000;2000", + "PEAK1P_ZPAS" : "52;1000;2000", + "PEAK1P_ZPCC" : "52;1000;2000", + "PEAK1P_ZPCS" : "52;1000;2000", + "PEAK1P_POS_MSG_X": "0.01", + "PEAK1P_POS_MSG_Y": "0.92" }, "dataSource": [ { "type": "PostProcessing", "name": "RecPP", "MOs": [ - "h_summary_ADC", "h_summary_TDC" + "h_summary_ADC" , "h_summary_TDC", "h_summary_TDCA", "h_summary_Peak1n", "h_summary_Peak1p" ] } ] diff --git a/DATA/production/qc-sync/glo-itstpc-mtch-qcmn-epn.json b/DATA/production/qc-sync/glo-itstpc-mtch-qcmn-epn.json index da8964a41..36aee3ed9 100644 --- a/DATA/production/qc-sync/glo-itstpc-mtch-qcmn-epn.json +++ b/DATA/production/qc-sync/glo-itstpc-mtch-qcmn-epn.json @@ -32,21 +32,32 @@ "maxNumberCycles" : "-1", "dataSource" : { "type" : "dataSamplingPolicy", - "name" : "ITSTPCmSamp" + "name" : "ITSTPCmSampK0" }, "taskParameters" : { - "GID" : "ITS-TPC,ITS", - "verbose" : "false", - "minPtCut" : "0.1f", - "etaCut" : "1.4f", - "minNTPCClustersCut" : "60", - "minDCACut" : "100.f", - "minDCACutY" : "10.f" + "GID": "ITS-TPC,ITS", + "verbose": "false", + "minPtITSCut": "0.1f", + "etaITSCut": "1e10f", + "minNITSClustersCut": "0", + "maxChi2PerClusterITS": "100000", + "minPtTPCCut": "0.1f", + "etaTPCCut": "0.9f", + "minNTPCClustersCut": "60", + "minDCACut": "100.f", + "minDCACutY": "10.f", + "minPtCut": "0.1f", + "maxPtCut": "20.f", + "etaCut": "1e10f", + "cutK0Mass": "0.05f", + "maxEtaK0": "0.8f", + "doK0QC": "true", + "trackSourcesK0": "" }, - "grpGeomRequest" : { - "geomRequest": "Aligned", - "askGRPECS": "false", - "askGRPLHCIF": "false", + "grpGeomRequest" : { + "geomRequest": "None", + "askGRPECS": "true", + "askGRPLHCIF": "true", "askGRPMagField": "true", "askMatLUT": "false", "askTime": "false", @@ -54,23 +65,23 @@ "needPropagatorD": "false" }, "location" : "local", - "localMachines": [ + "localMachines": [ "epn", - "localhost" + "localhost" ], "remoteMachine": "alio2-cr1-qc07.cern.ch", - "remotePort": "47761", - "localControl": "odc" + "remotePort": "47761", + "localControl": "odc" } } }, "dataSamplingPolicies" : [ { - "id" : "ITSTPCmSamp", + "id" : "ITSTPCmSampK0", "active" : "true", "machines" : [], "query_comment" : "checking every 10% matched track", - "query" : "trackITSTPC:GLO/TPCITS/0;trackITSTPCABREFS:GLO/TPCITSAB_REFS/0;trackITSTPCABCLID:GLO/TPCITSAB_CLID/0;trackTPC:TPC/TRACKS;trackTPCClRefs:TPC/CLUSREFS;trackITS:ITS/TRACKS/0;trackITSROF:ITS/ITSTrackROF/0;trackITSClIdx:ITS/TRACKCLSID/0;alpparITS:ITS/ALPIDEPARAM/0?lifetime=condition&ccdb-path=ITS/Config/AlpideParam", + "query" : "", "samplingConditions" : [ { "condition" : "random", diff --git a/DATA/production/qc-sync/glo-mchmid-mtch-qcmn-epn.json b/DATA/production/qc-sync/glo-mchmid-mtch-qcmn-epn.json new file mode 100644 index 000000000..d34062b50 --- /dev/null +++ b/DATA/production/qc-sync/glo-mchmid-mtch-qcmn-epn.json @@ -0,0 +1,66 @@ +{ + "qc": { + "config": { + "database": { + "implementation": "CCDB", + "host": "ali-qcdb.cern.ch:8083", + "username": "not_applicable", + "password": "not_applicable", + "name": "not_applicable" + }, + "Activity": {}, + "monitoring": { + "url": "influxdb-unix:///tmp/telegraf.sock" + }, + "consul": { + "url": "http://localhost:8500" + }, + "conditionDB": { + "url": "o2-ccdb.internal" + }, + "bookkeeping": { + "url": "alio2-cr1-hv-web01.cern.ch:4001" + } + }, + "tasks": { + "MUONTracks": { + "active": "true", + "className": "o2::quality_control_modules::muon::TracksTask", + "moduleName": "QcMUONCommon", + "detectorName": "GLO", + "cycleDurationSeconds": "180", + "maxNumberCycles": "-1", + "disableLastCycle": "true", + "dataSource": { + "type": "direct", + "query": "trackMCH:MCH/TRACKS;trackMCHROF:MCH/TRACKROFS;trackMCHTRACKCLUSTERS:MCH/TRACKCLUSTERS;mchtrackdigits:MCH/CLUSTERDIGITS;trackMID:MID/TRACKS/0;trackMIDROF:MID/TRACKROFS/0;trackMIDTRACKCLUSTERS:MID/TRACKCLUSTERS/0;trackClMIDROF:MID/TRCLUSROFS/0;matchMCHMID:GLO/MTC_MCHMID" + }, + "taskParameters": { + "maxTracksPerTF": "600", + "GID": "MCH,MID,MCH-MID" + }, + "grpGeomRequest": { + "geomRequest": "Aligned", + "askGRPECS": "false", + "askGRPLHCIF": "false", + "askGRPMagField": "false", + "askMatLUT": "false", + "askTime": "false", + "askOnceAllButField": "false", + "needPropagatorD": "false" + }, + "location": "local", + "mergingMode": "delta", + "localControl": "odc", + "localMachines": [ + "localhost", + "epn" + ], + "remotePort": "29514", + "remoteMachine": "alio2-cr1-qts02.cern.ch" + } + }, + "checks": {} + }, + "dataSamplingPolicies": [] +} diff --git a/DATA/production/qc-sync/glo-mftmch-mtch-qcmn-epn.json b/DATA/production/qc-sync/glo-mftmch-mtch-qcmn-epn.json new file mode 100644 index 000000000..064b0736f --- /dev/null +++ b/DATA/production/qc-sync/glo-mftmch-mtch-qcmn-epn.json @@ -0,0 +1,66 @@ +{ + "qc": { + "config": { + "database": { + "implementation": "CCDB", + "host": "ali-qcdb.cern.ch:8083", + "username": "not_applicable", + "password": "not_applicable", + "name": "not_applicable" + }, + "Activity": {}, + "monitoring": { + "url": "influxdb-unix:///tmp/telegraf.sock" + }, + "consul": { + "url": "http://localhost:8500" + }, + "conditionDB": { + "url": "o2-ccdb.internal" + }, + "bookkeeping": { + "url": "alio2-cr1-hv-web01.cern.ch:4001" + } + }, + "tasks": { + "MUONTracks": { + "active": "true", + "className": "o2::quality_control_modules::muon::TracksTask", + "moduleName": "QcMUONCommon", + "detectorName": "GLO", + "cycleDurationSeconds": "180", + "maxNumberCycles": "-1", + "disableLastCycle": "true", + "dataSource": { + "type": "direct", + "query": "trackMCH:MCH/TRACKS;trackMCHROF:MCH/TRACKROFS;trackMCHTRACKCLUSTERS:MCH/TRACKCLUSTERS;mchtrackdigits:MCH/CLUSTERDIGITS;trackMFT:MFT/TRACKS;trackMFTROF:MFT/MFTTrackROF;trackMFTClIdx:MFT/TRACKCLSID;alpparMFT:MFT/ALPIDEPARAM/0?lifetime=condition&ccdb-path=MFT/Config/AlpideParam;fwdtracks:GLO/GLFWD" + }, + "taskParameters": { + "maxTracksPerTF": "600", + "GID": "MFT,MCH,MFT-MCH" + }, + "grpGeomRequest": { + "geomRequest": "Aligned", + "askGRPECS": "false", + "askGRPLHCIF": "false", + "askGRPMagField": "false", + "askMatLUT": "false", + "askTime": "false", + "askOnceAllButField": "false", + "needPropagatorD": "false" + }, + "location": "local", + "mergingMode": "delta", + "localControl": "odc", + "localMachines": [ + "localhost", + "epn" + ], + "remotePort": "29514", + "remoteMachine": "alio2-cr1-qts02.cern.ch" + } + }, + "checks": {} + }, + "dataSamplingPolicies": [] +} diff --git a/DATA/production/qc-sync/glo-mftmchmid-mtch-qcmn-epn.json b/DATA/production/qc-sync/glo-mftmchmid-mtch-qcmn-epn.json new file mode 100644 index 000000000..3806ed288 --- /dev/null +++ b/DATA/production/qc-sync/glo-mftmchmid-mtch-qcmn-epn.json @@ -0,0 +1,66 @@ +{ + "qc": { + "config": { + "database": { + "implementation": "CCDB", + "host": "ali-qcdb.cern.ch:8083", + "username": "not_applicable", + "password": "not_applicable", + "name": "not_applicable" + }, + "Activity": {}, + "monitoring": { + "url": "influxdb-unix:///tmp/telegraf.sock" + }, + "consul": { + "url": "http://localhost:8500" + }, + "conditionDB": { + "url": "o2-ccdb.internal" + }, + "bookkeeping": { + "url": "alio2-cr1-hv-web01.cern.ch:4001" + } + }, + "tasks": { + "MUONTracks": { + "active": "true", + "className": "o2::quality_control_modules::muon::TracksTask", + "moduleName": "QcMUONCommon", + "detectorName": "GLO", + "cycleDurationSeconds": "180", + "maxNumberCycles": "-1", + "disableLastCycle": "true", + "dataSource": { + "type": "direct", + "query": "trackMCH:MCH/TRACKS;trackMCHROF:MCH/TRACKROFS;trackMCHTRACKCLUSTERS:MCH/TRACKCLUSTERS;mchtrackdigits:MCH/CLUSTERDIGITS;trackMFT:MFT/TRACKS;trackMFTROF:MFT/MFTTrackROF;trackMFTClIdx:MFT/TRACKCLSID;alpparMFT:MFT/ALPIDEPARAM/0?lifetime=condition&ccdb-path=MFT/Config/AlpideParam;fwdtracks:GLO/GLFWD;trackMID:MID/TRACKS/0;trackMIDROF:MID/TRACKROFS/0;trackMIDTRACKCLUSTERS:MID/TRACKCLUSTERS/0;trackClMIDROF:MID/TRCLUSROFS/0;matchMCHMID:GLO/MTC_MCHMID" + }, + "taskParameters": { + "maxTracksPerTF": "600", + "GID": "MFT,MCH,MID,MFT-MCH,MCH-MID,MFT-MCH-MID" + }, + "grpGeomRequest": { + "geomRequest": "Aligned", + "askGRPECS": "false", + "askGRPLHCIF": "false", + "askGRPMagField": "false", + "askMatLUT": "false", + "askTime": "false", + "askOnceAllButField": "false", + "needPropagatorD": "false" + }, + "location": "local", + "mergingMode": "delta", + "localControl": "odc", + "localMachines": [ + "localhost", + "epn" + ], + "remotePort": "29514", + "remoteMachine": "alio2-cr1-qts02.cern.ch" + } + }, + "checks": {} + }, + "dataSamplingPolicies": [] +} diff --git a/DATA/production/qc-sync/its.json b/DATA/production/qc-sync/its.json index e3a2acc4b..2d8fb9d78 100644 --- a/DATA/production/qc-sync/its.json +++ b/DATA/production/qc-sync/its.json @@ -46,17 +46,7 @@ "publishSummary1D": "0", "publishDetailedSummary": "0" }, - "grpGeomRequest" : { - "geomRequest": "Aligned", - "askGRPECS": "false", - "askGRPLHCIF": "false", - "askGRPMagField": "false", - "askMatLUT": "false", - "askTime": "false", - "askOnceAllButField": "true", - "needPropagatorD": "false" - }, - "localMachines": [ + "localMachines": [ "localhost", "epn" ], "localControl": "odc", diff --git a/DATA/production/qc-sync/itstpctof.json b/DATA/production/qc-sync/itstpctof.json index dcc986436..fefe85c87 100644 --- a/DATA/production/qc-sync/itstpctof.json +++ b/DATA/production/qc-sync/itstpctof.json @@ -53,7 +53,7 @@ "minDCACutY" : "10.f" }, "grpGeomRequest" : { - "geomRequest": "Aligned", + "geomRequest": "None", "askGRPECS": "false", "askGRPLHCIF": "false", "askGRPMagField": "true", diff --git a/DATA/production/qc-sync/itstpctrdtof.json b/DATA/production/qc-sync/itstpctrdtof.json index 2fb6363ba..087449715 100644 --- a/DATA/production/qc-sync/itstpctrdtof.json +++ b/DATA/production/qc-sync/itstpctrdtof.json @@ -53,7 +53,7 @@ "minDCACutY" : "10.f" }, "grpGeomRequest" : { - "geomRequest": "Aligned", + "geomRequest": "None", "askGRPECS": "false", "askGRPLHCIF": "false", "askGRPMagField": "true", diff --git a/DATA/production/qc-sync/mft-full-no-tracks.json b/DATA/production/qc-sync/mft-full-no-tracks.json index 6087535a8..79dd25657 100644 --- a/DATA/production/qc-sync/mft-full-no-tracks.json +++ b/DATA/production/qc-sync/mft-full-no-tracks.json @@ -58,6 +58,16 @@ "timeBinSize" : "0.1", "ROFLengthInBC" : "594" }, + "grpGeomRequest" : { + "geomRequest": "None", + "askGRPECS": "true", + "askGRPLHCIF": "false", + "askGRPMagField": "false", + "askMatLUT": "false", + "askTime": "false", + "askOnceAllButField": "false", + "needPropagatorD": "false" + }, "location": "local", "localMachines" : [ "flp" ], "remoteMachine": "any", @@ -75,21 +85,22 @@ "name": "mft-clusters" }, "taskParameters" : { + "onlineQC" : "1", "maxClusterROFSize" : "50000", "maxDuration" : "60000", "timeBinSize" : "0.1", "ROFLengthInBC" : "594" }, - "grpGeomRequest" : { - "geomRequest": "Aligned", - "askGRPECS": "false", + "grpGeomRequest" : { + "geomRequest": "None", + "askGRPECS": "true", "askGRPLHCIF": "false", - "askGRPMagField": "true", + "askGRPMagField": "false", "askMatLUT": "false", "askTime": "false", - "askOnceAllButField": "true", - "needPropagatorD": "false" - }, + "askOnceAllButField": "false", + "needPropagatorD": "false" + }, "location": "local", "localMachines": [ "epn", "localhost" @@ -127,8 +138,8 @@ "detectorName": "MFT", "policy": "OnEachSeparately", "checkParameters" : { - "ZoneThresholdMedium" : "2", - "ZoneThresholdBad" : "5" + "LadderThresholdMedium" : "1", + "LadderThresholdBad" : "2" }, "dataSource": [{ "type": "Task", @@ -144,8 +155,8 @@ "MOs" : ["mClusterOccupancy","mClusterPatternIndex","mClusterSizeSummary", "mGroupedClusterSizeSummary","mClusterOccupancySummary"] }], "checkParameters" : { - "ZoneThresholdMedium" : "2", - "ZoneThresholdBad" : "5" + "LadderThresholdMedium" : "1", + "LadderThresholdBad" : "2" }, "className": "o2::quality_control_modules::mft::QcMFTClusterCheck", "moduleName": "QcMFT", diff --git a/DATA/production/qc-sync/mft-full.json b/DATA/production/qc-sync/mft-full.json index 5a446e3e5..90ecc7c0f 100644 --- a/DATA/production/qc-sync/mft-full.json +++ b/DATA/production/qc-sync/mft-full.json @@ -58,6 +58,16 @@ "timeBinSize" : "0.1", "ROFLengthInBC" : "594" }, + "grpGeomRequest" : { + "geomRequest": "None", + "askGRPECS": "true", + "askGRPLHCIF": "false", + "askGRPMagField": "false", + "askMatLUT": "false", + "askTime": "false", + "askOnceAllButField": "false", + "needPropagatorD": "false" + }, "location": "local", "localMachines" : [ "flp" ], "remoteMachine": "any", @@ -75,21 +85,22 @@ "name": "mft-clusters" }, "taskParameters" : { + "onlineQC" : "1", "maxClusterROFSize" : "50000", "maxDuration" : "60000", "timeBinSize" : "0.1", "ROFLengthInBC" : "594" }, - "grpGeomRequest" : { - "geomRequest": "Aligned", - "askGRPECS": "false", + "grpGeomRequest" : { + "geomRequest": "None", + "askGRPECS": "true", "askGRPLHCIF": "false", - "askGRPMagField": "true", + "askGRPMagField": "false", "askMatLUT": "false", "askTime": "false", - "askOnceAllButField": "true", - "needPropagatorD": "false" - }, + "askOnceAllButField": "false", + "needPropagatorD": "false" + }, "location": "local", "localMachines": [ "epn", "localhost" @@ -109,6 +120,16 @@ "type" : "direct", "query" : "tracks:MFT/TRACKS/0;tracksrofs:MFT/MFTTrackROF/0;clustersrofs:MFT/CLUSTERSROF/0" }, + "grpGeomRequest" : { + "geomRequest": "None", + "askGRPECS": "true", + "askGRPLHCIF": "false", + "askGRPMagField": "false", + "askMatLUT": "false", + "askTime": "false", + "askOnceAllButField": "false", + "needPropagatorD": "false" + }, "location": "local", "localMachines": [ "epn", "localhost" @@ -153,8 +174,8 @@ "detectorName": "MFT", "policy": "OnEachSeparately", "checkParameters" : { - "ZoneThresholdMedium" : "2", - "ZoneThresholdBad" : "5" + "LadderThresholdMedium" : "1", + "LadderThresholdBad" : "2" }, "dataSource": [{ "type": "Task", @@ -170,8 +191,8 @@ "MOs" : ["mClusterOccupancy","mClusterPatternIndex","mClusterSizeSummary", "mGroupedClusterSizeSummary","mClusterOccupancySummary"] }], "checkParameters" : { - "ZoneThresholdMedium" : "2", - "ZoneThresholdBad" : "5" + "LadderThresholdMedium" : "1", + "LadderThresholdBad" : "2" }, "className": "o2::quality_control_modules::mft::QcMFTClusterCheck", "moduleName": "QcMFT", diff --git a/DATA/production/qc-sync/mid-digits.json b/DATA/production/qc-sync/mid-digits.json index 7bf1bda58..1539c4e1f 100644 --- a/DATA/production/qc-sync/mid-digits.json +++ b/DATA/production/qc-sync/mid-digits.json @@ -55,7 +55,7 @@ "id": "middigits", "active": "true", "machines": [], - "query": "digits:MID/DATA;digits_rof:MID/DATAROF", + "query": "digits:MID/DATA/0;digits_rof:MID/DATAROF/0", "samplingConditions": [ { "condition": "random", diff --git a/DATA/production/qc-sync/mid.json b/DATA/production/qc-sync/mid.json index fea6f247e..5775dc714 100644 --- a/DATA/production/qc-sync/mid.json +++ b/DATA/production/qc-sync/mid.json @@ -148,7 +148,7 @@ "epn", "localhost" ], - "query": "tracks:MID/TRACKS;trackrofs:MID/TRACKROFS", + "query": "tracks:MID/TRACKS/0;trackrofs:MID/TRACKROFS/0", "samplingConditions": [ { "condition": "random", @@ -165,7 +165,7 @@ "epn", "localhost" ], - "query": "clusters:MID/TRACKCLUSTERS;clusterrofs:MID/TRCLUSROFS", + "query": "clusters:MID/TRACKCLUSTERS/0;clusterrofs:MID/TRCLUSROFS/0", "samplingConditions": [ { "condition": "random", @@ -182,7 +182,7 @@ "epn", "localhost" ], - "query": "digits:MID/DATA;digits_rof:MID/DATAROF", + "query": "digits:MID/DATA/0;digits_rof:MID/DATAROF/0", "samplingConditions": [ { "condition": "random", diff --git a/DATA/production/qc-sync/qc-global-epn-staging.json b/DATA/production/qc-sync/qc-global-epn-staging.json index 479e52f97..154bf9587 100644 --- a/DATA/production/qc-sync/qc-global-epn-staging.json +++ b/DATA/production/qc-sync/qc-global-epn-staging.json @@ -23,7 +23,7 @@ }, "infologger": { "filterDiscardDebug": "true", - "filterDiscardLevel": "1", + "filterDiscardLevel": "3", "filterDiscardFile": "../../qc-_ID_.log", "filterRotateMaxBytes": "100000000", "filterRotateMaxFiles": "1" diff --git a/DATA/production/qc-sync/qc-global-epn.json b/DATA/production/qc-sync/qc-global-epn.json index 420bfeeaf..85ffa84aa 100644 --- a/DATA/production/qc-sync/qc-global-epn.json +++ b/DATA/production/qc-sync/qc-global-epn.json @@ -23,13 +23,13 @@ }, "infologger": { "filterDiscardDebug": "true", - "filterDiscardLevel": "1", + "filterDiscardLevel": "3", "filterDiscardFile": "../../qc-_ID_.log", "filterRotateMaxBytes": "100000000", "filterRotateMaxFiles": "1" }, "bookkeeping": { - "url": "alio2-cr1-hv-web01.cern.ch:4001" + "url": "alio2-cr1-hv-web02.cern.ch:4001" } } } diff --git a/DATA/production/qc-sync/qc-global.json b/DATA/production/qc-sync/qc-global.json index 596e0e8f5..f49cc66ba 100644 --- a/DATA/production/qc-sync/qc-global.json +++ b/DATA/production/qc-sync/qc-global.json @@ -23,7 +23,7 @@ }, "infologger": { "filterDiscardDebug": "true", - "filterDiscardLevel": "1" + "filterDiscardLevel": "3" } } } diff --git a/DATA/production/qc-sync/tpc.json b/DATA/production/qc-sync/tpc.json index d11c1581a..d4fa3a484 100644 --- a/DATA/production/qc-sync/tpc.json +++ b/DATA/production/qc-sync/tpc.json @@ -4,9 +4,6 @@ "database": { "implementation": "CCDB", "host": "ali-qcdb.cern.ch:8083", - "username": "not_applicable", - "password": "not_applicable", - "name": "not_applicable", "maxObjectSize" : "10000000" }, "Activity": { @@ -31,7 +28,6 @@ "detectorName": "TPC", "cycleDurationSeconds": "60", "resetAfterCycles": "1", - "maxNumberCycles": "-1", "dataSource": { "type": "dataSamplingPolicy", "name": "random-cluster" @@ -74,7 +70,6 @@ "detectorName": "TPC", "cycleDurationSeconds": "60", "resetAfterCycles": "10", - "maxNumberCycles": "-1", "dataSource": { "type": "direct", "query": "inputTracks:TPC/TRACKS/0" @@ -106,16 +101,25 @@ "detectorName": "TPC", "cycleDurationSeconds": "60", "resetAfterCycles": "10", - "maxNumberCycles": "-1", "dataSource": { "type": "dataSamplingPolicy", "name": "tpc-tracks" }, "taskParameters": { - "cutAbsEta": "1.", + "cutAbsEta": "1.5", "cutMinNCluster": "60", "cutMindEdxTot": "20." }, + "grpGeomRequest" : { + "geomRequest": "Aligned", + "askGRPECS": "false", + "askGRPLHCIF": "false", + "askGRPMagField": "true", + "askMatLUT": "true", + "askTime": "false", + "askOnceAllButField": "true", + "needPropagatorD": "false" + }, "location": "local", "localMachines": [ "localhost", @@ -157,7 +161,7 @@ { "condition": "random", "fraction": "0.01", - "seed": "1234" + "seed": "0" } ], "blocking": "false" diff --git a/DATA/production/qc-sync/trd.json b/DATA/production/qc-sync/trd.json index cdeb487f7..4895e8c85 100644 --- a/DATA/production/qc-sync/trd.json +++ b/DATA/production/qc-sync/trd.json @@ -22,7 +22,6 @@ "url": "o2-ccdb.internal" }, "infologger": { - "": "Message at this level or above are discarded (default: 21 - Trace)", "filterDiscardDebug": "false", "filterDiscardLevel": "11" } @@ -33,12 +32,15 @@ "className": "o2::quality_control_modules::trd::RawData", "moduleName": "QcTRD", "detectorName": "TRD", - "cycleDurationSeconds": "60", - "maxNumberCycles": "-1", + "cycleDurations": [ + {"cycleDurationSeconds": 30, "validitySeconds": 180}, + {"cycleDurationSeconds": 180, "validitySeconds": 1} + ], "dataSource": { "type": "direct", "query": "rawstats:TRD/RAWSTATS" }, + "disableLastCycle": "true", "location": "local", "localMachines": [ "epn", @@ -55,12 +57,12 @@ "className": "o2::quality_control_modules::trd::DigitsTask", "moduleName": "QcTRD", "detectorName": "TRD", - "cycleDurationSeconds": "60", - "maxNumberCycles": "-1", + "cycleDurationSeconds": "180", "dataSource": { "type": "dataSamplingPolicy", "name": "trdall" }, + "disableLastCycle": "true", "location": "local", "localMachines": [ "epn", @@ -81,12 +83,12 @@ "className": "o2::quality_control_modules::trd::TrackletsTask", "moduleName": "QcTRD", "detectorName": "TRD", - "cycleDurationSeconds": "60", - "maxNumberCycles": "-1", + "cycleDurationSeconds": "120", "dataSource": { "type": "dataSamplingPolicy", "name": "trdall" }, + "disableLastCycle": "true", "location": "local", "localMachines": [ "epn", @@ -108,12 +110,12 @@ "className": "o2::quality_control_modules::trd::PulseHeightTrackMatch", "moduleName": "QcTRD", "detectorName": "TRD", - "cycleDurationSeconds": "60", - "maxNumberCycles": "-1", + "cycleDurationSeconds": "180", "dataSource": { "type": "direct", "query": "phValues:TRD/PULSEHEIGHT" }, + "disableLastCycle": "true", "location": "local", "localMachines": [ "epn", @@ -133,12 +135,12 @@ "className": "o2::quality_control_modules::trd::TrackingTask", "moduleName": "QcTRD", "detectorName": "TRD", - "cycleDurationSeconds": "60", - "maxNumberCycles": "-1", + "cycleDurationSeconds": "180", "dataSource": { "type": "direct", "query": "trackITSTPCTRD:TRD/MATCH_ITSTPC;trigITSTPCTRD:TRD/TRGREC_ITSTPC" }, + "disableLastCycle": "true", "location": "local", "localMachines": [ "epn", @@ -164,12 +166,12 @@ "id": "trdall", "active": "true", "machines": [], - "query": "digits:TRD/DIGITS/0;tracklets:TRD/TRACKLETS/0;triggers:TRD/TRKTRGRD/0", + "query": "digits:TRD/DIGITS/0;tracklets:TRD/TRACKLETS/0;triggers:TRD/TRKTRGRD/0;noiseMap:TRD/NOISEMAP/0?lifetime=condition&ccdb-path=TRD/Calib/NoiseMapMCM;chamberStatus:TRD/CHSTATUS/0?lifetime=condition&ccdb-path=TRD/Calib/HalfChamberStatusQC;fedChamberStatus:TRD/FCHSTATUS/0?lifetime=condition&ccdb-path=TRD/Calib/DCSDPsFedChamberStatus", "samplingConditions": [ { "condition": "random", "fraction": "0.05", - "seed": "1234" + "seed": "0" } ], "blocking": "false" diff --git a/DATA/production/qc-workflow.sh b/DATA/production/qc-workflow.sh index 90501a593..6ce392301 100755 --- a/DATA/production/qc-workflow.sh +++ b/DATA/production/qc-workflow.sh @@ -20,10 +20,24 @@ OUTPUT_SUFFIX= add_QC_JSON() { if [[ ${2} =~ ^consul://.* ]]; then + [[ $EPNSYNCMODE == 1 ]] || { echo "Error fetching QC JSON $2: consul server is used for EPNSYNCMODE == 1 only" 1>&2 && exit 1; } TMP_FILENAME=$FETCHTMPDIR/$1.$RANDOM.$RANDOM.json curl -s -o $TMP_FILENAME "http://${GEN_TOPO_QC_CONSUL_SERVER}:8500/v1/kv/${2/consul:\/\//}?raw" if [[ $? != 0 ]]; then - echo "Error fetching QC JSON $2" + echo "Error fetching QC JSON $2 (3)" 1>&2 + exit 1 + fi + elif [[ ${2} =~ ^apricot://.* ]]; then + [[ $EPNSYNCMODE == 1 ]] || { echo "Error fetching QC JSON $2: apricot server is used for EPNSYNCMODE == 1 only" 1>&2 && exit 1; } + TMP_FILENAME=$FETCHTMPDIR/$1.$RANDOM.$RANDOM.json + if [[ ${2} =~ "?" ]]; then + curl -s -o $TMP_FILENAME "${GEN_TOPO_QC_APRICOT_SERVER}/${2/apricot:\/\/o2\//}\&run_type=${RUNTYPE:-}\&beam_type=${BEAMTYPE:-}\&process=true" + else + curl -s -o $TMP_FILENAME "${GEN_TOPO_QC_APRICOT_SERVER}/${2/apricot:\/\/o2\//}?run_type=${RUNTYPE:-}\&beam_type=${BEAMTYPE:-}\&process=true" + fi + + if [[ $? != 0 ]]; then + echo "Error fetching QC JSON $2 (4)" 1>&2 exit 1 fi else @@ -38,78 +52,91 @@ add_QC_JSON() { OUTPUT_SUFFIX+="-$1" } +JSON_TEMP_FILES="()" + QC_CONFIG= -QC_CONFIG_OVERRIDE= +: ${QC_CONFIG_OVERRIDE:=} # set to empty string only if not already set externally : ${QC_DETECTOR_CONFIG_OVERRIDE:=} # set to empty string only if not already set externally if [[ -z ${QC_JSON_FROM_OUTSIDE:-} && ! -z ${GEN_TOPO_QC_JSON_FILE:-} && -f $GEN_TOPO_QC_JSON_FILE ]]; then QC_JSON_FROM_OUTSIDE=$GEN_TOPO_QC_JSON_FILE elif [[ -z ${QC_JSON_FROM_OUTSIDE:-} ]]; then if [[ $EPNSYNCMODE == 1 || "${GEN_TOPO_LOAD_QC_JSON_FROM_CONSUL:-}" == "1" ]]; then # Sync processing running on the EPN - [[ -z "${QC_JSON_TPC:-}" ]] && QC_JSON_TPC=consul://o2/components/qc/ANY/any/tpc-full-qcmn - [[ -z "${QC_JSON_ITS:-}" ]] && QC_JSON_ITS=consul://o2/components/qc/ANY/any/its-qcmn-epn-full + [[ -z "${QC_JSON_TPC:-}" ]] && QC_JSON_TPC=apricot://o2/components/qc/ANY/any/tpc-full-qcmn + [[ -z "${QC_JSON_ITS:-}" ]] && QC_JSON_ITS=apricot://o2/components/qc/ANY/any/its-qcmn-epn-full if [[ -z "${QC_JSON_MFT:-}" ]]; then if has_detector MFT && has_processing_step MFT_RECO; then - QC_JSON_MFT=consul://o2/components/qc/ANY/any/mft-full-qcmn + QC_JSON_MFT=apricot://o2/components/qc/ANY/any/mft-full-qcmn else - QC_JSON_MFT=consul://o2/components/qc/ANY/any/mft-full-no-tracks-qcmn + QC_JSON_MFT=apricot://o2/components/qc/ANY/any/mft-full-no-tracks-qcmn fi fi if [[ -z "${QC_JSON_TOF:-}" ]]; then if has_detector_flp_processing TOF; then - QC_JSON_TOF=consul://o2/components/qc/ANY/any/tof-full-qcmn-on-epn + QC_JSON_TOF=apricot://o2/components/qc/ANY/any/tof-full-qcmn-on-epn else - QC_JSON_TOF=consul://o2/components/qc/ANY/any/tof-full-epn-qcmn-on-epn + QC_JSON_TOF=apricot://o2/components/qc/ANY/any/tof-full-epn-qcmn-on-epn fi fi - [[ -z "${QC_JSON_FDD:-}" ]] && QC_JSON_FDD=consul://o2/components/qc/ANY/any/fdd-digits-qc-epn - [[ -z "${QC_JSON_FT0:-}" ]] && QC_JSON_FT0=consul://o2/components/qc/ANY/any/ft0-digits-qc-epn - [[ -z "${QC_JSON_FV0:-}" ]] && QC_JSON_FV0=consul://o2/components/qc/ANY/any/fv0-digits-qc-epn + [[ -z "${QC_JSON_FDD:-}" ]] && QC_JSON_FDD=apricot://o2/components/qc/ANY/any/fdd-digits-qcmn-epn + [[ -z "${QC_JSON_FT0:-}" ]] && QC_JSON_FT0=apricot://o2/components/qc/ANY/any/ft0-digits-qcmn-epn + [[ -z "${QC_JSON_FV0:-}" ]] && QC_JSON_FV0=apricot://o2/components/qc/ANY/any/fv0-digits-qcmn-epn if [[ -z "${QC_JSON_EMC:-}" ]]; then if [[ "$BEAMTYPE" == "PbPb" ]]; then if has_detector CTP; then - QC_JSON_EMC=consul://o2/components/qc/ANY/any/emc-qcmn-epnall-withCTP-PbPb + QC_JSON_EMC=apricot://o2/components/qc/ANY/any/emc-qcmn-epnall-withCTP-PbPb else - QC_JSON_EMC=consul://o2/components/qc/ANY/any/emc-qcmn-epnall-PbPb + QC_JSON_EMC=apricot://o2/components/qc/ANY/any/emc-qcmn-epnall-PbPb fi else if has_detector CTP; then - QC_JSON_EMC=consul://o2/components/qc/ANY/any/emc-qcmn-epnall-withCTP + QC_JSON_EMC=apricot://o2/components/qc/ANY/any/emc-qcmn-epnall-withCTP else - QC_JSON_EMC=consul://o2/components/qc/ANY/any/emc-qcmn-epnall + QC_JSON_EMC=apricot://o2/components/qc/ANY/any/emc-qcmn-epnall fi fi fi - [[ -z "${QC_JSON_ZDC:-}" ]] && has_processing_step ZDC_RECO && QC_JSON_ZDC=consul://o2/components/qc/ANY/any/zdc-rec-epn + [[ -z "${QC_JSON_ZDC:-}" ]] && has_processing_step ZDC_RECO && QC_JSON_ZDC=apricot://o2/components/qc/ANY/any/zdc-full-qcmn if [[ -z "${QC_JSON_MCH:-}" ]]; then if has_detector MCH && has_processing_step MCH_RECO; then if has_track_source "MCH-MID"; then - QC_JSON_MCH=consul://o2/components/qc/ANY/any/mch-qcmn-epn-full-track-matching + QC_JSON_MCH=apricot://o2/components/qc/ANY/any/mch-qcmn-epn-full-track-matching else - QC_JSON_MCH=consul://o2/components/qc/ANY/any/mch-qcmn-epn-full + QC_JSON_MCH=apricot://o2/components/qc/ANY/any/mch-qcmn-epn-full fi else - QC_JSON_MCH=consul://o2/components/qc/ANY/any/mch-qcmn-epn-digits + QC_JSON_MCH=apricot://o2/components/qc/ANY/any/mch-qcmn-epn-digits fi fi if [[ -z "${QC_JSON_MID:-}" ]]; then if has_detector MID && has_processing_step MID_RECO; then - QC_JSON_MID=consul://o2/components/qc/ANY/any/mid-full-qcmn + QC_JSON_MID=apricot://o2/components/qc/ANY/any/mid-full-qcmn else - QC_JSON_MID=consul://o2/components/qc/ANY/any/mid-flp_raw-epn_digits-qcmn + QC_JSON_MID=apricot://o2/components/qc/ANY/any/mid-flp_raw-epn_digits-qcmn fi fi - [[ -z "${QC_JSON_CPV:-}" ]] && QC_JSON_CPV=consul://o2/components/qc/ANY/any/cpv-physics-qcmn-epn - [[ -z "${QC_JSON_TRD:-}" ]] && QC_JSON_TRD=consul://o2/components/qc/ANY/any/trd-full-qcmn - [[ -z "${QC_JSON_PHS:-}" ]] && QC_JSON_PHS=consul://o2/components/qc/ANY/any/phos-raw-clusters-epn - [[ -z "${QC_JSON_GLO_PRIMVTX:-}" ]] && QC_JSON_GLO_PRIMVTX=consul://o2/components/qc/ANY/any/glo-vtx-qcmn-epn - [[ -z "${QC_JSON_GLO_ITSTPC:-}" ]] && QC_JSON_GLO_ITSTPC=consul://o2/components/qc/ANY/any/glo-itstpc-mtch-qcmn-epn + [[ -z "${QC_JSON_CPV:-}" ]] && QC_JSON_CPV=apricot://o2/components/qc/ANY/any/cpv-physics-qcmn-epn + [[ -z "${QC_JSON_TRD:-}" ]] && QC_JSON_TRD=apricot://o2/components/qc/ANY/any/trd-full-qcmn + [[ -z "${QC_JSON_PHS:-}" ]] && QC_JSON_PHS=apricot://o2/components/qc/ANY/any/phos-raw-clusters-epn + [[ -z "${QC_JSON_CTP:-}" ]] && QC_JSON_CTP=apricot://o2/components/qc/ANY/any/ctp-raw-qc-epn + [[ -z "${QC_JSON_GLO_PRIMVTX:-}" ]] && QC_JSON_GLO_PRIMVTX=apricot://o2/components/qc/ANY/any/glo-vtx-qcmn-epn + [[ -z "${QC_JSON_GLO_ITSTPC:-}" ]] && QC_JSON_GLO_ITSTPC=apricot://o2/components/qc/ANY/any/glo-itstpc-mtch-qcmn-epn if [[ -z "${QC_JSON_TOF_MATCH:-}" ]]; then if has_tof_matching_source ITS-TPC && has_tof_matching_source ITS-TPC-TRD; then - QC_JSON_TOF_MATCH=consul://o2/components/qc/ANY/any/tof-qcmn-match-itstpctrdtof + QC_JSON_TOF_MATCH=apricot://o2/components/qc/ANY/any/tof-qcmn-match-itstpctrdtof elif has_tof_matching_source ITS-TPC; then - QC_JSON_TOF_MATCH=consul://o2/components/qc/ANY/any/tof-qcmn-match-itstpctof + QC_JSON_TOF_MATCH=apricot://o2/components/qc/ANY/any/tof-qcmn-match-itstpctof fi fi + if has_detectors_reco MFT MCH MID && has_matching_qc MFTMCH && has_matching_qc MCHMID; then + [[ -z "${QC_JSON_GLO_MFTMCH:-}" ]] && QC_JSON_GLO_MFTMCH=apricot://o2/components/qc/ANY/any/glo-mftmchmid-mtch-qcmn-epn + elif has_detectors_reco MFT MCH && has_matching_qc MFTMCH; then + [[ -z "${QC_JSON_GLO_MFTMCH:-}" ]] && QC_JSON_GLO_MFTMCH=apricot://o2/components/qc/ANY/any/glo-mftmch-mtch-qcmn-epn + elif has_detectors_reco MCH MID && has_matching_qc MCHMID; then + [[ -z "${QC_JSON_GLO_MCHMID:-}" ]] && QC_JSON_GLO_MCHMID=apricot://o2/components/qc/ANY/any/glo-mchmid-mtch-qcmn-epn + fi + if has_processing_step ENTROPY_ENCODER && [[ ! -z "$WORKFLOW_DETECTORS_CTF" ]] && [[ $WORKFLOW_DETECTORS_CTF != "NONE" ]] && has_detector CTP; then + [[ -z "${QC_JSON_CTF_SIZE:-}" ]] && QC_JSON_CTF_SIZE=apricot://o2/components/qc/ANY/any/glo-qc-data-size + fi if [[ "${GEN_TOPO_DEPLOYMENT_TYPE:-}" == "ALICE_STAGING" ]]; then [[ -z "${QC_JSON_GLOBAL:-}" ]] && QC_JSON_GLOBAL=$O2DPG_ROOT/DATA/production/qc-sync/qc-global-epn-staging.json # this must be last else @@ -146,6 +173,13 @@ elif [[ -z ${QC_JSON_FROM_OUTSIDE:-} ]]; then QC_JSON_TOF_MATCH=$O2DPG_ROOT/DATA/production/qc-sync/itstpctof.json fi fi + if has_detectors_reco MFT MCH MID && has_matching_qc MFTMCH && has_matching_qc MCHMID; then + [[ -z "${QC_JSON_GLO_MFTMCH:-}" ]] && QC_JSON_GLO_MFTMCH=$O2DPG_ROOT/DATA/production/qc-sync/glo-mftmchmid-mtch-qcmn-epn.json + elif has_detectors_reco MFT MCH && has_matching_qc MFTMCH; then + [[ -z "${QC_JSON_GLO_MFTMCH:-}" ]] && QC_JSON_GLO_MFTMCH=$O2DPG_ROOT/DATA/production/qc-sync/glo-mftmch-mtch-qcmn-epn.json + elif has_detectors_reco MCH MID && has_matching_qc MCHMID; then + [[ -z "${QC_JSON_GLO_MCHMID:-}" ]] && QC_JSON_GLO_MCHMID=$O2DPG_ROOT/DATA/production/qc-sync/glo-mchmid-mtch-qcmn-epn.json + fi [[ -z "${QC_JSON_GLOBAL:-}" ]] && QC_JSON_GLOBAL=$O2DPG_ROOT/DATA/production/qc-sync/qc-global.json # this must be last QC_CONFIG_OVERRIDE+="qc.config.conditionDB.url=${DPL_CONDITION_BACKEND:-http://alice-ccdb.cern.ch};" @@ -154,11 +188,18 @@ elif [[ -z ${QC_JSON_FROM_OUTSIDE:-} ]]; then [[ -z "${QC_JSON_ITS:-}" ]] && QC_JSON_ITS=$O2DPG_ROOT/DATA/production/qc-async/its.json [[ -z "${QC_JSON_MFT:-}" ]] && QC_JSON_MFT=$O2DPG_ROOT/DATA/production/qc-async/mft.json [[ -z "${QC_JSON_TOF:-}" ]] && QC_JSON_TOF=$O2DPG_ROOT/DATA/production/qc-async/tof.json + [[ -z "${QC_JSON_HMP:-}" ]] && QC_JSON_HMP=$O2DPG_ROOT/DATA/production/qc-async/hmp.json [[ -z "${QC_JSON_FT0:-}" ]] && QC_JSON_FT0=$O2DPG_ROOT/DATA/production/qc-async/ft0.json [[ -z "${QC_JSON_FV0:-}" ]] && QC_JSON_FV0=$O2DPG_ROOT/DATA/production/qc-async/fv0.json [[ -z "${QC_JSON_FDD:-}" ]] && QC_JSON_FDD=$O2DPG_ROOT/DATA/production/qc-async/fdd.json [[ -z "${QC_JSON_MID:-}" ]] && QC_JSON_MID=$O2DPG_ROOT/DATA/production/qc-async/mid.json - [[ -z "${QC_JSON_ZDC:-}" ]] && has_processing_step ZDC_RECO && QC_JSON_ZDC=$O2DPG_ROOT/DATA/production/qc-async/zdc.json + if [[ -z "${QC_JSON_ZDC:-}" ]] && has_processing_step ZDC_RECO; then + if [[ "$BEAMTYPE" == "PbPb" ]]; then + QC_JSON_ZDC=$O2DPG_ROOT/DATA/production/qc-async/zdcPbPb.json + else + QC_JSON_ZDC=$O2DPG_ROOT/DATA/production/qc-async/zdc.json + fi + fi if [[ -z "${QC_JSON_EMC:-}" ]]; then if [[ "$BEAMTYPE" == "PbPb" ]]; then QC_JSON_EMC=$O2DPG_ROOT/DATA/production/qc-async/emc_PbPb.json @@ -176,6 +217,13 @@ elif [[ -z ${QC_JSON_FROM_OUTSIDE:-} ]]; then add_QC_JSON MCH_TRACKS $O2DPG_ROOT/DATA/production/qc-async/mch-tracks.json fi fi + if has_detectors_reco MFT MCH MID && has_matching_qc MFTMCH && has_matching_qc MCHMID; then + [[ -z "${QC_JSON_GLO_MFTMCH:-}" ]] && QC_JSON_GLO_MFTMCH=$O2DPG_ROOT/DATA/production/qc-async/mftmchmid-tracks.json + elif has_detectors_reco MFT MCH && has_matching_qc MFTMCH; then + [[ -z "${QC_JSON_GLO_MFTMCH:-}" ]] && QC_JSON_GLO_MFTMCH=$O2DPG_ROOT/DATA/production/qc-async/mftmch-tracks.json + elif has_detectors_reco MCH MID && has_matching_qc MCHMID; then + [[ -z "${QC_JSON_GLO_MCHMID:-}" ]] && QC_JSON_GLO_MCHMID=$O2DPG_ROOT/DATA/production/qc-async/mchmid-tracks.json + fi [[ -z "${QC_JSON_CPV:-}" ]] && QC_JSON_CPV=$O2DPG_ROOT/DATA/production/qc-async/cpv.json [[ -z "${QC_JSON_PHS:-}" ]] && QC_JSON_PHS=$O2DPG_ROOT/DATA/production/qc-async/phs.json [[ -z "${QC_JSON_TRD:-}" ]] && QC_JSON_TRD=$O2DPG_ROOT/DATA/production/qc-async/trd.json @@ -225,6 +273,70 @@ elif [[ -z ${QC_JSON_FROM_OUTSIDE:-} ]]; then if [[ $i == "PRIMVTX" ]] && ! has_detector_reco ITS; then continue; fi if [[ $i == "ITSTPC" ]] && ! has_detectors_reco ITS TPC; then continue; fi add_QC_JSON GLO_$i ${!DET_JSON_FILE} + + if [[ $i == "ITSTPC" ]]; then + LOCAL_FILENAME=${JSON_FILES//*\ /} + # replace the input sources depending on the detector compostition and matching detectors + ITSTPCMatchQuery="trackITSTPC:GLO/TPCITS/0;trackITSTPCABREFS:GLO/TPCITSAB_REFS/0;trackITSTPCABCLID:GLO/TPCITSAB_CLID/0;trackTPC:TPC/TRACKS;trackTPCClRefs:TPC/CLUSREFS/0;trackITS:ITS/TRACKS/0;trackITSROF:ITS/ITSTrackROF/0;trackITSClIdx:ITS/TRACKCLSID/0;alpparITS:ITS/ALPIDEPARAM/0?lifetime=condition&ccdb-path=ITS/Config/AlpideParam;SVParam:GLO/SVPARAM/0?lifetime=condition&ccdb-path=GLO/Config/SVertexerParam" + TRACKSOURCESK0="ITS,TPC,ITS-TPC" + if [[ $BEAMTYPE != "cosmic" ]] && (has_processing_step MATCH_SECVTX || has_detector_matching SECVTX); then + if [[ $SYNCMODE == 1 ]] || [[ $EPNSYNCMODE == 1 ]]; then + HAS_K0_ENABLED=$(jq -r .qc.tasks.MTCITSTPC.taskParameters.doK0QC "${LOCAL_FILENAME}") + else + HAS_K0_ENABLED=$(jq -r .qc.tasks.GLOMatchTrITSTPC.taskParameters.doK0QC "${LOCAL_FILENAME}") + fi + if [[ $HAS_K0_ENABLED == "true" ]]; then + ITSTPCMatchQuery+=";p2decay3body:GLO/PVTX_3BODYREFS/0;decay3body:GLO/DECAYS3BODY/0;decay3bodyIdx:GLO/DECAYS3BODY_IDX/0;p2cascs:GLO/PVTX_CASCREFS/0;cascs:GLO/CASCS/0;cascsIdx:GLO/CASCS_IDX/0;p2v0s:GLO/PVTX_V0REFS/0;v0s:GLO/V0S/0;v0sIdx:GLO/V0S_IDX/0;pvtx_tref:GLO/PVTX_TRMTCREFS/0;pvtx_trmtc:GLO/PVTX_TRMTC/0;pvtx:GLO/PVTX/0;clusTPCoccmap:TPC/TPCOCCUPANCYMAP/0;clusTPC:TPC/CLUSTERNATIVE;clusTPCshmap:TPC/CLSHAREDMAP/0;trigTPC:TPC/TRIGGERWORDS/0" + if has_secvtx_source ITS-TPC-TRD; then + ITSTPCMatchQuery+=";trigITSTPCTRD:TRD/TRGREC_ITSTPC/0;trackITSTPCTRD:TRD/MATCH_ITSTPC/0" + TRACKSOURCESK0+=",ITS-TPC-TRD" + fi + if has_secvtx_source ITS-TPC-TOF; then + ITSTPCMatchQuery+=";matchITSTPCTOF:TOF/MTC_ITSTPC/0" + TRACKSOURCESK0+=",ITS-TPC-TOF" + fi + if has_secvtx_source ITS-TPC-TRD-TOF; then + ITSTPCMatchQuery+=";matchITSTPCTRDTOF:TOF/MTC_ITSTPCTRD/0" + TRACKSOURCESK0+=",ITS-TPC-TRD-TOF" + fi + if has_secvtx_source TPC-TRD; then + ITSTPCMatchQuery+=";trigTPCTRD:TRD/TRGREC_TPC/0;trackTPCTRD:TRD/MATCH_TPC/0" + TRACKSOURCESK0+=",TPC-TRD" + fi + if has_secvtx_source TPC-TOF; then + ITSTPCMatchQuery+=";matchTPCTOF:TOF/MTC_TPC/0;trackTPCTOF:TOF/TOFTRACKS_TPC/0" + TRACKSOURCESK0+=",TPC-TOF" + fi + if has_secvtx_source TPC-TRD-TOF; then + ITSTPCMatchQuery+=";matchTPCTRDTOF/TOF/MTC_TPCTRD/0" + TRACKSOURCESK0+=",TPC-TRD-TOF" + fi + if has_detector_in_secvtx_sources TOF; then + ITSTPCMatchQuery+=";tofcluster:TOF/CLUSTERS/0" + has_secvtx_source TOF && TRACKSOURCESK0+=",TOF" + fi + if has_secvtx_source TRD; then + TRACKSOURCESK0+=",TRD" + fi + fi + TEMP_FILE=$(mktemp "${GEN_TOPO_WORKDIR:+$GEN_TOPO_WORKDIR/}${i}"_XXXXXXX) + if [[ $SYNCMODE == 1 ]] || [[ $EPNSYNCMODE == 1 ]]; then + cat "${LOCAL_FILENAME}" | jq "(.dataSamplingPolicies[] | select(.id == \"ITSTPCmSampK0\") | .query) = \"$ITSTPCMatchQuery\" | .qc.tasks.MTCITSTPC.taskParameters.trackSourcesK0 = \"$TRACKSOURCESK0\"" >"$TEMP_FILE" + else + cat "${LOCAL_FILENAME}" | jq ".qc.tasks.GLOMatchTrITSTPC.dataSource.query = \"$ITSTPCMatchQuery\" | .qc.tasks.GLOMatchTrITSTPC.taskParameters.trackSourcesK0 = \"$TRACKSOURCESK0\"" >"$TEMP_FILE" + fi + else + # we need to force that the K0s part is disabled + TEMP_FILE=$(mktemp "${GEN_TOPO_WORKDIR:+$GEN_TOPO_WORKDIR/}${i}"_XXXXXXX) + if [[ $SYNCMODE == 1 ]] || [[ $EPNSYNCMODE == 1 ]]; then + cat "${LOCAL_FILENAME}" | jq "(.dataSamplingPolicies[] | select(.id == \"ITSTPCmSampK0\") | .query) = \"$ITSTPCMatchQuery\" | .qc.tasks.MTCITSTPC.taskParameters.trackSourcesK0 = \"$TRACKSOURCESK0\" | .qc.tasks.MTCITSTPC.taskParameters.doK0QC = \"false\"" >"$TEMP_FILE" + else + cat "${LOCAL_FILENAME}" | jq ".qc.tasks.GLOMatchTrITSTPC.dataSource.query = \"$ITSTPCMatchQuery\" | .qc.tasks.GLOMatchTrITSTPC.taskParameters.trackSourcesK0 = \"$TRACKSOURCESK0\" | .qc.tasks.GLOMatchTrITSTPC.taskParameters.doK0QC = \"false\"" >"$TEMP_FILE" + fi + fi + JSON_FILES=${JSON_FILES/$LOCAL_FILENAME/$TEMP_FILE} + JSON_TEMP_FILES+=("$TEMP_FILE") + fi fi done @@ -237,6 +349,12 @@ elif [[ -z ${QC_JSON_FROM_OUTSIDE:-} ]]; then fi done + # CTF QC + if [[ ! -z "${QC_JSON_CTF_SIZE:-}" ]]; then + add_QC_JSON GLO_CTF ${QC_JSON_CTF_SIZE} +# add_pipe_separated QC_DETECTOR_CONFIG_OVERRIDE '.qc.tasks.CTFSize.taskParameters.detectors=\"${WORKFLOW_DETECTORS}\"' + fi + # arbitrary extra QC if [[ ! -z "${QC_JSON_EXTRA:-}" ]]; then add_QC_JSON EXTRA ${QC_JSON_EXTRA} @@ -275,6 +393,11 @@ elif [[ -z ${QC_JSON_FROM_OUTSIDE:-} ]]; then fi MERGED_JSON_FILENAME=$(realpath $MERGED_JSON_FILENAME) + # Clean up: delete the temporary files after use + for tf in "${JSON_TEMP_FILES[@]}"; do + rm -f "$tf" + done + if [[ "${QC_REDIRECT_MERGER_TO_LOCALHOST:-}" == "1" ]]; then sed -i.bak -E 's/( *)"remoteMachine" *: *".*"(,?) *$/\1"remoteMachine": "127.0.0.1"\2/' $MERGED_JSON_FILENAME unlink $MERGED_JSON_FILENAME.bak @@ -291,6 +414,7 @@ elif [[ -z ${QC_JSON_FROM_OUTSIDE:-} ]]; then fi [[ $EPNSYNCMODE == 1 && $NUMAGPUIDS == 1 ]] && QC_CONFIG_OVERRIDE+="qc.config.infologger.filterDiscardFile=../../qc-_ID_-${NUMAID}.log;" +[[ $EPNSYNCMODE == 0 ]] && QC_CONFIG+=" --no-infologger" [[ ! -z $QC_CONFIG_OVERRIDE ]] && QC_CONFIG+=" --override-values \"$QC_CONFIG_OVERRIDE\"" @@ -312,7 +436,9 @@ if [[ ! -z "${QC_JSON_FROM_OUTSIDE:-}" ]]; then QC_CONFIG_PARAM="--local-batch=QC.root" fi fi + add_W o2-qc "--config json://$QC_JSON_FROM_OUTSIDE ${QC_CONFIG_PARAM} ${QC_CONFIG}" + fi if [[ ! -z ${GEN_TOPO_QC_JSON_FILE:-} ]]; then diff --git a/DATA/production/standalone-calibration.desc b/DATA/production/standalone-calibration.desc index 306cdc855..169f2ff9b 100644 --- a/DATA/production/standalone-calibration.desc +++ b/DATA/production/standalone-calibration.desc @@ -1,3 +1,5 @@ +EMC-pedestal-calibration: "O2PDPSuite" reco,1,1,"production/calib/emc-pedestal-processing.sh" calib,1,"production/calib/emc-pedestal-aggregator.sh" + FT0-time-offset-calibration: "O2PDPSuite" reco,5,5,"production/calib/ft0-timeoffset-processing.sh" calib,1,"production/calib/ft0-timeoffset-aggregator.sh" ITS-noise-calibration: "O2PDPSuite" reco,20,20,"NITSDECTHREADS=4 NITSDECTPIPELINES=6 production/calib/its-noise-processing.sh" calib,20,"NTHREADSACC=4 NTHREADSNORM=16 NITSACCPIPELINES=16 production/calib/its-noise-aggregator.sh" @@ -16,13 +18,17 @@ ITS-thr-full: "O2PDPSuite" reco,80,80,"RUNTYPE_ITS=thrfull production/calib/its- ITS-digital: "O2PDPSuite" reco,40,40,"RUNTYPE_ITS=digital production/calib/its-threshold-processing.sh" calib,40,"RUNTYPE_ITS=digital production/calib/its-threshold-aggregator.sh" +ITS-digital-no-mask: "O2PDPSuite" reco,40,40,"RUNTYPE_ITS=digitalnomask production/calib/its-threshold-processing.sh" calib,40,"RUNTYPE_ITS=digitalnomask production/calib/its-threshold-aggregator.sh" + ITS-pulselength: "O2PDPSuite" reco,40,40,"RUNTYPE_ITS=pulselength production/calib/its-threshold-processing.sh" calib,40,"RUNTYPE_ITS=pulselength production/calib/its-threshold-aggregator.sh" ITS-tot-1row: "O2PDPSuite" reco,40,40,"RUNTYPE_ITS=tot1row production/calib/its-threshold-processing.sh" calib,40,"RUNTYPE_ITS=tot1row production/calib/its-threshold-aggregator.sh" ITS-tot-fullfast: "O2PDPSuite" reco,40,40,"RUNTYPE_ITS=totfullfast production/calib/its-threshold-processing.sh" calib,40,"RUNTYPE_ITS=totfullfast production/calib/its-threshold-aggregator.sh" -ITS-vresetd: "O2PDPSuite" reco,5,5,"RUNTYPE_ITS=vresetd production/calib/its-threshold-processing.sh" calib,5,"RUNTYPE_ITS=vresetd production/calib/its-threshold-aggregator.sh" +ITS-vresetd-1d: "O2PDPSuite" reco,5,5,"RUNTYPE_ITS=vresetd-1d production/calib/its-threshold-processing.sh" calib,5,"RUNTYPE_ITS=vresetd-1d production/calib/its-threshold-aggregator.sh" + +ITS-vresetd-2d: "O2PDPSuite" reco,5,5,"RUNTYPE_ITS=vresetd-2d production/calib/its-threshold-processing.sh" calib,5,"RUNTYPE_ITS=vresetd-2d production/calib/its-threshold-aggregator.sh" TOF-diagnostic-calibration: "O2PDPSuite" reco,10,10,"SHMSIZE=120376524800 production/calib/tof-standalone-reco.sh" calib,4,"production/calib/tof-diagn-aggregator.sh" @@ -34,9 +40,9 @@ TPC-pulser-long: "O2PDPSuite" reco,1,1," production/calib/tpc-pulser-long.sh" TPC-pedestal: "O2PDPSuite" reco,1,1," production/calib/tpc-pedestal.sh" -TPC-laser: "O2PDPSuite" reco,1,1,"SHMSIZE=$((128 << 30)) GPUTYPE=HIP GPUMEMSIZE=$(( 24 << 30 )) HOSTMEMSIZE=$(( 5 << 30 )) production/calib/tpc-laser.sh" +TPC-laser: "O2PDPSuite" reco,1,1,"SHMSIZE=$((128 << 30)) production/calib/tpc-laser.sh" -TPC-laser-multi: "O2PDPSuite" reco,10,10,"SHMSIZE=$((128 << 30)) GPUTYPE=HIP GPUMEMSIZE=$(( 24 << 30 )) HOSTMEMSIZE=$(( 5 << 30 )) production/calib/tpc-laser-filter.sh" calib,1,"production/calib/tpc-laser-aggregator.sh" +TPC-laser-multi: "O2PDPSuite" reco,10,10,"SHMSIZE=$((128 << 30)) production/calib/tpc-laser-filter.sh" calib,1,"production/calib/tpc-laser-aggregator.sh" MFT-noise-calibration: "O2PDPSuite" reco,20,20,"production/calib/mft-noise-processing.sh" calib,20,"production/calib/mft-noise-aggregator.sh" diff --git a/DATA/production/workflow-multiplicities.sh b/DATA/production/workflow-multiplicities.sh index b3d4de384..213eeb2e1 100644 --- a/DATA/production/workflow-multiplicities.sh +++ b/DATA/production/workflow-multiplicities.sh @@ -11,10 +11,19 @@ SOURCE_GUARD_MULTIPLICITIES=1 : ${NMFTTHREADS:=2} -: ${SVERTEX_THREADS:=$(( $SYNCMODE == 1 ? 1 : 2 ))} +: ${SVERTEX_THREADS:=2} +if [[ $SYNCMODE == 1 ]]; then : ${ITSTRK_THREADS:=1} : ${ITSTPC_THREADS:=1} +else +: ${ITSTRK_THREADS:=2} +: ${ITSTPC_THREADS:=2} +fi + +: ${TPCTIMESERIES_THREADS:=1} + +: ${TOFMATCH_THREADS:=1} : ${HIGH_RATE_PP:=0} @@ -28,6 +37,8 @@ if [[ $SYNCMODE == 1 ]]; then NTRDTRKTHREADS=1; else NTRDTRKTHREADS=; fi : ${NGPURECOTHREADS:=-1} # -1 = auto-detect +[[ ! -z $RECO_NUM_NODES_WORKFLOW ]] && RECO_NUM_NODES_WORKFLOW_CMP=$((($RECO_NUM_NODES_WORKFLOW > 15 ? ($RECO_NUM_NODES_WORKFLOW < 230 ? $RECO_NUM_NODES_WORKFLOW : 230) : 15) * ($NUMAGPUIDS != 0 ? 2 : 1))) # Limit the lower scaling factor, multiply by 2 if we have 2 NUMA domains + # --------------------------------------------------------------------------------------------------------------------- # Process multiplicities @@ -38,19 +49,47 @@ N_F_CTF=$MULTIPLICITY_FACTOR_CTFENCODERS N_TPCTRK=$NGPUS if [[ ! -z ${OPTIMIZED_PARALLEL_ASYNC:-} ]]; then # Tuned multiplicities for async processing - if [[ $OPTIMIZED_PARALLEL_ASYNC == "pp_8cpu" ]]; then - [[ -z $TIMEFRAME_RATE_LIMIT ]] && TIMEFRAME_RATE_LIMIT=3 + if [[ ${OPTIMIZED_PARALLEL_ASYNC_AUTO_SHM_LIMIT:-} == 1 ]]; then + [[ ! -z ${TIMEFRAME_RATE_LIMIT:-} ]] && unset TIMEFRAME_RATE_LIMIT + [[ ! -z ${SHMSIZE:-} ]] && unset SHMSIZE + fi + if [[ $OPTIMIZED_PARALLEL_ASYNC == "8cpu" || $OPTIMIZED_PARALLEL_ASYNC == "8cpu_NVIDIA" || $OPTIMIZED_PARALLEL_ASYNC == "8cpu_AMD" ]]; then + [[ -z ${TIMEFRAME_RATE_LIMIT:-} ]] && TIMEFRAME_RATE_LIMIT=3 + [[ -z ${SHMSIZE:-} ]] && SHMSIZE=16000000000 + if [[ $OPTIMIZED_PARALLEL_ASYNC == "8cpu_NVIDIA" ]]; then + NGPUS=1 + GPUTYPE=CUDA + GPUMEMSIZE=$((25 << 30)) + N_TPCTRK=$NGPUS + elif [[ $OPTIMIZED_PARALLEL_ASYNC == "8cpu_AMD" ]]; then + NGPUS=1 + GPUTYPE=HIP + GPUMEMSIZE=$((25 << 30)) + N_TPCTRK=$NGPUS + fi NGPURECOTHREADS=5 - elif [[ $OPTIMIZED_PARALLEL_ASYNC == "pp_16cpu" ]]; then - [[ -z $TIMEFRAME_RATE_LIMIT ]] && TIMEFRAME_RATE_LIMIT=8 - [[ -z $SHMSIZE ]] && SHMSIZE=22000000000 + if [[ $BEAMTYPE == "pp" ]]; then + if (( $(echo "$RUN_IR > 800000" | bc -l) )); then + TIMEFRAME_RATE_LIMIT=1 + elif (( $(echo "$RUN_IR < 50000" | bc -l) )); then + TIMEFRAME_RATE_LIMIT=6 + else + TIMEFRAME_RATE_LIMIT=3 + fi + else # PbPb + TIMEFRAME_RATE_LIMIT=2 + SVERTEX_THREADS=5 + fi + elif [[ $OPTIMIZED_PARALLEL_ASYNC == "16cpu" ]]; then + [[ -z ${TIMEFRAME_RATE_LIMIT:-} ]] && TIMEFRAME_RATE_LIMIT=8 + [[ -z ${SHMSIZE:-} ]] && SHMSIZE=22000000000 NGPURECOTHREADS=9 NTRDTRKTHREADS=3 ITSTRK_THREADS=3 ITSTPC_THREADS=3 elif [[ $OPTIMIZED_PARALLEL_ASYNC == "pp_64cpu" ]]; then - [[ -z $TIMEFRAME_RATE_LIMIT ]] && TIMEFRAME_RATE_LIMIT=32 - [[ -z $SHMSIZE ]] && SHMSIZE=90000000000 + [[ -z ${TIMEFRAME_RATE_LIMIT:-} ]] && TIMEFRAME_RATE_LIMIT=32 + [[ -z ${SHMSIZE:-} ]] && SHMSIZE=90000000000 NGPURECOTHREADS=12 NTRDTRKTHREADS=3 ITSTRK_THREADS=3 @@ -62,28 +101,51 @@ if [[ ! -z ${OPTIMIZED_PARALLEL_ASYNC:-} ]]; then N_MCHCL=3 N_TOFMATCH=2 N_TPCENTDEC=3 - elif [[ $OPTIMIZED_PARALLEL_ASYNC == "pp_1gpu" ]]; then - [[ -z $TIMEFRAME_RATE_LIMIT ]] && TIMEFRAME_RATE_LIMIT=8 - [[ -z $SHMSIZE ]] && SHMSIZE=20000000000 - N_TOFMATCH=2 - N_MCHCL=3 - N_TPCENTDEC=2 - N_TPCITS=3 + elif [[ $OPTIMIZED_PARALLEL_ASYNC == "pp_1gpu_EPN" || $OPTIMIZED_PARALLEL_ASYNC == "pp_1gpu_EPN_unoptimized" ]]; then + [[ -z ${TIMEFRAME_RATE_LIMIT:-} ]] && TIMEFRAME_RATE_LIMIT=8 + [[ -z ${SHMSIZE:-} ]] && SHMSIZE=30000000000 + NGPUS=1 + GPUTYPE=HIP + GPUMEMSIZE=$((25 << 30)) N_MCHTRK=2 - N_ITSTRK=3 - NGPURECOTHREADS=8 + N_TPCTRK=$NGPUS + if [[ $OPTIMIZED_PARALLEL_ASYNC == "pp_1gpu_EPN" ]]; then + N_TOFMATCH=2 + N_MCHCL=3 + N_TPCENTDEC=2 + N_TPCITS=3 + N_ITSTRK=3 + NGPURECOTHREADS=8 + else + NGPURECOTHREADS=4 + fi NTRDTRKTHREADS=3 ITSTRK_THREADS=2 ITSTPC_THREADS=2 - elif [[ $OPTIMIZED_PARALLEL_ASYNC == "pp_4gpu" ]]; then - [[ -z $TIMEFRAME_RATE_LIMIT ]] && TIMEFRAME_RATE_LIMIT=45 - [[ -z $SHMSIZE ]] && SHMSIZE=100000000000 + elif [[ $OPTIMIZED_PARALLEL_ASYNC == "pp_gpu_NERSC" || $OPTIMIZED_PARALLEL_ASYNC == "pp_4gpu_EPN" ]]; then + if [[ -z ${TIMEFRAME_RATE_LIMIT:-} ]]; then + if [[ ! -z ${ALIEN_JDL_LPMANCHORYEAR} && ${ALIEN_JDL_LPMANCHORYEAR} -lt 2023 ]]; then + TIMEFRAME_RATE_LIMIT=45 + else + TIMEFRAME_RATE_LIMIT=120 + fi + fi + [[ -z ${SHMSIZE:-} ]] && SHMSIZE=100000000000 + if [[ $OPTIMIZED_PARALLEL_ASYNC == "pp_gpu_NERSC" ]]; then + NGPUS=1 + GPUTYPE=CUDA + else + NGPUS=4 + GPUTYPE=HIP + fi + GPUMEMSIZE=$((25 << 30)) NGPURECOTHREADS=8 NTRDTRKTHREADS=2 ITSTRK_THREADS=2 ITSTPC_THREADS=2 - NGPUS=4 - N_TPCTRK=4 + SVERTEX_THREADS=4 + TPCTIMESERIES_THREADS=2 + N_TPCTRK=$NGPUS N_FWDMATCH=2 N_PRIMVTXMATCH=1 N_PRIMVTX=1 @@ -97,25 +159,44 @@ if [[ ! -z ${OPTIMIZED_PARALLEL_ASYNC:-} ]]; then N_TPCENTDEC=6 N_TPCITS=12 N_ITSTRK=12 - elif [[ $OPTIMIZED_PARALLEL_ASYNC == "PbPb_4gpu" ]]; then - [[ -z $TIMEFRAME_RATE_LIMIT ]] && TIMEFRAME_RATE_LIMIT=20 - [[ -z $SHMSIZE ]] && SHMSIZE=128000000000 # SHM_LIMIT 3/4 + N_ITSCL=2 + export DPL_SMOOTH_RATE_LIMITING=1 + elif [[ $OPTIMIZED_PARALLEL_ASYNC == "PbPb_gpu_NERSC" || $OPTIMIZED_PARALLEL_ASYNC == "PbPb_4gpu_EPN" ]]; then + [[ -z ${TIMEFRAME_RATE_LIMIT:-} ]] && TIMEFRAME_RATE_LIMIT=35 + [[ -z ${SHMSIZE:-} ]] && SHMSIZE=100000000000 # SHM_LIMIT 3/4 + [[ -z ${TIMEFRAME_SHM_LIMIT:-} ]] && TIMEFRAME_SHM_LIMIT=$(($SHMSIZE / 3)) + if [[ $OPTIMIZED_PARALLEL_ASYNC == "PbPb_gpu_NERSC" ]]; then + NGPUS=1 + GPUTYPE=CUDA + else + NGPUS=4 + GPUTYPE=HIP + fi + GPUMEMSIZE=$((25 << 30)) NGPURECOTHREADS=8 - NTRDTRKTHREADS=4 - NGPUS=4 - N_TPCTRK=4 + NTRDTRKTHREADS=8 + ITSTRK_THREADS=5 + ITSTPC_THREADS=3 + SVERTEX_THREADS=20 + TOFMATCH_THREADS=2 + N_SECVTX=2 + N_TPCTRK=$NGPUS # time in s: pvtx 16, tof 30, trd 82 itstpc 53 its 200 mfttr 30 tpcent 23 hmp-clus 40 (25.11.22) - N_TPCENTDEC=$(math_max $((2 * $NGPUS / 4)) 1) - N_ITSTRK=$(math_max $((10 * $NGPUS / 4)) 1) - N_TPCITS=$(math_max $((4 * $NGPUS / 4)) 1) + N_TPCENTDEC=$(math_max $((4 * $NGPUS / 4)) 1) + N_ITSTRK=$(math_max $((12 * $NGPUS / 4)) 1) + N_TPCITS=$(math_max $((5 * $NGPUS / 4)) 1) N_MFTTRK=$(math_max $((3 * $NGPUS / 4)) 1) - N_TRDTRK=$(math_max $((7 * $NGPUS / 4)) 1) - N_TOFMATCH=$(math_max $((3 * $NGPUS / 4)) 1) + N_TRDTRK=$(math_max $((5 * $NGPUS / 4)) 1) + N_TOFMATCH=$(math_max $((5 * $NGPUS / 4)) 1) N_HMPCLUS=$(math_max $((3 * $NGPUS / 4)) 1) - CONFIG_EXTRA_PROCESS_o2_its_reco_workflow="ITSVertexerParam.nThreads=3;ITSCATrackerParam.nThreads=3;" - N_MCHCL=2 + N_ITSCL=4 + N_AODPROD=3 + N_MCHCL=9 + N_HMPMATCH=1 + N_MCHTRK=7 N_PRIMVTXMATCH=2 N_PRIMVTX=3 + # export DPL_SMOOTH_RATE_LIMITING=1 elif [[ $OPTIMIZED_PARALLEL_ASYNC == "PbPb_64cpu" ]]; then NGPURECOTHREADS=6 NTRDTRKTHREADS=2 @@ -126,22 +207,30 @@ if [[ ! -z ${OPTIMIZED_PARALLEL_ASYNC:-} ]]; then N_MCHTRK=1 N_TOFMATCH=9 N_TPCTRK=6 + elif [[ $OPTIMIZED_PARALLEL_ASYNC == "keep_root" ]]; then + TIMEFRAME_RATE_LIMIT=4 + SHMSIZE=30000000000 else echo "Invalid optimized setting '$OPTIMIZED_PARALLEL_ASYNC'" 1>&2 exit 1 fi + if [[ ${OPTIMIZED_PARALLEL_ASYNC_AUTO_SHM_LIMIT:-} == 1 && ${EPN_NODE_MI100:-} == 1 ]]; then + TIMEFRAME_RATE_LIMIT=$(($TIMEFRAME_RATE_LIMIT * 2)) + SHMSIZE=$(($SHMSIZE * 2)) + EPN_GLOBAL_SCALING="3 / 2" + fi elif [[ $EPNPIPELINES != 0 ]]; then NTRDTRKTHREADS=2 ITSTRK_THREADS=2 ITSTPC_THREADS=2 - RECO_NUM_NODES_WORKFLOW_CMP=$((($RECO_NUM_NODES_WORKFLOW > 15 ? ($RECO_NUM_NODES_WORKFLOW < 230 ? $RECO_NUM_NODES_WORKFLOW : 230) : 15) * ($NUMAGPUIDS != 0 ? 2 : 1))) # Limit the lower scaling factor, multiply by 2 if we have 2 NUMA domains # Tuned multiplicities for sync pp / Pb-Pb processing - if [[ $BEAMTYPE == "pp" ]]; then + if [[ $BEAMTYPE == "pp" || $LIGHTNUCLEI == "1" ]]; then N_ITSRAWDEC=$(math_max $((6 * $EPNPIPELINES * $NGPUS / 4)) 1) N_MFTRAWDEC=$(math_max $((2 * $EPNPIPELINES * $NGPUS / 4)) 1) if [[ "${GEN_TOPO_AUTOSCALE_PROCESSES:-}" == "1" && $RUNTYPE == "PHYSICS" ]]; then N_MCHCL=$(math_max $((6 * 100 / $RECO_NUM_NODES_WORKFLOW_CMP)) 1) fi + N_MCHRAWDEC=2 if [[ "$HIGH_RATE_PP" == "1" ]]; then N_TPCITS=$(math_max $((5 * $EPNPIPELINES * $NGPUS / 4)) 1) N_TPCENT=$(math_max $((4 * $EPNPIPELINES * $NGPUS / 4)) 1) @@ -158,6 +247,7 @@ elif [[ $EPNPIPELINES != 0 ]]; then else if [[ $BEAMTYPE == "PbPb" ]]; then N_ITSTRK=$(math_max $((2 * $EPNPIPELINES * $NGPUS / 4)) 1) + N_MCHRAWDEC=2 elif [[ $BEAMTYPE == "cosmic" ]]; then N_ITSTRK=$(math_max $((4 * $EPNPIPELINES * $NGPUS / 4)) 1) fi @@ -179,7 +269,7 @@ elif [[ $EPNPIPELINES != 0 ]]; then N_ITSRAWDEC=$(math_max $((3 * 60 / $RECO_NUM_NODES_WORKFLOW_CMP)) ${N_ITSRAWDEC:-1}) # This means, if we have 60 EPN nodes, we need at least 3 ITS RAW decoders (will be scaled down by a factor of two automatically if we have 2 NUMA domains) N_MFTRAWDEC=$(math_max $((3 * 60 / $RECO_NUM_NODES_WORKFLOW_CMP)) ${N_MFTRAWDEC:-1}) if [[ $RUNTYPE == "PHYSICS" || $RUNTYPE == "COSMICS" ]]; then - if [[ $BEAMTYPE == "pp" ]]; then + if [[ $BEAMTYPE == "pp" || $LIGHTNUCLEI == "1" ]]; then N_ITSTRK=$(math_max $((9 * 200 / $RECO_NUM_NODES_WORKFLOW_CMP)) ${N_ITSTRK:-1}) elif [[ $BEAMTYPE == "cosmic" ]]; then N_ITSTRK=$(math_max $((5 * 200 / $RECO_NUM_NODES_WORKFLOW_CMP)) ${N_ITSTRK:-1}) @@ -199,7 +289,7 @@ if [[ -z ${EVE_NTH_EVENT:-} ]]; then EVE_NTH_EVENT=2 elif [[ "$HIGH_RATE_PP" == "1" ]]; then EVE_NTH_EVENT=10 - elif [[ $BEAMTYPE == "pp" && "${ED_VERTEX_MODE:-}" == "1" ]]; then + elif [[ $BEAMTYPE == "pp" || $LIGHTNUCLEI == "1" ]]; then EVE_NTH_EVENT=$((4 * 250 / $RECO_NUM_NODES_WORKFLOW_CMP)) else # COSMICS / TECHNICALS / ... EVE_NTH_EVENT=1 @@ -211,13 +301,27 @@ if [[ "$HIGH_RATE_PP" == "1" ]]; then : ${CUT_RANDOM_FRACTION_ITS:=0.97} elif [[ $BEAMTYPE == "PbPb" ]]; then : ${CUT_RANDOM_FRACTION_ITS:=-1} - : ${CUT_MULT_MIN_ITS:=100} - : ${CUT_MULT_MAX_ITS:=200} + : ${CUT_MULT_MIN_ITS:=0} + : ${CUT_MULT_MAX_ITS:=400} : ${CUT_MULT_VTX_ITS:=20} else : ${CUT_RANDOM_FRACTION_ITS:=0.95} fi -[[ $RUNTYPE != "COSMICS" ]] && : ${CUT_RANDOM_FRACTION_MCH:=0.7} + +# Random data sampling fraction for MCH +if [[ $BEAMTYPE == "pp" || $LIGHTNUCLEI == "1" ]]; then + : ${CUT_RANDOM_FRACTION_MCH_WITH_ITS:=0.5} + : ${CUT_RANDOM_FRACTION_MCH_NO_ITS:=0.995} +elif [[ "$HIGH_RATE_PP" == "1" ]]; then + : ${CUT_RANDOM_FRACTION_MCH_WITH_ITS:=0.7} + : ${CUT_RANDOM_FRACTION_MCH_NO_ITS:=0.995} +elif [[ $BEAMTYPE == "PbPb" ]]; then + : ${CUT_RANDOM_FRACTION_MCH_WITH_ITS:=0.9} + : ${CUT_RANDOM_FRACTION_MCH_NO_ITS:=0.995} +else + : ${CUT_RANDOM_FRACTION_MCH_WITH_ITS:=0.99} + : ${CUT_RANDOM_FRACTION_MCH_NO_ITS:=0.99} +fi #if [[ "$HIGH_RATE_PP" == "1" ]]; then # Extra settings for HIGH_RATE_PP diff --git a/DATA/production/workflow-setup.sh b/DATA/production/workflow-setup.sh deleted file mode 100755 index 4a3b08834..000000000 --- a/DATA/production/workflow-setup.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash - -# This file is here until the references to it in O2 have been removed. -# The content has been moved to setenv.sh and gen_topo_helper_functions.sh diff --git a/DATA/testing/detectors/EMC/qc/emcQCTasksAll_multinode.json b/DATA/testing/detectors/EMC/qc/emcQCTasksAll_multinode.json deleted file mode 100644 index b0bf73812..000000000 --- a/DATA/testing/detectors/EMC/qc/emcQCTasksAll_multinode.json +++ /dev/null @@ -1,157 +0,0 @@ -{ - "qc": { - "config": { - "database": { - "implementation": "CCDB", - "host": "alio2-cr1-hv-qcdb1.cern.ch:8083", - "username": "not_applicable", - "password": "not_applicable", - "name": "not_applicable" - }, - "infologger": { - "filterDiscardDebug": "true", - "filterDiscardLevel": "1" - }, - "Activity": { - "number": "42", - "type": "2" - }, - "monitoring": { - "url": "infologger:///debug?qc" - }, - "consul": { - "url": "http://consul-test.cern.ch:8500" - }, - "conditionDB": { - "url": "ccdb-test.cern.ch:8080" - } - }, - "tasks": { - "RawTask": { - "active": "true", - "className": "o2::quality_control_modules::emcal::RawTask", - "moduleName": "QcEMCAL", - "detectorName": "EMC", - "cycleDurationSeconds": "60", - "maxNumberCycles": "-1", - "dataSource": { - "type": "dataSamplingPolicy", - "name": "emcrawdata" - }, - "location": "local", - "localMachines": [ - "epn" - ], - "remoteMachine": "alio2-cr1-qc02.cern.ch", - "remotePort": "47701", - "mergingMode": "delta", - "localControl": "odc" - }, - "DigitsTask": { - "active": "true", - "className": "o2::quality_control_modules::emcal::DigitsQcTask", - "moduleName": "QcEMCAL", - "detectorName": "EMC", - "cycleDurationSeconds": "60", - "maxNumberCycles": "-1", - "dataSource": { - "type": "dataSamplingPolicy", - "name": "emccells" - }, - "taskParameters": { - "nothing": "rien" - }, - "location": "local", - "localMachines": [ - "epn" - ], - "remoteMachine": "alio2-cr1-qc02.cern.ch", - "remotePort": "47702", - "mergingMode": "delta", - "localControl": "odc" - } - }, - "checks": { - "RawBunchMinAmplitude": { - "active": "true", - "className": "o2::quality_control_modules::emcal::RawCheck", - "moduleName": "QcEMCAL", - "policy": "OnEachSeparately", - "dataSource": [ - { - "type": "Task", - "name": "RawTask", - "MOs": ["BunchMinRawAmplitudeFull_PHYS", "BunchMinRawAmplitudeEMCAL_PHYS", "BunchMinRawAmplitudeDCAL_PHYS", - "BunchMinRawAmplitude_SM0_PHYS", "BunchMinRawAmplitude_SM1_PHYS", "BunchMinRawAmplitude_SM2_PHYS", - "BunchMinRawAmplitude_SM3_PHYS", "BunchMinRawAmplitude_SM4_PHYS", "BunchMinRawAmplitude_SM5_PHYS", - "BunchMinRawAmplitude_SM6_PHYS", "BunchMinRawAmplitude_SM7_PHYS", "BunchMinRawAmplitude_SM8_PHYS", - "BunchMinRawAmplitude_SM9_PHYS", "BunchMinRawAmplitude_SM10_PHYS", "BunchMinRawAmplitude_SM11_PHYS", - "BunchMinRawAmplitude_SM12_PHYS", "BunchMinRawAmplitude_SM13_PHYS", "BunchMinRawAmplitude_SM14_PHYS", - "BunchMinRawAmplitude_SM15_PHYS", "BunchMinRawAmplitude_SM16_PHYS", "BunchMinRawAmplitude_SM17_PHYS", - "BunchMinRawAmplitude_SM18_PHYS", "BunchMinRawAmplitude_SM19_PHYS" - ] - } - ] - }, - "RawErrorCheck": { - "active": "true", - "className": "o2::quality_control_modules::emcal::RawCheck", - "moduleName": "QcEMCAL", - "policy": "OnEachSeparately", - "dataSource": [ - { - "type": "Task", - "name": "RawTask", - "MOs": ["ErrorTypePerSM"] - } - ] - }, - "DigitsCheckAmplitude": { - "active": "true", - "className": "o2::quality_control_modules::emcal::DigitCheck", - "moduleName": "QcEMCAL", - "policy": "OnEachSeparately", - "detectorName": "EMC", - "dataSource": [ - { - "type": "Task", - "name": "DigitsTask", - "MOs": ["digitAmplitudeEMCAL_CAL", "digitAmplitudeEMCAL_PHYS", "digitAmplitudeDCAL_CAL", "digitAmplitudeDCAL_PHYS", - "digitAmplitude_CAL", "digitAmplitude_PHYS" - ] - } - ] - } - } - }, - "dataSamplingPolicies": [ - { - "id": "emcrawdata", - "active": "true", - "machines": ["epn"], - "query": "readout:EMC/RAWDATA", - "samplingConditions": [ - { - "condition": "random", - "fraction": "0.01", - "seed": "1248" - } - ], - "blocking": "false" - }, - { - "id": "emccells", - "active": "true", - "machines": ["epn"], - "query": "emcal-digits:EMC/CELLS;emcal-triggerecords:EMC/CELLSTRGR", - "samplingConditions": [ - { - "condition": "random", - "fraction": "0.1", - "seed": "1248" - } - ], - "blocking": "false" - } - ] - } \ No newline at end of file diff --git a/DATA/testing/detectors/EMC/qc/emcQCTasks_multinode.json b/DATA/testing/detectors/EMC/qc/emcQCTasks_multinode.json deleted file mode 100644 index 60a0c54a9..000000000 --- a/DATA/testing/detectors/EMC/qc/emcQCTasks_multinode.json +++ /dev/null @@ -1,75 +0,0 @@ -{ - "qc": { - "config": { - "database": { - "implementation": "CCDB", - "host": "alio2-cr1-hv-qcdb1.cern.ch:8083", - "username": "not_applicable", - "password": "not_applicable", - "name": "not_applicable" - }, - "infologger": { - "filterDiscardDebug": "true", - "filterDiscardLevel": "1" - }, - "Activity": { - "number": "42", - "type": "2" - }, - "monitoring": { - "url": "infologger:///debug?qc" - }, - "consul": { - "url": "http://consul-test.cern.ch:8500" - }, - "conditionDB": { - "url": "ccdb-test.cern.ch:8080" - } - }, - "tasks": { - "DigitsTask": { - "active": "true", - "className": "o2::quality_control_modules::emcal::DigitsQcTask", - "moduleName": "QcEMCAL", - "detectorName": "EMC", - "cycleDurationSeconds": "10", - "maxNumberCycles": "-1", - "dataSource": { - "type": "direct", - "query": "emcal-digits:EMC/CELLS;emcal-triggerecords:EMC/CELLSTRGR" - }, - "taskParameters": { - "nothing": "rien" - }, - "location": "local", - "localMachines": [ - "epn" - ], - "remoteMachine": "alio2-cr1-qc02.cern.ch", - "remotePort": "47702", - "mergingMode": "delta", - "localControl": "odc" - } - }, - "checks": { - "DigitsCheckAmplitude": { - "active": "true", - "className": "o2::quality_control_modules::emcal::DigitCheck", - "moduleName": "QcEMCAL", - "policy": "OnEachSeparately", - "detectorName": "EMC", - "dataSource": [ - { - "type": "Task", - "name": "DigitsTask", - "MOs": ["digitAmplitudeEMCAL_CAL", "digitAmplitudeEMCAL_PHYS", "digitAmplitudeDCAL_CAL", "digitAmplitudeDCAL_PHYS", - "digitAmplitude_CAL", "digitAmplitude_PHYS" - ] - } - ] - } - } - }, - "dataSamplingPolicies": [ - ] - } \ No newline at end of file diff --git a/DATA/testing/detectors/EMC/qc/emcQCTasks_singlenode.json b/DATA/testing/detectors/EMC/qc/emcQCTasks_singlenode.json deleted file mode 100644 index a97fb8a88..000000000 --- a/DATA/testing/detectors/EMC/qc/emcQCTasks_singlenode.json +++ /dev/null @@ -1,68 +0,0 @@ -{ - "qc": { - "config": { - "database": { - "implementation": "CCDB", - "host": "alio2-cr1-hv-qcdb1.cern.ch:8083", - "username": "not_applicable", - "password": "not_applicable", - "name": "not_applicable" - }, - "infologger": { - "filterDiscardDebug": "true", - "filterDiscardLevel": "1" - }, - "Activity": { - "number": "42", - "type": "2" - }, - "monitoring": { - "url": "infologger:///debug?qc" - }, - "consul": { - "url": "http://consul-test.cern.ch:8500" - }, - "conditionDB": { - "url": "ccdb-test.cern.ch:8080" - } - }, - "tasks": { - "DigitsTask": { - "active": "true", - "className": "o2::quality_control_modules::emcal::DigitsQcTask", - "moduleName": "QcEMCAL", - "detectorName": "EMC", - "cycleDurationSeconds": "10", - "maxNumberCycles": "-1", - "dataSource": { - "type": "direct", - "query": "emcal-digits:EMC/CELLS;emcal-triggerecords:EMC/CELLSTRGR" - }, - "taskParameters": { - "nothing": "rien" - }, - "location": "remote" - } - }, - "checks": { - "DigitsCheckAmplitude": { - "active": "true", - "className": "o2::quality_control_modules::emcal::DigitCheck", - "moduleName": "QcEMCAL", - "policy": "OnEachSeparately", - "detectorName": "EMC", - "dataSource": [ - { - "type": "Task", - "name": "DigitsTask", - "MOs": ["digitAmplitudeEMCAL_CAL", "digitAmplitudeEMCAL_PHYS", "digitAmplitudeDCAL_CAL", "digitAmplitudeDCAL_PHYS", - "digitAmplitude_CAL", "digitAmplitude_PHYS" - ] - } - ] - } - } - }, - "dataSamplingPolicies": [ - ] - } \ No newline at end of file diff --git a/DATA/testing/detectors/EMC/runEMCRawToDigitsRecoPileline.sh b/DATA/testing/detectors/EMC/runEMCRawToDigitsRecoPileline.sh deleted file mode 100755 index 0e8eb075f..000000000 --- a/DATA/testing/detectors/EMC/runEMCRawToDigitsRecoPileline.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env bash - -PROXY_INSPEC="A:EMC/RAWDATA;dd:FLP/DISTSUBTIMEFRAME/0;eos:***/INFORMATION" - -NCPU=12 #$(grep ^cpu\\scores /proc/cpuinfo | uniq | awk '{print $4}') -source common/getCommonArgs.sh -#HOST='$(hostname -s)-ib' - -INFOLOGGER_SEVERITY_RAWPROXY=warning -SEVERITY_RAWPROXY=warning -INFOLOGGER_SEVERITY=warning -SEVERITY=warning - -o2-dpl-raw-proxy $ARGS_ALL \ - --dataspec "$PROXY_INSPEC" \ - --readout-proxy "--channel-config 'name=readout-proxy,type=pull,method=connect,address=ipc://@tf-builder-pipe-0,transport=shmem,rateLogging=1'" \ - --severity $INFOLOGGER_SEVERITY_RAWPROXY \ - --infologger-severity $SEVERITY_RAWPROXY \ - | o2-emcal-reco-workflow $ARGS_ALL \ - --input-type raw \ - --output-type cells \ - --disable-root-input \ - --disable-root-output \ - --EMCALRawToCellConverterSpec "--fitmethod=\"gamma2\" --maxmessage=10" \ - --disable-mc \ - --severity $SEVERITY \ - --infologger-severity $INFOLOGGER_SEVERITY \ - --pipeline EMCALRawToCellConverterSpec:$NCPU \ - | o2-dpl-run $ARGS_ALL --dds ${WORKFLOWMODE_FILE} diff --git a/DATA/testing/detectors/EMC/runEMCRawToDigitsRecoPilelineCTF.sh b/DATA/testing/detectors/EMC/runEMCRawToDigitsRecoPilelineCTF.sh deleted file mode 100755 index f90949474..000000000 --- a/DATA/testing/detectors/EMC/runEMCRawToDigitsRecoPilelineCTF.sh +++ /dev/null @@ -1,52 +0,0 @@ -#!/usr/bin/env bash - -source /home/mfasel/alice/O2DataProcessing/common/setenv.sh - -DISPLAY=0 - -PROXY_INSPEC="A:EMC/RAWDATA;dd:FLP/DISTSUBTIMEFRAME/0;eos:***/INFORMATION" - -### Comment: MAKE SURE the channels match address=ipc://@tf-builder-pipe-0 - -hash=$1 - -VERBOSE="" -NCPU=12 #$(grep ^cpu\\scores /proc/cpuinfo | uniq | awk '{print $4}') -source common/getCommonArgs.sh -#HOST='$(hostname -s)-ib' - -# CTF compression dictionary -CTF_DICT="${FILEWORKDIR}/ctf_dictionary.root" -# min file size for CTF (accumulate CTFs until it is reached) -CTF_MINSIZE="500000000" -CTF_MAX_PER_FILE=10000 - -o2-dpl-raw-proxy $ARGS_ALL \ - --dataspec "$PROXY_INSPEC" \ - --readout-proxy "--channel-config 'name=readout-proxy,type=pull,method=connect,address=ipc://@tf-builder-pipe-0,transport=shmem,rateLogging=1'" \ - --severity info \ - --infologger-severity info \ - | o2-emcal-reco-workflow $ARGS_ALL \ - --input-type raw \ - --output-type cells \ - --disable-root-input \ - --disable-root-output \ - --disable-mc \ - --EMCALRawToCellConverterSpec "--fitmethod=\"gamma2\" --maxmessage=10" \ - --severity warning \ - --infologger-severity warning \ - --pipeline EMCALRawToCellConverterSpec:8 \ - | o2-emcal-entropy-encoder-workflow $ARGS_ALL \ - --ctf-dict "${CTF_DICT}" \ - --mem-factor 5 \ - --severity info \ - --infologger-severity warning \ - | o2-ctf-writer-workflow $ARGS_ALL \ - --configKeyValues "${CONFKEYVAL}" \ - --onlyDet $WORKFLOW_DETECTORS \ - --ctf-dict "${CTF_DICT}" \ - --output-dir $CTF_DIR \ - --meta-output-dir ${EPN2EOS_METAFILES_DIR} \ - --min-file-size "${CTF_MINSIZE}" \ - --max-ctf-per-file "${CTF_MAX_PER_FILE}" \ - | o2-dpl-run $ARGS_ALL --dds ${WORKFLOWMODE_FILE} diff --git a/DATA/testing/detectors/EMC/runEMCRawToDigitsRecoPilelineQCAlllocalCTF.sh b/DATA/testing/detectors/EMC/runEMCRawToDigitsRecoPilelineQCAlllocalCTF.sh deleted file mode 100755 index 0cb6ea090..000000000 --- a/DATA/testing/detectors/EMC/runEMCRawToDigitsRecoPilelineQCAlllocalCTF.sh +++ /dev/null @@ -1,62 +0,0 @@ -#!/usr/bin/env bash - -source /home/mfasel/alice/O2DataProcessing/common/setenv.sh - -PROXY_INSPEC="A:EMC/RAWDATA;dd:FLP/DISTSUBTIMEFRAME/0;eos:***/INFORMATION" - -NCPU=20 #$(grep ^cpu\\scores /proc/cpuinfo | uniq | awk '{print $4}') -source common/getCommonArgs.sh -#HOST='$(hostname -s)-ib' -HOST=epn - -QC_CONFIG=consul-json://alio2-cr1-hv-con01.cern.ch:8500/o2/components/qc/ANY/any/emc-qcmn-epnall -#QC_CONFIG=json:///home/epn/odc/files/emcQCTasks_multinode.json -INFOLOGGER_SEVERITY_RAWPROXY=warning -SEVERITY_RAWPROXY=warning -INFOLOGGER_SEVERITY=warning -SEVERITY=warning -INFOLOGGER_SEVERITY_QC=warning -SEVERITY_QC=warning - -# CTF compression dictionary -CTF_DICT="${FILEWORKDIR}/ctf_dictionary.root" -# min file size for CTF (accumulate CTFs until it is reached) -CTF_MINSIZE="500000000" -CTF_MAX_PER_FILE=10000 - -o2-dpl-raw-proxy $ARGS_ALL \ - --dataspec "$PROXY_INSPEC" \ - --readout-proxy "--channel-config 'name=readout-proxy,type=pull,method=connect,address=ipc://@tf-builder-pipe-0,transport=shmem,rateLogging=1'" \ - --severity $SEVERITY_RAWPROXY \ - --infologger-severity $INFOLOGGER_SEVERITY_RAWPROXY \ - | o2-emcal-reco-workflow $ARGS_ALL \ - --input-type raw \ - --output-type cells \ - --disable-root-input \ - --disable-root-output \ - --disable-mc \ - --EMCALRawToCellConverterSpec "--fitmethod=\"gamma2\" --maxmessage=10" \ - --severity $SEVERITY \ - --infologger-severity $INFOLOGGER_SEVERITY \ - --pipeline EMCALRawToCellConverterSpec:$NCPU \ - | o2-qc $ARGS_ALL \ - --config $QC_CONFIG \ - --local \ - --host $HOST \ - --severity $SEVERITY_QC \ - --infologger-severity $INFOLOGGER_SEVERITY_QC \ - | o2-emcal-entropy-encoder-workflow $ARGS_ALL \ - --ctf-dict "${CTF_DICT}" \ - --mem-factor 5 \ - --severity info \ - --infologger-severity warning \ - | o2-ctf-writer-workflow $ARGS_ALL \ - --configKeyValues "${CONFKEYVAL}" \ - --onlyDet $WORKFLOW_DETECTORS \ - --ctf-dict "${CTF_DICT}" \ - --output-dir $CTF_DIR \ - --meta-output-dir ${EPN2EOS_METAFILES_DIR} \ - --min-file-size "${CTF_MINSIZE}" \ - --max-ctf-per-file "${CTF_MAX_PER_FILE}" \ - | o2-dpl-run $ARGS_ALL --dds ${WORKFLOWMODE_FILE} - diff --git a/DATA/testing/detectors/EMC/runEMCRawToDigitsRecoPilelineQClocal.sh b/DATA/testing/detectors/EMC/runEMCRawToDigitsRecoPilelineQClocal.sh deleted file mode 100755 index 3bb2d27cb..000000000 --- a/DATA/testing/detectors/EMC/runEMCRawToDigitsRecoPilelineQClocal.sh +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env bash - -source /home/mfasel/alice/O2DataProcessing/common/setenv.sh - -PROXY_INSPEC="A:EMC/RAWDATA;dd:FLP/DISTSUBTIMEFRAME/0;eos:***/INFORMATION" - -NCPU=12 #$(grep ^cpu\\scores /proc/cpuinfo | uniq | awk '{print $4}') -source common/getCommonArgs.sh -#HOST='$(hostname -s)-ib' -HOST=epn - -QC_CONFIG=consul-json:///alio2-cr1-hv-con01.cern.ch:8500/o2/components/qc/ANY/any/emc-qcmn-epn -#QC_CONFIG=json:///home/epn/odc/files/emcQCTasks_multinode.json -INFOLOGGER_SEVERITY_RAWPROXY=warning -SEVERITY_RAWPROXY=warning -INFOLOGGER_SEVERITY=warning -SEVERITY=warning - -o2-dpl-raw-proxy $ARGS_ALL \ - --dataspec "$PROXY_INSPEC" \ - --readout-proxy "--channel-config 'name=readout-proxy,type=pull,method=connect,address=ipc://@tf-builder-pipe-0,transport=shmem,rateLogging=1'" \ - --severity $SEVERITY_RAWPROXY \ - --infologger-severity $INFOLOGGER_SEVERITY_RAWPROXY \ - | o2-emcal-reco-workflow $ARGS_ALL \ - --input-type raw \ - --output-type cells \ - --disable-root-input \ - --disable-root-output \ - --disable-mc \ - --EMCALRawToCellConverterSpec "--fitmethod=\"gamma2\" --maxmessage=10" \ - --severity $SEVERITY \ - --infologger-severity $INFOLOGGER_SEVERITY \ - --pipeline EMCALRawToCellConverterSpec:$NCPU \ - | o2-qc $ARGS_ALL \ - --config $QC_CONFIG \ - --local \ - --host $HOST \ - | o2-dpl-run $ARGS_ALL --dds ${WORKFLOWMODE_FILE} diff --git a/DATA/testing/detectors/EMC/runEMCRawToDigitsRecoPilelineQClocalCTF.sh b/DATA/testing/detectors/EMC/runEMCRawToDigitsRecoPilelineQClocalCTF.sh deleted file mode 100755 index 6f7f49fe4..000000000 --- a/DATA/testing/detectors/EMC/runEMCRawToDigitsRecoPilelineQClocalCTF.sh +++ /dev/null @@ -1,61 +0,0 @@ -#!/usr/bin/env bash - -source /home/mfasel/alice/O2DataProcessing/common/setenv.sh - -PROXY_INSPEC="A:EMC/RAWDATA;dd:FLP/DISTSUBTIMEFRAME/0;eos:***/INFORMATION" - -NCPU=20 #$(grep ^cpu\\scores /proc/cpuinfo | uniq | awk '{print $4}') -source common/getCommonArgs.sh -#HOST='$(hostname -s)-ib' -HOST=epn - -QC_CONFIG=consul-json://alio2-cr1-hv-con01.cern.ch:8500/o2/components/qc/ANY/any/emc-qcmn-flpepn -#QC_CONFIG=json:///home/epn/odc/files/emcQCTasks_multinode.json -INFOLOGGER_SEVERITY_RAWPROXY=warning -SEVERITY_RAWPROXY=warning -INFOLOGGER_SEVERITY=warning -SEVERITY=warning -INFOLOGGER_SEVERITY_QC=warning -SEVERITY_QC=warning - -# CTF compression dictionary -CTF_DICT="${FILEWORKDIR}/ctf_dictionary.root" -# min file size for CTF (accumulate CTFs until it is reached) -CTF_MINSIZE="500000000" -CTF_MAX_PER_FILE=10000 - -o2-dpl-raw-proxy $ARGS_ALL \ - --dataspec "$PROXY_INSPEC" \ - --readout-proxy "--channel-config 'name=readout-proxy,type=pull,method=connect,address=ipc://@tf-builder-pipe-0,transport=shmem,rateLogging=1'" \ - --severity $SEVERITY_RAWPROXY \ - --infologger-severity $INFOLOGGER_SEVERITY_RAWPROXY \ - | o2-emcal-reco-workflow $ARGS_ALL \ - --input-type raw \ - --output-type cells \ - --disable-root-input \ - --disable-root-output \ - --disable-mc \ - --EMCALRawToCellConverterSpec "--fitmethod=\"gamma2\" --maxmessage=10" \ - --severity $SEVERITY \ - --infologger-severity $INFOLOGGER_SEVERITY \ - --pipeline EMCALRawToCellConverterSpec:$NCPU \ - | o2-qc $ARGS_ALL \ - --config $QC_CONFIG \ - --local \ - --host $HOST \ - --severity $SEVERITY_QC \ - --infologger-severity $INFOLOGGER_SEVERITY_QC \ - | o2-emcal-entropy-encoder-workflow $ARGS_ALL \ - --ctf-dict "${CTF_DICT}" \ - --mem-factor 5 \ - --severity info \ - --infologger-severity warning \ - | o2-ctf-writer-workflow $ARGS_ALL \ - --configKeyValues "${CONFKEYVAL}" \ - --onlyDet $WORKFLOW_DETECTORS \ - --ctf-dict "${CTF_DICT}" \ - --output-dir $CTF_DIR \ - --meta-output-dir ${EPN2EOS_METAFILES_DIR} \ - --min-file-size "${CTF_MINSIZE}" \ - --max-ctf-per-file "${CTF_MAX_PER_FILE}" \ - | o2-dpl-run $ARGS_ALL --dds ${WORKFLOWMODE_FILE} diff --git a/DATA/testing/detectors/EMC/runEMCRawToDigitsRecoPilelineQClocalCTFSingle.sh b/DATA/testing/detectors/EMC/runEMCRawToDigitsRecoPilelineQClocalCTFSingle.sh deleted file mode 100755 index 19a5fbc25..000000000 --- a/DATA/testing/detectors/EMC/runEMCRawToDigitsRecoPilelineQClocalCTFSingle.sh +++ /dev/null @@ -1,57 +0,0 @@ -#!/usr/bin/env bash - -PROXY_INSPEC="A:EMC/RAWDATA;dd:FLP/DISTSUBTIMEFRAME/0;eos:***/INFORMATION" - -NCPU=12 #$(grep ^cpu\\scores /proc/cpuinfo | uniq | awk '{print $4}') -source common/getCommonArgs.sh -#HOST='$(hostname -s)-ib' -HOST=epn - -#QC_CONFIG=consul-json://alio2-cr1-hv-aliecs:8500/o2/components/qc/ANY/any/emc-qcmn-epn -QC_CONFIG=json:///home/epn/odc/files/emcQCTasks_multinode.json -INFOLOGGER_SEVERITY_RAWPROXY=warning -SEVERITY_RAWPROXY=warning -INFOLOGGER_SEVERITY=warning -SEVERITY=warning -INFOLOGGER_SEVERITY_QC=warning -SEVERITY_QC=warning - -# CTF compression dictionary -CTF_DICT="${FILEWORKDIR}/ctf_dictionary.root" -# min file size for CTF (accumulate CTFs until it is reached) -CTF_MINSIZE="500000000" -CTF_MAX_PER_FILE=10000 - -o2-dpl-raw-proxy $ARGS_ALL \ - --dataspec "$PROXY_INSPEC" \ - --readout-proxy "--channel-config 'name=readout-proxy,type=pull,method=connect,address=ipc://@tf-builder-pipe-0,transport=shmem,rateLogging=1'" \ - --severity $SEVERITY_RAWPROXY \ - --infologger-severity $INFOLOGGER_SEVERITY_RAWPROXY \ - | o2-emcal-reco-workflow $ARGS_ALL \ - --input-type raw \ - --output-type cells \ - --disable-root-input \ - --disable-root-output \ - --disable-mc \ - --EMCALRawToCellConverterSpec "--fitmethod=\"gamma2\" --maxmessage=10" \ - --severity $SEVERITY \ - --infologger-severity $INFOLOGGER_SEVERITY \ - --pipeline EMCALRawToCellConverterSpec:$NCPU \ - | o2-qc $ARGS_ALL \ - --config $QC_CONFIG \ - --severity $SEVERITY_QC \ - --infologger-severity $INFOLOGGER_SEVERITY_QC \ - | o2-emcal-entropy-encoder-workflow $ARGS_ALL \ - --ctf-dict "${CTF_DICT}" \ - --mem-factor 5 \ - --severity info \ - --infologger-severity warning \ - | o2-ctf-writer-workflow $ARGS_ALL \ - --configKeyValues "${CONFKEYVAL}" \ - --onlyDet $WORKFLOW_DETECTORS \ - --ctf-dict "${CTF_DICT}" \ - --output-dir $CTF_DIR \ - --meta-output-dir ${EPN2EOS_METAFILES_DIR} \ - --min-file-size "${CTF_MINSIZE}" \ - --max-ctf-per-file "${CTF_MAX_PER_FILE}" \ - | o2-dpl-run $ARGS_ALL --dds ${WORKFLOWMODE_FILE} diff --git a/DATA/testing/detectors/EMC/workflows.desc b/DATA/testing/detectors/EMC/workflows.desc deleted file mode 100644 index 2728c03aa..000000000 --- a/DATA/testing/detectors/EMC/workflows.desc +++ /dev/null @@ -1,8 +0,0 @@ -emc-qcall-ctf-3: "O2PDPSuite" reco,3,3,"EPN2EOS_METAFILES_DIR=/data/epn2eos_tool/epn2eos WORKFLOW_PARAMETERS=CTF,GPU,EVENT_DISPLAY WORKFLOW_DETECTORS=EMC SHMSIZE=64000000000 testing/detectors/EMC/runEMCRawToDigitsRecoPilelineQCAlllocalCTF.sh" -emc-qcall-ctf-4: "O2PDPSuite" reco,4,4,"EPN2EOS_METAFILES_DIR=/data/epn2eos_tool/epn2eos WORKFLOW_PARAMETERS=CTF,GPU,EVENT_DISPLAY WORKFLOW_DETECTORS=EMC SHMSIZE=64000000000 testing/detectors/EMC/runEMCRawToDigitsRecoPilelineQCAlllocalCTF.sh" -emc-qcall-ctf-5: "O2PDPSuite" reco,5,5,"EPN2EOS_METAFILES_DIR=/data/epn2eos_tool/epn2eos WORKFLOW_PARAMETERS=CTF,GPU,EVENT_DISPLAY WORKFLOW_DETECTORS=EMC SHMSIZE=64000000000 testing/detectors/EMC/runEMCRawToDigitsRecoPilelineQCAlllocalCTF.sh" -emc-qcall-ctf-6: "O2PDPSuite" reco,6,6,"EPN2EOS_METAFILES_DIR=/data/epn2eos_tool/epn2eos WORKFLOW_PARAMETERS=CTF,GPU,EVENT_DISPLAY WORKFLOW_DETECTORS=EMC SHMSIZE=64000000000 testing/detectors/EMC/runEMCRawToDigitsRecoPilelineQCAlllocalCTF.sh" -emc-qcall-ctf-7: "O2PDPSuite" reco,7,7,"EPN2EOS_METAFILES_DIR=/data/epn2eos_tool/epn2eos WORKFLOW_PARAMETERS=CTF,GPU,EVENT_DISPLAY WORKFLOW_DETECTORS=EMC SHMSIZE=64000000000 testing/detectors/EMC/runEMCRawToDigitsRecoPilelineQCAlllocalCTF.sh" -emc-qcall-ctf-8: "O2PDPSuite" reco,8,8,"EPN2EOS_METAFILES_DIR=/data/epn2eos_tool/epn2eos WORKFLOW_PARAMETERS=CTF,GPU,EVENT_DISPLAY WORKFLOW_DETECTORS=EMC SHMSIZE=64000000000 testing/detectors/EMC/runEMCRawToDigitsRecoPilelineQCAlllocalCTF.sh" -emc-qcall-ctf-9: "O2PDPSuite" reco,9,9,"EPN2EOS_METAFILES_DIR=/data/epn2eos_tool/epn2eos WORKFLOW_PARAMETERS=CTF,GPU,EVENT_DISPLAY WORKFLOW_DETECTORS=EMC SHMSIZE=64000000000 testing/detectors/EMC/runEMCRawToDigitsRecoPilelineQCAlllocalCTF.sh" -emc-qcall-ctf-13: "O2PDPSuite" reco,13,13,"EPN2EOS_METAFILES_DIR=/data/epn2eos_tool/epn2eos WORKFLOW_PARAMETERS=CTF,GPU,EVENT_DISPLAY WORKFLOW_DETECTORS=EMC SHMSIZE=64000000000 testing/detectors/EMC/runEMCRawToDigitsRecoPilelineQCAlllocalCTF.sh" \ No newline at end of file diff --git a/DATA/testing/detectors/FDD/fdd-ctf.sh b/DATA/testing/detectors/FDD/fdd-ctf.sh index 1f4c51ded..e3e943feb 100755 --- a/DATA/testing/detectors/FDD/fdd-ctf.sh +++ b/DATA/testing/detectors/FDD/fdd-ctf.sh @@ -15,7 +15,7 @@ MYDIR="$(dirname $(readlink -f $0))" PROXY_INSPEC="digits:FDD/DIGITSBC/0;channels:FDD/DIGITSCH/0;eos:***/INFORMATION;dd:FLP/DISTSUBTIMEFRAME/0" IN_CHANNEL="--channel-config 'name=readout-proxy,type=pull,method=connect,address=ipc://@$INRAWCHANNAME,transport=shmem,rateLogging=1'" -o2-dpl-raw-proxy ${ARGS_ALL} --readout-proxy "${IN_CHANNEL}" --dataspec "${PROXY_INSPEC}" \ +o2-dpl-raw-proxy ${ARGS_ALL} --readout-proxy "${IN_CHANNEL}" --dataspec "${PROXY_INSPEC}" --inject-missing-data \ | o2-fdd-entropy-encoder-workflow ${ARGS_ALL} --configKeyValues "$ARGS_ALL_CONFIG;" ${CTF_DICT} \ | o2-ctf-writer-workflow ${ARGS_ALL} ${ARGS_CTF} --configKeyValues "$ARGS_ALL_CONFIG;" --onlyDet FDD --output-dir $CTF_DIR --ctf-dict-dir $FILEWORKDIR --output-type ctf \ | o2-dpl-run $ARGS_ALL $GLOBALDPLOPT --dds ${WORKFLOWMODE_FILE} # option instead iof run to export DDS xml file diff --git a/DATA/testing/detectors/FDD/fdd-digits-ctf.sh b/DATA/testing/detectors/FDD/fdd-digits-ctf.sh index c27259d0c..a13d56942 100755 --- a/DATA/testing/detectors/FDD/fdd-digits-ctf.sh +++ b/DATA/testing/detectors/FDD/fdd-digits-ctf.sh @@ -15,7 +15,7 @@ MYDIR="$(dirname $(readlink -f $0))" PROXY_INSPEC="x:FDD/RAWDATA;eos:***/INFORMATION;dd:FLP/DISTSUBTIMEFRAME/0" IN_CHANNEL="--channel-config 'name=readout-proxy,type=pull,method=connect,address=ipc://@$INRAWCHANNAME,transport=shmem,rateLogging=1'" -o2-dpl-raw-proxy ${ARGS_ALL} --readout-proxy "${IN_CHANNEL}" --dataspec "${PROXY_INSPEC}" \ +o2-dpl-raw-proxy ${ARGS_ALL} --readout-proxy "${IN_CHANNEL}" --dataspec "${PROXY_INSPEC}" --inject-missing-data \ | o2-fdd-flp-dpl-workflow --disable-root-output ${ARGS_ALL} --configKeyValues "$ARGS_ALL_CONFIG;" --pipeline fdd-datareader-dpl:$NTHREADS \ | o2-fdd-entropy-encoder-workflow ${ARGS_ALL} --configKeyValues "$ARGS_ALL_CONFIG;" ${CTF_DICT} \ | o2-ctf-writer-workflow ${ARGS_ALL} ${ARGS_CTF} --configKeyValues "$ARGS_ALL_CONFIG;" --onlyDet FDD --output-dir $CTF_DIR --ctf-dict-dir $FILEWORKDIR --output-type ctf \ diff --git a/DATA/testing/detectors/FDD/fdd-digits-qc-ctf.sh b/DATA/testing/detectors/FDD/fdd-digits-qc-ctf.sh index af66422a5..0e109927b 100755 --- a/DATA/testing/detectors/FDD/fdd-digits-qc-ctf.sh +++ b/DATA/testing/detectors/FDD/fdd-digits-qc-ctf.sh @@ -15,7 +15,7 @@ MYDIR="$(dirname $(readlink -f $0))" PROXY_INSPEC="x:FDD/RAWDATA;eos:***/INFORMATION;dd:FLP/DISTSUBTIMEFRAME/0" IN_CHANNEL="--channel-config 'name=readout-proxy,type=pull,method=connect,address=ipc://@$INRAWCHANNAME,transport=shmem,rateLogging=1'" -o2-dpl-raw-proxy ${ARGS_ALL} --readout-proxy "${IN_CHANNEL}" --dataspec "${PROXY_INSPEC}" \ +o2-dpl-raw-proxy ${ARGS_ALL} --readout-proxy "${IN_CHANNEL}" --dataspec "${PROXY_INSPEC}" --inject-missing-data \ | o2-fdd-flp-dpl-workflow --disable-root-output ${ARGS_ALL} --configKeyValues "$ARGS_ALL_CONFIG;" --pipeline fdd-datareader-dpl:$NTHREADS \ | o2-fdd-entropy-encoder-workflow ${ARGS_ALL} --configKeyValues "$ARGS_ALL_CONFIG;" ${CTF_DICT} \ | o2-qc ${ARGS_ALL} --local --host epn --config json://${MYDIR}/fdd-digits-ds.json \ diff --git a/DATA/testing/detectors/FDD/run_fdd_ctf.sh b/DATA/testing/detectors/FDD/run_fdd_ctf.sh index ebcd5dd8c..c413798e9 100755 --- a/DATA/testing/detectors/FDD/run_fdd_ctf.sh +++ b/DATA/testing/detectors/FDD/run_fdd_ctf.sh @@ -25,7 +25,7 @@ export MULTIPLICITY_FACTOR_REST=1 # Factor to export OUTPUT_FILE_NAME=$HOME/topologies/fdd-ctf.xml -/opt/alisw/el8/GenTopo/bin/gen_topo.sh > $OUTPUT_FILE_NAME +/opt/alisw/el9/GenTopo/bin/gen_topo.sh > $OUTPUT_FILE_NAME if [ $? == 0 ]; then echo Generated XML topology $OUTPUT_FILE_NAME fi diff --git a/DATA/testing/detectors/FDD/run_fdd_digits_ctf.sh b/DATA/testing/detectors/FDD/run_fdd_digits_ctf.sh index 4430d0f29..c8ac580d6 100755 --- a/DATA/testing/detectors/FDD/run_fdd_digits_ctf.sh +++ b/DATA/testing/detectors/FDD/run_fdd_digits_ctf.sh @@ -25,7 +25,7 @@ export MULTIPLICITY_FACTOR_REST=1 # Factor to export OUTPUT_FILE_NAME=$HOME/topologies/fdd-digits-ctf.xml -/opt/alisw/el8/GenTopo/bin/gen_topo.sh > $OUTPUT_FILE_NAME +/opt/alisw/el9/GenTopo/bin/gen_topo.sh > $OUTPUT_FILE_NAME if [ $? == 0 ]; then echo Generated XML topology $OUTPUT_FILE_NAME fi diff --git a/DATA/testing/detectors/FDD/run_fdd_digits_qc_ctf.sh b/DATA/testing/detectors/FDD/run_fdd_digits_qc_ctf.sh index d7c37ac7b..797cf88c6 100755 --- a/DATA/testing/detectors/FDD/run_fdd_digits_qc_ctf.sh +++ b/DATA/testing/detectors/FDD/run_fdd_digits_qc_ctf.sh @@ -25,7 +25,7 @@ export MULTIPLICITY_FACTOR_REST=1 # Factor to export OUTPUT_FILE_NAME=$HOME/topologies/fdd-digits-qc-ctf.xml -/opt/alisw/el8/GenTopo/bin/gen_topo.sh > $OUTPUT_FILE_NAME +/opt/alisw/el9/GenTopo/bin/gen_topo.sh > $OUTPUT_FILE_NAME if [ $? == 0 ]; then echo Generated XML topology $OUTPUT_FILE_NAME fi diff --git a/DATA/testing/detectors/FT0/ft0-ctf.sh b/DATA/testing/detectors/FT0/ft0-ctf.sh index e0a4fbc09..91ea21b33 100755 --- a/DATA/testing/detectors/FT0/ft0-ctf.sh +++ b/DATA/testing/detectors/FT0/ft0-ctf.sh @@ -16,7 +16,7 @@ MYDIR="$(dirname $(readlink -f $0))" PROXY_INSPEC="digits:FT0/DIGITSBC/0;channels:FT0/DIGITSCH/0;eos:***/INFORMATION;dd:FLP/DISTSUBTIMEFRAME/0" IN_CHANNEL="--channel-config 'name=readout-proxy,type=pull,method=connect,address=ipc://@$INRAWCHANNAME,transport=shmem,rateLogging=1'" -o2-dpl-raw-proxy ${ARGS_ALL} --readout-proxy "${IN_CHANNEL}" --dataspec "${PROXY_INSPEC}" \ +o2-dpl-raw-proxy ${ARGS_ALL} --readout-proxy "${IN_CHANNEL}" --dataspec "${PROXY_INSPEC}" --inject-missing-data \ | o2-ft0-entropy-encoder-workflow ${ARGS_ALL} --configKeyValues "$ARGS_ALL_CONFIG;" ${CTF_DICT} \ | o2-ctf-writer-workflow ${ARGS_ALL} ${ARGS_CTF} --configKeyValues "$ARGS_ALL_CONFIG;" --onlyDet FT0 --output-dir $CTF_DIR --ctf-dict-dir $FILEWORKDIR --output-type ctf \ | o2-dpl-run $ARGS_ALL $GLOBALDPLOPT --dds ${WORKFLOWMODE_FILE} # option instead iof run to export DDS xml file diff --git a/DATA/testing/detectors/FT0/ft0-digits-qc-ctf.sh b/DATA/testing/detectors/FT0/ft0-digits-qc-ctf.sh index 8495cb385..b1869604e 100755 --- a/DATA/testing/detectors/FT0/ft0-digits-qc-ctf.sh +++ b/DATA/testing/detectors/FT0/ft0-digits-qc-ctf.sh @@ -1,13 +1,12 @@ #!/usr/bin/env bash +# Make common arguments and helper functions such as add_W available source common/setenv.sh +source common/getCommonArgs.sh +source common/gen_topo_helper_functions.sh -SEVERITY=WARNING -ARGS_ALL="--session default --severity $SEVERITY --shm-segment-id $NUMAID --shm-segment-size $SHMSIZE" -ARGS_ALL+=" --infologger-severity $SEVERITY" if [ -z $CTF_DIR ]; then CTF_DIR=$FILEWORKDIR; fi # Directory where to store dictionary files -#ARGS_ALL+=" --monitoring-backend influxdb-unix:///tmp/telegraf.sock" -ARGS_ALL_CONFIG="NameConf.mDirGRP=$FILEWORKDIR;NameConf.mDirGeom=$FILEWORKDIR;NameConf.mDirCollContext=$FILEWORKDIR;NameConf.mDirMatLUT=$FILEWORKDIR;keyval.input_dir=$FILEWORKDIR;keyval.output_dir=/dev/null" + CTF_DICT="--ctf-dict $FILEWORKDIR/ctf_dictionary.root" NTHREADS=2 # Output directory for the CTF, to write to the current dir., remove `--output-dir $CTFOUT` from o2-ctf-writer-workflow or set to `CTFOUT=\"""\"` @@ -18,6 +17,8 @@ MYDIR="$(dirname $(readlink -f $0))" PROXY_INSPEC="x:FT0/RAWDATA;eos:***/INFORMATION;dd:FLP/DISTSUBTIMEFRAME/0" IN_CHANNEL="--channel-config 'name=readout-proxy,type=pull,method=connect,address=ipc://@$INRAWCHANNAME,transport=shmem,rateLogging=1'" +# TODO use add_W function from gen_topo_helper_functions.sh to assemble workflow +# as done for example in https://github.com/AliceO2Group/O2DPG/blob/master/DATA/production/calib/its-threshold-processing.sh o2-dpl-raw-proxy ${ARGS_ALL} --readout-proxy "${IN_CHANNEL}" --dataspec "${PROXY_INSPEC}" \ | o2-ft0-flp-dpl-workflow --disable-root-output ${ARGS_ALL} --configKeyValues "$ARGS_ALL_CONFIG;" --pipeline ft0-datareader-dpl:$NTHREADS \ | o2-ft0-entropy-encoder-workflow ${ARGS_ALL} --configKeyValues "$ARGS_ALL_CONFIG;" ${CTF_DICT} \ diff --git a/DATA/testing/detectors/FT0/run_ft0_ctf.sh b/DATA/testing/detectors/FT0/run_ft0_ctf.sh index 8f1878d1b..6cc1d8252 100755 --- a/DATA/testing/detectors/FT0/run_ft0_ctf.sh +++ b/DATA/testing/detectors/FT0/run_ft0_ctf.sh @@ -25,7 +25,7 @@ export MULTIPLICITY_FACTOR_REST=1 # Factor to export OUTPUT_FILE_NAME=$HOME/topologies/ft0-ctf.xml -/opt/alisw/el8/GenTopo/bin/gen_topo.sh > $OUTPUT_FILE_NAME +/opt/alisw/el9/GenTopo/bin/gen_topo.sh > $OUTPUT_FILE_NAME if [ $? == 0 ]; then echo Generated XML topology $OUTPUT_FILE_NAME fi diff --git a/DATA/testing/detectors/FT0/run_ft0_digits_ctf.sh b/DATA/testing/detectors/FT0/run_ft0_digits_ctf.sh index caf0de573..7de20e592 100755 --- a/DATA/testing/detectors/FT0/run_ft0_digits_ctf.sh +++ b/DATA/testing/detectors/FT0/run_ft0_digits_ctf.sh @@ -25,7 +25,7 @@ export MULTIPLICITY_FACTOR_REST=1 # Factor to export OUTPUT_FILE_NAME=$HOME/topologies/ft0-digits-ctf.xml -/opt/alisw/el8/GenTopo/bin/gen_topo.sh > $OUTPUT_FILE_NAME +/opt/alisw/el9/GenTopo/bin/gen_topo.sh > $OUTPUT_FILE_NAME if [ $? == 0 ]; then echo Generated XML topology $OUTPUT_FILE_NAME fi diff --git a/DATA/testing/detectors/FT0/run_ft0_digits_qc_ctf.sh b/DATA/testing/detectors/FT0/run_ft0_digits_qc_ctf.sh index 8528245f0..97ec8eadc 100755 --- a/DATA/testing/detectors/FT0/run_ft0_digits_qc_ctf.sh +++ b/DATA/testing/detectors/FT0/run_ft0_digits_qc_ctf.sh @@ -25,7 +25,7 @@ export MULTIPLICITY_FACTOR_REST=1 # Factor to export OUTPUT_FILE_NAME=$HOME/topologies/ft0-digits-qc-ctf.xml -/opt/alisw/el8/GenTopo/bin/gen_topo.sh > $OUTPUT_FILE_NAME +/opt/alisw/el9/GenTopo/bin/gen_topo.sh > $OUTPUT_FILE_NAME if [ $? == 0 ]; then echo Generated XML topology $OUTPUT_FILE_NAME fi diff --git a/DATA/testing/detectors/FV0/fv0-ctf.sh b/DATA/testing/detectors/FV0/fv0-ctf.sh index 0fe721a33..c00a5932f 100755 --- a/DATA/testing/detectors/FV0/fv0-ctf.sh +++ b/DATA/testing/detectors/FV0/fv0-ctf.sh @@ -15,7 +15,7 @@ MYDIR="$(dirname $(readlink -f $0))" PROXY_INSPEC="digits:FV0/DIGITSBC/0;channels:FV0/DIGITSCH/0;eos:***/INFORMATION;dd:FLP/DISTSUBTIMEFRAME/0" IN_CHANNEL="--channel-config 'name=readout-proxy,type=pull,method=connect,address=ipc://@$INRAWCHANNAME,transport=shmem,rateLogging=1'" -o2-dpl-raw-proxy ${ARGS_ALL} --readout-proxy "${IN_CHANNEL}" --dataspec "${PROXY_INSPEC}" \ +o2-dpl-raw-proxy ${ARGS_ALL} --readout-proxy "${IN_CHANNEL}" --dataspec "${PROXY_INSPEC}" --inject-missing-data \ | o2-fv0-entropy-encoder-workflow ${ARGS_ALL} --configKeyValues "$ARGS_ALL_CONFIG;" ${CTF_DICT} \ | o2-ctf-writer-workflow ${ARGS_ALL} ${ARGS_CTF} --configKeyValues "$ARGS_ALL_CONFIG;" --onlyDet FV0 --output-dir $CTF_DIR --ctf-dict-dir $FILEWORKDIR --output-type ctf \ | o2-dpl-run $ARGS_ALL $GLOBALDPLOPT --dds ${WORKFLOWMODE_FILE} # option instead iof run to export DDS xml file diff --git a/DATA/testing/detectors/FV0/fv0-digits-ctf.sh b/DATA/testing/detectors/FV0/fv0-digits-ctf.sh index 4f5141d49..08ccb31e0 100755 --- a/DATA/testing/detectors/FV0/fv0-digits-ctf.sh +++ b/DATA/testing/detectors/FV0/fv0-digits-ctf.sh @@ -15,7 +15,7 @@ MYDIR="$(dirname $(readlink -f $0))" PROXY_INSPEC="x:FV0/RAWDATA;eos:***/INFORMATION;dd:FLP/DISTSUBTIMEFRAME/0" IN_CHANNEL="--channel-config 'name=readout-proxy,type=pull,method=connect,address=ipc://@$INRAWCHANNAME,transport=shmem,rateLogging=1'" -o2-dpl-raw-proxy ${ARGS_ALL} --readout-proxy "${IN_CHANNEL}" --dataspec "${PROXY_INSPEC}" \ +o2-dpl-raw-proxy ${ARGS_ALL} --readout-proxy "${IN_CHANNEL}" --dataspec "${PROXY_INSPEC}" --inject-missing-data \ | o2-fv0-flp-dpl-workflow --disable-root-output ${ARGS_ALL} --configKeyValues "$ARGS_ALL_CONFIG;" --pipeline fv0-datareader-dpl:$NTHREADS \ | o2-fv0-entropy-encoder-workflow ${ARGS_ALL} --configKeyValues "$ARGS_ALL_CONFIG;" ${CTF_DICT} \ | o2-ctf-writer-workflow ${ARGS_ALL} ${ARGS_CTF} --configKeyValues "$ARGS_ALL_CONFIG;" --onlyDet FV0 --output-dir $CTF_DIR --ctf-dict-dir $FILEWORKDIR --output-type ctf \ diff --git a/DATA/testing/detectors/FV0/fv0-digits-qc-ctf.sh b/DATA/testing/detectors/FV0/fv0-digits-qc-ctf.sh index 62a5accd9..05c170095 100755 --- a/DATA/testing/detectors/FV0/fv0-digits-qc-ctf.sh +++ b/DATA/testing/detectors/FV0/fv0-digits-qc-ctf.sh @@ -1,13 +1,13 @@ #!/usr/bin/env bash +# Make common arguments and helper functions such as add_W available source common/setenv.sh +source common/getCommonArgs.sh +source common/gen_topo_helper_functions.sh + -SEVERITY=WARNING -ARGS_ALL="--session default --severity $SEVERITY --shm-segment-id $NUMAID --shm-segment-size $SHMSIZE" -ARGS_ALL+=" --infologger-severity $SEVERITY" if [ -z $CTF_DIR ]; then CTF_DIR=$FILEWORKDIR; fi # Directory where to store dictionary files #ARGS_ALL+=" --monitoring-backend influxdb-unix:///tmp/telegraf.sock" -ARGS_ALL_CONFIG="NameConf.mDirGRP=$FILEWORKDIR;NameConf.mDirGeom=$FILEWORKDIR;NameConf.mDirCollContext=$FILEWORKDIR;NameConf.mDirMatLUT=$FILEWORKDIR;keyval.input_dir=$FILEWORKDIR;keyval.output_dir=/dev/null" CTF_DICT="--ctf-dict $FILEWORKDIR/ctf_dictionary.root" NTHREADS=2 # Output directory for the CTF, to write to the current dir., remove `--output-dir $CTFOUT` from o2-ctf-writer-workflow or set to `CTFOUT=\"""\"` @@ -18,7 +18,9 @@ MYDIR="$(dirname $(readlink -f $0))" PROXY_INSPEC="x:FV0/RAWDATA;eos:***/INFORMATION;dd:FLP/DISTSUBTIMEFRAME/0" IN_CHANNEL="--channel-config 'name=readout-proxy,type=pull,method=connect,address=ipc://@$INRAWCHANNAME,transport=shmem,rateLogging=1'" -o2-dpl-raw-proxy ${ARGS_ALL} --readout-proxy "${IN_CHANNEL}" --dataspec "${PROXY_INSPEC}" \ +# TODO use add_W function from gen_topo_helper_functions.sh to assemble workflow +# as done for example in https://github.com/AliceO2Group/O2DPG/blob/master/DATA/production/calib/its-threshold-processing.sh +o2-dpl-raw-proxy ${ARGS_ALL} --readout-proxy "${IN_CHANNEL}" --dataspec "${PROXY_INSPEC}" --inject-missing-data \ | o2-fv0-flp-dpl-workflow --disable-root-output ${ARGS_ALL} --configKeyValues "$ARGS_ALL_CONFIG;" --pipeline fv0-datareader-dpl:$NTHREADS \ | o2-fv0-entropy-encoder-workflow ${ARGS_ALL} --configKeyValues "$ARGS_ALL_CONFIG;" ${CTF_DICT} \ | o2-qc ${ARGS_ALL} --local --host epn --config json://${MYDIR}/fv0-digits-ds.json \ diff --git a/DATA/testing/detectors/FV0/run_fv0_ctf.sh b/DATA/testing/detectors/FV0/run_fv0_ctf.sh index a40f395f0..f8078d223 100755 --- a/DATA/testing/detectors/FV0/run_fv0_ctf.sh +++ b/DATA/testing/detectors/FV0/run_fv0_ctf.sh @@ -25,7 +25,7 @@ export MULTIPLICITY_FACTOR_REST=1 # Factor to export OUTPUT_FILE_NAME=$HOME/topologies/fv0-ctf.xml -/opt/alisw/el8/GenTopo/bin/gen_topo.sh > $OUTPUT_FILE_NAME +/opt/alisw/el9/GenTopo/bin/gen_topo.sh > $OUTPUT_FILE_NAME if [ $? == 0 ]; then echo Generated XML topology $OUTPUT_FILE_NAME fi diff --git a/DATA/testing/detectors/FV0/run_fv0_digits_ctf.sh b/DATA/testing/detectors/FV0/run_fv0_digits_ctf.sh index 213ec4828..307d0c682 100755 --- a/DATA/testing/detectors/FV0/run_fv0_digits_ctf.sh +++ b/DATA/testing/detectors/FV0/run_fv0_digits_ctf.sh @@ -25,7 +25,7 @@ export MULTIPLICITY_FACTOR_REST=1 # Factor to export OUTPUT_FILE_NAME=$HOME/topologies/fv0-digits-ctf.xml -/opt/alisw/el8/GenTopo/bin/gen_topo.sh > $OUTPUT_FILE_NAME +/opt/alisw/el9/GenTopo/bin/gen_topo.sh > $OUTPUT_FILE_NAME if [ $? == 0 ]; then echo Generated XML topology $OUTPUT_FILE_NAME fi diff --git a/DATA/testing/detectors/FV0/run_fv0_digits_qc_ctf.sh b/DATA/testing/detectors/FV0/run_fv0_digits_qc_ctf.sh index e86d80f6a..e11a16fea 100755 --- a/DATA/testing/detectors/FV0/run_fv0_digits_qc_ctf.sh +++ b/DATA/testing/detectors/FV0/run_fv0_digits_qc_ctf.sh @@ -25,7 +25,7 @@ export MULTIPLICITY_FACTOR_REST=1 # Factor to export OUTPUT_FILE_NAME=$HOME/topologies/fv0-digits-qc-ctf.xml -/opt/alisw/el8/GenTopo/bin/gen_topo.sh > $OUTPUT_FILE_NAME +/opt/alisw/el9/GenTopo/bin/gen_topo.sh > $OUTPUT_FILE_NAME if [ $? == 0 ]; then echo Generated XML topology $OUTPUT_FILE_NAME fi diff --git a/DATA/testing/detectors/MID/mid-calib-workflow.sh b/DATA/testing/detectors/MID/mid-calib-workflow.sh index 7ddbafe5d..a9ad5a7a7 100755 --- a/DATA/testing/detectors/MID/mid-calib-workflow.sh +++ b/DATA/testing/detectors/MID/mid-calib-workflow.sh @@ -3,11 +3,11 @@ # shellcheck disable=SC1091 source testing/detectors/MID/mid_common.sh -WORKFLOW="o2-dpl-raw-proxy $ARGS_ALL --dataspec \"$MID_RAW_PROXY_INSPEC\" --channel-config \"$MID_DPL_CHANNEL_CONFIG\" | " +WORKFLOW="o2-dpl-raw-proxy $ARGS_ALL --dataspec \"$MID_RAW_PROXY_INSPEC\" --inject-missing-data --channel-config \"$MID_DPL_CHANNEL_CONFIG\" | " WORKFLOW+="o2-mid-raw-to-digits-workflow $ARGS_ALL $MID_RAW_TO_DIGITS_OPTS | " WORKFLOW+="o2-mid-calibration-workflow $ARGS_ALL | " WORKFLOW+="o2-calibration-ccdb-populator-workflow $ARGS_ALL --configKeyValues \"$ARGS_ALL_CONFIG\" --ccdb-path=\"http://o2-ccdb.internal\" --sspec-min 0 --sspec-max 0 | " -WORKFLOW+="o2-calibration-ccdb-populator-workflow $ARGS_ALL --configKeyValues \"$ARGS_ALL_CONFIG\" --ccdb-path=\"http://alio2-cr1-flp199.cern.ch:8083\" --sspec-min 1 --sspec-max 1 --name-extention dcs | " +WORKFLOW+="o2-calibration-ccdb-populator-workflow $ARGS_ALL --configKeyValues \"$ARGS_ALL_CONFIG\" --ccdb-path=\"${DCSCCDBSERVER:-http://alio2-cr1-flp199-ib:8083}\" --sspec-min 1 --sspec-max 1 --name-extention dcs | " WORKFLOW+="o2-dpl-run $ARGS_ALL $GLOBALDPLOPT" if [ "$WORKFLOWMODE" == "print" ]; then diff --git a/DATA/testing/detectors/MID/mid-qcmn-epn-digits.json b/DATA/testing/detectors/MID/mid-qcmn-epn-digits.json index f63e66e8c..cf20c554c 100644 --- a/DATA/testing/detectors/MID/mid-qcmn-epn-digits.json +++ b/DATA/testing/detectors/MID/mid-qcmn-epn-digits.json @@ -55,7 +55,7 @@ "id": "mid-digits", "active": "true", "machines": [], - "query": "digits:MID/DATA;digits_rof:MID/DATAROF", + "query": "digits:MID/DATA/0;digits_rof:MID/DATAROF/0", "samplingConditions": [ { "condition": "random", diff --git a/DATA/testing/detectors/MID/mid-raw-to-ctf-qc-workflow.sh b/DATA/testing/detectors/MID/mid-raw-to-ctf-qc-workflow.sh index aa9919102..d731fb767 100755 --- a/DATA/testing/detectors/MID/mid-raw-to-ctf-qc-workflow.sh +++ b/DATA/testing/detectors/MID/mid-raw-to-ctf-qc-workflow.sh @@ -3,7 +3,7 @@ # shellcheck disable=SC1091 source testing/detectors/MID/mid_common.sh -WORKFLOW="o2-dpl-raw-proxy $ARGS_ALL --dataspec \"$MID_RAW_PROXY_INSPEC\" --channel-config \"$MID_DPL_CHANNEL_CONFIG\" | " +WORKFLOW="o2-dpl-raw-proxy $ARGS_ALL --dataspec \"$MID_RAW_PROXY_INSPEC\" --inject-missing-data --channel-config \"$MID_DPL_CHANNEL_CONFIG\" | " WORKFLOW+="o2-mid-raw-to-digits-workflow $ARGS_ALL $MID_RAW_TO_DIGITS_OPTS | " WORKFLOW+="o2-mid-entropy-encoder-workflow $ARGS_ALL | " WORKFLOW+="o2-ctf-writer-workflow $ARGS_ALL $MID_CTF_WRITER_OPTS | " diff --git a/DATA/testing/detectors/TOF/tof-epn-cosmics-dig.sh b/DATA/testing/detectors/TOF/tof-epn-cosmics-dig.sh index b8781db74..d96bae672 100755 --- a/DATA/testing/detectors/TOF/tof-epn-cosmics-dig.sh +++ b/DATA/testing/detectors/TOF/tof-epn-cosmics-dig.sh @@ -17,7 +17,7 @@ OUT_CHANNEL="name=downstream,method=connect,address=tcp://${calibration_node},ty PROXY_OUTSPEC="dd:FLP/DISTSUBTIMEFRAME/0;dig:TOF/DIGITS;head:TOF/DIGITHEADER;row:TOF/READOUTWINDOW;patt:TOF/PATTERNS;diafreq:TOF/DIAFREQ" -o2-dpl-raw-proxy ${ARGS_ALL} --dataspec "${PROXY_INSPEC}" \ +o2-dpl-raw-proxy ${ARGS_ALL} --dataspec "${PROXY_INSPEC}" --inject-missing-data \ --readout-proxy "--channel-config 'name=readout-proxy,type=pull,method=connect,address=ipc://@$INRAWCHANNAME,transport=shmem,rateLogging=1'" \ | o2-tof-reco-workflow --input-type raw --output-type digits --disable-root-output \ ${ARGS_ALL} --configKeyValues "$ARGS_ALL_CONFIG;" \ diff --git a/DATA/testing/detectors/TOF/tof-epn-cosmics-digNoQC.sh b/DATA/testing/detectors/TOF/tof-epn-cosmics-digNoQC.sh index d50e2c8e0..3546e5e23 100755 --- a/DATA/testing/detectors/TOF/tof-epn-cosmics-digNoQC.sh +++ b/DATA/testing/detectors/TOF/tof-epn-cosmics-digNoQC.sh @@ -17,7 +17,7 @@ OUT_CHANNEL="name=downstream,method=connect,address=tcp://${calibration_node},ty PROXY_OUTSPEC="dd:FLP/DISTSUBTIMEFRAME/0;dig:TOF/DIGITS;head:TOF/DIGITHEADER;row:TOF/READOUTWINDOW;patt:TOF/PATTERNS;diafreq:TOF/DIAFREQ" -o2-dpl-raw-proxy ${ARGS_ALL} --dataspec "${PROXY_INSPEC}" \ +o2-dpl-raw-proxy ${ARGS_ALL} --dataspec "${PROXY_INSPEC}" --inject-missing-data \ --readout-proxy "--channel-config 'name=readout-proxy,type=pull,method=connect,address=ipc://@$INRAWCHANNAME,transport=shmem,rateLogging=1'" \ | o2-tof-reco-workflow --input-type raw --output-type digits --disable-root-output \ ${ARGS_ALL} --configKeyValues "$ARGS_ALL_CONFIG;" \ diff --git a/DATA/testing/detectors/TOF/tof-epn-cosmics.sh b/DATA/testing/detectors/TOF/tof-epn-cosmics.sh index 32f489fd7..fbe7c4259 100755 --- a/DATA/testing/detectors/TOF/tof-epn-cosmics.sh +++ b/DATA/testing/detectors/TOF/tof-epn-cosmics.sh @@ -17,7 +17,7 @@ OUT_CHANNEL="name=downstream,method=connect,address=tcp://${calibration_node},ty PROXY_OUTSPEC="dd:FLP/DISTSUBTIMEFRAME/0;calclus:TOF/INFOCALCLUS;cosmics:TOF/INFOCOSMICS;trkcos:TOF/INFOTRACKCOS;trksiz:TOF/INFOTRACKSIZE" -o2-dpl-raw-proxy ${ARGS_ALL} --dataspec "${PROXY_INSPEC}" \ +o2-dpl-raw-proxy ${ARGS_ALL} --dataspec "${PROXY_INSPEC}" --inject-missing-data \ --readout-proxy "--channel-config 'name=readout-proxy,type=pull,method=connect,address=ipc://@$INRAWCHANNAME,transport=shmem,rateLogging=1'" \ | o2-tof-reco-workflow --input-type raw --output-type clusters \ ${ARGS_ALL} --configKeyValues "$ARGS_ALL_CONFIG;" \ diff --git a/DATA/testing/detectors/TOF/tof-epn-cosmicsNoQC.sh b/DATA/testing/detectors/TOF/tof-epn-cosmicsNoQC.sh index 24d37a8e2..28ece2314 100755 --- a/DATA/testing/detectors/TOF/tof-epn-cosmicsNoQC.sh +++ b/DATA/testing/detectors/TOF/tof-epn-cosmicsNoQC.sh @@ -17,7 +17,7 @@ OUT_CHANNEL="name=downstream,method=connect,address=tcp://${calibration_node},ty PROXY_OUTSPEC="dd:FLP/DISTSUBTIMEFRAME/0;calclus:TOF/INFOCALCLUS;cosmics:TOF/INFOCOSMICS;trkcos:TOF/INFOTRACKCOS;trksiz:TOF/INFOTRACKSIZE" -o2-dpl-raw-proxy ${ARGS_ALL} --dataspec "${PROXY_INSPEC}" \ +o2-dpl-raw-proxy ${ARGS_ALL} --dataspec "${PROXY_INSPEC}" --inject-missing-data \ --readout-proxy "--channel-config 'name=readout-proxy,type=pull,method=connect,address=ipc://@$INRAWCHANNAME,transport=shmem,rateLogging=1'" \ | o2-tof-reco-workflow --input-type raw --output-type clusters \ ${ARGS_ALL} --configKeyValues "$ARGS_ALL_CONFIG;" \ diff --git a/DATA/testing/detectors/TPC/tpc-krypton-raw.sh b/DATA/testing/detectors/TPC/tpc-krypton-raw.sh index 3644177fb..935454bbd 100755 --- a/DATA/testing/detectors/TPC/tpc-krypton-raw.sh +++ b/DATA/testing/detectors/TPC/tpc-krypton-raw.sh @@ -1,70 +1,55 @@ #!/usr/bin/env bash source common/setenv.sh - +source common/getCommonArgs.sh +source common/gen_topo_helper_functions.sh export SHMSIZE=$(( 128 << 30 )) # GB for the global SHMEM # for kr cluster finder -source common/getCommonArgs.sh -if [ $NUMAGPUIDS != 0 ]; then - ARGS_ALL+=" --child-driver 'numactl --membind $NUMAID --cpunodebind $NUMAID'" -fi -if [ $GPUTYPE != "CPU" ]; then - ARGS_ALL+=" --shm-mlock-segment-on-creation 1" -fi -if [ $GPUTYPE == "HIP" ]; then - if [ $NUMAID == 0 ] || [ $NUMAGPUIDS == 0 ]; then - export TIMESLICEOFFSET=0 - else - export TIMESLICEOFFSET=$NGPUS - fi - GPU_CONFIG_KEY+="GPU_proc.deviceNum=0;GPU_global.mutexMemReg=true;" - GPU_CONFIG+=" --environment \"ROCR_VISIBLE_DEVICES={timeslice${TIMESLICEOFFSET}}\"" - export HSA_NO_SCRATCH_RECLAIM=1 - #export HSA_TOOLS_LIB=/opt/rocm/lib/librocm-debug-agent.so.2 -else - GPU_CONFIG_KEY+="GPU_proc.deviceNum=-2;" -fi +PROXY_INSPEC="A:TPC/RAWDATA;dd:FLP/DISTSUBTIMEFRAME/0" +CALIB_INSPEC="A:TPC/RAWDATA;dd:FLP/DISTSUBTIMEFRAME/0" -if [ $GPUTYPE != "CPU" ]; then - GPU_CONFIG_KEY+="GPU_proc.forceMemoryPoolSize=$GPUMEMSIZE;" - if [ $HOSTMEMSIZE == "0" ]; then - HOSTMEMSIZE=$(( 1 << 30 )) - fi +NLANES=36 +SESSION="default" +ARGS_FILES="keyval.output_dir=/dev/null" +HOST=localhost + + + +if [[ ! -z ${TPC_KRYPTON_LANES:-} ]]; then + NLANES=${TPC_KRYPTON_LANES} fi -if [ $HOSTMEMSIZE != "0" ]; then - GPU_CONFIG_KEY+="GPU_proc.forceHostMemoryPoolSize=$HOSTMEMSIZE;" + +#QC_CONFIG="consul-json://alio2-cr1-hv-con01.cern.ch:8500/o2/components/qc/ANY/any/tpc-krypton-raw-qcmn" +QC_CONFIG="/o2/components/qc/ANY/any/tpc-krypton-raw-qcmn" + + + +WRITER_TYPE="--writer-type EPN --meta-output-dir $EPN2EOS_METAFILES_DIR --output-dir $CALIB_DIR --max-tf-per-file 8000" + +if [[ "${TPC_KRYPTON_NO_WRITEOUT:-}" == "1" ]]; then + WRITER_TYPE="--writer-type none" fi -PROXY_INSPEC="A:TPC/RAWDATA;dd:FLP/DISTSUBTIMEFRAME/0;eos:***/INFORMATION" -CALIB_INSPEC="A:TPC/RAWDATA;dd:FLP/DISTSUBTIMEFRAME/0;eos:***/INFORMATION" -NLANES=36 -SESSION="default" -PIPEADD="0" -ARGS_FILES="NameConf.mDirGRP=/home/epn/odc/files/;NameConf.mDirGeom=/home/epn/odc/files/;keyval.output_dir=/dev/null" -HOST=localhost -QC_CONFIG="consul-json://alio2-cr1-hv-con01.cern.ch:8500/o2/components/qc/ANY/any/tpc-raw-qcmn" - - -o2-dpl-raw-proxy $ARGS_ALL \ - --dataspec "$PROXY_INSPEC" \ - --readout-proxy '--channel-config "name=readout-proxy,type=pull,method=connect,address=ipc://@tf-builder-pipe-0,transport=shmem,rateLogging=1"' \ - | o2-tpc-raw-to-digits-workflow $ARGS_ALL \ - --input-spec "$CALIB_INSPEC" \ - --configKeyValues "$ARGS_FILES" \ - --remove-duplicates \ - --pipeline tpc-raw-to-digits-0:24 \ - --pedestal-url "http://ccdb-test.cern.ch:8080" \ - | o2-tpc-krypton-raw-filter $ARGS_ALL \ - --configKeyValues "$ARGS_FILES" \ - --lanes $NLANES \ - --writer-type EPN \ - --meta-output-dir /data/epn2eos_tool/epn2eos/ \ - --output-dir /data/tf/raw \ - --threshold-max 20 \ - --max-tf-per-file 8000 \ - --time-bins-before 20 \ - | o2-qc $ARGS_ALL --config $QC_CONFIG --local --host $HOST \ - | o2-dpl-run $ARGS_ALL --dds ${WORKFLOWMODE_FILE} | grep -v ERROR + +# TODO use add_W function from gen_topo_helper_functions.sh to assemble workflow +# as done for example in https://github.com/AliceO2Group/O2DPG/blob/master/DATA/production/calib/its-threshold-processing.sh + +WORKFLOW= +add_W o2-dpl-raw-proxy "--dataspec \"$PROXY_INSPEC\" --inject-missing-data --channel-config \"name=readout-proxy,type=pull,method=connect,address=ipc://@tf-builder-pipe-0,transport=shmem,rateLogging=1\"" "" 0 +add_W o2-tpc-raw-to-digits-workflow "--ignore-grp --input-spec \"$CALIB_INSPEC\" --remove-duplicates --pedestal-url \"http://o2-ccdb.internal\" --pipeline tpc-raw-to-digits-0:24 " "\"${ARGS_FILES}\";TPCDigitDump.LastTimeBin=446" +add_W o2-tpc-krypton-raw-filter "${WRITER_TYPE} --lanes $NLANES --threshold-max 20 --time-bins-before 20" "\"${ARGS_FILES}\"" +add_W o2-qc "--config $QC_CONFIG --local --host localhost" +WORKFLOW+="o2-dpl-run ${ARGS_ALL} ${GLOBALDPLOPT}" + +if [ $WORKFLOWMODE == "print" ]; then + echo Workflow command: + echo $WORKFLOW | sed "s/| */|\n/g" +else + # Execute the command we have assembled + WORKFLOW+=" --$WORKFLOWMODE ${WORKFLOWMODE_FILE}" + eval $WORKFLOW +fi + diff --git a/DATA/testing/detectors/TPC/tpc-krypton.sh b/DATA/testing/detectors/TPC/tpc-krypton.sh index c2c9b1b16..fbbd9d12c 100755 --- a/DATA/testing/detectors/TPC/tpc-krypton.sh +++ b/DATA/testing/detectors/TPC/tpc-krypton.sh @@ -2,71 +2,59 @@ source common/setenv.sh +source common/getCommonArgs.sh + +source common/gen_topo_helper_functions.sh + export GLOBAL_SHMSIZE=$(( 128 << 30 )) # GB for the global SHMEM # for kr cluster finder -source common/getCommonArgs.sh if [ $NUMAGPUIDS != 0 ]; then ARGS_ALL+=" --child-driver 'numactl --membind $NUMAID --cpunodebind $NUMAID'" fi -if [ $GPUTYPE != "CPU" ]; then - ARGS_ALL+=" --shm-mlock-segment-on-creation 1" -fi -if [ $GPUTYPE == "HIP" ]; then - if [ $NUMAID == 0 ] || [ $NUMAGPUIDS == 0 ]; then - export TIMESLICEOFFSET=0 - else - export TIMESLICEOFFSET=$NGPUS - fi - GPU_CONFIG_KEY+="GPU_proc.deviceNum=0;GPU_global.mutexMemReg=true;" - GPU_CONFIG+=" --environment \"ROCR_VISIBLE_DEVICES={timeslice${TIMESLICEOFFSET}}\"" - export HSA_NO_SCRATCH_RECLAIM=1 - #export HSA_TOOLS_LIB=/opt/rocm/lib/librocm-debug-agent.so.2 -else - GPU_CONFIG_KEY+="GPU_proc.deviceNum=-2;" -fi +PROXY_INSPEC="A:TPC/RAWDATA;dd:FLP/DISTSUBTIMEFRAME/0" +CALIB_INSPEC="A:TPC/RAWDATA;dd:FLP/DISTSUBTIMEFRAME/0" + + +NLANES=36 +SESSION="default" + +ARGS_FILES="keyval.output_dir=/dev/null" -if [ $GPUTYPE != "CPU" ]; then - GPU_CONFIG_KEY+="GPU_proc.forceMemoryPoolSize=$GPUMEMSIZE;" - if [ $HOSTMEMSIZE == "0" ]; then - HOSTMEMSIZE=$(( 1 << 30 )) - fi +QC_CONFIG="consul-json://alio2-cr1-hv-con01.cern.ch:8500/o2/components/qc/ANY/any/tpc-krypton-qcmn" +#QC_CONFIG="/o2/components/qc/ANY/any/tpc-krypton-qcmn" + +WRITER_TYPE="--writer-type EPN --meta-output-dir $EPN2EOS_METAFILES_DIR --output-dir $CALIB_DIR" + + +if [[ "${TPC_KRYPTON_NO_WRITEOUT:-}" == "1" ]]; then + WRITER_TYPE="--writer-type none" fi -if [ $HOSTMEMSIZE != "0" ]; then - GPU_CONFIG_KEY+="GPU_proc.forceHostMemoryPoolSize=$HOSTMEMSIZE;" + + +if [[ ! -z ${TPC_KRYPTON_LANES:-} ]]; then + NLANES=${TPC_KRYPTON_LANES} fi -PROXY_INSPEC="A:TPC/RAWDATA;dd:FLP/DISTSUBTIMEFRAME/0;eos:***/INFORMATION" -CALIB_INSPEC="A:TPC/RAWDATA;dd:FLP/DISTSUBTIMEFRAME/0;eos:***/INFORMATION" +# TODO use add_W function from gen_topo_helper_functions.sh to assemble workflow +# as done for example in https://github.com/AliceO2Group/O2DPG/blob/master/DATA/production/calib/its-threshold-processing.sh -WRITER_TYPE="none" -workflow_has_parameter ROOT && export WRITER_TYPE="EPN" -NLANES=1 -SESSION="default" -PIPEADD="0" -ARGS_FILES="NameConf.mDirGRP=/home/epn/odc/files/;NameConf.mDirGeom=/home/epn/odc/files/;keyval.output_dir=/dev/null" -QC_CONFIG="consul-json://alio2-cr1-hv-con01.cern.ch:8500/o2/components/qc/ANY/any/tpc-full-qcmn-krypton" -if [ $WRITER_TYPE == "EPN" ]; then - KR_CONFIG="--writer-type ${WRITER_TYPE} --meta-output-dir /data/epn2eos_tool/epn2eos/ --output-dir /data/tf/raw --max-tf-per-file 2000 " -else - KR_CONFIG="--writer-type ${WRITER_TYPE} " -fi +# Add binarry to workflow command USAGE: add_W [BINARY] [COMMAND_LINE_OPTIONS] [CONFIG_KEY_VALUES] [Add ARGS_ALL_CONFIG, optional, default = 1] +WORKFLOW= +add_W o2-dpl-raw-proxy "--dataspec \"$PROXY_INSPEC\" --inject-missing-data --channel-config \"name=readout-proxy,type=pull,method=connect,address=ipc://@tf-builder-pipe-0,transport=shmem,rateLogging=1\"" "" 0 +add_W o2-tpc-raw-to-digits-workflow "--ignore-grp --input-spec \"$CALIB_INSPEC\" --remove-duplicates --pipeline tpc-raw-to-digits-0:20 " "\"${ARGS_FILES}\";TPCDigitDump.LastTimeBin=14256" +add_W o2-tpc-krypton-clusterer "${WRITER_TYPE} --lanes $NLANES --configFile=\"/home/wiechula/processData/inputFilesTracking/krypton/krBoxCluster.largeBox.cuts.krMap.ini\"" "\"${ARGS_FILES}\"" +add_W o2-qc "--config $QC_CONFIG --local --host localhost" +WORKFLOW+="o2-dpl-run ${ARGS_ALL} ${GLOBALDPLOPT}" -o2-dpl-raw-proxy $ARGS_ALL \ - --dataspec "$PROXY_INSPEC" \ - --readout-proxy "--channel-config 'name=readout-proxy,type=pull,method=connect,address=ipc://@tf-builder-pipe-${PIPEADD},transport=shmem,rateLogging=1'" \ - | o2-tpc-raw-to-digits-workflow $ARGS_ALL \ - --input-spec "$CALIB_INSPEC" \ - --configKeyValues "$ARGS_FILES" \ - --remove-duplicates \ - --pipeline tpc-raw-to-digits-0:12 \ - | o2-tpc-krypton-clusterer $ARGS_ALL \ - --lanes $NLANES \ - --configKeyValues "$ARGS_FILES" \ - --configFile="/home/wiechula/processData/inputFilesTracking/krypton/krBoxCluster.largeBox.cuts.krMap.ini" \ - $KR_CONFIG \ - | o2-qc $ARGS_ALL --config $QC_CONFIG --local --host localhost \ - | o2-dpl-run --dds ${WORKFLOWMODE_FILE} | grep -v ERROR +if [ $WORKFLOWMODE == "print" ]; then + echo Workflow command: + echo $WORKFLOW | sed "s/| */|\n/g" +else + # Execute the command we have assembled + WORKFLOW+=" --$WORKFLOWMODE ${WORKFLOWMODE_FILE}" + eval $WORKFLOW +fi diff --git a/DATA/testing/detectors/TPC/tpc-laser-raw-filter.sh b/DATA/testing/detectors/TPC/tpc-laser-raw-filter.sh index 557b25c42..f24fbcd2c 100755 --- a/DATA/testing/detectors/TPC/tpc-laser-raw-filter.sh +++ b/DATA/testing/detectors/TPC/tpc-laser-raw-filter.sh @@ -4,44 +4,39 @@ source common/setenv.sh source common/getCommonArgs.sh -source common/gen_topo_helper_functions.sh - -export SHMSIZE=$(( 128 << 30 )) # GB for the global SHMEM # for kr cluster finder +source common/gen_topo_helper_functions.sh if [ $NUMAGPUIDS != 0 ]; then ARGS_ALL+=" --child-driver 'numactl --membind $NUMAID --cpunodebind $NUMAID'" fi -PROXY_INSPEC="A:TPC/RAWDATA;dd:FLP/DISTSUBTIMEFRAME/0;eos:***/INFORMATION" -CALIB_INSPEC="A:TPC/RAWDATA;dd:FLP/DISTSUBTIMEFRAME/0;eos:***/INFORMATION" +PROXY_INSPEC="A:TPC/RAWDATA;dd:FLP/DISTSUBTIMEFRAME/0" +CALIB_INSPEC="A:TPC/RAWDATA;dd:FLP/DISTSUBTIMEFRAME/0" NLANES=36 SESSION="default" PIPEADD="0" -ARGS_FILES="NameConf.mDirGRP=/home/epn/odc/files/;NameConf.mDirGeom=/home/epn/odc/files/;keyval.output_dir=/dev/null" HOST=localhost - QC_CONFIG="consul-json://alio2-cr1-hv-con01.cern.ch:8500/o2/components/qc/ANY/any/tpc-raw-qcmn" - - -o2-dpl-raw-proxy $ARGS_ALL \ - --dataspec "$PROXY_INSPEC" \ - --readout-proxy '--channel-config "name=readout-proxy,type=pull,method=connect,address=ipc://@tf-builder-pipe-0,transport=shmem,rateLogging=1"' \ - | o2-tpc-raw-to-digits-workflow $ARGS_ALL \ - --input-spec "$CALIB_INSPEC" \ - --configKeyValues "$ARGS_FILES" \ - --remove-duplicates \ - --pipeline tpc-raw-to-digits-0:24 \ - | o2-tpc-krypton-raw-filter $ARGS_ALL \ - --configKeyValues "$ARGS_FILES" \ - --lanes $NLANES \ - --writer-type EPN \ - --meta-output-dir $EPN2EOS_METAFILES_DIR \ - --output-dir $CALIB_DIR \ - --threshold-max 20 \ - --max-tf-per-file 8000 \ - --time-bins-before 20 \ - --max-time-bins 650 \ - | o2-qc $ARGS_ALL --config ${QC_CONFIG} --local --host $HOST \ - | o2-dpl-run $ARGS_ALL --dds ${WORKFLOWMODE_FILE} ${GLOBALDPLOPT} +QC_CONFIG_CONSUL=/o2/components/qc/ANY/any/tpc-raw-qcmn +# TODO use add_W function from gen_topo_helper_functions.sh to assemble workflow +# as done for example in https://github.com/AliceO2Group/O2DPG/blob/master/DATA/production/calib/its-threshold-processing.sh + +WORKFLOW= +add_W o2-dpl-raw-proxy "--dataspec \"$PROXY_INSPEC\" --inject-missing-data --channel-config \"name=readout-proxy,type=pull,method=connect,address=ipc://@tf-builder-pipe-0,transport=shmem,rateLogging=1\"" "" 0 +add_W o2-tpc-raw-to-digits-workflow "--ignore-grp --input-spec \"$CALIB_INSPEC\" --remove-duplicates --pipeline tpc-raw-to-digits-0:20" +add_W o2-tpc-krypton-raw-filter "tpc-raw-to-digits-0:24 --lanes $NLANES --writer-type EPN --meta-output-dir $EPN2EOS_METAFILES_DIR --output-dir $CALIB_DIR --threshold-max 20 --max-tf-per-file 8000 --time-bins-before 20 --max-time-bins 650" +add_QC_from_consul "${QC_CONFIG_CONSUL}" "--local --host localhost" + + + +WORKFLOW+="o2-dpl-run ${ARGS_ALL} ${GLOBALDPLOPT}" +if [ $WORKFLOWMODE == "print" ]; then + echo Workflow command: + echo $WORKFLOW | sed "s/| */|\n/g" +else + # Execute the command we have assembled + WORKFLOW+=" --$WORKFLOWMODE ${WORKFLOWMODE_FILE}" + eval $WORKFLOW +fi diff --git a/DATA/testing/detectors/TPC/tpc-pattern-generator.sh b/DATA/testing/detectors/TPC/tpc-pattern-generator.sh index d6a0550ad..7e8b59b61 100755 --- a/DATA/testing/detectors/TPC/tpc-pattern-generator.sh +++ b/DATA/testing/detectors/TPC/tpc-pattern-generator.sh @@ -22,21 +22,25 @@ CALIB_INSPEC="A:TPC/RAWDATA;dd:FLP/DISTSUBTIMEFRAME/0;eos:***/INFORMATION" ### Comment: MAKE SURE the channels match address=ipc://@tf-builder-pipe-0 HOST=localhost QC_CONFIG="consul-json://alio2-cr1-hv-con01.cern.ch:8500/o2/components/qc/ANY/any/tpc-raw-qcmn" +QC_CONFIG_CONSUL="/o2/components/qc/ANY/any/tpc-raw-qcmn" +EXTRA_CONFIG="TPCDigitDump.NoiseThreshold=3;TPCDigitDump.LastTimeBin=600" +WORKFLOW= +add_W o2-dpl-raw-proxy "--dataspec \"$PROXY_INSPEC\" --inject-missing-data --channel-config \"name=readout-proxy,type=pull,method=connect,address=ipc://@tf-builder-pipe-0,transport=shmem,rateLogging=1\"" "" 0 +add_W o2-tpc-raw-to-digits-workflow "--ignore-grp --input-spec \"$CALIB_INSPEC\" --remove-duplicates --pipeline tpc-raw-to-digits-0:20" "${EXTRA_CONFIG}" +add_QC_from_consul "${QC_CONFIG_CONSUL}" "--local --host lcoalhost" +WORKFLOW+="o2-dpl-run ${ARGS_ALL} ${GLOBALDPLOPT}" +if [ $WORKFLOWMODE == "print" ]; then + echo Workflow command: + echo $WORKFLOW | sed "s/| */|\n/g" +else + # Execute the command we have assembled + WORKFLOW+=" --$WORKFLOWMODE ${WORKFLOWMODE_FILE}" + eval $WORKFLOW +fi -o2-dpl-raw-proxy $ARGS_ALL \ - --dataspec "$PROXY_INSPEC" \ - --readout-proxy "--channel-config 'name=readout-proxy,type=pull,method=connect,address=ipc://@tf-builder-pipe-0,transport=shmem,rateLogging=1'" \ - | o2-tpc-raw-to-digits-workflow $ARGS_ALL \ - --input-spec "$CALIB_INSPEC" \ - --configKeyValues "TPCDigitDump.NoiseThreshold=3;TPCDigitDump.LastTimeBin=600;$ARGS_ALL_CONFIG" \ - --pipeline tpc-raw-to-digits-0:20 \ - --remove-duplicates \ - | o2-qc $ARGS_ALL --config $QC_CONFIG --local --host $HOST \ - | o2-dpl-run $ARGS_ALL --dds ${WORKFLOWMODE_FILE} - diff --git a/DATA/testing/detectors/TPC/workflows.desc b/DATA/testing/detectors/TPC/workflows.desc index 1e763d763..4007a87b1 100644 --- a/DATA/testing/detectors/TPC/workflows.desc +++ b/DATA/testing/detectors/TPC/workflows.desc @@ -1,3 +1,4 @@ TPC-krypton: "O2PDPSuite" reco,60,60,"SHMSIZE=128000000000 testing/detectors/TPC/tpc-krypton.sh" -TPC-laser-raw-filter: "O2PDPSuite" reco,80,80,"SHMSIZE=128000000000 testing/detectors/TPC/tpc-laser-raw-filter.sh" -TPC-pattern-generator: "O2PDPSuite" reco,1,1,"SHMSIZE=$((112 << 30)) GPUTYPE=HIP GPUMEMSIZE=$(( 24 << 30 )) HOSTMEMSIZE=$(( 5 << 30 )) testing/detectors/TPC/tpc-pattern-generator.sh" +TPC-krypton-raw: "O2PDPSuite" reco,60,60,"SHMSIZE=128000000000 testing/detectors/TPC/tpc-krypton-raw.sh" +TPC-laser-raw-filter: "O2PDPSuite" reco,80,80,"SHMSIZE=$((128 << 30)) GPUTYPE=HIP GPUMEMSIZE=$(( 24 << 30 )) HOSTMEMSIZE=$(( 5 << 30 )) testing/detectors/TPC/tpc-laser-raw-filter.sh" +TPC-pattern-generator: "O2PDPSuite" reco,1,1,"SHMSIZE=$((112 << 30)) GPUTYPE=HIP GPUMEMSIZE=$(( 24 << 30 )) HOSTMEMSIZE=$(( 5 << 30 )) testing/detectors/TPC/tpc-pattern-generator.sh" diff --git a/DATA/testing/detectors/TRD/trd-krypton.sh b/DATA/testing/detectors/TRD/trd-krypton.sh index cfeec45d8..7c643f9b9 100755 --- a/DATA/testing/detectors/TRD/trd-krypton.sh +++ b/DATA/testing/detectors/TRD/trd-krypton.sh @@ -33,7 +33,7 @@ CTF_CONFIG+=" --require-free-disk 53687091200 --wait-for-free-disk $CTF_FREE_DIS # Start with an empty workflow WORKFLOW= -add_W o2-dpl-raw-proxy "--dataspec \"$PROXY_INSPEC\" --readout-proxy \"--channel-config \\\"name=readout-proxy,type=pull,method=connect,address=ipc://@tf-builder-pipe-0,transport=shmem,rateLogging=1\\\"\"" "" 0 +add_W o2-dpl-raw-proxy "--dataspec \"$PROXY_INSPEC\" --inject-missing-data --readout-proxy \"--channel-config \\\"name=readout-proxy,type=pull,method=connect,address=ipc://@tf-builder-pipe-0,transport=shmem,rateLogging=1\\\"\"" "" 0 add_W o2-trd-datareader "--disable-root-output --pipeline trd-datareader:$TRD_N_READERS" add_W o2-trd-kr-clusterer "--disable-root-input --meta-output-dir $EPN2EOS_METAFILES_DIR --output-dir $CALIB_DIR --autosave-interval 105000 --pipeline trd-kr-clusterer:8" if workflow_has_parameter QC && has_detector_qc TRD; then diff --git a/DATA/testing/detectors/TRD/trd-noise.sh b/DATA/testing/detectors/TRD/trd-noise.sh index 4b84b1ef6..cb18ed703 100755 --- a/DATA/testing/detectors/TRD/trd-noise.sh +++ b/DATA/testing/detectors/TRD/trd-noise.sh @@ -22,7 +22,7 @@ fi # Start with an empty workflow WORKFLOW= -add_W o2-dpl-raw-proxy "--dataspec \"$PROXY_INSPEC\" --readout-proxy \"--channel-config \\\"name=readout-proxy,type=pull,method=connect,address=ipc://@tf-builder-pipe-0,transport=shmem,rateLogging=1\\\"\"" "" 0 +add_W o2-dpl-raw-proxy "--dataspec \"$PROXY_INSPEC\" --inject-missing-data --readout-proxy \"--channel-config \\\"name=readout-proxy,type=pull,method=connect,address=ipc://@tf-builder-pipe-0,transport=shmem,rateLogging=1\\\"\"" "" 0 add_W o2-trd-datareader "--disable-root-output --every-nth-tf $TRD_REJECTION_FACTOR --pipeline trd-datareader:$TRD_N_READERS" add_W o2-calibration-trd-workflow "--noise --calib-dds-collection-index 0" add_W o2-calibration-ccdb-populator-workflow "--ccdb-path $TRD_CCDB_PATH" diff --git a/DATA/testing/examples/example-calib-processing.sh b/DATA/testing/examples/example-calib-processing.sh index e52adaf78..18ae83e87 100755 --- a/DATA/testing/examples/example-calib-processing.sh +++ b/DATA/testing/examples/example-calib-processing.sh @@ -9,7 +9,7 @@ source common/getCommonArgs.sh PROXY_INSPEC="A:ITS/RAWDATA;dd:FLP/DISTSUBTIMEFRAME/0;eos:***/INFORMATION" PROXY_OUTSPEC="downstreamA:ITS/COMPCLUSTERS/0;downstreamB:ITS/PATTERNS/0;downstreamC:ITS/CLUSTERSROF/0" -WORKFLOW="o2-dpl-raw-proxy ${ARGS_ALL} --dataspec \"$PROXY_INSPEC\" --channel-config \"name=readout-proxy,type=pull,method=connect,address=ipc://@$INRAWCHANNAME,transport=shmem,rateLogging=0\" | " +WORKFLOW="o2-dpl-raw-proxy ${ARGS_ALL} --dataspec \"$PROXY_INSPEC\" --inject-missing-data --channel-config \"name=readout-proxy,type=pull,method=connect,address=ipc://@$INRAWCHANNAME,transport=shmem,rateLogging=0\" | " WORKFLOW+="o2-itsmft-stf-decoder-workflow ${ARGS_ALL} --configKeyValues \"$ARGS_ALL_CONFIG\" --nthreads 4 | " WORKFLOW+="o2-dpl-output-proxy ${ARGS_ALL} --dataspec \"$PROXY_OUTSPEC\" --proxy-channel-name its-noise-input-proxy --channel-config \"name=its-noise-input-proxy,method=connect,type=push,transport=zeromq,rateLogging=0\" | " WORKFLOW+="o2-dpl-run ${ARGS_ALL} ${GLOBALDPLOPT}" diff --git a/DATA/testing/examples/example-workflow.sh b/DATA/testing/examples/example-workflow.sh index 15d5a3449..ea3c5a16f 100755 --- a/DATA/testing/examples/example-workflow.sh +++ b/DATA/testing/examples/example-workflow.sh @@ -1,17 +1,21 @@ #!/usr/bin/env bash +# Make common arguments and helper functions such as add_W available source common/setenv.sh - -SEVERITY=warning source common/getCommonArgs.sh +source common/gen_topo_helper_functions.sh +# Define input data required by DPL (in this case all RAWDATA from TPC) PROXY_INSPEC="A:TPC/RAWDATA;dd:FLP/DISTSUBTIMEFRAME/0;eos:***/INFORMATION" -o2-dpl-raw-proxy $ARGS_ALL \ - --dataspec "$PROXY_INSPEC" \ - --readout-proxy "--channel-config 'name=readout-proxy,type=pull,method=connect,address=ipc://@$INRAWCHANNAME,transport=shmem,rateLogging=1'" \ - | o2-tpc-raw-to-digits-workflow $ARGS_ALL \ - --input-spec "$PROXY_INSPEC" \ - --remove-duplicates \ - --configKeyValues "$ARGS_ALL_CONFIG;TPCDigitDump.LastTimeBin=1000;" \ - | o2-dpl-run $ARGS_ALL $GLOBALDPLOPT --dds ${WORKFLOWMODE_FILE} +# Start with an empty workflow +WORKFLOW= +# Add required workflows via add_W helper (usage: add_W [BINARY] [COMMAND_LINE_OPTIONS] [CONFIG_KEY_VALUES] [Add ARGS_ALL_CONFIG, optional, default = 1]) +add_W o2-dpl-raw-proxy "--dataspec \"$PROXY_INSPEC\" --inject-missing-data --readout-proxy \"--channel-config \\\"name=readout-proxy,type=pull,method=connect,address=ipc://@tf-builder-pipe-0,transport=shmem,rateLogging=1\\\"\"" "" 0 +add_W o2-tpc-raw-to-digits-workflow "--input-spec \"$PROXY_INSPEC\" --remove-duplicates" "TPCDigitDump.LastTimeBin=1000" + +# Finally add the o2-dpl-run workflow manually, allow for either printing the workflow or creating a topology (default) +WORKFLOW+="o2-dpl-run $GLOBALDPLOPT $ARGS_ALL" +[[ $WORKFLOWMODE != "print" ]] && WORKFLOW+=" --${WORKFLOWMODE} ${WORKFLOWMODE_FILE:-}" +[[ $WORKFLOWMODE == "print" || "${PRINT_WORKFLOW:-}" == "1" ]] && echo "#Workflow command:\n\n${WORKFLOW}\n" | sed -e "s/\\\\n/\n/g" -e"s/| */| \\\\\n/g" | eval cat $( [[ $WORKFLOWMODE == "dds" ]] && echo '1>&2') +if [[ $WORKFLOWMODE != "print" ]]; then eval $WORKFLOW; else true; fi diff --git a/DATA/testing/private/afurs/fdd-ft0-fv0-ctf.sh b/DATA/testing/private/afurs/fdd-ft0-fv0-ctf.sh index e8de7fd1d..b63fe32a1 100755 --- a/DATA/testing/private/afurs/fdd-ft0-fv0-ctf.sh +++ b/DATA/testing/private/afurs/fdd-ft0-fv0-ctf.sh @@ -24,7 +24,7 @@ MYDIR="$(dirname $(readlink -f $0))" PROXY_INSPEC="digfdd:FDD/DIGITSBC/0;chanfdd:FDD/DIGITSCH/0;digft0:FT0/DIGITSBC/0;chanft0:FT0/DIGITSCH/0;digfv0:FV0/DIGITSBC/0;chanfv0:FV0/DIGITSCH/0;eos:***/INFORMATION;dd:FLP/DISTSUBTIMEFRAME/0" IN_CHANNEL="--channel-config 'name=readout-proxy,type=pull,method=connect,address=ipc://@$INRAWCHANNAME,transport=shmem,rateLogging=1'" -o2-dpl-raw-proxy ${ARGS_ALL} --readout-proxy "${IN_CHANNEL}" --dataspec "${PROXY_INSPEC}" \ +o2-dpl-raw-proxy ${ARGS_ALL} --readout-proxy "${IN_CHANNEL}" --dataspec "${PROXY_INSPEC}" --inject-missing-data \ | o2-fdd-entropy-encoder-workflow ${ARGS_ALL} --configKeyValues "$ARGS_ALL_CONFIG;" ${CTF_DICT} \ | o2-ft0-entropy-encoder-workflow ${ARGS_ALL} --configKeyValues "$ARGS_ALL_CONFIG;" ${CTF_DICT} \ | o2-fv0-entropy-encoder-workflow ${ARGS_ALL} --configKeyValues "$ARGS_ALL_CONFIG;" ${CTF_DICT} \ diff --git a/DATA/testing/private/afurs/fdd-ft0-fv0-digits-qc-ctf.sh b/DATA/testing/private/afurs/fdd-ft0-fv0-digits-qc-ctf.sh index f14bc2db0..9dd26457d 100755 --- a/DATA/testing/private/afurs/fdd-ft0-fv0-digits-qc-ctf.sh +++ b/DATA/testing/private/afurs/fdd-ft0-fv0-digits-qc-ctf.sh @@ -24,7 +24,7 @@ MYDIR="$(dirname $(readlink -f $0))" PROXY_INSPEC="x:FT0/RAWDATA;y:FV0/RAWDATA;z:FDD/RAWDATA;eos:***/INFORMATION;dd:FLP/DISTSUBTIMEFRAME/0" IN_CHANNEL="--channel-config 'name=readout-proxy,type=pull,method=connect,address=ipc://@$INRAWCHANNAME,transport=shmem,rateLogging=1'" -o2-dpl-raw-proxy ${ARGS_ALL} --readout-proxy "${IN_CHANNEL}" --dataspec "${PROXY_INSPEC}" \ +o2-dpl-raw-proxy ${ARGS_ALL} --readout-proxy "${IN_CHANNEL}" --dataspec "${PROXY_INSPEC}" --inject-missing-data \ | o2-fdd-flp-dpl-workflow --disable-root-output ${ARGS_ALL} --configKeyValues "$ARGS_ALL_CONFIG;" --pipeline fdd-datareader-dpl:$NTHREADS \ | o2-ft0-flp-dpl-workflow --disable-root-output ${ARGS_ALL} --configKeyValues "$ARGS_ALL_CONFIG;" --pipeline ft0-datareader-dpl:$NTHREADS \ | o2-fv0-flp-dpl-workflow --disable-root-output ${ARGS_ALL} --configKeyValues "$ARGS_ALL_CONFIG;" --pipeline fv0-datareader-dpl:$NTHREADS \ diff --git a/DATA/testing/private/afurs/ft0-fv0-ctf.sh b/DATA/testing/private/afurs/ft0-fv0-ctf.sh index f875f48f8..9430812f7 100755 --- a/DATA/testing/private/afurs/ft0-fv0-ctf.sh +++ b/DATA/testing/private/afurs/ft0-fv0-ctf.sh @@ -24,7 +24,7 @@ MYDIR="$(dirname $(readlink -f $0))" PROXY_INSPEC="digft0:FT0/DIGITSBC/0;chanft0:FT0/DIGITSCH/0;digfv0:FV0/DIGITSBC/0;chanfv0:FV0/DIGITSCH/0;eos:***/INFORMATION;dd:FLP/DISTSUBTIMEFRAME/0" IN_CHANNEL="--channel-config 'name=readout-proxy,type=pull,method=connect,address=ipc://@$INRAWCHANNAME,transport=shmem,rateLogging=1'" -o2-dpl-raw-proxy ${ARGS_ALL} --readout-proxy "${IN_CHANNEL}" --dataspec "${PROXY_INSPEC}" \ +o2-dpl-raw-proxy ${ARGS_ALL} --readout-proxy "${IN_CHANNEL}" --dataspec "${PROXY_INSPEC}" --inject-missing-data \ | o2-ft0-entropy-encoder-workflow ${ARGS_ALL} --configKeyValues "$ARGS_ALL_CONFIG;" ${CTF_DICT} \ | o2-fv0-entropy-encoder-workflow ${ARGS_ALL} --configKeyValues "$ARGS_ALL_CONFIG;" ${CTF_DICT} \ | o2-ctf-writer-workflow ${ARGS_ALL} ${ARGS_CTF} --configKeyValues "$ARGS_ALL_CONFIG;" --onlyDet FT0,FV0 --output-dir $CTF_DIR --ctf-dict-dir $FILEWORKDIR --output-type ctf \ diff --git a/DATA/testing/private/afurs/ft0-fv0-digits-qc-ctf.sh b/DATA/testing/private/afurs/ft0-fv0-digits-qc-ctf.sh index 0474c05e4..e22e6a622 100755 --- a/DATA/testing/private/afurs/ft0-fv0-digits-qc-ctf.sh +++ b/DATA/testing/private/afurs/ft0-fv0-digits-qc-ctf.sh @@ -24,7 +24,7 @@ MYDIR="$(dirname $(readlink -f $0))" PROXY_INSPEC="x:FT0/RAWDATA;y:FV0/RAWDATA;eos:***/INFORMATION;dd:FLP/DISTSUBTIMEFRAME/0" IN_CHANNEL="--channel-config 'name=readout-proxy,type=pull,method=connect,address=ipc://@$INRAWCHANNAME,transport=shmem,rateLogging=1'" -o2-dpl-raw-proxy ${ARGS_ALL} --readout-proxy "${IN_CHANNEL}" --dataspec "${PROXY_INSPEC}" \ +o2-dpl-raw-proxy ${ARGS_ALL} --readout-proxy "${IN_CHANNEL}" --dataspec "${PROXY_INSPEC}" --inject-missing-data \ | o2-fv0-flp-dpl-workflow --disable-root-output ${ARGS_ALL} --configKeyValues "$ARGS_ALL_CONFIG;" --pipeline fv0-datareader-dpl:$NTHREADS \ | o2-ft0-flp-dpl-workflow --disable-root-output ${ARGS_ALL} --configKeyValues "$ARGS_ALL_CONFIG;" --pipeline ft0-datareader-dpl:$NTHREADS \ | o2-qc ${ARGS_ALL} --local --host epn --config json://${MYDIR}/ft0-fv0-digits-ds.json \ diff --git a/DATA/testing/private/afurs/run_fdd_ft0_fv0_ctf.sh b/DATA/testing/private/afurs/run_fdd_ft0_fv0_ctf.sh index 00f79f4e3..cef2b6338 100755 --- a/DATA/testing/private/afurs/run_fdd_ft0_fv0_ctf.sh +++ b/DATA/testing/private/afurs/run_fdd_ft0_fv0_ctf.sh @@ -25,7 +25,7 @@ export MULTIPLICITY_FACTOR_REST=1 # Factor to export OUTPUT_FILE_NAME=$HOME/topologies/fdd-ft0-fv0-ctf.xml -/opt/alisw/el8/GenTopo/bin/gen_topo.sh > $OUTPUT_FILE_NAME +/opt/alisw/el9/GenTopo/bin/gen_topo.sh > $OUTPUT_FILE_NAME if [ $? == 0 ]; then echo Generated XML topology $OUTPUT_FILE_NAME fi diff --git a/DATA/testing/private/afurs/run_fdd_ft0_fv0_digits_qc_ctf.sh b/DATA/testing/private/afurs/run_fdd_ft0_fv0_digits_qc_ctf.sh index a86068b24..c3494c6cb 100755 --- a/DATA/testing/private/afurs/run_fdd_ft0_fv0_digits_qc_ctf.sh +++ b/DATA/testing/private/afurs/run_fdd_ft0_fv0_digits_qc_ctf.sh @@ -24,7 +24,7 @@ export MULTIPLICITY_FACTOR_REST=1 # Factor to export OUTPUT_FILE_NAME=$HOME/topologies/fdd-ft0-fv0-digits-qc-ctf.xml -/opt/alisw/el8/GenTopo/bin/gen_topo.sh > $OUTPUT_FILE_NAME +/opt/alisw/el9/GenTopo/bin/gen_topo.sh > $OUTPUT_FILE_NAME if [ $? == 0 ]; then echo Generated XML topology $OUTPUT_FILE_NAME fi diff --git a/DATA/testing/private/afurs/run_ft0_fv0_ctf.sh b/DATA/testing/private/afurs/run_ft0_fv0_ctf.sh index 50d7f6a4a..45b7e2678 100755 --- a/DATA/testing/private/afurs/run_ft0_fv0_ctf.sh +++ b/DATA/testing/private/afurs/run_ft0_fv0_ctf.sh @@ -25,7 +25,7 @@ export MULTIPLICITY_FACTOR_REST=1 # Factor to export OUTPUT_FILE_NAME=$HOME/topologies/ft0-fv0-ctf.xml -/opt/alisw/el8/GenTopo/bin/gen_topo.sh > $OUTPUT_FILE_NAME +/opt/alisw/el9/GenTopo/bin/gen_topo.sh > $OUTPUT_FILE_NAME if [ $? == 0 ]; then echo Generated XML topology $OUTPUT_FILE_NAME fi diff --git a/DATA/testing/private/afurs/run_ft0_fv0_digits_qc_ctf.sh b/DATA/testing/private/afurs/run_ft0_fv0_digits_qc_ctf.sh index 375ba8281..6ccc3315b 100755 --- a/DATA/testing/private/afurs/run_ft0_fv0_digits_qc_ctf.sh +++ b/DATA/testing/private/afurs/run_ft0_fv0_digits_qc_ctf.sh @@ -24,7 +24,7 @@ export MULTIPLICITY_FACTOR_REST=1 # Factor to export OUTPUT_FILE_NAME=$HOME/topologies/ft0-fv0-digits-qc-ctf.xml -/opt/alisw/el8/GenTopo/bin/gen_topo.sh > $OUTPUT_FILE_NAME +/opt/alisw/el9/GenTopo/bin/gen_topo.sh > $OUTPUT_FILE_NAME if [ $? == 0 ]; then echo Generated XML topology $OUTPUT_FILE_NAME fi diff --git a/DATA/testing/private/mfasel/runEMCRawToDigitsRecoPilelineQCAlllocalCTF.sh b/DATA/testing/private/mfasel/runEMCRawToDigitsRecoPilelineQCAlllocalCTF.sh index e9821147f..ca917a4ca 100755 --- a/DATA/testing/private/mfasel/runEMCRawToDigitsRecoPilelineQCAlllocalCTF.sh +++ b/DATA/testing/private/mfasel/runEMCRawToDigitsRecoPilelineQCAlllocalCTF.sh @@ -25,7 +25,7 @@ CTF_MINSIZE="500000000" CTF_MAX_PER_FILE=10000 o2-dpl-raw-proxy $ARGS_ALL \ - --dataspec "$PROXY_INSPEC" \ + --dataspec "$PROXY_INSPEC" --inject-missing-data \ --readout-proxy "--channel-config 'name=readout-proxy,type=pull,method=connect,address=ipc://@tf-builder-pipe-0,transport=shmem,rateLogging=1'" \ --severity $SEVERITY_RAWPROXY \ --infologger-severity $INFOLOGGER_SEVERITY_RAWPROXY \ diff --git a/DATA/testing/private/shahoian/runTF_PB.sh b/DATA/testing/private/shahoian/runTF_PB.sh index 5882ebce0..c841c7043 100755 --- a/DATA/testing/private/shahoian/runTF_PB.sh +++ b/DATA/testing/private/shahoian/runTF_PB.sh @@ -60,5 +60,5 @@ do export GEN_TOPO_WORKFLOW_NAME=$wf EXT="xml" [ ! -z $WORKFLOWMODE ] && [ $WORKFLOWMODE == "print" ] && EXT="sh" - /opt/alisw/el8/GenTopo/bin/gen_topo.sh > "$HOME/gen_topo/PB/${GEN_TOPO_WORKFLOW_NAME}.${EXT}" + /opt/alisw/el9/GenTopo/bin/gen_topo.sh > "$HOME/gen_topo/PB/${GEN_TOPO_WORKFLOW_NAME}.${EXT}" done diff --git a/DATA/testing/private/shahoian/runTF_ext_dpl.sh b/DATA/testing/private/shahoian/runTF_ext_dpl.sh index d9e80b3fe..d4e91f89f 100755 --- a/DATA/testing/private/shahoian/runTF_ext_dpl.sh +++ b/DATA/testing/private/shahoian/runTF_ext_dpl.sh @@ -41,5 +41,5 @@ do export GEN_TOPO_WORKFLOW_NAME=$wf EXT="xml" [ ! -z $WORKFLOWMODE ] && [ $WORKFLOWMODE == "print" ] && EXT="sh" - /opt/alisw/el8/GenTopo/bin/gen_topo.sh > "$HOME/gen_topo/dpl/${GEN_TOPO_WORKFLOW_NAME}.${EXT}" + /opt/alisw/el9/GenTopo/bin/gen_topo.sh > "$HOME/gen_topo/dpl/${GEN_TOPO_WORKFLOW_NAME}.${EXT}" done diff --git a/DATA/testing/private/shahoian/run_PB.sh b/DATA/testing/private/shahoian/run_PB.sh index f17c0637d..3b0dd807c 100755 --- a/DATA/testing/private/shahoian/run_PB.sh +++ b/DATA/testing/private/shahoian/run_PB.sh @@ -57,5 +57,5 @@ do export GEN_TOPO_WORKFLOW_NAME=$wf EXT="xml" [ ! -z $WORKFLOWMODE ] && [ $WORKFLOWMODE == "print" ] && EXT="sh" - /opt/alisw/el8/GenTopo/bin/gen_topo.sh > "$HOME/gen_topo/PB/${GEN_TOPO_WORKFLOW_NAME}.${EXT}" + /opt/alisw/el9/GenTopo/bin/gen_topo.sh > "$HOME/gen_topo/PB/${GEN_TOPO_WORKFLOW_NAME}.${EXT}" done diff --git a/DATA/testing/private/shahoian/run_ext.sh b/DATA/testing/private/shahoian/run_ext.sh index ac5e4a566..0288e169c 100755 --- a/DATA/testing/private/shahoian/run_ext.sh +++ b/DATA/testing/private/shahoian/run_ext.sh @@ -33,5 +33,5 @@ jq -n 'reduce inputs as $s (input; .qc.tasks += ($s.qc.tasks) | .qc.checks += ($ for wf in "$@" do export GEN_TOPO_WORKFLOW_NAME=$wf - /opt/alisw/el8/GenTopo/bin/gen_topo.sh > $HOME/gen_topo/test/${GEN_TOPO_WORKFLOW_NAME}.xml + /opt/alisw/el9/GenTopo/bin/gen_topo.sh > $HOME/gen_topo/test/${GEN_TOPO_WORKFLOW_NAME}.xml done diff --git a/DATA/testing/private/shahoian/run_ext_dpl.sh b/DATA/testing/private/shahoian/run_ext_dpl.sh index 039d36a46..348d54c7f 100755 --- a/DATA/testing/private/shahoian/run_ext_dpl.sh +++ b/DATA/testing/private/shahoian/run_ext_dpl.sh @@ -43,5 +43,5 @@ do export GEN_TOPO_WORKFLOW_NAME=$wf EXT="xml" [ ! -z $WORKFLOWMODE ] && [ $WORKFLOWMODE == "print" ] && EXT="sh" - /opt/alisw/el8/GenTopo/bin/gen_topo.sh > "$HOME/gen_topo/dpl/${GEN_TOPO_WORKFLOW_NAME}.${EXT}" + /opt/alisw/el9/GenTopo/bin/gen_topo.sh > "$HOME/gen_topo/dpl/${GEN_TOPO_WORKFLOW_NAME}.${EXT}" done diff --git a/DATA/testing/private/shahoian/run_test.sh b/DATA/testing/private/shahoian/run_test.sh index 21650c063..1c994f317 100755 --- a/DATA/testing/private/shahoian/run_test.sh +++ b/DATA/testing/private/shahoian/run_test.sh @@ -44,5 +44,5 @@ export WORKFLOWMODE=print for wf in "$@" do export GEN_TOPO_WORKFLOW_NAME=$wf - /opt/alisw/el8/GenTopo/bin/gen_topo.sh > $HOME/gen_topo/test/${GEN_TOPO_WORKFLOW_NAME}.xml + /opt/alisw/el9/GenTopo/bin/gen_topo.sh > $HOME/gen_topo/test/${GEN_TOPO_WORKFLOW_NAME}.xml done diff --git a/DATA/testing/private/zampolli/calib/run.sh b/DATA/testing/private/zampolli/calib/run.sh index e5f8b7ebb..bf4dc2a53 100755 --- a/DATA/testing/private/zampolli/calib/run.sh +++ b/DATA/testing/private/zampolli/calib/run.sh @@ -31,9 +31,9 @@ do echo Processing workflow $wf export GEN_TOPO_WORKFLOW_NAME=$wf export WORKFLOWMODE=dds - /opt/alisw/el8/GenTopo/bin/gen_topo.sh > $HOME/gen_topo_calib/dplTest/${GEN_TOPO_WORKFLOW_NAME}.xml + /opt/alisw/el9/GenTopo/bin/gen_topo.sh > $HOME/gen_topo_calib/dplTest/${GEN_TOPO_WORKFLOW_NAME}.xml # export WORKFLOWMODE=print - # /opt/alisw/el8/GenTopo/bin/gen_topo.sh > $HOME/gen_topo_calib/dpl/${GEN_TOPO_WORKFLOW_NAME}.sh + # /opt/alisw/el9/GenTopo/bin/gen_topo.sh > $HOME/gen_topo_calib/dpl/${GEN_TOPO_WORKFLOW_NAME}.sh done diff --git a/DATA/testing/private/zampolli/runTF_ext_dpl.sh b/DATA/testing/private/zampolli/runTF_ext_dpl.sh index 5de8c7367..4f2279639 100755 --- a/DATA/testing/private/zampolli/runTF_ext_dpl.sh +++ b/DATA/testing/private/zampolli/runTF_ext_dpl.sh @@ -43,5 +43,5 @@ do echo echo Processing workflow $wf export GEN_TOPO_WORKFLOW_NAME=$wf - /opt/alisw/el8/GenTopo/bin/gen_topo.sh > $HOME/gen_topo/dpl/${GEN_TOPO_WORKFLOW_NAME}.xml + /opt/alisw/el9/GenTopo/bin/gen_topo.sh > $HOME/gen_topo/dpl/${GEN_TOPO_WORKFLOW_NAME}.xml done diff --git a/DATA/testing/private/zampolli/run_ext_dpl.sh b/DATA/testing/private/zampolli/run_ext_dpl.sh index 855fb2f1d..d7506480f 100755 --- a/DATA/testing/private/zampolli/run_ext_dpl.sh +++ b/DATA/testing/private/zampolli/run_ext_dpl.sh @@ -45,8 +45,8 @@ do echo Processing workflow $wf export GEN_TOPO_WORKFLOW_NAME=$wf export WORKFLOWMODE=dds - /opt/alisw/el8/GenTopo/bin/gen_topo.sh > $HOME/gen_topo/dplTest/${GEN_TOPO_WORKFLOW_NAME}.xml + /opt/alisw/el9/GenTopo/bin/gen_topo.sh > $HOME/gen_topo/dplTest/${GEN_TOPO_WORKFLOW_NAME}.xml # export WORKFLOWMODE=print -# /opt/alisw/el8/GenTopo/bin/gen_topo.sh > $HOME/gen_topo/dpl/${GEN_TOPO_WORKFLOW_NAME}.sh +# /opt/alisw/el9/GenTopo/bin/gen_topo.sh > $HOME/gen_topo/dpl/${GEN_TOPO_WORKFLOW_NAME}.sh done diff --git a/DATA/testing/private/zampolli/zampolli-workflow.sh b/DATA/testing/private/zampolli/zampolli-workflow.sh index fe633d2e5..3406cb882 100755 --- a/DATA/testing/private/zampolli/zampolli-workflow.sh +++ b/DATA/testing/private/zampolli/zampolli-workflow.sh @@ -11,7 +11,7 @@ ARGS_ALL_CONFIG="NameConf.mDirGRP=$FILEWORKDIR;NameConf.mDirGeom=$FILEWORKDIR;Na PROXY_INSPEC="A:TPC/RAWDATA;dd:FLP/DISTSUBTIMEFRAME/0;eos:***/INFORMATION" o2-dpl-raw-proxy $ARGS_ALL \ - --dataspec "$PROXY_INSPEC" \ + --dataspec "$PROXY_INSPEC" --inject-missing-data \ --readout-proxy "--channel-config 'name=readout-proxy,type=pull,method=connect,address=ipc://@$INRAWCHANNAME,transport=shmem,rateLogging=1'" \ | o2-tpc-raw-to-digits-workflow $ARGS_ALL \ --input-spec "$PROXY_INSPEC" \ diff --git a/DATA/tools/datadistribution_workflows/dd-discard.xml b/DATA/tools/datadistribution_workflows/dd-discard.xml index 361e690cf..3a5178b63 100644 --- a/DATA/tools/datadistribution_workflows/dd-discard.xml +++ b/DATA/tools/datadistribution_workflows/dd-discard.xml @@ -1,7 +1,7 @@ - GEN_TOPO_TFBUILDER_ENV_VARIABLES /home/epn/odc/tfbuilder.sh standalone + GEN_TOPO_TFBUILDER_ENV_VARIABLES /usr/local/bin/tfbuilder.sh standalone TfBuilderRequirement diff --git a/DATA/tools/datadistribution_workflows/dd-disk.xml b/DATA/tools/datadistribution_workflows/dd-disk.xml index 094c79c8f..9651e83f1 100644 --- a/DATA/tools/datadistribution_workflows/dd-disk.xml +++ b/DATA/tools/datadistribution_workflows/dd-disk.xml @@ -1,7 +1,7 @@ - GEN_TOPO_TFBUILDER_ENV_VARIABLES /home/epn/odc/tfbuilder.sh disk2G + GEN_TOPO_TFBUILDER_ENV_VARIABLES /usr/local/bin/tfbuilder.sh disk2G TfBuilderRequirement diff --git a/DATA/tools/datadistribution_workflows/dd-processing-disk.xml b/DATA/tools/datadistribution_workflows/dd-processing-disk.xml index 005db63af..baa2fb917 100644 --- a/DATA/tools/datadistribution_workflows/dd-processing-disk.xml +++ b/DATA/tools/datadistribution_workflows/dd-processing-disk.xml @@ -1,7 +1,7 @@ - GEN_TOPO_TFBUILDER_ENV_VARIABLES /home/epn/odc/tfbuilder.sh disk2G_data + GEN_TOPO_TFBUILDER_ENV_VARIABLES /usr/local/bin/tfbuilder.sh disk2G_data TfBuilderRequirement diff --git a/DATA/tools/datadistribution_workflows/dd-processing.xml b/DATA/tools/datadistribution_workflows/dd-processing.xml index cb6cd7c79..1e13d2023 100644 --- a/DATA/tools/datadistribution_workflows/dd-processing.xml +++ b/DATA/tools/datadistribution_workflows/dd-processing.xml @@ -1,7 +1,7 @@ - GEN_TOPO_TFBUILDER_ENV_VARIABLES /home/epn/odc/tfbuilder.sh data + GEN_TOPO_TFBUILDER_ENV_VARIABLES /usr/local/bin/tfbuilder.sh data TfBuilderRequirement diff --git a/DATA/tools/epn/gen_topo.sh b/DATA/tools/epn/gen_topo.sh index 973dd64b5..3785fa5ac 100755 --- a/DATA/tools/epn/gen_topo.sh +++ b/DATA/tools/epn/gen_topo.sh @@ -4,7 +4,7 @@ # Author: David Rohr # This script is developed within O2DPG: https://github.com/AliceO2Group/O2DPG/blob/master/DATA/tools/epn/gen_topo.sh -# It is installed as package GenTopo to the updateable RPM path /opt/alisw/el8/GenTopo/bin/ on the EPNs +# It is installed as package GenTopo to the updateable RPM path /opt/alisw/el10/GenTopo/bin/ on the EPNs # The purpose of this script is to separate the topology generation (which is in O2DPG) from the setting of the EPN-related settings # This script contains only the EPN related settings @@ -18,7 +18,10 @@ if [[ -z "$EPN2EOS_METAFILES_DIR" ]] && [[ "0$WORKFLOWMODE" != "0print" ]]; then export EPN2EOS_METAFILES_DIR=/data/epn2eos_tool/epn2eos # Directory for epn2eos meta data files fi if [[ $USER == "epn" ]]; then - [[ -z "$GEN_TOPO_WORKDIR" ]] && export GEN_TOPO_WORKDIR=/scratch/services/gen_topo # Working directory for checkout of O2DPG repository and for XML cache. If this directory is wiped, gen_topo will recreate all necessary content the next time it runs. The folder should be persistent to cache workflows. + if [[ -z "$GEN_TOPO_WORKDIR" ]]; then + mkdir -p /var/tmp/gen_topo + export GEN_TOPO_WORKDIR=/var/tmp/gen_topo # Working directory for checkout of O2DPG repository and for XML cache. If this directory is wiped, gen_topo will recreate all necessary content the next time it runs. The folder should be persistent to cache workflows. + fi else [[ -z "$GEN_TOPO_WORKDIR" ]] && export GEN_TOPO_WORKDIR=$HOME/gen_topo # Working directory for checkout of O2DPG repository and for XML cache. If this directory is wiped, gen_topo will recreate all necessary content the next time it runs. The folder should be persistent to cache workflows. mkdir -p $HOME/gen_topo @@ -28,21 +31,17 @@ if [[ "0$GEN_TOPO_ONTHEFLY" == "01" ]]; then export SHM_MANAGER_SHMID=1 ;fi # Command for topology merging if [[ -z "$GEN_TOPO_ODC_EPN_TOPO_CMD" ]]; then - export GEN_TOPO_ODC_EPN_TOPO_CMD='env - PYTHONPATH+=/usr/local/lib/python3.9/site-packages:/usr/local/lib64/python3.9/site-packages /usr/local/bin/epn-topo-merger' + export GEN_TOPO_ODC_EPN_TOPO_CMD='env - PYTHONPATH+=/usr/local/lib/python3.12/site-packages:/usr/local/lib64/python3.12/site-packages /usr/local/bin/epn-topo-merger' fi # Command for postprocessing of topology generation after topology caching if [[ -z "$GEN_TOPO_ODC_EPN_TOPO_POST_CACHING_CMD" ]]; then - export GEN_TOPO_ODC_EPN_TOPO_POST_CACHING_CMD='env - PYTHONPATH+=/usr/local/lib/python3.9/site-packages:/usr/local/lib64/python3.9/site-packages /usr/local/bin/epn-topo-alloc' + export GEN_TOPO_ODC_EPN_TOPO_POST_CACHING_CMD='env - PYTHONPATH+=/usr/local/lib/python3.12/site-packages:/usr/local/lib64/python3.12/site-packages /usr/local/bin/epn-topo-alloc' fi # Extra arguments for topology merger if [[ -z "$GEN_TOPO_ODC_EPN_TOPO_POST_CACHING_ARGS" ]]; then - if [[ "${GEN_TOPO_DEPLOYMENT_TYPE:-}" == "ALICE_STAGING" ]]; then - export GEN_TOPO_ODC_EPN_TOPO_POST_CACHING_ARGS="--recozone staging-mi50 --reco100zone staging-mi100 --calibzone calib" - else - export GEN_TOPO_ODC_EPN_TOPO_POST_CACHING_ARGS="--recozone online-mi50 --reco100zone online-mi100 --calibzone calib" - fi + export GEN_TOPO_ODC_EPN_TOPO_POST_CACHING_ARGS="--recozone online-mi50 --reco100zone online-mi100 --calibzone calib" fi if [[ -z "$GEN_TOPO_MI100_NODES" ]]; then export GEN_TOPO_MI100_NODES=-1; fi @@ -56,8 +55,7 @@ else fi fi # Run stage 2 of GenTopo, which does the PDP part, still from hardcoded updatable RPM path -/opt/alisw/el8/GenTopo/bin/gen_topo_o2dpg.sh +/opt/alisw/el10/GenTopo/bin/gen_topo_o2dpg.sh if [ $? != 0 ]; then - echo topology generation failed 1>&2 exit 1 fi diff --git a/DATA/tools/epn/gen_topo_logged.sh b/DATA/tools/epn/gen_topo_logged.sh index 9533712c7..3e0ce0260 100755 --- a/DATA/tools/epn/gen_topo_logged.sh +++ b/DATA/tools/epn/gen_topo_logged.sh @@ -17,7 +17,7 @@ if [[ ! -z $GEN_TOPO_LOGDATE ]]; then fi STDERRFILE=$(mktemp) -/opt/alisw/el8/GenTopo/bin/gen_topo.sh 2> $STDERRFILE +/opt/alisw/el10/GenTopo/bin/gen_topo.sh 2> $STDERRFILE RETVAL=$? if [[ ! -z $GEN_TOPO_LOGDATE ]]; then @@ -26,6 +26,8 @@ if [[ ! -z $GEN_TOPO_LOGDATE ]]; then while read STDERRLINE; do echo "$GEN_TOPO_LOGDATE $ECS_ENVIRONMENT_ID : $STDERRLINE" >> /var/log/topology/gen-topo.log done < $STDERRFILE + echo "FATAL $(tail -n 1 $STDERRFILE)" 1>&2 + echo -e "\n - full stderr output:" 1>&2 fi fi diff --git a/DATA/tools/epn/gen_topo_o2dpg.sh b/DATA/tools/epn/gen_topo_o2dpg.sh index 29346796d..217ddba21 100755 --- a/DATA/tools/epn/gen_topo_o2dpg.sh +++ b/DATA/tools/epn/gen_topo_o2dpg.sh @@ -2,7 +2,6 @@ # Some defaults [[ -z "$GEN_TOPO_STDERR_LOGGING" ]] && export GEN_TOPO_STDERR_LOGGING=1 # Enable logging of stderr messages -[[ -z "$IS_SIMULATED_DATA" ]] && export IS_SIMULATED_DATA=0 # by default we are processing raw data # Check settings coming from AliECS via env variables if [[ -z "$GEN_TOPO_HASH" ]]; then echo \$GEN_TOPO_HASH missing; exit 1; fi # Flag whether source is a hash or a folder @@ -22,6 +21,17 @@ if [[ -z "$MULTIPLICITY_FACTOR_REST" ]]; then echo \$MULTIPLICITY_FACTOR_REST mi if [[ -z "$RECOSHMSIZE" ]]; then echo \$RECOSHMSIZE missing; exit 1; fi # SHM Size for reconstruction collections if [[ -z "$DDSHMSIZE" ]]; then echo \$DDSHMSIZE missing; exit 1; fi # SHM Size for DD +# In case of debug mode, overwrite some settings +if [[ "${DEBUG_TOPOLOGY_GENERATION:=0}" == "1" ]]; then + echo "Debugging mode enabled. Setting options accordingly" 1>&2 + export RECO_NUM_NODES_OVERRIDE=1 # to avoid slurm query, specify number of nodes to fixed value + export GEN_TOPO_MI100_NODES=1 # also for MI100 nodes + export GEN_TOPO_OVERRIDE_TEMPDIR=$PWD # keep temporary files like QC jsons in local directory + export EPN2EOS_METAFILES_DIR=/tmp # nothing is written here, just needs to be set to something + unset ECS_ENVIRONMENT_ID + unset GEN_TOPO_CACHE_HASH +fi + # Check settings coming from the EPN if [[ -z "$FILEWORKDIR" ]]; then echo \$FILEWORKDIR missing; exit 1; fi if [[ -z "$INRAWCHANNAME" ]]; then echo \$INRAWCHANNAME missing; exit 1; fi @@ -72,11 +82,19 @@ while true; do break fi fi - if [[ ! -d O2DPG ]]; then git clone https://github.com/AliceO2Group/O2DPG.git 1>&2 || { echo O2DPG checkout failed 1>&2; exit 1; }; fi - cd O2DPG + for CHECKOUTATTEMPT in 1 2; do + if [[ ! -d O2DPG ]]; then git clone https://github.com/AliceO2Group/O2DPG.git 1>&2 || { echo O2DPG checkout failed 1>&2; exit 1; }; fi + cd O2DPG + rm -f DATA/core_dump_* + git reset --hard HEAD &> /dev/null && git clean -d -f &> /dev/null && break + [[ $CHECKOUTATTEMPT -eq 2 ]] && { echo git reset error 1>&2; exit 1; } + echo "Clean-up of O2DPG repository failed. Removing repository and cloning it from scratch" 1>&2 + cd $GEN_TOPO_WORKDIR || { echo Cannot enter work dir 1>&2; exit 1; } + rm -rf O2DPG + done git checkout $GEN_TOPO_SOURCE &> /dev/null if [[ $? != 0 ]]; then - git fetch --tags origin 1>&2 || { echo Repository update failed 1>&2; exit 1; } + git fetch -f --tags origin 1>&2 || { echo Repository update failed 1>&2; exit 1; } git checkout $GEN_TOPO_SOURCE &> /dev/null || { echo commit does not exist 1>&2; exit 1; } fi # At a tag, or a detached non-dirty commit, but not on a branch @@ -103,6 +121,7 @@ while true; do break done + if [[ ! -z "$GEN_TOPO_ODC_EPN_TOPO_POST_CACHING_CMD" ]] && [[ "0$WORKFLOWMODE" != "0print" ]]; then TMP_POST_CACHING_CMD="$GEN_TOPO_ODC_EPN_TOPO_POST_CACHING_CMD $GEN_TOPO_ODC_EPN_TOPO_POST_CACHING_ARGS" TMP_POST_CACHING_NMIN=$(( $RECO_NUM_NODES_OVERRIDE > $RECO_MAX_FAIL_NODES_OVERRIDE ? $RECO_NUM_NODES_OVERRIDE - $RECO_MAX_FAIL_NODES_OVERRIDE : 0 )) @@ -115,7 +134,7 @@ if [[ ! -z "$GEN_TOPO_ODC_EPN_TOPO_POST_CACHING_CMD" ]] && [[ "0$WORKFLOWMODE" ! fi TMP_POST_CACHING_CMD+=" -o $GEN_TOPO_WORKDIR/output.xml.new $GEN_TOPO_WORKDIR/output.xml" echo "Running post-caching topo-merger command: $TMP_POST_CACHING_CMD" 1>&2 - eval $TMP_POST_CACHING_CMD 1>&2 || { echo Error during EPN topology-merger resource allocation 1>&2; exit 1; } + eval $TMP_POST_CACHING_CMD 1>&2 || { echo Error during EPN resource allocation 1>&2; exit 1; } mv -f $GEN_TOPO_WORKDIR/output.xml.new $GEN_TOPO_WORKDIR/output.xml 1>&2 fi @@ -126,6 +145,9 @@ if [[ ! -z "$ECS_ENVIRONMENT_ID" && -d "/var/log/topology/" && $USER == "epn" ]] fi cat $GEN_TOPO_WORKDIR/output.xml -echo Removing temporary output file $GEN_TOPO_WORKDIR/output.xml 1>&2 -rm $GEN_TOPO_WORKDIR/output.xml + +if [[ "$DEBUG_TOPOLOGY_GENERATION" == "0" ]]; then + echo Removing temporary output file $GEN_TOPO_WORKDIR/output.xml 1>&2 + rm $GEN_TOPO_WORKDIR/output.xml +fi rm -f $GEN_TOPO_LOCKFILE diff --git a/DATA/tools/epn/run.sh b/DATA/tools/epn/run.sh index f16c074fe..48bb688b9 100755 --- a/DATA/tools/epn/run.sh +++ b/DATA/tools/epn/run.sh @@ -35,8 +35,8 @@ if [[ "0$GEN_TOPO_RUN_HOME" == "01" ]]; then [[ -z $O2DPG_ROOT || -z $O2_ROOT ]] && { echo "ERROR: O2 and O2DPG must be in the environment!"; exit 1; } $O2DPG_ROOT/DATA/tools/epn/gen_topo.sh > $OUTPUT_FILE_NAME else - [[ ! -f /opt/alisw/el8/GenTopo/bin/gen_topo.sh ]] && { echo "ERROR: EPN installation of gen_topo.sh missing. Are you trying to run at home? Then please set GEN_TOPO_RUN_HOME=1!"; exit 1; } - /opt/alisw/el8/GenTopo/bin/gen_topo.sh > $OUTPUT_FILE_NAME + [[ ! -f /opt/alisw/el10/GenTopo/bin/gen_topo.sh ]] && { echo "ERROR: EPN installation of gen_topo.sh missing. Are you trying to run at home? Then please set GEN_TOPO_RUN_HOME=1!"; exit 1; } + /opt/alisw/el10/GenTopo/bin/gen_topo.sh > $OUTPUT_FILE_NAME fi if [[ $? == 0 ]]; then echo Generated XML topology $OUTPUT_FILE_NAME diff --git a/DATA/tools/parse b/DATA/tools/parse index dd2169b52..8c68e55f4 100755 --- a/DATA/tools/parse +++ b/DATA/tools/parse @@ -60,9 +60,9 @@ else: modulecmd = " module purge ; " if 'GEN_TOPO_OVERRIDE_MODULE_DIR' in os.environ: - mod.module('unuse', '/opt/alisw/el8/modulefiles') + mod.module('unuse', '/opt/alisw/el10/modulefiles') mod.module('use', os.environ['GEN_TOPO_OVERRIDE_MODULE_DIR']) - modulecmd += ' module unuse /opt/alisw/el8/modulefiles 2>&1 ; module use ' + os.environ['GEN_TOPO_OVERRIDE_MODULE_DIR'] + ' 2>&1 ; ' + modulecmd += ' module unuse /opt/alisw/el10/modulefiles 2>&1 ; module use ' + os.environ['GEN_TOPO_OVERRIDE_MODULE_DIR'] + ' 2>&1 ; ' f = open(sys.argv[1], 'r') for line in f: @@ -141,6 +141,10 @@ for line in f: command_preopt += ' SHMSIZE=' + (str(int(int(os.environ['RECOSHMSIZE']) * 3 / 2)) if mi100node else os.environ['RECOSHMSIZE']) if mi100node: command_preopt += ' EPN_NODE_MI100=1 EPN_GLOBAL_SCALING="3/2"' + if os.getenv('DISABLE_MI100_SERIALIZATION', '0') == '1': + command_preopt += ' DISABLE_MI100_SERIALIZATION=1' + if os.getenv('FULL_MI100_SERIALIZATION', '0') == '1': + command_preopt += ' FULL_MI100_SERIALIZATION=1' if reco_num_nodes_override == 0: command_preopt += ' RECO_NUM_NODES_WORKFLOW=' + wf[1] if is_calib_workflow: @@ -152,7 +156,7 @@ for line in f: command += ' > ' + filename print('Running DPL command', command) starttime = time.time() - retVal = subprocess.run(command, shell=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE) + retVal = subprocess.run(command, shell=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE, input="") print('Execution time: ', time.time() - starttime) tmpchk = retVal.stderr.decode() + retVal.stdout.decode() haserror = 0 @@ -219,7 +223,7 @@ for line in f: odccommand += ' ' + os.environ['GEN_TOPO_ODC_EPN_TOPO_ARGS'] if True: replacestring = '' - dd_env_variables = ['DD_DISK_FRACTION', 'EPN_DD_TEST', 'EPN_DD_TEST_2', 'EPN_DD_TEST_3', 'SHM_MANAGER_SHMID'] + dd_env_variables = ['DD_DISK_FRACTION', 'TF_SINK_NUM_TFS', 'DD_STF_MIN_ID', 'DD_STF_MAX_ID', 'EPN_DD_TEST', 'EPN_DD_TEST_2', 'EPN_DD_TEST_3', 'SHM_MANAGER_SHMID'] for i in dd_env_variables: if i in os.environ: replacestring += ' ' + i + '=' + os.environ[i] diff --git a/GRID/utils/extractErroredLogFiles.sh b/GRID/utils/extractErroredLogFiles.sh new file mode 100755 index 000000000..20a0750ba --- /dev/null +++ b/GRID/utils/extractErroredLogFiles.sh @@ -0,0 +1,41 @@ +#!/usr/bin/env bash + +# We determine the O2DPG task that failed (as listed in stdout) and extract the relevant log automatically +# Beware that errors might occur outside of O2DPG tasks such as in preprocessing etc or not visible in logs + +mytar () { + tar "$@" +} +if [[ $(uname) == "Darwin" ]]; then + echo "Running on macOS. This needs gnu-tar" + $(which gtar) + mytar () { + gtar "$@" + } +fi + +errored_tasks="" +find ./ -name "stdout*" -exec grep -H "failed.*retry" {} ';' | sed 's/ failed.*//' | tr ":" " " | while IFS= read -r line; do + stdoutpath=$(echo "$line" | awk '{print $1}') # Extracting the first column + logfile=$(echo "$line" | awk '{print $2}') # Extracting the second column + + dir=$(dirname ${stdoutpath}) + cd ${dir} + # determine a timeframe number (if applicable) + # Extracting the timeframe 'tf' + tf=${logfile#*_} # Removes everything before the first underscore + tf=${tf%.log} # Removes the ".log" extension + echo "Extracted timeframe ${tf}" + + # extract the general log archive + unzip -n log_archive.zip + # extract specific task log from debug archive + mytar -xvzf debug_log_archive.tgz --wildcards "*${logfile}.log" + if [[ ${logfile} == *"qedsim"* || ${logfile} == *"sgnsim"* || ${logfile} == *"bkgsim"* ]]; then + # simulation has few more files to inspect + mytar -xvzf debug_log_archive.tgz --wildcards "*${tf}*serverlog*" + mytar -xvzf debug_log_archive.tgz --wildcards "*${tf}*workerlog*" + mytar -xvzf debug_log_archive.tgz --wildcards "*${tf}*mergerlog*" + fi + cd $OLDPWD +done diff --git a/GRID/utils/extractFailureMessage.sh b/GRID/utils/extractFailureMessage.sh new file mode 100755 index 000000000..4c8c391e5 --- /dev/null +++ b/GRID/utils/extractFailureMessage.sh @@ -0,0 +1,38 @@ +#!/usr/bin/env bash + +# script to check for indication of failure in a file +# and to report on the first error + +filetocheck=$1 +if [ ! "${filetocheck}" ]; then + echo "Specify a file to search" + exit 1 +fi + +# list of typical failure patterns (append if needed) +pattern="-e \"\<[Ee]xception\" \ + -e \"segmentation violation\" \ + -e \"error while setting up workflow\" \ + -e \"bus error\" \ + -e \"Assertion.*failed\" \ + -e \"Fatal in\" \ + -e \"libc++abi.*terminating\" \ + -e \"There was a crash.\" \ + -e \"arrow.*Check failed\" \ + -e \"terminate called after\" \ + -e \"terminate called without an active\" \ + -e \"\]\[FATAL\]\" \ + -e \"\*\*\* Program crashed\" \ + -e \"\*\*\* Fatal Exception\" \ + -e \"was killed abnormally\" \ + -e \"line.*Killed\" \ + -e \"\*\*\* Error in\"" # <--- LIBC fatal error messages + +CMD="grep -m 1 -B2 -A10 ${pattern} $filetocheck" +T=$(eval "${CMD}") +if [ "$?" -eq 0 ]; then + echo "" + echo "==============================ERROR in ${filetocheck} ==============================" + echo "" + echo "${T}" +fi diff --git a/GRID/utils/fetch_output_onfailure.sh b/GRID/utils/fetch_output_onfailure.sh index beeb7d4d0..08a5ab452 100755 --- a/GRID/utils/fetch_output_onfailure.sh +++ b/GRID/utils/fetch_output_onfailure.sh @@ -1,6 +1,6 @@ #!/bin/bash -# a script which fetches error files for each failing subjob of a masterjob +# a script which fetches error files for each failing subjob of an AliEn GRID masterjob MY_JOBID=$1 if [ ! "${MY_JOBID}" ]; then @@ -8,22 +8,48 @@ if [ ! "${MY_JOBID}" ]; then exit 1 fi +# these are all subjobids SUBJOBIDS=($(alien.py ps --trace ${MY_JOBID} | awk '/Subjob submitted/' | sed 's/.*submitted: //' | tr '\n' ' ')) -OutputDir=/tmp/AlienLogs_${MY_JOBID} +# these are the one that failed +FAILEDSUBJOBIDS=($(alien.py ps -a -m ${MY_JOBID} -f ERROR_ALL | awk '/EE/{print $2}' | tr '\n' ' ')) -# -job_number=${#SUBJOBIDS[@]} +CurrDir=${PWD} +OutputDir=/tmp/AlienLogs_${MY_JOBID} +job_number=${#FAILEDSUBJOBIDS[@]} +echo "Found ${job_number} failed job ids" +echo "Registering output for retrieval" +RecycleBase="" +# First pass to do register output for ((i = 0; i < job_number; i++)); do - jobid=${SUBJOBIDS[i]} - STATUS=$(alien.py ps -j ${jobid} | awk '//{print $4}') - echo "Status of ${jobid} is $STATUS" - if [ ${STATUS} == "EE" ]; then - # nothing - RecycleOutputDir=$(alien.py ps --trace ${jobid} | awk '/Going to uploadOutputFiles/' | sed 's/.*outputDir=//' | sed 's/)//') - alien.py registerOutput ${jobid} - echo "Recycle out is ${RecycleOutputDir}" - alien.py cp ${RecycleOutputDir}/'*' file:${OutputDir}/${jobid} + jobid=${FAILEDSUBJOBIDS[i]} + if [ ! "${RecycleBase}" ]; then + RecycleOutputDir=$(alien.py ps --trace ${jobid} | awk '/Going to uploadOutputFiles/' | sed 's/.*outputDir=//' | sed 's/)//' | sort -u | head -n1) + # /alice/cern.ch/user/a/aliprod/recycle/alien-job-2974093751 + RecycleBase=${RecycleOutputDir%-${jobid}} # Removes the ${jobid} and yields the recycle base path fi -done \ No newline at end of file + alien.py registerOutput ${jobid} > /dev/null 2>&1 +done + +# wait a bit to allow propagation of "registerOutput" +sleep 1 + +echo "Downloading output" +# Second pass to copy files +for ((i = 0; i < job_number; i++)); do + jobid=${FAILEDSUBJOBIDS[i]} + RecycleOutputDir="${RecycleBase}-${jobid}" + alien.py cp ${RecycleOutputDir}/'*archive*' file:${OutputDir}/${jobid} + [ -f ${OutputDir}/${jobid}/log_archive.zip ] && unzip -q -o ${OutputDir}/${jobid}/log_archive.zip -d ${OutputDir}/${jobid} +done + +echo " ... Going to automatic extraction of log files ... " + +# We determine the O2DPG task that failed (as listed in stdout) and extract the relevant log automatically +# Beware that errors might occur outside of O2DPG tasks such as in preprocessing etc or not visible in logs +cd ${OutputDir} +# call the extraction script +${O2DPG_ROOT}/GRID/utils/extractErroredLogFiles.sh +echo "Files have been extracted to ${OutputDir}" +cd ${CurrDir} diff --git a/GRID/utils/getCCDBTimeMachineTimestamp.py b/GRID/utils/getCCDBTimeMachineTimestamp.py new file mode 100755 index 000000000..f5b4619e6 --- /dev/null +++ b/GRID/utils/getCCDBTimeMachineTimestamp.py @@ -0,0 +1,353 @@ +#!/usr/bin/env python3 + +# Estimates a CCDB TimeMachine "not-after" timestamp for a given run + reco pass, +# for use as ALIEN_JDL_CCDB_CONDITION_NOT_AFTER in anchored MC. +# +# Async-reco passes are not themselves pinned to a single CCDB instant: each +# per-timeframe job sees CCDB at its own wall-clock execution time. This tool +# approximates that instant from the AliEn catalog timestamps of the pass's own +# per-timeframe reco output for the given run, since that is the closest thing +# to "when did this run actually get processed under this pass" that is left on +# record. It never guesses when it cannot find that record. +# +# started 25.08.2026; Sandro Wenzel + +import argparse +import dataclasses +from dataclasses import dataclass, asdict, fields +import json +import os +import requests +import statistics +import subprocess +import sys +import urllib.request +from datetime import datetime, timezone + +# hack to find the script with helper functions +o2dpg_root = os.environ.get("O2DPG_ROOT") +if o2dpg_root is None: + raise EnvironmentError("O2DPG_ROOT is not set in the environment.") +mc_prodinfo_path = os.path.abspath(os.path.join(o2dpg_root, "MC", "prodinfo")) +sys.path.append(mc_prodinfo_path) +# FIXME: these should probably be moved to a common script +from mcprodinfo_ccdb_upload import extract_metadata_blocks_from_CCDB + +CCDB_HOST = "http://alice-ccdb.cern.ch" + + +@dataclass(frozen=True) +class CCDBTimeMachineTS: + """ + struct for CCDB time machine timestamp + """ + LPMProductionTag: str + Period: str + Pass: str + NotAfter: int + + +def alien_json(*args): + """Run alien.py with -json and return the parsed 'results' list.""" + cmd = ["alien.py", "-json"] + list(args) + proc = subprocess.run(cmd, capture_output=True, text=True) + if proc.returncode != 0: + return None + try: + return json.loads(proc.stdout)["results"] + except (json.JSONDecodeError, KeyError): + return None + + +def find_tf_output_files(pass_dir, run): + """LFNs of root_archive.zip directly inside this run's o2_ctf_run* per-TF + output directories (excludes same-named files from downstream QC/skim/train + jobs that live deeper under the same pass directory).""" + results = alien_json("find", pass_dir, "root_archive.zip") + if not results: + return [] + marker = f"/o2_ctf_run{int(run):08d}_orbit" + return sorted(r["lfn"] for r in results if marker in r["lfn"]) + + +def sample_evenly(items, n): + if len(items) <= n: + return items + step = len(items) / n + return [items[int(i * step)] for i in range(n)] + + +def stat_ctime(lfn): + results = alien_json("stat", lfn) + if not results: + return None + try: + return int(results[0]["ctime"]) + except (KeyError, ValueError, TypeError): + return None + + +def human(ts_ms): + return datetime.fromtimestamp(ts_ms / 1000, tz=timezone.utc).strftime("%Y-%m-%d %H:%M:%S UTC") + + +def get_ccdb_objects(url, timeout = 15): + req = urllib.request.Request(url, headers={"Accept": "application/json"}) + try: + with urllib.request.urlopen(req, timeout=timeout) as resp: + data = json.load(resp) + except Exception as exc: + return {"error": str(exc)} + + objects = data.get("objects", []) + return objects + + +def get_run_start_tf(run): + run_info_url = f"{CCDB_HOST}/browse/RCT/Info/RunInformation/{run}" + objects = get_ccdb_objects(run_info_url) + + return objects[0]["STF"] + + +def ccdb_cross_check(run, path, not_after, timeout=15): + """Report which object a correctly time-machine-pinned query at not_after + would actually resolve to, and flag if CCDB has since been updated for the + same validity window. Never raises; returns None on any failure.""" + timestamp = get_run_start_tf(run) + url = f"{CCDB_HOST}/browse/{path}/{timestamp}" + result = {"timestamp": timestamp} + + objects = get_ccdb_objects(url) + if not objects: + return {**result, "error": "no CCDB object covers this instant"} + + as_of = [o for o in objects if o.get("Created", 0) <= not_after] + newest = max(objects, key=lambda o: o.get("Created", 0)) + result.update({"newest": newest, "as_of": None, "outdated": False}) + if as_of: + result["as_of"] = max(as_of, key=lambda o: o.get("Created", 0)) + result["outdated"] = newest["Created"] > result["as_of"]["Created"] + return result + + +def describe_object(o): + return (f"Created {human(o['Created'])}, JIRA={o.get('JIRA', '?')}, " + f"comment=\"{o.get('comment', '')}\"") + + +def make_cert_paths(cert_dir="/tmp"): + key_path = os.environ.get("JALIEN_TOKEN_KEY") + cert_path = os.environ.get("JALIEN_TOKEN_CERT") + if key_path == None and cert_path == None: + uid = os.getuid() + cert_path = os.path.join(cert_dir, f"tokencert_{uid}.pem") + key_path = os.path.join(cert_dir, f"tokenkey_{uid}.pem") + + return cert_path, key_path + + +def get_NotAfter(year, period, run, passname, samples=9, ccdb_check_path="TPC/Calib/CorrectionMapV2", ccdb_check=True): + """ + Estimates the CCDBTimeMachine NotAfter timestamp from the output of the production of a given period/run/pass. + Cross-checks the estimated NotAfter timestamp vs the creation time of a CCDB object which is currently + valid for the given run. + + Returns: + int: CCDBTimeMachineTS NotAfter timestamp + """ + pass_dir = f"/alice/data/{year}/{period}/{run}/{passname}/" + print(f"[getCCDBTimeMachineTimestamp] Looking for TF output under {pass_dir}", file=sys.stderr) + + matches = find_tf_output_files(pass_dir, run) + if not matches: + sys.exit(f"[getCCDBTimeMachineTimestamp] No o2_ctf_run{int(run):08d}_orbit* output found " + f"under {pass_dir}. Check year/period/run/pass, or this run+pass may not use this " + f"catalog layout. Refusing to guess a timestamp.") + + sampled = sample_evenly(matches, samples) + ctimes = [] + for lfn in sampled: + ctime = stat_ctime(lfn) + if ctime is not None: + ctimes.append(ctime) + print(f"[getCCDBTimeMachineTimestamp] {human(ctime)} {lfn}", file=sys.stderr) + + if len(ctimes) < 2: + sys.exit(f"[getCCDBTimeMachineTimestamp] Only got {len(ctimes)} usable timestamp(s) out of " + f"{len(sampled)} sampled files ({len(matches)} found in total); refusing to guess.") + + not_after = int(statistics.median(ctimes)) + spread_h = (max(ctimes) - min(ctimes)) / 3.6e6 + print(f"[getCCDBTimeMachineTimestamp] {len(ctimes)} samples out of {len(matches)} TF outputs found; " + f"span {human(min(ctimes))} .. {human(max(ctimes))} ({spread_h:.1f} h)", file=sys.stderr) + print(f"[getCCDBTimeMachineTimestamp] Recommended NotAfter: {not_after} ({human(not_after)})", + file=sys.stderr) + + if ccdb_check: + check = ccdb_cross_check(run, ccdb_check_path, not_after) + check_timestamp = f"/{check['timestamp']}" if check else "" + if check is None or "error" in check: + reason = check["error"] if check else "unknown error" + print(f"[getCCDBTimeMachineTimestamp] CCDB cross-check on {ccdb_check_path}{check_timestamp} skipped: {reason}", + file=sys.stderr) + elif check["as_of"] is None: + print(f"[getCCDBTimeMachineTimestamp] CCDB cross-check on {ccdb_check_path}{check_timestamp}: no object " + f"existed yet at the recommended timestamp (earliest is {describe_object(check['newest'])})", + file=sys.stderr) + else: + print(f"[getCCDBTimeMachineTimestamp] CCDB cross-check on {ccdb_check_path}{check_timestamp}: as-of object is " + f"{describe_object(check['as_of'])}", file=sys.stderr) + if check["outdated"]: + print(f"[getCCDBTimeMachineTimestamp] note: CCDB's current object for this window is newer " + f"({describe_object(check['newest'])}) - it postdates this pass's processing and was " + f"not what this run actually saw.", file=sys.stderr) + + return not_after + + +def query_CCDBTimeMachineTS(base_url, user, period, passname): + """ + Queries CCDBTimeMachineTS from CCDB for a given period and pass. + + Returns: + obtained NotAfter timestamp (int) or None + """ + # Construct user ID-specific cert and key paths + cert_path, key_path = make_cert_paths() + + # Build full URL + query = f"1/Period={period}/Pass={passname}" + user_path = 'Users/' + user[0] + '/' + user + url = f"{base_url}/browse/{user_path}/CCDBTimeMachineTS/{query}" + + print(f"[getCCDBTimeMachineTimestamp] Checking if CCDBTimeMachineTS already exists: {url}", file=sys.stderr) + + response = requests.get(url, cert=(cert_path, key_path), verify=False) + if response.status_code == 200: + meta = extract_metadata_blocks_from_CCDB(response.content.decode('utf-8')) + if (len(meta) > 0): + def filter_known_fields(cls, data: dict) -> dict: + valid_keys = {f.name for f in fields(cls)} + return {k: v for k, v in data.items() if k in valid_keys} + + clean_meta = filter_known_fields(CCDBTimeMachineTS, meta[0]) + print(f"[getCCDBTimeMachineTimestamp] Found object for {clean_meta['Period']}, Pass: {clean_meta['Pass']}, " + f"NotAfter: {clean_meta['NotAfter']} ({human(clean_meta['NotAfter'])}), uploaded by LPMProductionTag {clean_meta['LPMProductionTag']}", + file=sys.stderr) + return clean_meta['NotAfter'] + + return None + + +def upload_CCDBTimeMachineTS(base_url, user, keys): + """ + Uploads an empty .dat file as CCDBTimeMachineTS with the corresponding meta data provided by `keys` + + Returns: + Response object from the POST request + """ + # Create an empty file + empty_file = "empty.dat" + with open(empty_file, "w") as f: + f.write("0") + + # Construct user ID-specific cert and key paths + cert_path, key_path = make_cert_paths() + + # Build full URL + query = "/".join(f"{k}={v}" for k, v in keys.items()) + user_path = 'Users/' + user[0] + '/' + user + start = 1 + stop = start + 1 + url = f"{base_url}/{user_path}/CCDBTimeMachineTS/{start}/{stop}/{query}" + + print(f"[getCCDBTimeMachineTimestamp] Uploading CCDBTimeMachineTS: {url}", file=sys.stderr) + + # Prepare request + with open(empty_file, 'rb') as f: + files = {'blob': f} + response = requests.post(url, files=files, cert=(cert_path, key_path), verify=False) + + os.remove(empty_file) + + if response.status_code != 201: + print(f"[getCCDBTimeMachineTimestamp] Upload failed, response: {response.status_code}", file=sys.stderr) + + +def get_CCDBTimeMachineTS(production_tag, year, period, run, passname, + samples=9, ccdb_check_path="TPC/Calib/CorrectionMapV2", ccdb_check=True, + ccdb_url="https://alice-ccdb.cern.ch", username="aliprod", + force_overwrite=False, publish_to_ccdb=True, skip_ccdb_query=False): + """ + Makes a query to CCDB for a CCDBTimeMachineTS for given period and pass. + If an object is found, the NotAfter timestamp is returned. + If no object is found, the NotAfter timestamp is estimated from the reco output of the given period/run/pass. + In case an object is found, a new upload can be forced by setting the argument `force_overwrite=True` + + Returns: + int: Either the queried or the estimated NotAfter timestamp + """ + if not skip_ccdb_query: + not_after = query_CCDBTimeMachineTS(ccdb_url, username, period, passname) + else: + not_after = None + + if not_after is not None and force_overwrite == True: + print("[getCCDBTimeMachineTimestamp] Existing object will be overwritten", file=sys.stderr) + + if not_after == None or force_overwrite == True: + print("[getCCDBTimeMachineTimestamp] Estimating CCDBTimeMachineTS for " + f"LPMProductionTag: {production_tag}, Period: {period}, Pass: {passname}", file=sys.stderr) + not_after = get_NotAfter(year, period, run, passname, samples, ccdb_check_path, ccdb_check) + time_machine_ts = CCDBTimeMachineTS(production_tag, period, passname, not_after) + if publish_to_ccdb: + upload_CCDBTimeMachineTS(ccdb_url, username, dataclasses.asdict(time_machine_ts)) + + return not_after + + +def main(): + parser = argparse.ArgumentParser( + description="Estimate a CCDB TimeMachine not-after timestamp for a run+pass, " + "from AliEn catalog timestamps of that pass's own reco output.") + parser.add_argument("--production-tag", default=os.environ.get("ALIEN_JDL_LPMPRODUCTIONTAG"), + help="production tag to mark which production uploads an object (default: $ALIEN_JDL_LPMPRODUCTIONTAG)") + parser.add_argument("--year", default=os.environ.get("ALIEN_JDL_LPMANCHORYEAR"), + help="data-taking year, e.g. 2023 (default: $ALIEN_JDL_LPMANCHORYEAR)") + parser.add_argument("--period", default=os.environ.get("ALIEN_JDL_LPMANCHORPRODUCTION"), + help="period name, e.g. LHC23s (default: $ALIEN_JDL_LPMANCHORPRODUCTION)") + parser.add_argument("--run", default=os.environ.get("ALIEN_JDL_LPMANCHORRUN"), + help="run number (default: $ALIEN_JDL_LPMANCHORRUN)") + parser.add_argument("--pass", dest="passname", default=os.environ.get("ALIEN_JDL_LPMANCHORPASSNAME"), + help="pass name, e.g. apass4 (default: $ALIEN_JDL_LPMANCHORPASSNAME)") + parser.add_argument("--samples", type=int, default=9, + help="number of per-timeframe output files to sample (default: 9)") + parser.add_argument("--ccdb-check-path", default="TPC/Calib/CorrectionMapV2", + help="CCDB path to cross-check the estimate against (default: TPC/Calib/CorrectionMapV2)") + parser.add_argument("--no-ccdb-check", action="store_true", + help="skip the CCDB cross-check after calculating the NotAfter") + parser.add_argument("--publish-to-ccdb", action="store_true", + help="upload the CCDBTimeMachineTS object to the CCDB") + parser.add_argument("--skip-ccdb-query", action="store_true", + help="skip CCDB query for existing objects while publishing") + parser.add_argument("--force-overwrite", action="store_true", + help="overwrite the current CCDBTimeMachineTS object in the CCDB if one exists") + parser.add_argument("--ccdb-url", default="https://alice-ccdb.cern.ch", + help="ccdb host for the upload") + parser.add_argument("--username", default="aliprod", + help="username to be used for the upload to CCDB") + args = parser.parse_args() + + missing = [n for n in ("year", "period", "run", "passname") if not getattr(args, n)] + if missing: + parser.error("missing required value(s): " + ", ".join(missing)) + + not_after = get_CCDBTimeMachineTS(args.production_tag, args.year, args.period, args.run, args.passname, + args.samples, args.ccdb_check_path, not args.no_ccdb_check, args.ccdb_url, args.username, + args.force_overwrite, args.publish_to_ccdb, args.skip_ccdb_query) + print(not_after) + + +if __name__ == "__main__": + main() diff --git a/GRID/utils/getReproducerScript.sh b/GRID/utils/getReproducerScript.sh new file mode 100755 index 000000000..d95204b38 --- /dev/null +++ b/GRID/utils/getReproducerScript.sh @@ -0,0 +1,132 @@ +#!/bin/bash +ALIEN_PID=$1 + +# Epoch-ms the generated reproducer will pin its CCDB queries to (see the +# INJECTION block below for why). This MUST be the moment the *original* job +# ran; "now" is only the same thing when this script runs inside that very job. +# +# $2 given -> use it verbatim (the escape hatch for +# regenerating an old job's reproducer) +# running inside the job -> now == the job's start time, correct +# otherwise -> unknown; do NOT guess, skip the pinning +# +# The last case matters: silently pinning to the wrong moment is worse than not +# pinning at all, because it looks authoritative while reconstructing a CCDB +# view that never existed for this job. +NOT_AFTER=$2 +if [ -z "${NOT_AFTER}" ]; then + if [ -n "${ALIEN_PROC_ID}" ] && [ "${ALIEN_PROC_ID}" = "${ALIEN_PID}" ]; then + NOT_AFTER=$(date +%s%3N) + else + echo "[getReproducerScript] Not running inside job ${ALIEN_PID} and no start" + echo "[getReproducerScript] time given, so CCDB will NOT be pinned: the" + echo "[getReproducerScript] reproducer will use present-day conditions." + echo "[getReproducerScript] Re-run as: $0 ${ALIEN_PID} " + fi +fi + +if [ ${JALIEN_TOKEN_CERT} ]; then + TOKENCERT=${JALIEN_TOKEN_CERT} + TOKENKEY=${JALIEN_TOKEN_KEY} +else + if [ -f ${TMPDIR:-/tmp}/tokencert_`id -u`.pem ]; then + TOKENCERT=${TMPDIR:-/tmp}/tokencert_`id -u`.pem; + fi + if [ -f ${TMPDIR:-/tmp}/tokenkey_`id -u`.pem ]; then + TOKENKEY=${TMPDIR:-/tmp}/tokenkey_`id -u`.pem; + fi +fi + +if [ ! ${TOKENCERT} ]; then + echo "This needs a tokencert and tokenkey file in the tmp folder" + exit 1 +fi + +SCRIPT=reproducer_script_${ALIEN_PID}.sh +# talk to MonaLisa to fetch the script provided by Costin +curl 'https://alimonitor.cern.ch/users/jobenv.jsp?pid='${ALIEN_PID} \ + -H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36' \ + --insecure --cert ${TOKENCERT} --key ${TOKENKEY} -o ${SCRIPT} + +# If the JDL already declares a CCDB time-machine timestamp, the job pinned +# itself and that value is restored further down by the MonaLisa env block (and +# consumed by anchorMC.sh). Injecting our own would fight it, and the JDL's is +# the authoritative one, so stand down. +CCDB_PIN_BLOCK="" +if grep -q "ALIEN_JDL_CCDB_CONDITION_NOT_AFTER" "${SCRIPT}"; then + echo "[getReproducerScript] JDL already sets CCDB_CONDITION_NOT_AFTER; keeping it." +elif [ -n "${NOT_AFTER}" ]; then + # Pin every CCDB query to the time the ORIGINAL job ran ("time machine"). + # Without this the reproducer resolves each object to whatever is newest in + # CCDB *today*, so any re-upload since the job ran silently changes the + # result -- including the collision context, which is otherwise fully + # deterministic given the fixed seed (cf. O2-7093). This reconstructs the + # job's CCDB view rather than sharing a declared one, because production jobs + # are themselves normally unpinned (o2dpg_sim_workflow.py defaults + # --condition-not-after to year 2077). + # Unset ALICEO2_CCDB_CONDITION_NOT_AFTER before running to get present-day + # conditions instead. + CCDB_PIN_BLOCK="export ALICEO2_CCDB_CONDITION_NOT_AFTER=\${ALICEO2_CCDB_CONDITION_NOT_AFTER:-${NOT_AFTER}}" + echo "[getReproducerScript] Pinning reproducer CCDB queries to ${NOT_AFTER}." +fi + +# Define the Apptainer injection block which makes sure +# that the job script is automatically executed in apptainer +INJECTION=' +export ALIEN_PID=#ALIEN_PID# +#CCDB_PIN_BLOCK# +# Check if the script is running inside an Apptainer (Singularity) container +if [ -z "$APPTAINER_NAME" ] && [ -z "$SINGULARITY_NAME" ]; then + # Relaunch this script inside the container + + export WORKDIR=/tmp/foo-${ALIEN_PID} + if [ ! -d ${WORKDIR} ]; then + mkdir ${WORKDIR} + fi + + # - copy the certificate token into /tmp/ inside the container + mkdir ${WORKDIR}/tmp + cp /tmp/token*pem ${WORKDIR}/tmp + + # - copy the job script into workdir + cp $0 ${WORKDIR} + + # detect architecture (ARM or X86) + ARCH=$(uname -i) + if [ "$ARCH" == "aarch64" ] || [ "$ARCH" == "x86_64" ]; then + echo "Detected hardware architecture : $ARCH" + else + echo "Invalid architecture ${ARCH} detected. Exiting" + exit 1 + fi + if [ "$ARCH" == "aarch64" ]; then + ISAARCH64="1" + fi + + CONTAINER="/cvmfs/alice.cern.ch/containers/fs/apptainer/compat_el9-${ARCH}" + APPTAINER_EXEC="/cvmfs/alice.cern.ch/containers/bin/apptainer/${ARCH}/current/bin/apptainer" + + # we can actually analyse the local JDL to find the package and set it up for the container + ${APPTAINER_EXEC} exec -C -B /cvmfs:/cvmfs,${WORKDIR}:/workdir,${WORKDIR}/tmp:/tmp --pwd /workdir -C ${CONTAINER} "$0" + exit $? +fi +' + +# Inject the block after the first line (shebang) +awk -v block="$INJECTION" 'NR==1 {print; print block; next} 1' "$SCRIPT" > tmpfile && mv tmpfile "$SCRIPT" + +# take out sandboxing structure +sed -i "/echo \"Create a fresh sandbox at every attempt of running the job: alien-job-$ALIEN_PID\"/d" "$SCRIPT" +sed -i "/rm -rf alien-job-$ALIEN_PID/d" "$SCRIPT" +sed -i "/mkdir -p alien-job-$ALIEN_PID\/tmp/d" "$SCRIPT" +sed -i "/cd alien-job-$ALIEN_PID/d" "$SCRIPT" + +# replace the PID +sed -i "s/#ALIEN_PID#/${ALIEN_PID}/g" "$SCRIPT" + +# substitute the CCDB pinning placeholder (dropping the line entirely when we +# decided not to pin -- see above) +awk -v blk="$CCDB_PIN_BLOCK" '$0=="#CCDB_PIN_BLOCK#"{ if (blk!="") print blk; next } 1' \ + "$SCRIPT" > tmpfile && mv tmpfile "$SCRIPT" + +chmod +x "${SCRIPT}" diff --git a/GRID/utils/grid_submit.sh b/GRID/utils/grid_submit.sh index e51c52b0d..5d11e1a2b 100755 --- a/GRID/utils/grid_submit.sh +++ b/GRID/utils/grid_submit.sh @@ -194,7 +194,7 @@ ONGRID=0 JOBTTL=82000 CPUCORES=8 -PRODSPLIT=1 +PRODSPLIT=${PRODSPLIT:-1} # this tells us to continue an existing job --> in this case we don't create a new workdir while [ $# -gt 0 ] ; do case $1 in @@ -208,7 +208,7 @@ while [ $# -gt 0 ] ; do --cores) CPUCORES=$2; shift 2 ;; # allow to specify the CPU cores (check compatibility with partition !) --dry) DRYRUN="ON"; shift 1 ;; # do a try run and not actually interact with the GRID (just produce local jdl file) --o2tag) O2TAG=$2; shift 2 ;; # - --packagespec) PACKAGESPEC=$2; shift 2 ;; # the alisw, cvmfs package list (command separated - example: '"VO_ALICE@FLUKA_VMC::4-1.1-vmc3-1","VO_ALICE@O2::daily-20230628-0200-1"') + --packagespec) PACKAGESPEC=$2; shift 2 ;; # the alisw, cvmfs package list (command separated - example: '"VO_ALICE@FLUKA_VMC::4-1.1-vmc3-1","VO_ALICE@O2::daily-20230628-0200-1"') --asuser) ASUSER=$2; shift 2 ;; # --label) JOBLABEL=$2; shift 2 ;; # label identifying the production (e.g. as a production identifier) --mattermost) MATTERMOSTHOOK=$2; shift 2 ;; # if given, status and metric information about the job will be sent to this hook @@ -216,8 +216,11 @@ while [ $# -gt 0 ] ; do --prodsplit) PRODSPLIT=$2; shift 2 ;; # allows to set JDL production split level (useful to easily replicate workflows) --singularity) SINGULARITY=ON; shift 1 ;; # run everything inside singularity --wait) WAITFORALIEN=ON; shift 1 ;; #wait for alien jobs to finish + --wait-any) WAITFORALIENANY=ON; WAITFORALIEN=ON; shift 1 ;; #wait for any good==done alien jobs to return --outputspec) OUTPUTSPEC=$2; shift 2 ;; #provide comma separate list of JDL file specs to be put as part of JDL Output field (example '"*.log@disk=1","*.root@disk=2"') - -h) Usage ; exit ;; + --split-on-collection) DATACOLLECTION=$2; shift 2 ;; # this will split the jobs on InputDataCollection and "file" mode + --inputfile) INPUTFILES+=("$2"); shift 2 ;; # extra file to pre-stage via JDL InputFile (repeatable). Accepts a bare LFN or "LF:". Staged by AliEn before the job starts, so it is NOT charged to job CPU/walltime (unlike an in-job 'alien.py cp'). + -h) Usage ; exit ;; --help) Usage ; exit ;; --fetch-output) FETCHOUTPUT=ON; shift 1 ;; # if to fetch all JOB output locally (to make this job as if it ran locally); only works when we block until all JOBS EXIT *) break ;; @@ -227,8 +230,20 @@ export JOBTTL export JOBLABEL export MATTERMOSTHOOK export CONTROLSERVER -export PRODSPLIT -[[ $PRODSPLIT -gt 100 ]] && echo "Production split needs to be smaller than 100 for the moment" && exit 1 + +# Validate the production split level and derive the zero-padding width used for the +# per-subjob OutputDir counter (#alien_counter_i#) and for local fetching. The width +# adapts to PRODSPLIT (minimum 3, so existing 3-digit layouts are unchanged), which lifts +# the former hard cap of 100 while keeping OutputDir naming consistent at any scale. +[[ ! $PRODSPLIT =~ ^[0-9]+$ || $PRODSPLIT -lt 1 ]] && echo "Production split must be a positive integer (got '${PRODSPLIT}')" && exit 1 +COUNTERWIDTH=${#PRODSPLIT} +[[ $COUNTERWIDTH -lt 3 ]] && COUNTERWIDTH=3 + +# check for presence of jq (needed in code path to fetch output files) +[[ "$FETCHOUTPUT" ]] && { which jq &> /dev/null || { echo "Could not find jq command. Please load or install" && exit 1; }; } + +# check if script is actually a valid file and fail early if not +[[ "${SCRIPT}" ]] && [[ ! -f "${SCRIPT}" ]] && echo "Script file ${SCRIPT} does not exist .. aborting" && exit 1 # analyse options: # we should either run with --script or with -c @@ -265,9 +280,10 @@ pok "Set the job name by running $0 " # Generate local workdir # if [[ "${ONGRID}" == "0" ]]; then - WORKDIR=${WORKDIR:-/tmp/alien_work/$(basename "$MY_JOBWORKDIR")} - [ ! -d "${WORKDIR}" ] && mkdir -p ${WORKDIR} - [ ! "${CONTINUE_WORKDIR}" ] && cp "${MY_JOBSCRIPT}" "${WORKDIR}/alien_jobscript.sh" + GRID_SUBMIT_WORKDIR=${GRID_SUBMIT_WORKDIR:-${TMPDIR:-/tmp}/alien_work/$(basename "$MY_JOBWORKDIR")} + echo "WORKDIR FOR THIS JOB IS ${GRID_SUBMIT_WORKDIR}" + [ ! -d "${GRID_SUBMIT_WORKDIR}" ] && mkdir -p ${GRID_SUBMIT_WORKDIR} + [ ! "${CONTINUE_WORKDIR}" ] && cp "${MY_JOBSCRIPT}" "${GRID_SUBMIT_WORKDIR}/alien_jobscript.sh" fi # @@ -287,7 +303,7 @@ if [[ "${IS_ALIEN_JOB_SUBMITTER}" ]]; then # read preamble from job file which is used whenever command line not given - # a) OutputSpec + # -) OutputSpec [[ ! ${OUTPUTSPEC} ]] && OUTPUTSPEC=$(grep "^#JDL_OUTPUT=" ${SCRIPT} | sed 's/#JDL_OUTPUT=//') echo "Found OutputSpec to be ${OUTPUTSPEC}" if [ ! ${OUTPUTSPEC} ]; then @@ -298,7 +314,28 @@ if [[ "${IS_ALIEN_JOB_SUBMITTER}" ]]; then # check if this is a list and if all parts are properly quoted OUTPUTSPEC=$(sanitize_tokens_with_quotes ${OUTPUTSPEC}) fi - # b) PackageSpec + # -) ErrorOutputSpec + [[ ! ${ERROROUTPUTSPEC} ]] && ERROROUTPUTSPEC=$(grep "^#JDL_ERROROUTPUT=" ${SCRIPT} | sed 's/#JDL_ERROROUTPUT=//') + echo "Found ErrorOutputSpec to be ${ERROROUTPUTSPEC}" + if [ ${ERROROUTPUTSPEC} ]; then + # check if this is a list and if all parts are properly quoted + ERROROUTPUTSPEC=$(sanitize_tokens_with_quotes ${ERROROUTPUTSPEC}) + fi + # -) Special singularity / Apptainer image + [[ ! ${IMAGESPEC} ]] && IMAGESPEC=$(grep "^#JDL_IMAGE=" ${SCRIPT} | sed 's/#JDL_IMAGE=//') + echo "Found Container Image to be ${IMAGESPEC}" + + # -) Requirements-Spec + REQUIRESPEC=$(grep "^#JDL_REQUIRE=" ${SCRIPT} | sed 's/#JDL_REQUIRE=//') + if [ ! "${REQUIRESPEC}" ]; then + echo "No Requirement setting found; Setting to default" + REQUIRESPEC="{member(other.GridPartitions,"${GRIDPARTITION:-multicore_8}")};" + echo "Requirement is ${REQUIRESPEC}" + fi + + echo "Requirements JDL entry is ${REQUIRESPEC}" + + # -) PackageSpec [[ ! ${PACKAGESPEC} ]] && PACKAGESPEC=$(grep "^#JDL_PACKAGE=" ${SCRIPT} | sed 's/#JDL_PACKAGE=//') echo "Found PackagesSpec to be ${PACKAGESPEC}" ## sanitize package spec @@ -323,17 +360,33 @@ if [[ "${IS_ALIEN_JOB_SUBMITTER}" ]]; then cd "$(dirname "$0")" THIS_SCRIPT="$PWD/$(basename "$0")" - cd "${WORKDIR}" + cd "${GRID_SUBMIT_WORKDIR}" QUOT='"' + SPLITMODE="production:1-${PRODSPLIT}" + if [ "${DATACOLLECTION}" ]; then + SPLITMODE="file" + fi + + # Assemble the JDL InputFile list: always the job script, plus any extra files + # requested via --inputfile (each pre-staged by AliEn before the job starts). + INPUTFILE_ENTRIES="${QUOT}LF:${MY_JOBWORKDIR}/alien_jobscript.sh${QUOT}" + for _extra in "${INPUTFILES[@]}"; do + [[ "${_extra}" == LF:* ]] || _extra="LF:${_extra}" + INPUTFILE_ENTRIES="${INPUTFILE_ENTRIES}, ${QUOT}${_extra}${QUOT}" + done # ---- Generate JDL ---------------- # TODO: Make this configurable or read from a preamble section in the jobfile cat > "${MY_JOBNAMEDATE}.jdl" <> "${MY_JOBNAMEDATE}.jdl" # add output spec echo "Packages = {"${PACKAGESPEC}"};" >> "${MY_JOBNAMEDATE}.jdl" # add package spec + [ $ERROROUTPUTSPEC ] && echo "OutputErrorE = {"${ERROROUTPUTSPEC}"};" >> "${MY_JOBNAMEDATE}.jdl" # add error output files + [ $IMAGESPEC ] && echo "DebugTag = {\"${IMAGESPEC}\"};" >> "${MY_JOBNAMEDATE}.jdl" # use special singularity image to run job + # echo "Requirements = {"${REQUIREMENTSSPEC}"} >> "${MY_JOBNAMEDATE}.jdl" + [ "$REQUIRESPEC" ] && echo "Requirements = ${REQUIRESPEC}" >> "${MY_JOBNAMEDATE}.jdl" # "output_arch.zip:output/*@disk=2", # "checkpoint*.tar@disk=2" @@ -349,10 +406,21 @@ EOF if [ ! "${DRYRUN}" ]; then command_file="alien_commands.txt" + if [ "$DATACOLLECTION" ]; then + pok "Preparing data collection XML file" + alien.py toXml $(cat ${DATACOLLECTION}) > collection.xml + if [ "$?" != "0" ]; then + per "Problem with data collection preparation" + exit 1 + fi + fi + pok "Preparing job \"$MY_JOBNAMEDATE\"" ( # assemble all GRID interaction in a single script / transaction [ -f "${command_file}" ] && rm ${command_file} + echo "user ${MY_USER}" >> ${command_file} + echo "whoami" >> ${command_file} [ ! "${CONTINUE_WORKDIR}" ] && echo "rmdir ${MY_JOBWORKDIR}" >> ${command_file} # remove existing job dir # echo "mkdir ${MY_BINDIR}" >> ${command_file} # create bindir echo "mkdir ${MY_JOBPREFIX}" >> ${command_file} # create job output prefix @@ -361,6 +429,9 @@ EOF echo "rm ${MY_BINDIR}/${MY_JOBNAMEDATE}.sh" >> ${command_file} # remove current job script echo "cp file:${PWD}/${MY_JOBNAMEDATE}.jdl alien://${MY_JOBWORKDIR}/${MY_JOBNAMEDATE}.jdl@DISK=1" >> ${command_file} # copy the jdl echo "cp file:${THIS_SCRIPT} alien://${MY_BINDIR}/${MY_JOBNAMEDATE}.sh@DISK=1" >> ${command_file} # copy current job script to AliEn + if [ "${DATACOLLECTION}" ]; then + echo "cp file:collection.xml alien://${MY_JOBWORKDIR}/collection.xml" >> ${command_file} + fi [ ! "${CONTINUE_WORKDIR}" ] && echo "cp file:${MY_JOBSCRIPT} alien://${MY_JOBWORKDIR}/alien_jobscript.sh" >> ${command_file} ) > alienlog.txt 2>&1 @@ -383,13 +454,20 @@ EOF fi + # true while a subjob is not final; ERROR_A (resubmitted) and a failed query count as active + subjobs_active() { + jq -e '(.results | length) == 0 or (.results | any(.status | IN("INSERTING", "SPLITTING", "WAITING", "ASSIGNED", "STARTED", "RUNNING", "SAVING", "SAVED", "ERROR_A")))' \ + <<<"$1" >/dev/null + [ $? -ne 1 ] + } + # wait here until all ALIEN jobs have returned spin[3]="-" spin[2]="/" spin[1]="|" spin[0]="\\" JOBSTATUS="I" - if [ "{WAITFORALIEN}" ]; then + if [ "${WAITFORALIEN}" ]; then echo -n "Waiting for jobs to return ... Last status : ${spin[0]} ${JOBSTATUS}" fi counter=0 @@ -399,43 +477,70 @@ EOF echo -ne "\b\b\b${spin[$((counter%4))]} ${JOBSTATUS}" let counter=counter+1 if [ ! "${counter}" == "100" ]; then + # ensures that we see spinner ... but only check for new job + # status every 100 * 0.5 = 50s? continue fi - let counter=0 - JOBSTATUS=$(alien.py ps -j ${MY_JOBID} | awk '//{print $4}') - # echo -ne "Waiting for jobs to return; Last status ${JOBSTATUS}" - if [ "$JOBSTATUS" == "D" ]; then - echo "Job done" - WAITFORALIEN="" + let counter=0 # reset counter + + # this is the global job status (a D here means the production is done) + JOBSTATUS=$(alien.py ps -j ${MY_JOBID} | awk '//{print $3}') # this is the global job status + # in addition we may query individual splits + if [ -n "${WAITFORALIENANY}" ]; then + DETAILED_STATUS_JSON=$(ALIENPY_JSON=true alien.py ps -a -m "${MY_JOBID}") + # check if any is already marked as DONE + if jq -e '.results | any(.status == "DONE")' <<<"${DETAILED_STATUS_JSON}" >/dev/null; then + JOBSTATUS="D" + echo "At least one good job" + else + # check if there are still jobs running/waiting; if not also finish + # this could happen when all jobs are zombies (in which case we also finish) + if ! subjobs_active "${DETAILED_STATUS_JSON}"; then + JOBSTATUS="D" # some job finished successfully + echo "No remaining good job" + fi + fi + elif [ "${JOBSTATUS}" == "D" ]; then + # the masterjob can briefly show DONE while an ERROR_A subjob is resubmitted + if subjobs_active "$(ALIENPY_JSON=true alien.py ps -a -m "${MY_JOBID}")"; then + JOBSTATUS="S" + fi + fi + + if [ "${JOBSTATUS}" == "D" ]; then + echo "${WAITFORALIENANY:+At least one }Job(s) done" + WAITFORALIEN="" # guarantees to go out of outer while loop if [ "${FETCHOUTPUT}" ]; then - SUBJOBIDS="" - while [ ! ${SUBJOBIDS} ]; do - SUBJOBIDS=($(alien.py ps --trace ${MY_JOBID} | awk '/Subjob submitted/' | sed 's/.*submitted: //' | tr '\n' ' ')) - sleep 1 - done - # TODO: make this happen in a single alien.py session and with parallel copying - echo "Fetching results" - for splitcounter in `seq 1 ${PRODSPLIT}`; do - # we still need to check if this particular subjob was successful - SUBJOBSTATUS=$(alien.py ps -j ${SUBJOBIDS[splitcounter-1]} | awk '//{print $4}') - if [ "$SUBJOBSTATUS" == "D" ]; then - SPLITOUTDIR=$(printf "%03d" ${splitcounter}) - [ ! -f ${SPLITOUTDIR} ] && mkdir ${SPLITOUTDIR} - echo "Fetching result files for subjob ${splitcounter} into ${PWD}" - CPCMD="alien.py cp ${MY_JOBWORKDIR}/${SPLITOUTDIR}/* file:./${SPLITOUTDIR}" - eval "${CPCMD}" 2> /dev/null - else - echo "Not fetching files for subjob ${splitcounter} since job code is ${SUBJOBSTATUS}" - fi - done - wait + SUBJOBIDS=() + SUBJOBSTATUSES=() + echo "Fetching subjob info" + while [ "${#SUBJOBIDS[@]}" == "0" ]; do + QUERYRESULT=$(ALIENPY_JSON=true alien.py ps -a -m ${MY_JOBID}) + SUBJOBIDS=($(echo ${QUERYRESULT} | jq -r '.results[].id' | tr '\n' ' ')) + SUBJOBSTATUSES=($(echo ${QUERYRESULT} | jq -r '.results[].status' | tr '\n' ' ')) + # echo "LENGTH SUBJOBS ${#SUBJOBIDS[@]}" + sleep 1 + done + # TODO: make this happen with parallel copying + echo "Fetching results for ${PRODSPLIT} sub-jobs" + for splitcounter in `seq 1 ${PRODSPLIT}`; do + let jobindex=splitcounter-1 + THIS_STATUS=${SUBJOBSTATUSES[jobindex]} + THIS_JOB=${SUBJOBIDS[jobindex]} + echo "Fetching for job ${THIS_JOB}" + if [ "${THIS_STATUS}" == "DONE" ]; then + SPLITOUTDIR=$(printf "%0${COUNTERWIDTH}d" ${splitcounter}) + [ ! -f ${SPLITOUTDIR} ] && mkdir ${SPLITOUTDIR} + echo "Fetching result files for subjob ${splitcounter} into ${PWD}" + CPCMD="alien.py cp ${MY_JOBWORKDIR}/${SPLITOUTDIR}/* file:./${SPLITOUTDIR}" + eval "${CPCMD}" 2> /dev/null + else + echo "Not fetching files for subjob ${splitcounter} since job code is ${THIS_STATUS}" + fi + done fi fi - if [[ "${FOO:0:1}" == [EK] ]]; then - echo "Job error occured" - exit 1 - fi done # get the job data products locally if requested @@ -449,16 +554,33 @@ if [[ ${SINGULARITY} ]]; then # if singularity was asked we restart this script within a container # it's actually much like the GRID mode --> which is why we set JALIEN_TOKEN_CERT set -x - cp $0 ${WORKDIR} - singularity exec -C -B /cvmfs:/cvmfs,${WORKDIR}:/workdir --env JALIEN_TOKEN_CERT="foo" --pwd /workdir /cvmfs/alice.cern.ch/containers/fs/singularity/centos7 $0 \ + cp $0 ${GRID_SUBMIT_WORKDIR} + + # detect architecture (ARM or X86) + ARCH=$(uname -i) + if [ "$ARCH" == "aarch64" ] || [ "$ARCH" == "x86_64" ]; then + echo "Detected hardware architecture : $ARCH" + else + echo "Invalid architecture ${ARCH} detected. Exiting" + exit 1 + fi + if [ "$ARCH" == "aarch64" ]; then + ISAARCH64="1" + fi + + CONTAINER="/cvmfs/alice.cern.ch/containers/fs/apptainer/compat_el9-${ARCH}" + APPTAINER_EXEC="/cvmfs/alice.cern.ch/containers/bin/apptainer/${ARCH}/current/bin/apptainer" + + # we can actually analyse the local JDL to find the package and set it up for the container + ${APPTAINER_EXEC} exec -C -B /cvmfs:/cvmfs,${GRID_SUBMIT_WORKDIR}:/workdir --pwd /workdir -C ${CONTAINER} /workdir/grid_submit.sh \ ${CONTINUE_WORKDIR:+"-c ${CONTINUE_WORKDIR}"} --local ${O2TAG:+--o2tag ${O2TAG}} --ttl ${JOBTTL} --label ${JOBLABEL:-label} ${MATTERMOSTHOOK:+--mattermost ${MATTERMOSTHOOK}} ${CONTROLSERVER:+--controlserver ${CONTROLSERVER}} set +x exit $? fi if [[ "${ONGRID}" == 0 ]]; then - banner "Executing job in directory ${WORKDIR}" - cd "${WORKDIR}" 2> /dev/null + banner "Executing job in directory ${GRID_SUBMIT_WORKDIR}" + cd "${GRID_SUBMIT_WORKDIR}" 2> /dev/null fi exec &> >(tee -a alien_log_${ALIEN_PROC_ID:-0}.txt) @@ -476,7 +598,6 @@ banner "Limits" ulimit -a banner "OS detection" -lsb_release -a || true cat /etc/os-release || true cat /etc/redhat-release || true @@ -504,11 +625,21 @@ cat /proc/meminfo > alien_meminfo.log # ----------- PREPARE SOME ALIEN ENV -- useful for the job ----------- if [ "${ONGRID}" = "1" ]; then + export ALIENPY_JCENTRAL=alice-jcentral.cern.ch notify_mattermost "STARTING GRID ${ALIEN_PROC_ID} CHECK $(which alien.py)" alien.py ps --jdl ${ALIEN_PROC_ID} > this_jdl.jdl ALIEN_JOB_OUTPUTDIR=$(grep "OutputDir" this_jdl.jdl | awk '//{print $3}' | sed 's/"//g' | sed 's/;//') ALIEN_DRIVER_SCRIPT=$0 + # determine subjob id from the structure of the outputfolder + # can use basename tool since this is like a path + SUBJOBID=$(echo $(basename "${ALIEN_JOB_OUTPUTDIR}") | sed 's/^0*//') # Remove leading zeros if present + + # we expose some information about prodsplit and subjob id to the jobs + # so that they can adjust/contextualise the payload + export ALIEN_O2DPG_GRIDSUBMIT_PRODSPLIT=${PRODSPLIT} + export ALIEN_O2DPG_GRIDSUBMIT_SUBJOBID=${SUBJOBID} + #notify_mattermost "ALIEN JOB OUTDIR IS ${ALIEN_JOB_OUTPUTDIR}" export ALIEN_JOB_OUTPUTDIR @@ -528,31 +659,23 @@ if [ "${ONGRID}" = "1" ]; then fi fi -# ----------- DOWNLOAD ADDITIONAL HELPERS ---------------------------- -curl -o analyse_CPU.py https://raw.githubusercontent.com/sawenzel/AliceO2/swenzel/cpuana/Utilities/Tools/analyse_CPU.py &> /dev/null -chmod +x analyse_CPU.py -export PATH=$PATH:$PWD -export JOBUTILS_MONITORCPU=ON -export JOBUTILS_WRAPPER_SLEEP=5 -#export JOBUTILS_JOB_KILLINACTIVE=180 # kill inactive jobs after 3 minutes --> will be the task of pipeline runner? (or make it optional) -export JOBUTILS_MONITORMEM=ON +# export JOBUTILS_MONITORCPU=ON +# export JOBUTILS_WRAPPER_SLEEP=5 +# export JOBUTILS_JOB_KILLINACTIVE=180 # kill inactive jobs after 3 minutes --> will be the task of pipeline runner? (or make it optional) +# export JOBUTILS_MONITORMEM=ON # ----------- EXECUTE ACTUAL JOB ------------------------------------ # source the actual job script from the work dir chmod +x ./alien_jobscript.sh ./alien_jobscript.sh +# fetch the return code +RC=$? -# just to be sure that we get the logs -cp alien_log_${ALIEN_PROC_ID:-0}.txt logtmp_${ALIEN_PROC_ID:-0}.txt -[ "${ALIEN_JOB_OUTPUTDIR}" ] && upload_to_Alien logtmp_${ALIEN_PROC_ID:-0}.txt ${ALIEN_JOB_OUTPUTDIR}/ +# just to be sure that we get the logs (temporarily disabled since the copy seems to hang sometimes) +#cp alien_log_${ALIEN_PROC_ID:-0}.txt logtmp_${ALIEN_PROC_ID:-0}.txt +#[ "${ALIEN_JOB_OUTPUTDIR}" ] && upload_to_Alien logtmp_${ALIEN_PROC_ID:-0}.txt ${ALIEN_JOB_OUTPUTDIR}/ -# MOMENTARILY WE ZIP ALL LOG FILES -ziparchive=logs_PROCID${ALIEN_PROC_ID:-0}.zip -find ./ -name "*.log*" -exec zip ${ziparchive} {} ';' -find ./ -name "*mergerlog*" -exec zip ${ziparchive} {} ';' -find ./ -name "*serverlog*" -exec zip ${ziparchive} {} ';' -find ./ -name "*workerlog*" -exec zip ${ziparchive} {} ';' -find ./ -name "alien_log*.txt" -exec zip ${ziparchive} {} ';' +echo "Job done ... exiting with ${RC}" # We need to exit for the ALIEN JOB HANDLER! -exit 0 +exit ${RC} diff --git a/GRID/utils/jdl_to_script.py b/GRID/utils/jdl_to_script.py index 711b7ab79..54ab7f319 100755 --- a/GRID/utils/jdl_to_script.py +++ b/GRID/utils/jdl_to_script.py @@ -26,7 +26,7 @@ def toList(token): inner=token[1:-1] # attention, this might not work if comma inside a string return [l.lstrip().rstrip() for l in inner.split(",")] - return token + return [token] def cleanFileName(filename): # remove " and 'LF:' from alien file names diff --git a/GRID/utils/querymasterjob.sh b/GRID/utils/querymasterjob.sh new file mode 100755 index 000000000..1034ebe42 --- /dev/null +++ b/GRID/utils/querymasterjob.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +# A script which queries the (latest) masterjob given an AliEn jobtype + +MY_JOBTYPE=$1 +if [ ! "${MY_JOBTYPE}" ]; then + echo "Please provide a job type as first argument" + exit 1 +fi + +# use curl to fetch the table of jobs for this jobtype +URL="https://alimonitor.cern.ch/prod/jobs.jsp?t=${MY_JOBTYPE}&res_path=csv" +echo "Querying ${URL}" +latestjob=$(curl --cert ~/.globus/usercert.pem --key ~/.globus/userkey.pem -k -s --no-keepalive -H "Connection: close" ${URL} 2> /dev/null | grep -v "Run" | head -n 1) + +#The output format is +#PID;Run number;Packages;Output directory;Total jobs;Done jobs;Running jobs;Waiting jobs;Error jobs;Other jobs;Wall time;Saving time;Output size +# split string on ";" +IFS=';' read -r -a tokens <<< "$latestjob" +masterid=${tokens[0]} +if [ ! "${masterid}" ]; then + exit 1 +fi + +totaljobs=${tokens[4]} +donejobs=${tokens[5]} +runningjobs=${tokens[6]} +erroredjobs=${tokens[8]} +echo "MasterID $masterid" +echo "TotalJobs $totaljobs" +echo "DoneJobs $donejobs" +echo "RunningJobs $runningjobs" +echo "ErroredJobs $erroredjobs" diff --git a/GRID/utils/runGRIDContainerized.sh b/GRID/utils/runGRIDContainerized.sh new file mode 100755 index 000000000..3fc2380a4 --- /dev/null +++ b/GRID/utils/runGRIDContainerized.sh @@ -0,0 +1,64 @@ +#!/usr/bin/env bash + +# Runs a job containerized ... as would be the case on the GRID. +# Mimics what the AliEn job handler does. + +SCRIPT=$1 +[ $SCRIPT == "" ] && echo "Please provide a script to run" && exit 1 +echo "Trying to run script ${SCRIPT} in a container environment" + +# detect architecture (ARM or X86) +ARCH=$(uname -i) +if [ "$ARCH" == "aarch64" ] || [ "$ARCH" == "x86_64" ]; then + echo "Detected hardware architecture : $ARCH" +else + echo "Invalid architecture ${ARCH} detected. Exiting" + exit 1 +fi +# we just use the default singularity container (if not already set) +APPTAINER_CONTAINER=${APPTAINER_CONTAINER:-/cvmfs/alice.cern.ch/containers/fs/apptainer/compat_el9-${ARCH}} + +# create workdir if not specified externally +if [ ! "${WORK_DIR}" ]; then + WORK_DIR=$(mktemp -d /tmp/alien-job-XXXXXX) +fi +echo "This job will be run in $WORK_DIR" + +if [ ! -d "${WORK_DIR}" ]; then + echo "working directory ${WORK_DIR} does not exist; Please create before running" + exit 1 +fi + +# copy script to WORK_DIR +cp ${SCRIPT} ${WORK_DIR}/job.sh + +# export certificates - belonging to current user (need to be created before) +ALIEN_CERTFILE=$(find /tmp -type f -name 'tokencert*pem' -user `whoami` 2> /dev/null) +ALIEN_KEYFILE=$(find /tmp -type f -name 'tokenkey*pem' -user `whoami` 2> /dev/null) + +[ "${ALIEN_CERTFILE}" == "" ] && echo "No certificate file found; Initialize a token with alien-init-token or similar" && exit 1 +[ "${ALIEN_KEYFILE}" == "" ] && echo "No certificate file found; Initialize a token with alien-init-token or similar" && exit 1 + +cp ${ALIEN_CERTFILE} ${WORK_DIR}/usercert.pem +cp ${ALIEN_KEYFILE} ${WORK_DIR}/userkey.pem + +echo "JALIEN_TOKEN_CERT=/workdir/usercert.pem" > ${WORK_DIR}/envfile +echo "JALIEN_TOKEN_KEY=/workdir/userkey.pem" >> ${WORK_DIR}/envfile +if [ "${GRID_CVMFS_PACKAGE}" ]; then + echo "GRID_CVMFS_PACKAGE=${GRID_CVMFS_PACKAGE}" >> ${WORK_DIR}/envfile +fi + +# load a CVMFS package if we are asked to do so -- but do it as part of the job +LOADER='if [ "${GRID_CVMFS_PACKAGE}" ]; then + /cvmfs/alice.cern.ch/bin/alienv printenv ${GRID_CVMFS_PACKAGE} > cvmfs_env + source cvmfs_env +fi' +# Inject the block after the first line (shebang) +JOBSCRIPT=${WORK_DIR}/job.sh +awk -v block="$LOADER" 'NR==1 {print; print block; next} 1' "$JOBSCRIPT" > tmpfile && mv tmpfile "$JOBSCRIPT" +chmod +x "${JOBSCRIPT}" + +# launch job = script inside the container in the workdir +APPTAINER_EXEC=${APPTAINER_EXEC:-"/cvmfs/alice.cern.ch/containers/bin/apptainer/${ARCH}/current/bin/apptainer"} +${APPTAINER_SUDO:+sudo} ${APPTAINER_EXEC} exec -C -B /cvmfs:/cvmfs,${WORK_DIR}:/workdir \ + --pwd /workdir --env-file ${WORK_DIR}/envfile ${APPTAINER_CONTAINER} bash /workdir/job.sh diff --git a/MC/README.md b/MC/README.md index 86408ccb3..b8863f484 100644 --- a/MC/README.md +++ b/MC/README.md @@ -18,7 +18,7 @@ Corresponding files will be created in the current directory, QC objects will be ``` alienv enter O2/latest O2Physics/latest QualityControl/latest O2DPG/latest cd MC/run/examples -./O2DPG_pp_minbias_multiple_tf_qc.sh +./O2DPG_pp_minbias.sh ``` If the script does not succeed, contact the repository maintainers. Sometimes an intermittent issue might appear, then it might be worth executing the script again - it will pick up from the latest failed task. @@ -67,7 +67,7 @@ def include_all_QC_finalization(ntimeframes, standalone): ``` -6. Delete the files generated by the workflow during step 2 and run the `O2DPG_pp_minbias_multiple_tf_qc.sh` script again. +6. Delete the files generated by the workflow during step 2 and run the `O2DPG_pp_minbias.sh` script again. Verify that the QC Task succeeds. You can run only the parts of the workflow which are required to reach your QC task by adding `-tt _finalize` to `o2_dpg_workflow_runner.py`. Logs are available under task names in their working directories: tf when processing TFs and QC during finalization. @@ -90,7 +90,7 @@ Add your QC post-processing workflow there using the `add_QC_postprocessing` fun See its in-code documentation for argument explanation and follow the existing examples. Please make sure to set the correct `needs`, so the post-processing workflow is executed only when the required QC objects are already in the QCDB. -3. Delete the files generated by the workflow during the previous steps and run the `O2DPG_pp_minbias_multiple_tf_qc.sh` script again. +3. Delete the files generated by the workflow during the previous steps and run the `O2DPG_pp_minbias.sh` script again. Verify that the QC post-processing workflow succeeds. You can run only the parts of the workflow which are required to reach your QC task by adding `-tt _finalize` to `o2_dpg_workflow_runner.py`. Relevant logs are available under task names in the `QC` directory. diff --git a/MC/analysis_testing/analysis_test.sh b/MC/analysis_testing/analysis_test.sh index e934b6d47..600118101 100755 --- a/MC/analysis_testing/analysis_test.sh +++ b/MC/analysis_testing/analysis_test.sh @@ -49,12 +49,6 @@ else shift shift ;; - --add-common-args) - add_common_args=" ${2} ${3} " - shift - shift - shift - ;; *) echo "ERROR: Unknown argument ${1}" exit 1 @@ -66,7 +60,6 @@ fi # basic checks [[ "${testanalysis}" == "" ]] && { echo "ERROR: No analysis specified to be run" ; exit 1 ; } [[ "${aod}" == "" ]] && { echo "ERROR: No AOD found to be analysed" ; exit 1 ; } -[[ "${add_common_args}" != "" ]] && add_common_args="--add-common-args ${add_common_args}" # check if enabled enabled=$($O2DPG_ROOT/MC/analysis_testing/o2dpg_analysis_test_config.py check -t ${testanalysis} --status) @@ -77,7 +70,7 @@ mkdir Analysis 2>/dev/null include_disabled=${include_disabled:+--include-disabled} workflow_path="Analysis/workflow_analysis_test_${testanalysis}.json" rm ${workflow_path} 2>/dev/null -$O2DPG_ROOT/MC/analysis_testing/o2dpg_analysis_test_workflow.py --is-mc -f ${aod} -o ${workflow_path} --only-analyses ${testanalysis} ${include_disabled} ${add_common_args} +$O2DPG_ROOT/MC/analysis_testing/o2dpg_analysis_test_workflow.py --is-mc --split-analyses -f ${aod} -o ${workflow_path} --only-analyses ${testanalysis} ${include_disabled} [[ ! -f "${workflow_path}" ]] && { echo "Could not construct workflow for analysis ${testanalysis}" ; exit 1 ; } $O2DPG_ROOT/MC/bin/o2_dpg_workflow_runner.py -f ${workflow_path} -tt Analysis_${testanalysis}$ --rerun-from Analysis_${testanalysis}$ diff --git a/MC/analysis_testing/o2dpg_analysis_test_config.py b/MC/analysis_testing/o2dpg_analysis_test_config.py index 7e9c23d83..c188fc881 100755 --- a/MC/analysis_testing/o2dpg_analysis_test_config.py +++ b/MC/analysis_testing/o2dpg_analysis_test_config.py @@ -3,7 +3,7 @@ import sys import argparse from os import environ -from os.path import join, exists +from os.path import join, exists, isdir import json # make sure O2DPG + O2 is loaded @@ -14,14 +14,29 @@ sys.exit(1) +def get_config(path=None): + default_path = join(O2DPG_ROOT, "MC", "config", "analysis_testing", "json", "analyses_config.json") + if not path: + path = default_path + else: + if isdir(path): + # assume to look for analyses_config.json in this directory + path = join(path, "analyses_config.json") + + if not exists(path): + print(f"WARNING: Cannot locate config for AnalysisQC at custom path {path}. Use default at {default_path}") + path = default_path + + with open(path, "r") as f: + return json.load(f)["analyses"] + + def modify(args): """ modify and create a new config """ - analyses = None - with open (args.config, "r") as f: - analyses = json.load(f)["analyses"] + analyses = get_config(args.config) for ana in analyses: if args.disable_tasks and ana["name"] in args.disable_tasks: @@ -51,38 +66,35 @@ def print_status(enabled): return print("DISABLED") - analyses = None - with open (args.config, "r") as f: - analyses = json.load(f)["analyses"] + analyses = get_config(args.config) for ana in analyses: - if ana["name"] == args.task: - if args.status: - print_status(ana["enabled"]) - if args.applicable_to: - if ana.get("valid_mc", False): - print("mc") - if ana.get("valid_data", False): - print("data") + if ana["name"] != args.task: + continue + if args.status: + print_status(ana["enabled"]) + if args.applicable_to: + if ana.get("valid_mc", False): + print("mc") + if ana.get("valid_data", False): + print("data") - return 0 + return 0 # analysis not found - print(f"UNKNOWN") + print(f"WARNING: Unknown task {args.task}") return 1 def show_tasks(args): """ - Browse through analyses and see what is en-/disabled + Browse through analyses and print what is en-/disabled """ if not args.enabled and not args.disabled: args.enabled = True args.disabled = True - analyses = None - with open (args.config, "r") as f: - analyses = json.load(f)["analyses"] + analyses = get_config(args.config) for ana in analyses: if (args.enabled and ana["enabled"]) or (args.disabled and not ana["enabled"]): @@ -92,9 +104,19 @@ def show_tasks(args): def validate_output(args): - analyses = None - with open (args.config, "r") as f: - analyses = json.load(f)["analyses"] + """ + Validation after running tasks + + * check for corresponding *.log_done files from WF runner + * check for exit code in *.log + * check if expected output files are there + """ + + if not args.config: + # first see if config is not explicitly given, then use the directory where the analyses to check are located + args.config = args.directory + + analyses = get_config(args.config) # global return code ret = 0 @@ -105,11 +127,10 @@ def validate_output(args): analysis_name = ana["name"] if args.tasks: - if analysis_name in args.tasks: - # tasks were specified explicitly, make sure to take them into account at all costs - include_disabled = True - else: + if analysis_name not in args.tasks: continue + # tasks were specified explicitly, make sure to take them into account at all costs + include_disabled = True if not ana["enabled"] and not include_disabled: # continue if disabled and not including those @@ -126,6 +147,7 @@ def validate_output(args): # expected to have no output continue + # check if the expected outputs are there for expected_output in ana["expected_output"]: expected_output = join(analysis_dir, expected_output) if not exists(expected_output): @@ -135,6 +157,7 @@ def validate_output(args): logfile = join(analysis_dir, f"Analysis_{analysis_name}.log") if exists(logfile): + # check the exit code in the *.log file exit_code = "0" with open(logfile, "r") as f: for line in f: @@ -146,6 +169,8 @@ def validate_output(args): break if exit_code != "0": continue + + # check if the *.log_done file is there logfile_done = join(analysis_dir, f"Analysis_{analysis_name}.log_done") if not exists(logfile_done): print(f"Apparently, analysis {analysis_name} did not run successfully, {logfile_done} does not exist.") @@ -155,12 +180,14 @@ def validate_output(args): def main(): - """entry point when run directly from command line""" - parser = argparse.ArgumentParser(description='Modify analysis configuration and write new config') + """ + entry point when run directly from command line + """ + parser = argparse.ArgumentParser(description='Check, validate or modify analysis configuration') sub_parsers = parser.add_subparsers(dest="command") config_parser = argparse.ArgumentParser(add_help=False) - config_parser.add_argument("-c", "--config", help="input configuration to modify", default=join(O2DPG_ROOT, "MC", "config", "analysis_testing", "json", "analyses_config.json")) + config_parser.add_argument("-c", "--config", help="input configuration to use") # modify config modify_parser = sub_parsers.add_parser("modify", parents=[config_parser]) diff --git a/MC/analysis_testing/o2dpg_analysis_test_utils.py b/MC/analysis_testing/o2dpg_analysis_test_utils.py index 1a3901fe6..298bf001e 100755 --- a/MC/analysis_testing/o2dpg_analysis_test_utils.py +++ b/MC/analysis_testing/o2dpg_analysis_test_utils.py @@ -4,8 +4,10 @@ # Analsysis task utilities # import sys -from os import environ -from os.path import join, exists, abspath, expanduser +from os import environ, listdir +from os.path import join, abspath + +import json # make sure O2DPG + O2 is loaded O2DPG_ROOT=environ.get('O2DPG_ROOT') @@ -22,40 +24,41 @@ ANALYSIS_VALID_DATA = "data" ANALYSIS_COLLISION_SYSTEM_PP = "pp" ANALYSIS_COLLISION_SYSTEM_PBPB = "pbpb" -ANALYSIS_CONFIGURATION_PREFIX = "analysis-testing" -ANALYSIS_DEFAULT_CONFIGURATION = {ANALYSIS_COLLISION_SYSTEM_PP: {ANALYSIS_VALID_MC: join(O2DPG_ROOT, "MC", "config", "analysis_testing", "json", "default", ANALYSIS_COLLISION_SYSTEM_PP, f"{ANALYSIS_CONFIGURATION_PREFIX}-{ANALYSIS_VALID_MC}.json"), - ANALYSIS_VALID_DATA: join(O2DPG_ROOT, "MC", "config", "analysis_testing", "json", "default", ANALYSIS_COLLISION_SYSTEM_PP, f"{ANALYSIS_CONFIGURATION_PREFIX}-{ANALYSIS_VALID_DATA}.json")}, - ANALYSIS_COLLISION_SYSTEM_PBPB: {ANALYSIS_VALID_MC: join(O2DPG_ROOT, "MC", "config", "analysis_testing", "json", "default", ANALYSIS_COLLISION_SYSTEM_PBPB, f"{ANALYSIS_CONFIGURATION_PREFIX}-{ANALYSIS_VALID_MC}.json"), - ANALYSIS_VALID_DATA: join(O2DPG_ROOT, "MC", "config", "analysis_testing", "json", "default", ANALYSIS_COLLISION_SYSTEM_PBPB, f"{ANALYSIS_CONFIGURATION_PREFIX}-{ANALYSIS_VALID_DATA}.json")}} - - -def sanitize_configuration_path(path): - # sanitize path - path = path.replace("json://", "") - if path[0] != "$": - # only do this if there is no potential environment variable given as the first part of the path - path = abspath(expanduser(path)) - return f"json://{path}" - - -def get_default_configuration(data_or_mc, collision_system): - path = ANALYSIS_DEFAULT_CONFIGURATION.get(collision_system, None) - if not path: - print(f"ERROR: Unknown collision system {collision_system}") - return None - return path[data_or_mc] +ANALYSIS_MERGED_ANALYSIS_NAME = "MergedAnalyses" + +def adjust_configuration_line(line, data_or_mc, collision_system): + line = line.replace('!ANALYSIS_QC_is_mc!', str(data_or_mc == ANALYSIS_VALID_MC).lower()) + line = line.replace('!ANALYSIS_QC_is_data!', str(data_or_mc == ANALYSIS_VALID_DATA).lower()) + if collision_system == "pp": + line = line.replace('!OVERWRITEAXISRANGEFORPBPBVALUE!', "false") + else: + line = line.replace('!OVERWRITEAXISRANGEFORPBPBVALUE!', "true") + if collision_system == "pbpb": + line = line.replace('!ISLOWFLUX!', "false") + else: + line = line.replace('!ISLOWFLUX!', "true") + return line -def get_configuration(analysis_name, data_or_mc, collision_system): - path = join(O2DPG_ROOT, "MC", "config", "analysis_testing", "json", analysis_name, collision_system, f"{ANALYSIS_CONFIGURATION_PREFIX}-{data_or_mc}.json") - if not exists(path): - path = get_default_configuration(data_or_mc, collision_system) - if not path: - return None - print(f"INFO: Use default configuration for {analysis_name}") - return sanitize_configuration_path(path) - return sanitize_configuration_path(path) +def adjust_and_get_configuration_path(data_or_mc, collision_system, output_dir): + + final_config = {} + path = join(O2DPG_ROOT, "MC", "config", "analysis_testing", "json", "dpl") + for config_path in listdir(path): + if not config_path.endswith('.json'): + continue + json_string = "" + with open(join(path, config_path), 'r') as f: + for line in f: + json_string += adjust_configuration_line(line, data_or_mc, collision_system) + final_config |= json.loads(json_string) + # now we can do some adjustments + output_path = abspath(join(output_dir, 'dpl-config.json')) + with open(output_path, 'w') as f: + json.dump(final_config, f, indent=2) + + return output_path def get_collision_system(collision_system=None): @@ -69,7 +72,7 @@ def full_ana_name(raw_ana_name): return f"{ANALYSIS_LABEL}_{raw_ana_name}" -def get_common_args_as_string(analysis_name, all_common_args): +def get_common_args_as_string(ana, all_common_args): """ all_common_args is of the form [-shm-segment-size , -readers , ...] @@ -88,6 +91,11 @@ def make_args_string(args_map_in): "readers": 1, "aod-memory-rate-limit": 500000000} + # get common args from analysis configuration and add to args_map + common_args_from_config = ana.get("common_args", {}) + for key, value in common_args_from_config.items(): + args_map[key] = value + # arguments dedicated for this analysis args_map_overwrite = {} @@ -98,6 +106,8 @@ def make_args_string(args_map_in): print("ERROR: Cannot digest common args.") return None + analysis_name = ana["name"] + for i in range(0, len(all_common_args), 2): tokens = all_common_args[i].split("-") key = "-".join(tokens[1:]) diff --git a/MC/analysis_testing/o2dpg_analysis_test_workflow.py b/MC/analysis_testing/o2dpg_analysis_test_workflow.py index a3a3e37c8..758d356e9 100755 --- a/MC/analysis_testing/o2dpg_analysis_test_workflow.py +++ b/MC/analysis_testing/o2dpg_analysis_test_workflow.py @@ -63,7 +63,7 @@ import sys import importlib.util import argparse -from os import environ +from os import environ, makedirs from os.path import join, exists, abspath, expanduser import json @@ -80,7 +80,7 @@ o2dpg_workflow_utils = importlib.util.module_from_spec(spec) sys.modules[module_name] = o2dpg_workflow_utils spec.loader.exec_module(o2dpg_workflow_utils) -from o2dpg_workflow_utils import createTask, dump_workflow +from o2dpg_workflow_utils import createTask, dump_workflow, createGlobalInitTask module_name = "o2dpg_analysis_test_utils" spec = importlib.util.spec_from_file_location(module_name, join(O2DPG_ROOT, "MC", "analysis_testing", "o2dpg_analysis_test_utils.py")) @@ -90,7 +90,7 @@ from o2dpg_analysis_test_utils import * -def create_ana_task(name, cmd, output_dir, *, needs=None, extraarguments="-b", is_mc=False): +def create_ana_task(name, cmd, output_dir, *, cpu=1, mem='2000', needs=None, extraarguments="-b", is_mc=False): """Quick helper to create analysis task This creates an analysis task from various arguments @@ -114,7 +114,7 @@ def create_ana_task(name, cmd, output_dir, *, needs=None, extraarguments="-b", i if needs is None: # set to empty list needs = [] - task = createTask(name=full_ana_name(name), cwd=join(output_dir, name), lab=[ANALYSIS_LABEL, name], cpu=1, mem='2000', needs=needs) + task = createTask(name=full_ana_name(name), cwd=join(output_dir, name), lab=[ANALYSIS_LABEL, name], cpu=cpu, mem=mem, needs=needs) if is_mc: task["labels"].append(ANALYSIS_LABEL_ON_MC) task['cmd'] = f"{cmd} {extraarguments}" @@ -138,38 +138,6 @@ def load_analyses(analyses_only=None, include_disabled_analyses=False): return collect_analyses -def add_analysis_post_processing_tasks(workflow): - """add post-processing step to analysis tasks if possible - - Args: - workflow: list - current list of tasks - """ - analyses_to_add_for = {} - # collect analyses in current workflow - for task in workflow: - if ANALYSIS_LABEL in task["labels"]: - analyses_to_add_for[task["name"]] = task - - for ana in load_analyses(include_disabled_analyses=True): - if not ana["expected_output"]: - continue - ana_name_raw = ana["name"] - post_processing_macro = join(O2DPG_ROOT, "MC", "analysis_testing", "post_processing", f"{ana_name_raw}.C") - if not exists(post_processing_macro): - continue - ana_name = full_ana_name(ana_name_raw) - if ana_name not in analyses_to_add_for: - continue - pot_ana = analyses_to_add_for[ana_name] - cwd = pot_ana["cwd"] - needs = [ana_name] - task = createTask(name=f"{ANALYSIS_LABEL}_post_processing_{ana_name_raw}", cwd=join(cwd, "post_processing"), lab=[ANALYSIS_LABEL, f"{ANALYSIS_LABEL}PostProcessing", ana_name_raw], cpu=1, mem='2000', needs=needs) - input_files = ",".join([f"../{eo}" for eo in ana["expected_output"]]) - cmd = f"\\(\\\"{input_files}\\\",\\\"./\\\"\\)" - task["cmd"] = f"root -l -b -q {post_processing_macro}{cmd}" - workflow.append(task) - def get_additional_workflows(input_aod): additional_workflows = [] @@ -186,9 +154,13 @@ def get_additional_workflows(input_aod): from ROOT import TGrid TGrid.Connect("alien") froot = TFile.Open(input_aod, "READ") - found_O2collision_001 = False - found_O2zdc_001 = False - found_O2bc_001 = False + # Link of tables and converters + o2_analysis_converters = {"O2collision_001": "o2-analysis-collision-converter --doNotSwap", + "O2zdc_001": "o2-analysis-zdc-converter", + "O2bc_001": "o2-analysis-bc-converter", + "O2v0_002": "o2-analysis-v0converter", + "O2trackextra_001": "o2-analysis-tracks-extra-converter", + "O2ft0corrected": "o2-analysis-ft0-corrected-table"} for i in froot.GetListOfKeys(): if "DF_" not in i.GetName(): continue @@ -196,23 +168,14 @@ def get_additional_workflows(input_aod): # print(i) for j in df_dir.GetListOfKeys(): # print(j) - if "O2collision_001" in j.GetName(): - found_O2collision_001 = True - if "O2zdc_001" in j.GetName(): - found_O2zdc_001 = True - if "O2bc_001" in j.GetName(): - found_O2bc_001 = True - if not found_O2collision_001: - additional_workflows.append("o2-analysis-collision-converter --doNotSwap") - if not found_O2zdc_001: - additional_workflows.append("o2-analysis-zdc-converter") - if not found_O2bc_001: - additional_workflows.append("o2-analysis-bc-converter") - break + if j.GetName() in o2_analysis_converters: + o2_analysis_converters.pop(j.GetName()) + for i in o2_analysis_converters: + additional_workflows.append(o2_analysis_converters[i]) return additional_workflows -def add_analysis_tasks(workflow, input_aod="./AO2D.root", output_dir="./Analysis", *, analyses_only=None, is_mc=True, collision_system=None, needs=None, autoset_converters=False, include_disabled_analyses=False, timeout=None, add_common_args=None): +def add_analysis_tasks(workflow, input_aod="./AO2D.root", output_dir="./Analysis", *, analyses_only=None, is_mc=True, collision_system=None, needs=None, autoset_converters=False, include_disabled_analyses=False, timeout=None, split_analyses=False): """Add default analyses to user workflow Args: @@ -237,12 +200,27 @@ def add_analysis_tasks(workflow, input_aod="./AO2D.root", output_dir="./Analysis input_aod = f"@{input_aod}" additional_workflows = [] - if autoset_converters: # This is needed to run with the latest TAG of the O2Physics with the older data + if autoset_converters: # This is needed to run with the latest TAG of the O2Physics with the older data additional_workflows = get_additional_workflows(input_aod) data_or_mc = ANALYSIS_VALID_MC if is_mc else ANALYSIS_VALID_DATA collision_system = get_collision_system(collision_system) + # list of lists, each sub-list corresponds to one analysis pipe to be executed + analysis_pipes = [] + # collect the names corresponding to analysis pipes + analysis_names = [] + # cpu and mem of each task + analysis_cpu_mem = [] + # a list of all tasks to be put together + merged_analysis_pipe = additional_workflows.copy() + # cpu and mem of merged analyses + merged_analysis_cpu_mem = [0, 0] + # expected output of merged analysis + merged_analysis_expected_output = [] + # analyses config to write + analyses_config = [] + for ana in load_analyses(analyses_only, include_disabled_analyses=include_disabled_analyses): if is_mc and not ana.get("valid_mc", False): print(f"INFO: Analysis {ana['name']} not added since not valid in MC") @@ -250,31 +228,68 @@ def add_analysis_tasks(workflow, input_aod="./AO2D.root", output_dir="./Analysis if not is_mc and not ana.get("valid_data", False): print(f"INFO: Analysis {ana['name']} not added since not valid in data") continue - - configuration = get_configuration(ana["name"], data_or_mc, collision_system) - if not configuration: - print(f"INFO: Analysis {ana['name']} excluded due to no valid configuration") + if analyses_only and ana['name'] not in analyses_only: + # filter on analyses if requested continue - print(f"INFO: Analysis {ana['name']} uses configuration {configuration}") - add_common_args_ana = get_common_args_as_string(ana["name"], add_common_args) - if not add_common_args_ana: - print(f"ERROR: Cannot parse common args for analysis {ana['name']}") + if split_analyses: + # only the individual analyses, no merged + analysis_pipes.append(ana['tasks']) + analysis_names.append(ana['name']) + analysis_cpu_mem.append((1, 2000)) + analyses_config.append(ana) continue - for i in additional_workflows: - if i not in ana["tasks"]: - # print("Appending extra task", i, "to analysis", ana["name"], "as it is not there yet and needed for conversion") - ana["tasks"].append(i) - piped_analysis = f" --configuration {configuration} | ".join(ana["tasks"]) - piped_analysis += f" --configuration {configuration} --aod-file {input_aod}" - piped_analysis += add_common_args_ana + merged_analysis_pipe.extend(ana['tasks']) + # underestimate what a single analysis would take in the merged case. + # Putting everything into one big pipe does not mean that the resources scale the same! + merged_analysis_cpu_mem[0] += 0.5 + merged_analysis_cpu_mem[1] += 700 + merged_analysis_expected_output.extend(ana['expected_output']) + + if not split_analyses: + # add the merged analysis + analysis_pipes.append(merged_analysis_pipe) + analysis_names.append(ANALYSIS_MERGED_ANALYSIS_NAME) + # take at least the resources estimated for a single analysis + analysis_cpu_mem.append((max(1, merged_analysis_cpu_mem[0]), max(2000, merged_analysis_cpu_mem[1]))) + merged_analysis_expected_output = list(set(merged_analysis_expected_output)) + # config of the merged analysis. Since it doesn't exist in the previous config, but we would like to have it defined, do it here + analyses_config.append({'name': ANALYSIS_MERGED_ANALYSIS_NAME, + 'valid_mc': is_mc, + 'valid_data': not is_mc, + 'enabled': True, + 'tasks': merged_analysis_pipe, + 'expected_output': merged_analysis_expected_output}) + + + # now we need to create the output directory where we want the final configurations to go + output_dir_config = join(output_dir, 'config') + if not exists(output_dir_config): + makedirs(output_dir_config) + + # write the analysis config of this + with open(join(output_dir, 'analyses_config.json'), 'w') as f: + json.dump({'analyses': analyses_config}, f, indent=2) + + configuration = adjust_and_get_configuration_path(data_or_mc, collision_system, output_dir_config) + + for analysis_name, analysis_pipe, analysis_res in zip(analysis_names, analysis_pipes, analysis_cpu_mem): + # remove duplicates if they are there for nay reason (especially in the merged case) + analysis_pipe = list(set(analysis_pipe)) + analysis_pipe_assembled = [] + for executable_string in analysis_pipe: + # the input executable might come already with some configurations, the very first token is the actual executable + executable_string += f' --configuration json://{configuration}' + analysis_pipe_assembled.append(executable_string) + + # put together, add AOD and timeout if requested + analysis_pipe_assembled = ' | '.join(analysis_pipe_assembled) + analysis_pipe_assembled += f' --aod-file {input_aod} --shm-segment-size 3000000000 --readers 1 --aod-memory-rate-limit 500000000' if timeout is not None: - piped_analysis += f" --time-limit {timeout}" - workflow.append(create_ana_task(ana["name"], piped_analysis, output_dir, needs=needs, is_mc=is_mc)) + analysis_pipe_assembled += f' --time-limit {timeout}' - # append potential post-processing - add_analysis_post_processing_tasks(workflow) + workflow.append(create_ana_task(analysis_name, analysis_pipe_assembled, output_dir, cpu=analysis_res[0], mem=analysis_res[1], needs=needs, is_mc=is_mc)) def add_analysis_qc_upload_tasks(workflow, period_name, run_number, pass_name): @@ -305,7 +320,6 @@ def add_analysis_qc_upload_tasks(workflow, period_name, run_number, pass_name): # search through workflow stages if we can find the requested analysis pot_ana = analyses_to_add_for[ana_name] cwd = pot_ana["cwd"] - qc_tag = f"Analysis{ana_name_raw}" needs = [ana_name] provenance = "qc_mc" if ANALYSIS_LABEL_ON_MC in pot_ana["labels"] else "qc" for eo in ana["expected_output"]: @@ -327,8 +341,10 @@ def run(args): print("ERROR: QC upload was requested, however in that case a --pass-name and --period-name are required") return 1 - workflow = [] - add_analysis_tasks(workflow, args.input_file, expanduser(args.analysis_dir), is_mc=args.is_mc, analyses_only=args.only_analyses, autoset_converters=args.autoset_converters, include_disabled_analyses=args.include_disabled, timeout=args.timeout, collision_system=args.collision_system, add_common_args=args.add_common_args) + ### setup global environment variables which are valid for all tasks, set as first task + global_env = {"ALICEO2_CCDB_CONDITION_NOT_AFTER": args.condition_not_after} if args.condition_not_after else None + workflow = [createGlobalInitTask(global_env)] + add_analysis_tasks(workflow, args.input_file, expanduser(args.analysis_dir), is_mc=args.is_mc, analyses_only=args.only_analyses, autoset_converters=args.autoset_converters, include_disabled_analyses=args.include_disabled, timeout=args.timeout, collision_system=args.collision_system, split_analyses=args.split_analyses) if args.with_qc_upload: add_analysis_qc_upload_tasks(workflow, args.period_name, args.run_number, args.pass_name) if not workflow: @@ -354,7 +370,9 @@ def main(): parser.add_argument("--autoset-converters", dest="autoset_converters", action="store_true", help="Compatibility mode to automatically set the converters for the analysis") parser.add_argument("--timeout", type=int, default=None, help="Timeout for analysis tasks in seconds.") parser.add_argument("--collision-system", dest="collision_system", help="Set the collision system. If not set, tried to be derived from ALIEN_JDL_LPMInterationType. Fallback to pp") - parser.add_argument("--add-common-args", dest="add_common_args", nargs="*", help="Pass additional common arguments per analysis, for instance --add-common-args EMCAL-shm-segment-size 2500000000 will add --shm-segment-size 2500000000 to the EMCAL analysis") + parser.add_argument('--condition-not-after', dest="condition_not_after", type=int, help="only consider CCDB objects not created after this timestamp (for TimeMachine)", default=3385078236000) + parser.add_argument('--split-analyses', dest='split_analyses', action='store_true', help='Split into single analyses pipes to be executed.') + parser.set_defaults(func=run) args = parser.parse_args() return(args.func(args)) diff --git a/MC/analysis_testing/post_processing/PWGMMMDnDeta.C b/MC/analysis_testing/post_processing/PWGMMMDnDeta.C deleted file mode 100644 index 21ab0e42e..000000000 --- a/MC/analysis_testing/post_processing/PWGMMMDnDeta.C +++ /dev/null @@ -1,39 +0,0 @@ -void PWGMMMDnDeta(const char* inputFiles, const char* outDir) -{ - // filenames are given as one string where the file names are separated by "," - std::stringstream toSplit(inputFiles); - std::string token; - std::vector tokenList; - std::string thisFile; - - while (std::getline(toSplit, token, ',')) { - if (token.find("AnalysisResults.root") != std::string::npos) { - thisFile = token; - break; - } - } - - if (thisFile.empty()) { - // nothing to post-process - return; - } - TFile f(thisFile.c_str(), "UPDATE"); - if (f.IsZombie()) { - std::cout << "Cannot open file " << thisFile << " for post-processing\n"; - return; - } - auto hPT = (TH1*)f.Get("pseudorapidity-density/Tracks/Control/PtEfficiency"); - auto hPTGen = (TH1*)f.Get("pseudorapidity-density/Tracks/Control/PtGen"); - hPT->SetDirectory(nullptr); - hPTGen->SetDirectory(nullptr); - auto hPTTrackingEff = (TH1*)hPT->Clone("trackingEfficiency"); - hPTTrackingEff->SetDirectory(nullptr); - - hPTTrackingEff->Divide(hPTTrackingEff, hPTGen, 1, 1, "b"); - hPTTrackingEff->SetTitle("tracking efficiency"); - - auto d = f.mkdir("O2DPG-post-processing"); - d->WriteTObject(hPTTrackingEff); - f.Write(); - f.Close(); -} diff --git a/MC/bin/o2_dpg_workflow_runner.py b/MC/bin/o2_dpg_workflow_runner.py index ecb6975d3..fda79a19f 100755 --- a/MC/bin/o2_dpg_workflow_runner.py +++ b/MC/bin/o2_dpg_workflow_runner.py @@ -1,1363 +1,33 @@ #!/usr/bin/env python3 +"""Run the workflow runner selected by ALIEN_O2DPG_WORKFLOW_RUNNER. -# started February 2021, sandro.wenzel@cern.ch +'legacy' (the default) is the original single-file runner, 'new' is the +o2dpg_runner package under MC/workflow_runner. All arguments are passed on +unchanged. +""" -import re -import subprocess -import shlex -import time -import json -import logging import os -import signal -import socket import sys -import traceback -import platform -try: - from graphviz import Digraph - havegraphviz=True -except ImportError: - havegraphviz=False - -formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s') - -sys.setrecursionlimit(100000) - -import argparse -import psutil -max_system_mem=psutil.virtual_memory().total - -sys.path.append(os.path.join(os.path.dirname(__file__), '.', 'o2dpg_workflow_utils')) -from o2dpg_workflow_utils import read_workflow - -# defining command line options -parser = argparse.ArgumentParser(description='Parallel execution of a (O2-DPG) DAG data/job pipeline under resource contraints.', - formatter_class=argparse.ArgumentDefaultsHelpFormatter) - -parser.add_argument('-f','--workflowfile', help='Input workflow file name', required=True) -parser.add_argument('-jmax','--maxjobs', help='Number of maximal parallel tasks.', default=100) -parser.add_argument('-k','--keep-going', action='store_true', help='Keep executing the pipeline as far possibe (not stopping on first failure)') -parser.add_argument('--dry-run', action='store_true', help='Show what you would do.') -parser.add_argument('--visualize-workflow', action='store_true', help='Saves a graph visualization of workflow.') -parser.add_argument('--target-labels', nargs='+', help='Runs the pipeline by target labels (example "TPC" or "DIGI").\ - This condition is used as logical AND together with --target-tasks.', default=[]) -parser.add_argument('-tt','--target-tasks', nargs='+', help='Runs the pipeline by target tasks (example "tpcdigi"). By default everything in the graph is run. Regular expressions supported.', default=["*"]) -parser.add_argument('--produce-script', help='Produces a shell script that runs the workflow in serialized manner and quits.') -parser.add_argument('--rerun-from', help='Reruns the workflow starting from given task (or pattern). All dependent jobs will be rerun.') -parser.add_argument('--list-tasks', help='Simply list all tasks by name and quit.', action='store_true') -parser.add_argument('--update-resources', dest="update_resources", help='Read resource estimates from a JSON and apply where possible.') - -parser.add_argument('--mem-limit', help='Set memory limit as scheduling constraint (in MB)', default=0.9*max_system_mem/1024./1024) -parser.add_argument('--cpu-limit', help='Set CPU limit (core count)', default=8) -parser.add_argument('--cgroup', help='Execute pipeline under a given cgroup (e.g., 8coregrid) emulating resource constraints. This m\ -ust exist and the tasks file must be writable to with the current user.') -parser.add_argument('--stdout-on-failure', action='store_true', help='Print log files of failing tasks to stdout,') -parser.add_argument('--webhook', help=argparse.SUPPRESS) # log some infos to this webhook channel -parser.add_argument('--checkpoint-on-failure', help=argparse.SUPPRESS) # debug option making a debug-tarball and sending to specified address - # argument is alien-path -parser.add_argument('--retry-on-failure', help=argparse.SUPPRESS, default=0) # number of times a failing task is retried -parser.add_argument('--no-rootinit-speedup', help=argparse.SUPPRESS, action='store_true') # disable init of ROOT environment vars to speedup init/startup -parser.add_argument('--action-logfile', help='Logfilename for action logs. If none given, pipeline_action_#PID.log will be used') -parser.add_argument('--metric-logfile', help='Logfilename for metric logs. If none given, pipeline_metric_#PID.log will be used') -args = parser.parse_args() - -def setup_logger(name, log_file, level=logging.INFO): - """To setup as many loggers as you want""" - - handler = logging.FileHandler(log_file, mode='w') - handler.setFormatter(formatter) - - logger = logging.getLogger(name) - logger.setLevel(level) - logger.addHandler(handler) - - return logger - -# first file logger -actionlogger = setup_logger('pipeline_action_logger', ('pipeline_action_' + str(os.getpid()) + '.log', args.action_logfile)[args.action_logfile!=None], level=logging.DEBUG) - -# second file logger -metriclogger = setup_logger('pipeline_metric_logger', ('pipeline_metric_' + str(os.getpid()) + '.log', args.action_logfile)[args.action_logfile!=None]) - -# Immediately log imposed memory and CPU limit as well as further useful meta info -_ , meta = read_workflow(args.workflowfile) -meta["cpu_limit"] = args.cpu_limit -meta["mem_limit"] = args.mem_limit -meta["workflow_file"] = os.path.abspath(args.workflowfile) -meta["target_task"] = args.target_tasks -meta["rerun_from"] = args.rerun_from -meta["target_labels"] = args.target_labels -metriclogger.info(meta) - -# for debugging without terminal access -# TODO: integrate into standard logger -def send_webhook(hook, t): - if hook!=None: - command="curl -X POST -H 'Content-type: application/json' --data '{\"text\":\" " + str(t) + "\"}' " + str(hook) + " &> /dev/null" - os.system(command) - -# A fallback solution to getting all child procs -# in case psutil has problems (PermissionError). -# It returns the same list as psutil.children(recursive=True). -def getChildProcs(basepid): - cmd=''' - childprocs() { - local parent=$1 - if [ ! "$2" ]; then - child_pid_list="" - fi - if [ "$parent" ] ; then - child_pid_list="$child_pid_list $parent" - for childpid in $(pgrep -P ${parent}); do - childprocs $childpid "nottoplevel" - done; - fi - # return via a string list (only if toplevel) - if [ ! "$2" ]; then - echo "${child_pid_list}" - fi - } - ''' - cmd = cmd + '\n' + 'childprocs ' + str(basepid) - output = subprocess.check_output(cmd, shell=True) - plist = [] - for p in output.strip().split(): - try: - proc=psutil.Process(int(p)) - except psutil.NoSuchProcess: - continue - - plist.append(proc) - return plist - -# -# Code section to find all topological orderings -# of a DAG. This is used to know when we can schedule -# things in parallel. -# Taken from https://www.geeksforgeeks.org/all-topological-sorts-of-a-directed-acyclic-graph/ - -# class to represent a graph object -class Graph: - - # Constructor - def __init__(self, edges, N): - - # A List of Lists to represent an adjacency list - self.adjList = [[] for _ in range(N)] - - # stores in-degree of a vertex - # initialize in-degree of each vertex by 0 - self.indegree = [0] * N - - # add edges to the undirected graph - for (src, dest) in edges: - - # add an edge from source to destination - self.adjList[src].append(dest) - - # increment in-degree of destination vertex by 1 - self.indegree[dest] = self.indegree[dest] + 1 - -# Recursive function to find all topological orderings of a given DAG -def findAllTopologicalOrders(graph, path, discovered, N, allpaths, maxnumber=1): - if len(allpaths) >= maxnumber: - return - - # do for every vertex - for v in range(N): - - # proceed only if in-degree of current node is 0 and - # current node is not processed yet - if graph.indegree[v] == 0 and not discovered[v]: - - # for every adjacent vertex u of v, reduce in-degree of u by 1 - for u in graph.adjList[v]: - graph.indegree[u] = graph.indegree[u] - 1 - - # include current node in the path and mark it as discovered - path.append(v) - discovered[v] = True - - # recur - findAllTopologicalOrders(graph, path, discovered, N, allpaths) - - # backtrack: reset in-degree information for the current node - for u in graph.adjList[v]: - graph.indegree[u] = graph.indegree[u] + 1 - - # backtrack: remove current node from the path and - # mark it as undiscovered - path.pop() - discovered[v] = False - - # record valid ordering - if len(path) == N: - allpaths.append(path.copy()) - - -# get all topological orderings of a given DAG as a list -def printAllTopologicalOrders(graph, maxnumber=1): - # get number of nodes in the graph - N = len(graph.adjList) - - # create an auxiliary space to keep track of whether vertex is discovered - discovered = [False] * N - - # list to store the topological order - path = [] - allpaths = [] - # find all topological ordering and print them - findAllTopologicalOrders(graph, path, discovered, N, allpaths, maxnumber=maxnumber) - return allpaths - -# <--- end code section for topological sorts - -# find all tasks that depend on a given task (id); when a cache -# dict is given we can fill for the whole graph in one pass... -def find_all_dependent_tasks(possiblenexttask, tid, cache={}): - c=cache.get(tid) - if c!=None: - return c - - daughterlist=[tid] - # possibly recurse - for n in possiblenexttask[tid]: - c = cache.get(n) - if c == None: - c = find_all_dependent_tasks(possiblenexttask, n, cache) - daughterlist = daughterlist + c - cache[n]=c - - cache[tid]=daughterlist - return list(set(daughterlist)) - - -# wrapper taking some edges, constructing the graph, -# obtain all topological orderings and some other helper data structures -def analyseGraph(edges, nodes): - # Number of nodes in the graph - N = len(nodes) - - # candidate list trivial - nextjobtrivial = { n:[] for n in nodes } - # startnodes - nextjobtrivial[-1] = nodes - for e in edges: - nextjobtrivial[e[0]].append(e[1]) - if nextjobtrivial[-1].count(e[1]): - nextjobtrivial[-1].remove(e[1]) - - # find topological orderings of the graph - # create a graph from edges - graph = Graph(edges, N) - orderings = printAllTopologicalOrders(graph) - - return (orderings, nextjobtrivial) - - -def draw_workflow(workflowspec): - if not havegraphviz: - print('graphviz not installed, cannot draw workflow') - return - - dot = Digraph(comment='MC workflow') - nametoindex={} - index=0 - # nodes - for node in workflowspec['stages']: - name=node['name'] - nametoindex[name]=index - dot.node(str(index), name) - index=index+1 - - # edges - for node in workflowspec['stages']: - toindex = nametoindex[node['name']] - for req in node['needs']: - fromindex = nametoindex[req] - dot.edge(str(fromindex), str(toindex)) - - dot.render('workflow.gv') - -# builds the graph given a "taskuniverse" list -# builds accompagnying structures tasktoid and idtotask -def build_graph(taskuniverse, workflowspec): - tasktoid={ t[0]['name']:i for i, t in enumerate(taskuniverse, 0) } - # print (tasktoid) - - nodes = [] - edges = [] - for t in taskuniverse: - nodes.append(tasktoid[t[0]['name']]) - for n in t[0]['needs']: - edges.append((tasktoid[n], tasktoid[t[0]['name']])) - - return (edges, nodes) - - -# loads json into dict, e.g. for workflow specification -def load_json(workflowfile): - fp=open(workflowfile) - workflowspec=json.load(fp) - return workflowspec - - -# filters the original workflowspec according to wanted targets or labels -# returns a new workflowspec -def filter_workflow(workflowspec, targets=[], targetlabels=[]): - if len(targets)==0: - return workflowspec - if len(targetlabels)==0 and len(targets)==1 and targets[0]=="*": - return workflowspec - - transformedworkflowspec = workflowspec - - def task_matches(t): - for filt in targets: - if filt=="*": - return True - if re.match(filt, t)!=None: - return True - return False - - def task_matches_labels(t): - # when no labels are given at all it's ok - if len(targetlabels)==0: - return True - - for l in t['labels']: - if targetlabels.count(l)!=0: - return True - return False - - # The following sequence of operations works and is somewhat structured. - # However, it builds lookups used elsewhere as well, so some CPU might be saved by reusing - # some structures across functions or by doing less passes on the data. - - # helper lookup - tasknametoid = { t['name']:i for i, t in enumerate(workflowspec['stages'],0) } - - # check if a task can be run at all - # or not due to missing requirements - def canBeDone(t,cache={}): - ok = True - c = cache.get(t['name']) - if c != None: - return c - for r in t['needs']: - taskid = tasknametoid.get(r) - if taskid != None: - if not canBeDone(workflowspec['stages'][taskid], cache): - ok = False - break - else: - ok = False - break - cache[t['name']] = ok - return ok - - okcache = {} - # build full target list - full_target_list = [ t for t in workflowspec['stages'] if task_matches(t['name']) and task_matches_labels(t) and canBeDone(t,okcache) ] - full_target_name_list = [ t['name'] for t in full_target_list ] - - # build full dependency list for a task t - def getallrequirements(t): - _l=[] - for r in t['needs']: - fulltask = workflowspec['stages'][tasknametoid[r]] - _l.append(fulltask) - _l=_l+getallrequirements(fulltask) - return _l - - full_requirements_list = [ getallrequirements(t) for t in full_target_list ] - - # make flat and fetch names only - full_requirements_name_list = list(set([ item['name'] for sublist in full_requirements_list for item in sublist ])) - - # inner "lambda" helper answering if a task "name" is needed by given targets - def needed_by_targets(name): - if full_target_name_list.count(name)!=0: - return True - if full_requirements_name_list.count(name)!=0: - return True - return False - - # we finaly copy everything matching the targets as well - # as all their requirements - transformedworkflowspec['stages']=[ l for l in workflowspec['stages'] if needed_by_targets(l['name']) ] - return transformedworkflowspec - - -# builds topological orderings (for each timeframe) -def build_dag_properties(workflowspec): - globaltaskuniverse = [ (l, i) for i, l in enumerate(workflowspec['stages'], 1) ] - timeframeset = set( l['timeframe'] for l in workflowspec['stages'] ) - - edges, nodes = build_graph(globaltaskuniverse, workflowspec) - tup = analyseGraph(edges, nodes.copy()) - # - global_next_tasks = tup[1] - - - dependency_cache = {} - # weight influences scheduling order can be anything user defined ... for the moment we just prefer to stay within a timeframe - # then take the number of tasks that depend on a task as further weight - # TODO: bring in resource estimates from runtime, CPU, MEM - # TODO: make this a policy of the runner to study different strategies - def getweight(tid): - return (globaltaskuniverse[tid][0]['timeframe'], len(find_all_dependent_tasks(global_next_tasks, tid, dependency_cache))) - - task_weights = [ getweight(tid) for tid in range(len(globaltaskuniverse)) ] - - for tid in range(len(globaltaskuniverse)): - actionlogger.info("Score for " + str(globaltaskuniverse[tid][0]['name']) + " is " + str(task_weights[tid])) - - # print (global_next_tasks) - return { 'nexttasks' : global_next_tasks, 'weights' : task_weights, 'topological_ordering' : tup[0] } - -# update the resource estimates of a workflow based on resources given via JSON -def update_resource_estimates(workflow, resource_json): - resource_dict = load_json(resource_json) - stages = workflow["stages"] - - for task in stages: - if task["timeframe"] >= 1: - tf = task["timeframe"] - name = "_".join(task["name"].split("_")[:-1]) - else: - name = task["name"] - - if name not in resource_dict: - continue - - new_resources = resource_dict[name] - - # memory - newmem = new_resources.get("mem", None) - if newmem is not None: - oldmem = task["resources"]["mem"] - actionlogger.info("Updating mem estimate for " + task["name"] + " from " + str(oldmem) + " to " + str(newmem)) - task["resources"]["mem"] = newmem - newcpu = new_resources.get("cpu", None) - - # cpu - if newcpu is not None: - oldcpu = task["resources"]["cpu"] - rel_cpu = task["resources"]["relative_cpu"] - if rel_cpu is not None: - # respect the relative CPU settings - # By default, the CPU value in the workflow is already scaled if relative_cpu is given. - # The new estimate on the other hand is not yet scaled so it needs to be done here. - newcpu *= rel_cpu - actionlogger.info("Updating cpu estimate for " + task["name"] + " from " + str(oldcpu) + " to " + str(newcpu)) - task["resources"]["cpu"] = newcpu - -# a function to read a software environment determined by alienv into -# a python dictionary -def get_alienv_software_environment(packagestring): - """ - packagestring is something like O2::v202298081-1,O2Physics::xxx - """ - # alienv printenv packagestring --> dictionary - # for the moment this works with CVMFS only - cmd="/cvmfs/alice.cern.ch/bin/alienv printenv " + packagestring - proc = subprocess.Popen([cmd], stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) - - envstring, err = proc.communicate() - # see if the printenv command was successful - if len(err.decode()) > 0: - print (err.decode()) - raise Exception - - # the software environment is now in the evnstring - # split it on semicolon - envstring=envstring.decode() - tokens=envstring.split(";") - # build envmap - envmap = {} - for t in tokens: - # check if assignment - if t.count("=") > 0: - assignment = t.rstrip().split("=") - envmap[assignment[0]] = assignment[1] - elif t.count("export") > 0: - # the case when we export or a simple variable - # need to consider the case when this has not been previously assigned - variable = t.split()[1] - if not variable in envmap: - envmap[variable]="" - - return envmap - -# -# functions for execution; encapsulated in a WorkflowExecutor class -# -class WorkflowExecutor: - # Constructor - def __init__(self, workflowfile, args, jmax=100): - self.args=args - self.workflowfile = workflowfile - self.workflowspec = load_json(workflowfile) - self.globalenv = self.extract_global_environment(self.workflowspec) # initialize global environment settings - for e in self.globalenv: - if os.environ.get(e, None) == None: - actionlogger.info("Applying global environment from init section " + str(e) + " : " + str(self.globalenv[e])) - os.environ[e] = str(self.globalenv[e]) - - self.workflowspec = filter_workflow(self.workflowspec, args.target_tasks, args.target_labels) - - if not self.workflowspec['stages']: - if args.target_tasks: - print ('Apparently some of the chosen target tasks are not in the workflow') - exit (0) - print ('Workflow is empty. Nothing to do') - exit (0) - - workflow = build_dag_properties(self.workflowspec) - if args.visualize_workflow: - draw_workflow(self.workflowspec) - self.possiblenexttask = workflow['nexttasks'] - self.taskweights = workflow['weights'] - self.topological_orderings = workflow['topological_ordering'] - self.taskuniverse = [ l['name'] for l in self.workflowspec['stages'] ] - self.idtotask = [ 0 for l in self.taskuniverse ] - self.tasktoid = {} - for i in range(len(self.taskuniverse)): - self.tasktoid[self.taskuniverse[i]]=i - self.idtotask[i]=self.taskuniverse[i] - - if args.update_resources: - update_resource_estimates(self.workflowspec, args.update_resources) - - self.maxmemperid = [ self.workflowspec['stages'][tid]['resources']['mem'] for tid in range(len(self.taskuniverse)) ] - self.cpuperid = [ self.workflowspec['stages'][tid]['resources']['cpu'] for tid in range(len(self.taskuniverse)) ] - self.curmembooked = 0 - self.curcpubooked = 0 - self.curmembooked_backfill = 0 - self.curcpubooked_backfill = 0 - self.memlimit = float(args.mem_limit) # some configurable number - self.cpulimit = float(args.cpu_limit) - self.procstatus = { tid:'ToDo' for tid in range(len(self.workflowspec['stages'])) } - self.taskneeds= { t:set(self.getallrequirements(t)) for t in self.taskuniverse } - self.stoponfailure = not (args.keep_going == True) - print ("Stop on failure ",self.stoponfailure) - self.max_jobs_parallel = int(jmax) - self.scheduling_iteration = 0 - self.process_list = [] # list of currently scheduled tasks with normal priority - self.backfill_process_list = [] # list of curently scheduled tasks with low backfill priority (not sure this is needed) - self.pid_to_psutilsproc = {} # cache of putilsproc for resource monitoring - self.pid_to_files = {} # we can auto-detect what files are produced by which task (at least to some extent) - self.pid_to_connections = {} # we can auto-detect what connections are opened by which task (at least to some extent) - signal.signal(signal.SIGINT, self.SIGHandler) - signal.siginterrupt(signal.SIGINT, False) - self.nicevalues = [ os.nice(0) for tid in range(len(self.taskuniverse)) ] - self.internalmonitorcounter = 0 # internal use - self.internalmonitorid = 0 # internal use - self.tids_marked_toretry = [] # sometimes we might want to retry a failed task (simply because it was "unlucky") and we put them here - self.retry_counter = [ 0 for tid in range(len(self.taskuniverse)) ] # we keep track of many times retried already - self.task_retries = [ self.workflowspec['stages'][tid].get('retry_count',0) for tid in range(len(self.taskuniverse)) ] # the per task specific "retry" number -> needs to be parsed from the JSON - - self.semaphore_values = { self.workflowspec['stages'][tid].get('semaphore'):0 for tid in range(len(self.taskuniverse)) if self.workflowspec['stages'][tid].get('semaphore')!=None } # keeps current count of semaphores (defined in the json workflow). used to achieve user-defined "critical sections". - self.alternative_envs = {} # mapping of taskid to alternative software envs (to be applied on a per-task level) - # init alternative software environments - self.init_alternative_software_environments() - - def SIGHandler(self, signum, frame): - # basically forcing shut down of all child processes - actionlogger.info("Signal " + str(signum) + " caught") - try: - procs = psutil.Process().children(recursive=True) - except (psutil.NoSuchProcess): - pass - except (psutil.AccessDenied, PermissionError): - procs = getChildProcs(os.getpid()) - - for p in procs: - actionlogger.info("Terminating " + str(p)) - try: - p.terminate() - except (psutil.NoSuchProcess, psutil.AccessDenied): - pass - - gone, alive = psutil.wait_procs(procs, timeout=3) - for p in alive: - try: - actionlogger.info("Killing " + str(p)) - p.kill() - except (psutil.NoSuchProcess, psutil.AccessDenied): - pass - - exit (1) - - - def extract_global_environment(self, workflowspec): - """Checks if the workflow contains a dedicated init task - defining a global environment. Extract information and remove from workflowspec. - """ - init_index = 0 # this has to be the first task in the workflow - globalenv = {} - if workflowspec['stages'][init_index]['name'] == '__global_init_task__': - env = workflowspec['stages'][init_index].get('env', None) - if env != None: - globalenv = { e : env[e] for e in env } - del workflowspec['stages'][init_index] - - return globalenv - - def getallrequirements(self, t): - l=[] - for r in self.workflowspec['stages'][self.tasktoid[t]]['needs']: - l.append(r) - l=l+self.getallrequirements(r) - return l - - def get_done_filename(self, tid): - name = self.workflowspec['stages'][tid]['name'] - workdir = self.workflowspec['stages'][tid]['cwd'] - # name and workdir define the "done" file as used by taskwrapper - # this assumes that taskwrapper is used to actually check if something is to be rerun - done_filename = workdir + '/' + name + '.log_done' - return done_filename - - # removes the done flag from tasks that need to be run again - def remove_done_flag(self, listoftaskids): - for tid in listoftaskids: - done_filename = self.get_done_filename(tid) - name=self.workflowspec['stages'][tid]['name'] - if args.dry_run: - print ("Would mark task " + name + " as to be done again") - else: - print ("Marking task " + name + " as to be done again") - if os.path.exists(done_filename) and os.path.isfile(done_filename): - os.remove(done_filename) - - # submits a task as subprocess and records Popen instance - def submit(self, tid, nice=os.nice(0)): - actionlogger.debug("Submitting task " + str(self.idtotask[tid]) + " with nice value " + str(nice)) - c = self.workflowspec['stages'][tid]['cmd'] - workdir = self.workflowspec['stages'][tid]['cwd'] - if not workdir=='': - if os.path.exists(workdir) and not os.path.isdir(workdir): - actionlogger.error('Cannot create working dir ... some other resource exists already') - return None - - if not os.path.isdir(workdir): - os.makedirs(workdir) - - self.procstatus[tid]='Running' - if args.dry_run: - drycommand="echo \' " + str(self.scheduling_iteration) + " : would do " + str(self.workflowspec['stages'][tid]['name']) + "\'" - return subprocess.Popen(['/bin/bash','-c',drycommand], cwd=workdir) - - taskenv = os.environ.copy() - # add task specific environment - if self.workflowspec['stages'][tid].get('env')!=None: - taskenv.update(self.workflowspec['stages'][tid]['env']) - - # apply specific (non-default) software version, if any - # (this was setup earlier) - alternative_env = self.alternative_envs.get(tid, None) - if alternative_env != None: - actionlogger.info('Applying alternative software environment to task ' + self.idtotask[tid]) - for entry in alternative_env: - # overwrite what is present in default - taskenv[entry] = alternative_env[entry] - - p = psutil.Popen(['/bin/bash','-c',c], cwd=workdir, env=taskenv) - try: - p.nice(nice) - self.nicevalues[tid]=nice - except (psutil.NoSuchProcess, psutil.AccessDenied): - actionlogger.error('Couldn\'t set nice value of ' + str(p.pid) + ' to ' + str(nice)) - self.nicevalues[tid]=os.nice(0) - return p - - def ok_to_submit(self, tid, backfill=False): - softcpufactor=1 - softmemfactor=1 - if backfill: - softcpufactor=1.5 - sotmemfactor=1.5 - - # check semaphore - sem = self.workflowspec['stages'][tid].get('semaphore') - if sem != None: - if self.semaphore_values[sem] > 0: - return False - - # check other resources - if not backfill: - # analyse CPU - okcpu = (self.curcpubooked + float(self.cpuperid[tid]) <= self.cpulimit) - # analyse MEM - okmem = (self.curmembooked + float(self.maxmemperid[tid]) <= self.memlimit) - actionlogger.debug ('Condition check --normal-- for ' + str(tid) + ':' + str(self.idtotask[tid]) + ' CPU ' + str(okcpu) + ' MEM ' + str(okmem)) - return (okcpu and okmem) - else: - # only backfill one job at a time - if self.curcpubooked_backfill > 0: - return False - - # not backfilling jobs which either take much memory or use lot's of CPU anyway - # conditions are somewhat arbitrary and can be played with - if float(self.cpuperid[tid]) > 0.9*float(self.args.cpu_limit): - return False - if float(self.maxmemperid[tid])/float(self.args.cpu_limit) >= 1900: - return False - - # analyse CPU - okcpu = (self.curcpubooked_backfill + float(self.cpuperid[tid]) <= self.cpulimit) - okcpu = okcpu and (self.curcpubooked + self.curcpubooked_backfill + float(self.cpuperid[tid]) <= softcpufactor*self.cpulimit) - # analyse MEM - okmem = (self.curmembooked + self.curmembooked_backfill + float(self.maxmemperid[tid]) <= softmemfactor*self.memlimit) - actionlogger.debug ('Condition check --backfill-- for ' + str(tid) + ':' + str(self.idtotask[tid]) + ' CPU ' + str(okcpu) + ' MEM ' + str(okmem)) - return (okcpu and okmem) - return False - - - def ok_to_skip(self, tid): - done_filename = self.get_done_filename(tid) - if os.path.exists(done_filename) and os.path.isfile(done_filename): - return True - return False - - def book_resources(self, tid, backfill = False): - # books the resources used by a certain task - # semaphores - sem = self.workflowspec['stages'][tid].get('semaphore') - if sem != None: - self.semaphore_values[sem]+=1 - - # CPU + MEM - if not backfill: - self.curmembooked+=float(self.maxmemperid[tid]) - self.curcpubooked+=float(self.cpuperid[tid]) - else: - self.curmembooked_backfill+=float(self.maxmemperid[tid]) - self.curcpubooked_backfill+=float(self.cpuperid[tid]) - - def unbook_resources(self, tid, backfill = False): - # "frees" the nominal resources used by a certain task from the accounting - # so that other jobs can be scheduled - sem = self.workflowspec['stages'][tid].get('semaphore') - if sem != None: - self.semaphore_values[sem]-=1 - - # CPU + MEM - if not backfill: - self.curmembooked-=float(self.maxmemperid[tid]) - self.curcpubooked-=float(self.cpuperid[tid]) - else: - self.curmembooked_backfill-=float(self.maxmemperid[tid]) - self.curcpubooked_backfill-=float(self.cpuperid[tid]) - - - def try_job_from_candidates(self, taskcandidates, process_list, finished): - self.scheduling_iteration = self.scheduling_iteration + 1 - - # remove "done / skippable" tasks immediately - tasks_skipped = False - for tid in taskcandidates.copy(): # <--- the copy is important !! otherwise this loop is not doing what you think - if self.ok_to_skip(tid): - finished.append(tid) - taskcandidates.remove(tid) - tasks_skipped = True - actionlogger.info("Skipping task " + str(self.idtotask[tid])) - - # if tasks_skipped: - # return # ---> we return early in order to preserve some ordering (the next candidate tried should be daughters of skipped jobs) - - # the ordinary process list part - initialcandidates=taskcandidates.copy() - for tid in initialcandidates: - actionlogger.debug ("trying to submit " + str(tid) + ':' + str(self.idtotask[tid])) - if (len(self.process_list) + len(self.backfill_process_list) < self.max_jobs_parallel) and self.ok_to_submit(tid): - p=self.submit(tid) - if p!=None: - self.book_resources(tid) - self.process_list.append((tid,p)) - taskcandidates.remove(tid) - # minimal delay - time.sleep(0.1) - else: - break #---> we break at first failure assuming some priority (other jobs may come in via backfill) - - # the backfill part for remaining candidates - initialcandidates=taskcandidates.copy() - for tid in initialcandidates: - actionlogger.debug ("trying to backfill submit " + str(tid) + ':' + str(self.idtotask[tid])) - - if (len(self.process_list) + len(self.backfill_process_list) < self.max_jobs_parallel) and self.ok_to_submit(tid, backfill=True): - p=self.submit(tid, 19) - if p!=None: - self.book_resources(tid, backfill=True) - self.process_list.append((tid,p)) - taskcandidates.remove(tid) #-> not sure about this one - # minimal delay - time.sleep(0.1) - else: - continue - - def stop_pipeline_and_exit(self, process_list): - # kill all remaining jobs - for p in process_list: - p[1].kill() - - exit(1) - - def monitor(self, process_list): - self.internalmonitorcounter+=1 - if self.internalmonitorcounter % 5 != 0: - return - - self.internalmonitorid+=1 - - globalCPU=0. - globalPSS=0. - globalCPU_backfill=0. - globalPSS_backfill=0. - resources_per_task = {} - for tid, proc in process_list: - # proc is Popen object - pid=proc.pid - if self.pid_to_files.get(pid)==None: - self.pid_to_files[pid]=set() - self.pid_to_connections[pid]=set() - try: - psutilProcs = [ proc ] - # use psutil for CPU measurement - psutilProcs = psutilProcs + proc.children(recursive=True) - except (psutil.NoSuchProcess): - continue - - except (psutil.AccessDenied, PermissionError): - psutilProcs = psutilProcs + getChildProcs(pid) - - # accumulate total metrics (CPU, memory) - totalCPU = 0. - totalPSS = 0. - totalSWAP = 0. - totalUSS = 0. - for p in psutilProcs: - """ - try: - for f in p.open_files(): - self.pid_to_files[pid].add(str(f.path)+'_'+str(f.mode)) - for f in p.connections(kind="all"): - remote=f.raddr - if remote==None: - remote='none' - self.pid_to_connections[pid].add(str(f.type)+"_"+str(f.laddr)+"_"+str(remote)) - except Exception: - pass - """ - thispss=0 - thisuss=0 - # MEMORY part - try: - fullmem=p.memory_full_info() - thispss=getattr(fullmem,'pss',0) #<-- pss not available on MacOS - totalPSS=totalPSS + thispss - totalSWAP=totalSWAP + fullmem.swap - thisuss=fullmem.uss - totalUSS=totalUSS + thisuss - except (psutil.NoSuchProcess, psutil.AccessDenied): - pass - - # CPU part - # fetch existing proc or insert - cachedproc = self.pid_to_psutilsproc.get(p.pid) - if cachedproc!=None: - try: - thiscpu = cachedproc.cpu_percent(interval=None) - except (psutil.NoSuchProcess, psutil.AccessDenied): - thiscpu = 0. - totalCPU = totalCPU + thiscpu - # thisresource = {'iter':self.internalmonitorid, 'pid': p.pid, 'cpu':thiscpu, 'uss':thisuss/1024./1024., 'pss':thispss/1024./1024.} - # metriclogger.info(thisresource) - else: - self.pid_to_psutilsproc[p.pid] = p - try: - self.pid_to_psutilsproc[p.pid].cpu_percent() - except (psutil.NoSuchProcess, psutil.AccessDenied): - pass - - resources_per_task[tid]={'iter':self.internalmonitorid, 'name':self.idtotask[tid], 'cpu':totalCPU, 'uss':totalUSS/1024./1024., 'pss':totalPSS/1024./1024, 'nice':self.nicevalues[tid], 'swap':totalSWAP, 'label':self.workflowspec['stages'][tid]['labels']} - metriclogger.info(resources_per_task[tid]) - send_webhook(self.args.webhook, resources_per_task) - - for r in resources_per_task.values(): - if r['nice']==os.nice(0): - globalCPU+=r['cpu'] - globalPSS+=r['pss'] - else: - globalCPU_backfill+=r['cpu'] - globalPSS_backfill+=r['pss'] - - if globalPSS > self.memlimit: - metriclogger.info('*** MEMORY LIMIT PASSED !! ***') - # --> We could use this for corrective actions such as killing jobs currently back-filling - # (or better hibernating) - - def waitforany(self, process_list, finished, failingtasks): - failuredetected = False - failingpids = [] - if len(process_list)==0: - return False - - for p in list(process_list): - pid = p[1].pid - tid = p[0] # the task id of this process - returncode = 0 - if not self.args.dry_run: - returncode = p[1].poll() - if returncode!=None: - actionlogger.info ('Task ' + str(pid) + ' ' + str(tid)+':'+str(self.idtotask[tid]) + ' finished with status ' + str(returncode)) - # account for cleared resources - self.unbook_resources(tid, backfill = self.nicevalues[tid]!=os.nice(0) ) - self.procstatus[tid]='Done' - finished.append(tid) - process_list.remove(p) - if returncode != 0: - print (str(self.idtotask[tid]) + ' failed ... checking retry') - # we inspect if this is something "unlucky" which could be resolved by a simple resubmit - if self.is_worth_retrying(tid) and ((self.retry_counter[tid] < int(args.retry_on_failure)) or (self.retry_counter[tid] < int(self.task_retries[tid]))): - print (str(self.idtotask[tid]) + ' to be retried') - actionlogger.info ('Task ' + str(self.idtotask[tid]) + ' failed but marked to be retried ') - self.tids_marked_toretry.append(tid) - self.retry_counter[tid] += 1 - - else: - failuredetected = True - failingpids.append(pid) - failingtasks.append(tid) - - if failuredetected and self.stoponfailure: - actionlogger.info('Stoping pipeline due to failure in stages with PID ' + str(failingpids)) - # self.analyse_files_and_connections() - if self.args.stdout_on_failure: - self.cat_logfiles_tostdout(failingtasks) - self.send_checkpoint(failingtasks, self.args.checkpoint_on_failure) - self.stop_pipeline_and_exit(process_list) - - # empty finished means we have to wait more - return len(finished)==0 - - - def get_logfile(self, tid): - # determines the logfile name for this task - taskspec = self.workflowspec['stages'][tid] - taskname = taskspec['name'] - filename = taskname + '.log' - directory = taskspec['cwd'] - return directory + '/' + filename - - - def is_worth_retrying(self, tid): - # This checks for some signatures in logfiles that indicate that a retry of this task - # might have a chance. - # Ideally, this should be made user configurable. Either the user could inject a lambda - # or a regular expression to use. For now we just put a hard coded list - logfile = self.get_logfile(tid) - - return True #! --> for now we just retry tasks a few times - - # 1) ZMQ_EVENT + interrupted system calls (DPL bug during shutdown) - # Not sure if grep is faster than native Python text search ... - # status = os.system('grep "failed setting ZMQ_EVENTS" ' + logfile + ' &> /dev/null') - # if os.WEXITSTATUS(status) == 0: - # return True - - # return False - - - def cat_logfiles_tostdout(self, taskids): - # In case of errors we can cat the logfiles for this taskname - # to stdout. Assuming convention that "taskname" translates to "taskname.log" logfile. - for tid in taskids: - logfile = self.get_logfile(tid) - if os.path.exists(logfile): - print (' ----> START OF LOGFILE ', logfile, ' -----') - os.system('cat ' + logfile) - print (' <---- END OF LOGFILE ', logfile, ' -----') - - def send_checkpoint(self, taskids, location): - # Makes a tarball containing all files in the base dir - # (timeframe independent) and the dir with corrupted timeframes - # and copies it to a specific ALIEN location. Not a core function - # just some tool get hold on error conditions appearing on the GRID. - - def get_tar_command(dir='./', flags='cf', findtype='f', filename='checkpoint.tar'): - return 'find ' + str(dir) + ' -maxdepth 1 -type ' + str(findtype) + ' -print0 | xargs -0 tar ' + str(flags) + ' ' + str(filename) - - if location != None: - print ('Making a failure checkpoint') - # let's determine a filename from ALIEN_PROC_ID - hostname - and PID - - aliprocid=os.environ.get('ALIEN_PROC_ID') - if aliprocid == None: - aliprocid = 0 - - fn='pipeline_checkpoint_ALIENPROC' + str(aliprocid) + '_PID' + str(os.getpid()) + '_HOST' + socket.gethostname() + '.tar' - actionlogger.info("Checkpointing to file " + fn) - tarcommand = get_tar_command(filename=fn) - actionlogger.info("Taring " + tarcommand) - - # create a README file with instruction on how to use checkpoint - readmefile=open('README_CHECKPOINT_PID' + str(os.getpid()) + '.txt','w') - - for tid in taskids: - taskspec = self.workflowspec['stages'][tid] - name = taskspec['name'] - readmefile.write('Checkpoint created because of failure in task ' + name + '\n') - readmefile.write('In order to reproduce with this checkpoint, do the following steps:\n') - readmefile.write('a) setup the appropriate O2sim environment using alienv\n') - readmefile.write('b) run: $O2DPG_ROOT/MC/bin/o2_dpg_workflow_runner.py -f workflow.json -tt ' + name + '$ --retry-on-failure 0\n') - readmefile.close() - - # first of all the base directory - os.system(tarcommand) - - # then we add stuff for the specific timeframes ids if any - for tid in taskids: - taskspec = self.workflowspec['stages'][tid] - directory = taskspec['cwd'] - if directory != "./": - tarcommand = get_tar_command(dir=directory, flags='rf', filename=fn) - actionlogger.info("Tar command is " + tarcommand) - os.system(tarcommand) - # same for soft links - tarcommand = get_tar_command(dir=directory, flags='rf', findtype='l', filename=fn) - actionlogger.info("Tar command is " + tarcommand) - os.system(tarcommand) - - # prepend file:/// to denote local file - fn = "file://" + fn - actionlogger.info("Local checkpoint file is " + fn) - - # location needs to be an alien path of the form alien:///foo/bar/ - copycommand='alien.py cp ' + fn + ' ' + str(location) + '@disk:1' - actionlogger.info("Copying to alien " + copycommand) - os.system(copycommand) - - def init_alternative_software_environments(self): - """ - Initiatialises alternative software environments for specific tasks, if there - is an annotation in the workflow specificiation. - """ - - environment_cache = {} - # go through all the tasks once and setup environment - for taskid in range(len(self.workflowspec['stages'])): - packagestr = self.workflowspec['stages'][taskid].get("alternative_alienv_package") - if packagestr == None: - continue - - if environment_cache.get(packagestr) == None: - environment_cache[packagestr] = get_alienv_software_environment(packagestr) - - self.alternative_envs[taskid] = environment_cache[packagestr] - - - def analyse_files_and_connections(self): - for p,s in self.pid_to_files.items(): - for f in s: - print("F" + str(f) + " : " + str(p)) - for p,s in self.pid_to_connections.items(): - for c in s: - print("C" + str(c) + " : " + str(p)) - #print(str(p) + " CONS " + str(c)) - try: - # check for intersections - for p1, s1 in self.pid_to_files.items(): - for p2, s2 in self.pid_to_files.items(): - if p1!=p2: - if type(s1) is set and type(s2) is set: - if len(s1)>0 and len(s2)>0: - try: - inters = s1.intersection(s2) - except Exception: - print ('Exception during intersect inner') - pass - if (len(inters)>0): - print ('FILE Intersection ' + str(p1) + ' ' + str(p2) + ' ' + str(inters)) - # check for intersections - for p1, s1 in self.pid_to_connections.items(): - for p2, s2 in self.pid_to_connections.items(): - if p1!=p2: - if type(s1) is set and type(s2) is set: - if len(s1)>0 and len(s2)>0: - try: - inters = s1.intersection(s2) - except Exception: - print ('Exception during intersect inner') - pass - if (len(inters)>0): - print ('CON Intersection ' + str(p1) + ' ' + str(p2) + ' ' + str(inters)) - - # check for intersections - #for p1, s1 in slf.pid_to_files.items(): - # for p2, s2 in self.pid_to_files.items(): - # if p1!=p2 and len(s1.intersection(s2))!=0: - # print ('Intersection found files ' + str(p1) + ' ' + str(p2) + ' ' + s1.intersection(s2)) - except Exception as e: - exc_type, exc_obj, exc_tb = sys.exc_info() - fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] - print(exc_type, fname, exc_tb.tb_lineno) - print('Exception during intersect outer') - pass - - def is_good_candidate(self, candid, finishedtasks): - if self.procstatus[candid] != 'ToDo': - return False - needs = set([self.tasktoid[t] for t in self.taskneeds[self.idtotask[candid]]]) - if set(finishedtasks).intersection(needs) == needs: - return True - return False - - def emit_code_for_task(self, tid, lines): - actionlogger.debug("Submitting task " + str(self.idtotask[tid])) - taskspec = self.workflowspec['stages'][tid] - c = taskspec['cmd'] - workdir = taskspec['cwd'] - env = taskspec.get('env') - # in general: - # try to make folder - lines.append('[ ! -d ' + workdir + ' ] && mkdir ' + workdir + '\n') - # cd folder - lines.append('cd ' + workdir + '\n') - # set local environment - if env!=None: - for e in env.items(): - lines.append('export ' + e[0] + '=' + str(e[1]) + '\n') - # do command - lines.append(c + '\n') - # unset local environment - if env!=None: - for e in env.items(): - lines.append('unset ' + e[0] + '\n') - - # cd back - lines.append('cd $OLDPWD\n') - - - # produce a bash script that runs workflow standalone - def produce_script(self, filename): - # pick one of the correct task orderings - taskorder = self.topological_orderings[0] - outF = open(filename, "w") - - lines=[] - # header - lines.append('#!/usr/bin/env bash\n') - lines.append('#THIS FILE IS AUTOGENERATED\n') - lines.append('export JOBUTILS_SKIPDONE=ON\n') - - # we record the global environment setting - # in particular to capture global workflow initialization - lines.append('#-- GLOBAL INIT SECTION FROM WORKFLOW --\n') - for e in self.globalenv: - lines.append('export ' + str(e) + '=' + str(self.globalenv[e]) + '\n') - lines.append('#-- TASKS FROM WORKFLOW --\n') - for tid in taskorder: - print ('Doing task ' + self.idtotask[tid]) - self.emit_code_for_task(tid, lines) - - outF.writelines(lines) - outF.close() - - - # print error message when no progress can be made - def noprogress_errormsg(self): - # TODO: rather than writing this out here; refer to the documentation discussion this? - msg = """Scheduler runtime error: The scheduler is not able to make progress although we have a non-zero candidate set. - -Explanation: This is typically the case because the **ESTIMATED** resource requirements for some tasks -in the workflow exceed the available number of CPU cores or the memory (as explicitely or implicitely determined from the ---cpu-limit and --mem-limit options). Often, this might be the case on laptops with <=16GB of RAM if one of the tasks -is demanding ~16GB. In this case, one could try to tell the scheduler to use a slightly higher memory limit -with an explicit --mem-limit option (for instance `--mem-limit 20000` to set to 20GB). This might work whenever the -**ACTUAL** resource usage of the tasks is smaller than anticipated (because only small test cases are run). - -In addition it might be worthwile running the workflow without this resource aware, dynamic scheduler. -This is possible by converting the json workflow into a linearized shell script and by directly executing the shell script. -Use the `--produce-script myscript.sh` option for this. -""" - print (msg, file=sys.stderr) - - def execute(self): - starttime = time.perf_counter() - psutil.cpu_percent(interval=None) - os.environ['JOBUTILS_SKIPDONE'] = "ON" - errorencountered = False - - def speedup_ROOT_Init(): - """initialize some env variables that speed up ROOT init - and prevent ROOT from spawning many short-lived child - processes""" - - # only do it on Linux - if platform.system() != 'Linux': - return - - if os.environ.get('ROOT_LDSYSPATH')!=None and os.environ.get('ROOT_CPPSYSINCL')!=None: - # do nothing if already defined - return - - # a) the PATH for system libraries - # search taken from ROOT TUnixSystem - cmd='LD_DEBUG=libs LD_PRELOAD=DOESNOTEXIST ls /tmp/DOESNOTEXIST 2>&1 | grep -m 1 "system search path" | sed \'s/.*=//g\' | awk \'//{print $1}\'' - proc = subprocess.Popen([cmd], stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) - libpath, err = proc.communicate() - if not (args.no_rootinit_speedup == True): - print ("setting up ROOT system") - os.environ['ROOT_LDSYSPATH'] = libpath.decode() - - # b) the PATH for compiler includes needed by Cling - cmd='LC_ALL=C c++ -xc++ -E -v /dev/null 2>&1 | sed -n \'/^.include/,${/^ \/.*++/{p}}\'' # | sed \'s/ //\'' - proc = subprocess.Popen([cmd], stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) - incpath, err = proc.communicate() - incpaths = [ line.lstrip() for line in incpath.decode().splitlines() ] - joined = ':'.join(incpaths) - if not (args.no_rootinit_speedup == True): - os.environ['ROOT_CPPSYSINCL'] = joined - - speedup_ROOT_Init() - - # we make our own "tmp" folder - # where we can put stuff such as tmp socket files etc (for instance DPL FAIR-MQ sockets) - # (In case of running within docker/singularity, this may not be so important) - if not os.path.isdir("./.tmp"): - os.mkdir("./.tmp") - if os.environ.get('FAIRMQ_IPC_PREFIX')==None: - socketpath = os.getcwd() + "/.tmp" - actionlogger.info("Setting FAIRMQ socket path to " + socketpath) - os.environ['FAIRMQ_IPC_PREFIX'] = socketpath - - # some maintenance / init work - if args.list_tasks: - print ('List of tasks in this workflow:') - for i,t in enumerate(self.workflowspec['stages'],0): - print (t['name'] + ' (' + str(t['labels']) + ')' + ' ToDo: ' + str(not self.ok_to_skip(i))) - exit (0) - - if args.produce_script != None: - self.produce_script(args.produce_script) - exit (0) - - if args.rerun_from: - reruntaskfound=False - for task in self.workflowspec['stages']: - taskname=task['name'] - if re.match(args.rerun_from, taskname): - reruntaskfound=True - taskid=self.tasktoid[taskname] - self.remove_done_flag(find_all_dependent_tasks(self.possiblenexttask, taskid)) - if not reruntaskfound: - print('No task matching ' + args.rerun_from + ' found; cowardly refusing to do anything ') - exit (1) - - # ***************** - # main control loop - # ***************** - currenttimeframe=1 - candidates = [ tid for tid in self.possiblenexttask[-1] ] - - self.process_list=[] # list of tuples of nodes ids and Popen subprocess instances - - finishedtasks=[] # global list of finished tasks - try: - - while True: - # sort candidate list according to task weights - candidates = [ (tid, self.taskweights[tid]) for tid in candidates ] - candidates.sort(key=lambda tup: (tup[1][0],-tup[1][1])) # prefer small and same timeframes first then prefer important tasks within frameframe - # remove weights - candidates = [ tid for tid,_ in candidates ] - - finished = [] # --> to account for finished because already done or skipped - actionlogger.debug('Sorted current candidates: ' + str([(c,self.idtotask[c]) for c in candidates])) - self.try_job_from_candidates(candidates, self.process_list, finished) - if len(candidates) > 0 and len(self.process_list) == 0: - self.noprogress_errormsg() - send_webhook(self.args.webhook,"Unable to make further progress: Quitting") - errorencountered = True - break - - finished_from_started = [] # to account for finished when actually started - failing = [] - while self.waitforany(self.process_list, finished_from_started, failing): - if not args.dry_run: - self.monitor(self.process_list) # ---> make async to normal operation? - time.sleep(1) # <--- make this incremental (small wait at beginning) - else: - time.sleep(0.001) - - finished = finished + finished_from_started - actionlogger.debug("finished now :" + str(finished_from_started)) - finishedtasks = finishedtasks + finished - - # if a task was marked "failed" and we come here (because - # we use --keep-going) ... we need to take out the pid from finished - if len(failing) > 0: - # remove these from those marked finished in order - # not to continue with their children - errorencountered = True - for t in failing: - finished = [ x for x in finished if x != t ] - finishedtasks = [ x for x in finishedtasks if x != t ] - - # if a task was marked as "retry" we simply put it back into the candidate list - if len(self.tids_marked_toretry) > 0: - # we need to remove these first of all from those marked finished - for t in self.tids_marked_toretry: - finished = [ x for x in finished if x != t ] - finishedtasks = [ x for x in finishedtasks if x != t ] - - candidates = candidates + self.tids_marked_toretry - self.tids_marked_toretry = [] - - # new candidates - for tid in finished: - if self.possiblenexttask.get(tid)!=None: - potential_candidates=list(self.possiblenexttask[tid]) - for candid in potential_candidates: - # try to see if this is really a candidate: - if self.is_good_candidate(candid, finishedtasks) and candidates.count(candid)==0: - candidates.append(candid) - - actionlogger.debug("New candidates " + str( candidates)) - send_webhook(self.args.webhook, "New candidates " + str(candidates)) - - if len(candidates)==0 and len(self.process_list)==0: - break - except Exception as e: - exc_type, exc_obj, exc_tb = sys.exc_info() - fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] - print(exc_type, fname, exc_tb.tb_lineno) - traceback.print_exc() - print ('Cleaning up ') +_HERE = os.path.dirname(os.path.realpath(__file__)) - self.SIGHandler(0,0) +RUNNERS = { + "legacy": os.path.join(_HERE, "o2dpg_workflow_runner_legacy.py"), + "new": os.path.join(_HERE, os.pardir, "workflow_runner", + "o2dpg_workflow_runner.py"), +} - endtime = time.perf_counter() - statusmsg = "success" - if errorencountered: - statusmsg = "with failures" +DEFAULT_RUNNER = "legacy" - print ('\n**** Pipeline done ' + statusmsg + ' (global_runtime : {:.3f}s) *****\n'.format(endtime-starttime)) - actionlogger.debug("global_runtime : {:.3f}s".format(endtime-starttime)) - return errorencountered +def main(): + which = os.getenv("ALIEN_O2DPG_WORKFLOW_RUNNER", DEFAULT_RUNNER).strip() + script = RUNNERS.get(which) + if script is None: + sys.exit(f"ALIEN_O2DPG_WORKFLOW_RUNNER={which!r} is not one of " + f"{', '.join(sorted(RUNNERS))}") + os.execv(sys.executable, [sys.executable, script] + sys.argv[1:]) -if args.cgroup!=None: - myPID=os.getpid() - command="echo " + str(myPID) + " > /sys/fs/cgroup/cpuset/"+args.cgroup+"/tasks" - actionlogger.info("applying cgroups " + command) - os.system(command) -executor=WorkflowExecutor(args.workflowfile,jmax=args.maxjobs,args=args) -exit (executor.execute()) +if __name__ == "__main__": + main() diff --git a/MC/bin/o2_hybrid_gen.py b/MC/bin/o2_hybrid_gen.py new file mode 100755 index 000000000..b3f0e9048 --- /dev/null +++ b/MC/bin/o2_hybrid_gen.py @@ -0,0 +1,155 @@ +#!/usr/bin/env python3 + +# This script creates a template JSON file for the configuration of the hybrid generator +# The generators to be used are passed as a list of strings via the --gen flag, while the +# output filename is set using --output. All the generators available in O2sim are supported. +# Alternatively the --iniFile flag can be used to specify external generators that are configured +# with ini files. The --clone flag allows the user to create multiple instances of the generator list, +# which is a useful feature when running multi-threaded event pool. This can be enabled via setting +# the --mode flag to 'parallel', which is set "sequential" by default. +# Since the script uses the ROOT dictionary to import the parameters names, O2 must be loaded, otherwise +# the template generation will not work. +# Example: +# $O2DPG_ROOT/MC/bin/o2_hybrid_gen.py --gen pythia8 boxgen external extkinO2 hepmc pythia8hf --clone 2 \ +# --output config.json --mode parallel --iniFile /path/to/file0.ini /path/to/file1.ini + +import argparse +import json +import ROOT +import cppyy +import numpy as np + +# Get the TClass object for the struct +tclass = ROOT.TClass.GetClass("o2::eventgen::Pythia8GenConfig") +tclass1 = ROOT.TClass.GetClass("o2::eventgen::BoxGenConfig") + +gens_params = {"pythia8": "o2::eventgen::Pythia8GenConfig", "external": "o2::eventgen::ExternalGenConfig", + "evtpool": "o2::eventgen::EventPoolGenConfig", "extkinO2": "o2::eventgen::O2KineGenConfig", + "hepmc": "o2::eventgen::HepMCGenConfig", "boxgen": "o2::eventgen::BoxGenConfig"} +cmd_params = "o2::eventgen::FileOrCmdGenConfig" +gens_instances = {"pythia8": ROOT.o2.eventgen.Pythia8GenConfig(), "external": ROOT.o2.eventgen.ExternalGenConfig(), + "evtpool": ROOT.o2.eventgen.EventPoolGenConfig(), "extkinO2": ROOT.o2.eventgen.O2KineGenConfig(), + "hepmc": ROOT.o2.eventgen.HepMCGenConfig(), "boxgen": ROOT.o2.eventgen.BoxGenConfig()} +cmd_instance = ROOT.o2.eventgen.FileOrCmdGenConfig() + +def get_params(instance, class_name): + tclass = ROOT.TClass.GetClass(class_name) + members = tclass.GetListOfDataMembers() + params = {} + for member in members: + if isinstance(member, ROOT.TDataMember): + member_value = getattr(instance, member.GetName()) + params[member.GetName()] = member_value + # replace C++ strings and arrays + for key, value in params.items(): + if isinstance(value, cppyy.gbl.std.string): + # convert to a JSON serialisable python string + params[key] = str(value) + elif hasattr(value, '__len__') and hasattr(value, '__getitem__'): + # convert C++ numerical array to python array, no string arrays are declared as parameters, so far + params[key] = np.array(value).tolist() + return params + +def main(): + parser = argparse.ArgumentParser(description='Create a JSON file from command line flags.') + parser.add_argument('--gen', type=str, nargs='+', help='List of generators to be used') + parser.add_argument('--iniFile', type=str, nargs='+', help='List of external generators configured with ini files') + parser.add_argument('--mode', type=str, help='Run generator in sequential or parallel mode for quicker event generation (multi-threading)') + parser.add_argument('--output', type=str, required=True, help='Output JSON file path') + parser.add_argument('--clone', type=int, help='Number of clones to make of the generator list') + parser.add_argument('--trigger', action='store_true', help='Add triggers to the template JSON file') + + args = parser.parse_args() + + # Check if the mode is valid + valid_modes = ["sequential", "parallel"] + mode = args.mode if args.mode in valid_modes else "sequential" + if args.mode and args.mode not in valid_modes: + print(f"Mode {args.mode} not valid. Please use 'sequential' or 'parallel'") + print("Setting sequential mode as default") + else: + print(f"Running in {mode} mode") + + # Available options for trigger are "off", "or", "and" + # in all the other cases the trigger is forced "off" + + add_trigger = lambda d: d.update({"triggers": {"mode": "off", "specs": [{"macro": "", "function": ""}]}}) if args.trigger else None + + # put in a list all the elementes in the gen flag + noConfGen = ["pythia8pp", "pythia8hf", "pythia8hi", "pythia8powheg"] + gens = [] + if args.gen is None and args.iniFile is None: + print("No generators specified") + exit(1) + if args.gen: + print(f"Generators to be used: {args.gen}") + for gen in args.gen: + if gen in gens_params: + if gen == "hepmc": + configs = [get_params(cmd_instance, cmd_params), get_params(gens_instances[gen], gens_params[gen])] + gens.append({ + 'name': gen, + 'config': { + "configcmd": configs[0], + "confighepmc": configs[1] + } + }) + add_trigger(gens[-1]) + else: + configs = get_params(gens_instances[gen],gens_params[gen]) + gens.append({ + 'name': gen, + 'config': configs + }) + add_trigger(gens[-1]) + elif gen in noConfGen: + gens.append({ + "name": gen, + "config": "" + }) + add_trigger(gens[-1]) + else: + print(f"Generator {gen} not found in the list of available generators") + exit(1) + + if args.iniFile: + print(f"External generators to be used: {args.iniFile}") + for ini in args.iniFile: + if ".ini" != ini[-4:]: + print(f"File {ini} is not an ini file") + exit(1) + configs = get_params(gens_instances["external"],gens_params["external"]) + configs["iniFile"] = ini + gens.append({ + 'name': 'external', + 'config': configs + }) + add_trigger(gens[-1]) + + if args.clone: + if args.clone < 2: + print("The cloning value must be greater than 1") + print("Skipping procedure...") + else: + print(f"Cloning the generator list {args.clone} times") + gens = gens * args.clone + + + # fill fractions with 1 for each generator + fractions = [1] * len(gens) + + # Put gens and fractions in the data dictionary + data = { + "mode": mode, + "generators": gens, + "fractions": fractions + } + + # Write the data dictionary to a JSON file + with open(args.output, 'w') as f: + json.dump(data, f, indent=2) + + print(f"JSON file created at {args.output}") + +if __name__ == "__main__": + main() diff --git a/MC/bin/o2dpg-workflow-tools.py b/MC/bin/o2dpg-workflow-tools.py index 78b82294e..09bd4b115 100755 --- a/MC/bin/o2dpg-workflow-tools.py +++ b/MC/bin/o2dpg-workflow-tools.py @@ -15,15 +15,16 @@ def extend(args): is kept """ # load workflows - workflow_orig = read_workflow(args.orig_wf) - workflow_extend = read_workflow(args.extend_wf) + workflow_orig, meta = read_workflow(args.orig_wf) + workflow_extend, _ = read_workflow(args.extend_wf) # extend workflow_orig.extend(workflow_extend) # dump in new file filename = args.output if args.output else args.orig_wf - dump_workflow(workflow_orig, filename) + # propagate meta information from original workflow that is extended + dump_workflow(workflow_orig, filename, meta) def create(args): diff --git a/MC/bin/o2dpg_determine_eventstat.py b/MC/bin/o2dpg_determine_eventstat.py new file mode 100755 index 000000000..2daddd870 --- /dev/null +++ b/MC/bin/o2dpg_determine_eventstat.py @@ -0,0 +1,131 @@ +#!/usr/bin/env python3 + +# +# Script that determines final accounting numbers / event statistics +# for reporting back to MonaLisa. +# +# Analyses the AO2D / kinematics output of an O2DPG simulation run +# and creates a file of the form +# +# inputN_passedN_errorsN_outputN.stat +# +# which is picked up and used by the MonaLisa system. +# +# See discussion in https://alice.its.cern.ch/jira/browse/O2-4553; +# Here outputN would be the number of events/collisions produced in this job. + +import ROOT +import argparse +import os +import re + +parser = argparse.ArgumentParser(description='', + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + +parser.add_argument('-f','--aod-file', default="AO2D.root", help='AO2D file to check') +args = parser.parse_args() + +def write_stat_file(eventcount): + """ + writes a file conforming to MonaLisa convention + """ + + filename = '0_0_0_' + str(eventcount) + '.stat' + # touche/create a new file + with open(filename, 'w') as f: + print ("#This file is autogenerated", file=f) + print ("#It tells MonaLisa about the number of produced MC events", file=f) + print ("#Numer of MC collisions in AOD : " + str(eventcount), file=f) + +def read_collisioncontext_eventcount(file): + """ + determines MC eventcount from collision context files + """ + pass + +def find_files_matching_pattern(directory='.', pattern='.*'): + matching_files = [] + + # Walk through the directory and its subdirectories + for root, dirs, files in os.walk(directory): + for file_name in files: + # Check if the filename matches the regular expression pattern + if re.match(pattern, file_name): + matching_files.append(os.path.join(root, file_name)) + + return matching_files + +def read_GEANT_eventcount(file): + # Open the ROOT file + eventcount = 0 + tfile = ROOT.TFile.Open(file) + if tfile: + simtree = tfile.Get("o2sim") + if simtree and isinstance(simtree, ROOT.TTree): + eventcount = simtree.GetEntries() + + tfile.Close() + return eventcount + +def read_accumulated_GEANT_eventcount(directory = "."): + """ + Determines the MC eventcount from GEANT kinematics files sitting + in directory/tfX/ subdirectories. + """ + pattern_to_match = r'sgn.*_Kine.root' + kine_files = find_files_matching_pattern(directory, pattern_to_match) + eventcount = 0 + for f in kine_files: + eventcount = eventcount + read_GEANT_eventcount(f) + return eventcount + +def read_AO2D_eventcount(file): + """ + determines MC eventcount from (final) AO2D file + """ + eventcount = 0 + + # Open the ROOT file + tfile = ROOT.TFile.Open(file) + + # Get the list of keys (TKeys) in the ROOT files + keys = tfile.GetListOfKeys() + + # Iterate through the keys "DF_" keys and accumulate + # stored MC collisions + colfound = 0 + + for key in keys: + key_name = key.GetName() + if key_name.startswith("DF_"): + obj = key.ReadObj() + + # get the list of keys of available tables + tablelist = obj.GetListOfKeys() + for tab in tablelist: + # the O2mccollision_ tree contains the simulated collisions + # but the version number might change so better to loop over keys and do matching + tabname = tab.GetName() + if re.match("^O2mccollision(_[0-9]*)?$", tabname): + coltreekey = obj.GetKey(tabname) + coltree = coltreekey.ReadObj() + if coltree and isinstance(coltree, ROOT.TTree): + eventcount = eventcount + coltree.GetEntries() + colfound = colfound + 1 + + if colfound == 0: + print ("ERROR: No MC collision table found") + + # Close the files + tfile.Close() + return eventcount + +AO2D_eventcount = read_AO2D_eventcount(args.aod_file) + +# for debugging +# GEANT_eventcount = read_accumulated_GEANT_eventcount() +# if AO2D_eventcount != GEANT_eventcount: +# print ("WARN: AO2D MC event count and GEANT event count differ") + +print ("Found " + str(AO2D_eventcount) + " events in AO2D file") +write_stat_file(AO2D_eventcount) diff --git a/MC/bin/o2dpg_dpl_config_tools.py b/MC/bin/o2dpg_dpl_config_tools.py new file mode 100755 index 000000000..6849ea3dc --- /dev/null +++ b/MC/bin/o2dpg_dpl_config_tools.py @@ -0,0 +1,377 @@ +#!/usr/bin/env python3 + +import json +import re +import sys +from collections import defaultdict +from copy import deepcopy +import os +from o2dpg_workflow_utils import merge_dicts +import shlex + +BUILTIN_BLACKLIST = { + "--session", "--severity", "--shm-segment-id", "--shm-segment-size", + "--resources-monitoring", "--resources-monitoring-dump-interval", + "--delay", "--loop", "--early-forward-policy", "--fairmq-rate-logging", + "--pipeline", "--disable-mc", "--disable-root-input", "--timeframes-rate-limit", + "--timeframes-rate-limit-ipcid", + "--lumi-type", # TPC corrections are treated separately in o2dpg_sim_workflow + "--corrmap-lumi-mode", # TPC corrections are treated separately in o2dpg_sim_workflow + "--enable-M-shape-correction" # TPC correction option not needed in MC +} + +def parse_command_string(cmd_str): + """ + Parse a DPL command string into structured config_base: + { + "executable": str, + "options": {key: val, ...}, + "configKeyValues": {"Group": {subkey: val}} + } + """ + try: + tokens = shlex.split(cmd_str, posix=False) + except ValueError as e: + print(f"[ERROR] Failed to parse command string: {cmd_str}") + raise e + + if not tokens: + return {} + + exe = tokens[0] + opts = {} + config_keyvals_raw = None + + i = 1 + while i < len(tokens): + token = tokens[i] + if token.startswith('--') or (token.startswith('-') and len(token) == 2): + key = token # preserve the dash prefix: "-b" or "--run-number" + if i + 1 < len(tokens) and not tokens[i + 1].startswith('-'): + value = tokens[i + 1].strip('"').strip("'") + i += 1 + else: + value = True + if key == "--configKeyValues": + config_keyvals_raw = value + else: + opts[key] = value + i += 1 + + config_kv_parsed, config_groups = {}, set() + if config_keyvals_raw: + config_kv_parsed, config_groups = parse_configKeyValues_block(config_keyvals_raw) + + return { + "executable": exe, + "options": opts, + "configKeyValues": config_kv_parsed, + "configKeyGroups": sorted(config_groups) + } + +def parse_command_string_symmetric(cmd_str, configname = None): + """ + Parses a DPL command string into the same structure as parse_workflow_config(...): + { + "ConfigParams": { group: {key: value, ...} }, + "Executables": { + "o2-executable": { + "full": {...}, + "filtered": {...}, + "blacklisted": [], + "configKeyValues": [group, ...] + } + } + } + """ + try: + tokens = shlex.split(cmd_str, posix=False) + except ValueError as e: + print(f"[ERROR] Failed to parse command string: {cmd_str}") + raise e + + if not tokens: + return {} + + exe = os.path.basename(tokens[0]) if configname == None else configname + opts = {} + config_kv_raw = None + + i = 1 + while i < len(tokens): + token = tokens[i] + if token.startswith('--') or (token.startswith('-') and len(token) == 2): + key = token # preserve the dash prefix: "-b" or "--run-number" + if i + 1 < len(tokens) and not tokens[i + 1].startswith('-'): + value = tokens[i + 1].strip('"').strip("'") + i += 1 + else: + value = True + if key == "--configKeyValues": + config_kv_raw = value + else: + opts[key] = value + i += 1 + + config_params = {} + config_key_groups = [] + + if config_kv_raw: + parsed_kv, groups = parse_configKeyValues_block(config_kv_raw) + config_params = parsed_kv + config_key_groups = sorted(groups) + + return { + "ConfigParams": config_params, + "Executables": { + exe: { + "full": opts, + "filtered": dict(opts), + "blacklisted": [], + "configKeyValues": config_key_groups + } + } + } + + +def parse_configKeyValues_block(raw_value): + result = defaultdict(dict) + seen_groups = set() + raw_value = raw_value.replace('\\"', '"').replace("\\'", "'") + parts = raw_value.split(";") + for part in parts: + part = part.strip() + if not part or "=" not in part: + continue + key, val = part.split("=", 1) + key = key.strip() + val = val.strip() + if "." in key: + group, subkey = key.split(".", 1) + result[group][subkey] = val + seen_groups.add(group) + return dict(result), seen_groups + +def log_line(logger, message): + if logger is None or logger == sys.stdout: + print(message) + elif isinstance(logger, str): + with open(logger, "a") as f: + f.write(message + "\n") + else: + logger.write(message + "\n") + +def quote_for_nested_string(val): + s = str(val) + # Already double-quoted? + if s.startswith('"') and s.endswith('"'): + return s + # Escape inner quotes + s_escaped = s.replace('"', r'\"') + return f'"{s_escaped}"' + +def quote_if_needed(val): + # Only quote values that are likely to break shell parsing + # or contain nested shell-sensitive characters + s = str(val) + if re.search(r'[ \t;:&|<>]', s): + return quote_for_nested_string(s) + return s + +def modify_dpl_command(cmd_str, config_anchor, allow_overwrite=False, logger=None, configname=None): + # check if cmd_str is given as list, in which case we transfrom to string + if isinstance(cmd_str, list) == True: + cmd_str = " ".join(filter(None, cmd_str)) + + base = parse_command_string(cmd_str) + exe = base["executable"] + existing_opts = base["options"] + existing_kv = base["configKeyValues"] + + # Start building new command + new_cmd = [exe] + added = [] + overwritten = [] + + exe_basename = os.path.basename(exe) if configname == None else configname + anchor_exec = None + if "Executables" in config_anchor: + anchor_exec = config_anchor["Executables"].get(exe_basename, None) + if anchor_exec == None: + # try without the Executable section + anchor_exec = config_anchor.get(exe_basename, None) + + if anchor_exec == None: + print(f"[WARN] No anchor config found for {exe}") + return cmd_str + + anchor_opts = anchor_exec.get("filtered", {}) + anchor_kv_groups = anchor_exec.get("configKeyValues", []) + + # --- Step 1: Reconstruct executable and its CLI options + new_cmd = [exe] + added = [] + overwritten = [] + + # Step 1: Existing options (preserved or overwritten) + for key, val in existing_opts.items(): + if allow_overwrite and key in anchor_opts: + val = anchor_opts[key] + overwritten.append(key) + new_cmd.append(f"{key} {quote_if_needed(val)}" if val is not True else f"{key}") + + # Step 2: Add missing options from anchor + for key, val in anchor_opts.items(): + if key not in existing_opts: + new_cmd.append(f"{key} {quote_if_needed(val)}" if val is not True else f"{key}") + added.append(key) + + # what about config-key values (should already be done) Merge configKeyValues + merged_kv = deepcopy(existing_kv) + # for group in anchor_kv_groups: + # group_kvs = config_anchor.get("ConfigParams", {}).get(group, {}) + # if group not in merged_kv: + # merged_kv[group] = group_kvs + # elif allow_overwrite: + # merged_kv[group].update(group_kvs) + + if merged_kv: + kv_flat = [f"{g}.{k}={v}" for g, kv in merged_kv.items() for k, v in kv.items()] + new_cmd.append('--configKeyValues "' + ";".join(kv_flat) + '"') + + # log changes + log_line(logger, f"\n[Executable: {exe}]") + if added: + log_line(logger, f" Added options: {added}") + if overwritten: + log_line(logger, f" Overwritten options: {overwritten}") + if not added and not overwritten: + log_line(logger, f" No changes made to command.") + + return " ".join(new_cmd) + +# CLI: Parse log + blacklist into output.json +def parse_configKeyValues(raw_value): + return parse_configKeyValues_block(raw_value) + +def parse_workflow_config(log_path): + config_params = defaultdict(dict) + executables = {} + + with open(log_path) as f: + for line in f: + line = line.strip() + if not line or line.startswith("#"): + continue + + parsed = parse_command_string(line) + exe = parsed["executable"] + config_groups_used = parsed["configKeyGroups"] + full_opts = parsed["options"] + + for group, kv in parsed["configKeyValues"].items(): + config_params[group].update(kv) + + executables[exe] = { + "configKeyValues": sorted(config_groups_used), + "full": full_opts + } + + return config_params, executables + + +def apply_blacklist(executables, blacklist_cfg): + for exe, data in executables.items(): + full_opts = data["full"] + exe_blacklist = set(blacklist_cfg.get(exe, [])) + total_blacklist = BUILTIN_BLACKLIST.union(exe_blacklist) + + blacklisted = [] + filtered = {} + + for key, val in full_opts.items(): + if key in total_blacklist: + blacklisted.append(key) + else: + filtered[key] = val + + data["blacklisted"] = sorted(blacklisted) + data["filtered"] = filtered + data["full"] = deepcopy(full_opts) # keep original + return executables + +def dpl_option_from_config(config, dpl_workflow, key, section = "filtered", default_value = None): + """ + Utility to extract a DPL option for workflow dpl_workflow from + the configuration dict "config". The configuration is: + - either a flattish JSON produced by older tool parse-async-WorkflowConfig.py + - more structured version produced by o2dpg_dpl_config_tools (this tool) + """ + if "Executables" in config: + # new standard + value = config["Executables"].get(dpl_workflow,{}).get(section,{}).get(key, None) + if value == None: + print (f"Could not lookup key/option {key} from {dpl_workflow}") + value = default_value + return value + else: + # backward compatible versions + dpl_workflow_key = dpl_workflow + '-options' + if dpl_workflow_key in config: + return config.get(dpl_workflow_key, {}).get(key, default_value) + dpl_workflow_key = dpl_workflow_key + if dpl_workflow_key in config: + return config.get(dpl_workflow_key, {}).get(key, default_value) + return default_value + +def main(): + if len(sys.argv) == 4: + log_path = sys.argv[1] + blacklist_path = sys.argv[2] + output_path = sys.argv[3] + + with open(blacklist_path) as f: + blacklist_data = json.load(f) + + config_params, executables = parse_workflow_config(log_path) + executables = apply_blacklist(executables, blacklist_data) + + result = { + "ConfigParams": dict(config_params), + "Executables": executables + } + + with open(output_path, "w") as out: + json.dump(result, out, indent=2) + + print(f"[INFO] Wrote structured config to: {output_path}") + else: + print("Usage:") + print(" CLI parsing: python3 dpl_config_tools.py workflowconfig.log blacklist.json output.json") + print(" Python usage: import and call parse_command_string() or modify_dpl_command()") + + +class TaskFinalizer: + def __init__(self, anchor_config, allow_overwrite=False, logger=None): + self.anchor_config = anchor_config + self.allow_overwrite = allow_overwrite + self.logger = logger + self.final_config = { + "ConfigParams": {}, + "Executables": {} + } + + def __call__(self, cmd_str_or_list, configname = None): + final_cmd = modify_dpl_command(cmd_str_or_list, self.anchor_config.get("Executables",{}), logger=self.logger, configname=configname) + this_config_final = parse_command_string_symmetric(final_cmd, configname) + print (this_config_final) + merge_dicts (self.final_config, this_config_final) + return final_cmd + + def dump_collected_config(self, path): + with open(path, "w") as f: + json.dump(self.final_config, f, indent=2) + + +if __name__ == "__main__": + main() diff --git a/MC/bin/o2dpg_qc_finalization_workflow.py b/MC/bin/o2dpg_qc_finalization_workflow.py index 09d1e7b24..815425968 100755 --- a/MC/bin/o2dpg_qc_finalization_workflow.py +++ b/MC/bin/o2dpg_qc_finalization_workflow.py @@ -14,6 +14,7 @@ import argparse from os import environ, mkdir from os.path import join, dirname, isdir +import re sys.path.append(join(dirname(__file__), '.', 'o2dpg_workflow_utils')) @@ -33,7 +34,7 @@ def QC_finalize_name(name): return name + "_finalize" qcdir = "QC" -def include_all_QC_finalization(ntimeframes, standalone, run, productionTag): +def include_all_QC_finalization(ntimeframes, standalone, run, productionTag, conditionDB, qcdbHost, beamType): stages = [] @@ -45,12 +46,20 @@ def add_QC_finalization(taskName, qcConfigPath, needs=None): if standalone == True: needs = [] elif needs == None: - needs = [taskName + '_local' + str(tf) for tf in range(1, ntimeframes + 1)] + needs = [taskName + '_local_' + str(tf) for tf in range(1, ntimeframes + 1)] task = createTask(name=QC_finalize_name(taskName), needs=needs, cwd=qcdir, lab=["QC"], cpu=1, mem='2000') - task['cmd'] = f'o2-qc --config {qcConfigPath} --remote-batch {taskName}.root' + \ - f' --override-values "qc.config.Activity.number={run};qc.config.Activity.periodName={productionTag}"' + \ + def remove_json_prefix(path): + return re.sub(r'^json://', '', path) + + configFilePathOnDisk = remove_json_prefix(qcConfigPath) + # we check if the configFilePath actually exists in the currently loaded software. Otherwise we exit immediately and gracefully + task['cmd'] = ' if [ -f ' + configFilePathOnDisk + ' ]; then { ' + task['cmd'] += f'o2-qc --config {qcConfigPath} --remote-batch {taskName}.root' + \ + f' --override-values "qc.config.database.host={qcdbHost};qc.config.Activity.number={run};qc.config.Activity.type=PHYSICS;qc.config.Activity.periodName={productionTag};qc.config.Activity.beamType={beamType};qc.config.conditionDB.url={conditionDB}"' + \ ' ' + getDPL_global_options() + task['cmd'] += ' ;} else { echo "Task ' + taskName + ' not performed due to config file not found "; } fi' + stages.append(task) ## Adds a postprocessing QC workflow @@ -66,29 +75,50 @@ def add_QC_postprocessing(taskName, qcConfigPath, needs, runSpecific, prodSpecif task = createTask(name=taskName, needs=needs, cwd=qcdir, lab=["QC"], cpu=1, mem='2000') overrideValues = '--override-values "' overrideValues += f'qc.config.Activity.number={run};' if runSpecific else 'qc.config.Activity.number=0;' - overrideValues += f'qc.config.Activity.periodName={productionTag}"' if prodSpecific else 'qc.config.Activity.periodName="' + overrideValues += f'qc.config.Activity.type=PHYSICS;' + overrideValues += f'qc.config.Activity.periodName={productionTag};' if prodSpecific else 'qc.config.Activity.periodName=;' + overrideValues += f'qc.config.Activity.beamType={beamType};' + overrideValues += f'qc.config.database.host={qcdbHost};qc.config.conditionDB.url={conditionDB}"' task['cmd'] = f'o2-qc --config {qcConfigPath} ' + \ overrideValues + ' ' + getDPL_global_options() stages.append(task) ## The list of remote-batch workflows (reading the merged QC tasks results, applying Checks, uploading them to QCDB) MFTDigitsQCneeds = [] - for flp in range(5): - MFTDigitsQCneeds.extend(['mftDigitsQC'+str(flp)+'_local'+str(tf) for tf in range(1, ntimeframes + 1)]) - add_QC_finalization('mftDigitsQC', 'json://${O2DPG_ROOT}/MC/config/QC/json/mft-digits-0.json', MFTDigitsQCneeds) - add_QC_finalization('mftClustersQC', 'json://${O2DPG_ROOT}/MC/config/QC/json/mft-clusters.json') - add_QC_finalization('mftTracksQC', 'json://${O2DPG_ROOT}/MC/config/QC/json/mft-tracks.json') - add_QC_finalization('emcCellQC', 'json://${O2DPG_ROOT}/MC/config/QC/json/emc-cell-task.json') - #add_QC_finalization('tpcTrackingQC', 'json://${O2DPG_ROOT}/MC/config/QC/json/tpc-qc-tracking-direct.json') + if isActive('MFT'): + for flp in range(5): + MFTDigitsQCneeds.extend(['mftDigitsQC'+str(flp)+'_local'+str(tf) for tf in range(1, ntimeframes + 1)]) + add_QC_finalization('mftDigitsQC', 'json://${O2DPG_ROOT}/MC/config/QC/json/mft-digits-0.json', MFTDigitsQCneeds) + add_QC_finalization('mftClustersQC', 'json://${O2DPG_ROOT}/MC/config/QC/json/mft-clusters.json') + add_QC_finalization('mftTracksQC', 'json://${O2DPG_ROOT}/MC/config/QC/json/mft-tracks.json') + add_QC_finalization('mftMCTracksQC', 'json://${O2DPG_ROOT}/MC/config/QC/json/mft-tracks-mc.json') + if isActive('EMC'): + add_QC_finalization('emcRecoQC', 'json://${O2DPG_ROOT}/MC/config/QC/json/emc-reco-tasks.json') + add_QC_finalization('emcBCQC', 'json://${O2DPG_ROOT}/MC/config/QC/json/emc-reco-tasks.json') add_QC_finalization('tpcStandardQC', 'json://${O2DPG_ROOT}/MC/config/QC/json/tpc-qc-standard-direct.json') - add_QC_finalization('trdDigitsQC', 'json://${O2DPG_ROOT}/MC/config/QC/json/trd-digits-task.json') + if isActive('TRD'): + add_QC_finalization('trdDigitsQC', 'json://${O2DPG_ROOT}/MC/config/QC/json/trd-standalone-task.json') + add_QC_finalization('trdTrackingQC', 'json://${O2DPG_ROOT}/MC/config/QC/json/trd-tracking-task.json') add_QC_finalization('vertexQC', 'json://${O2DPG_ROOT}/MC/config/QC/json/vertexing-qc-direct-mc.json') add_QC_finalization('ITSTPCmatchQC', 'json://${O2DPG_ROOT}/MC/config/QC/json/ITSTPCmatchedTracks_direct_MC.json') add_QC_finalization('TOFMatchQC', 'json://${O2DPG_ROOT}/MC/config/QC/json/tofMatchedTracks_ITSTPCTOF_TPCTOF_direct_MC.json') add_QC_finalization('tofDigitsQC', 'json://${O2DPG_ROOT}/MC/config/QC/json/tofdigits.json') add_QC_finalization('TOFMatchWithTRDQC', 'json://${O2DPG_ROOT}/MC/config/QC/json/tofMatchedTracks_AllTypes_direct_MC.json') - add_QC_finalization('ITSTrackSimTask', 'json://${O2DPG_ROOT}/MC/config/QC/json/its-mc-tracks-qc.json') - add_QC_finalization('ITSTracksClusters', 'json://${O2DPG_ROOT}/MC/config/QC/json/its-clusters-tracks-qc.json') + add_QC_finalization('ITSTrackSimTaskQC', 'json://${O2DPG_ROOT}/MC/config/QC/json/its-mc-tracks-qc.json') + add_QC_finalization('ITSTracksClustersQC', 'json://${O2DPG_ROOT}/MC/config/QC/json/its-clusters-tracks-qc.json') + if isActive('MID'): + add_QC_finalization('MIDTaskQC', 'json://${O2DPG_ROOT}/MC/config/QC/json/mid-task.json') + if isActive('MCH'): + add_QC_finalization('MCHDigitsTaskQC', 'json://${O2DPG_ROOT}/MC/config/QC/json/mch-digits-task.json') + add_QC_finalization('MCHErrorsTaskQC', 'json://${O2DPG_ROOT}/MC/config/QC/json/mch-errors-task.json') + add_QC_finalization('MCHRecoTaskQC', 'json://${O2DPG_ROOT}/MC/config/QC/json/mch-reco-task.json') + add_QC_finalization('MCHTracksTaskQC', 'json://${O2DPG_ROOT}/MC/config/QC/json/mch-tracks-task.json') + if isActive('MCH') and isActive('MID'): + add_QC_finalization('MCHMIDTracksTaskQC', 'json://${O2DPG_ROOT}/MC/config/QC/json/mchmid-tracks-task.json') + if isActive('MCH') and isActive('MID') and isActive('MFT'): + add_QC_finalization('MUONTracksMFTTaskQC', 'json://${O2DPG_ROOT}/MC/config/QC/json/mftmchmid-tracks-task.json') + elif isActive('MCH') and isActive('MFT'): + add_QC_finalization('MCHMFTTaskQC', 'json://${O2DPG_ROOT}/MC/config/QC/json/mftmch-tracks-task.json') if isActive('FT0') and isActive('TRD'): add_QC_finalization('tofft0PIDQC', 'json://${O2DPG_ROOT}/MC/config/QC/json/pidft0tof.json') elif isActive('FT0'): @@ -98,12 +128,19 @@ def add_QC_postprocessing(taskName, qcConfigPath, needs, runSpecific, prodSpecif else: add_QC_finalization('tofPIDQC', 'json://${O2DPG_ROOT}/MC/config/QC/json/pidtofNoTRD.json') add_QC_finalization('RecPointsQC', 'json://${O2DPG_ROOT}/MC/config/QC/json/ft0-reconstruction-config.json') - add_QC_finalization('CPVDigitsQC', 'json://${O2DPG_ROOT}/MC/config/QC/json/cpv-digits-task.json') - add_QC_finalization('CPVClustersQC', 'json://${O2DPG_ROOT}/MC/config/QC/json/cpv-clusters-task.json') - add_QC_finalization('PHSCellsClustersQC', 'json://${O2DPG_ROOT}/MC/config/QC/json/phs-cells-clusters-task.json') + if isActive('FV0'): + add_QC_finalization('FV0DigitsQC', 'json://${O2DPG_ROOT}/MC/config/QC/json/fv0-digits.json') + if isActive('FDD'): + add_QC_finalization('FDDRecPointsQC', 'json://${O2DPG_ROOT}/MC/config/QC/json/fdd-recpoints.json') + if isActive('CPV'): + add_QC_finalization('CPVDigitsQC', 'json://${O2DPG_ROOT}/MC/config/QC/json/cpv-digits-task.json') + add_QC_finalization('CPVClustersQC', 'json://${O2DPG_ROOT}/MC/config/QC/json/cpv-clusters-task.json') + if isActive('PHS'): + add_QC_finalization('PHSCellsClustersQC', 'json://${O2DPG_ROOT}/MC/config/QC/json/phs-cells-clusters-task.json') # The list of QC Post-processing workflows - add_QC_postprocessing('tofTrendingHits', 'json://${O2DPG_ROOT}/MC/config/QC/json/tof-trending-hits.json', [QC_finalize_name('tofDigitsQC')], runSpecific=False, prodSpecific=True) + if isActive('TOF'): + add_QC_postprocessing('tofTrendingHits', 'json://${O2DPG_ROOT}/MC/config/QC/json/tof-trending-hits.json', [QC_finalize_name('tofDigitsQC')], runSpecific=False, prodSpecific=True) return stages @@ -116,7 +153,9 @@ def main() -> int: parser.add_argument('-o',help='output workflow file', default='workflow.json') parser.add_argument('-run',help="Run number for this MC", default=300000) parser.add_argument('-productionTag',help="Production tag for this MC", default='unknown') - + parser.add_argument('-conditionDB',help="CCDB url for QC workflows", default='http://alice-ccdb.cern.ch') + parser.add_argument('-qcdbHost',help="QCDB url for QC object uploading", default='http://ali-qcdbmc-gpn.cern.ch:8083') + parser.add_argument('-beamType',help="Collision system, e.g. PbPb, pp", default='') args = parser.parse_args() print (args) @@ -138,7 +177,7 @@ def main() -> int: mkdir(qcdir) workflow={} - workflow['stages'] = include_all_QC_finalization(ntimeframes=1, standalone=True, run=args.run, productionTag=args.productionTag) + workflow['stages'] = include_all_QC_finalization(ntimeframes=1, standalone=True, run=args.run, productionTag=args.productionTag, conditionDB=args.conditionDB, qcdbHost=args.qcdbHost, beamType=args.beamType) dump_workflow(workflow["stages"], args.o) diff --git a/MC/bin/o2dpg_sim_config.py b/MC/bin/o2dpg_sim_config.py index cb9987cf9..0ffc69e9c 100755 --- a/MC/bin/o2dpg_sim_config.py +++ b/MC/bin/o2dpg_sim_config.py @@ -1,3 +1,8 @@ +from functools import lru_cache +import subprocess +import re +import os + def create_sim_config(args): # creates a generic simulation config # based on arguments args (run number, energy, ...) originally passed @@ -23,9 +28,19 @@ def add(cfg, flatconfig): if 302000 <= int(args.run) and int(args.run) < 309999: add(config, {"ITSAlpideParam.roFrameLengthInBC" : 198}) # ITS reco settings - add(config, {"ITSVertexerParam.phiCut" : 0.5, - "ITSVertexerParam.clusterContributorsCut" : 3, - "ITSVertexerParam.tanLambdaCut" : 0.2}) + add(config, {"ITSVertexerParam.phiCut": 0.4, + "ITSVertexerParam.tanLambdaCut": 0.17, + "ITSVertexerParam.pairCut": 0.0317563, + "ITSVertexerParam.clusterCut": 0.6640964, + "ITSVertexerParam.coarseZWindow": 0.2049018, + "ITSVertexerParam.seedDedupZCut": 0.0711793, + "ITSVertexerParam.refitDedupZCut": 0.0680009, + "ITSVertexerParam.duplicateZCut": 0.1582193, + "ITSVertexerParam.finalSelectionZCut": 0.1081465, + "ITSVertexerParam.duplicateDistance2Cut": 0.0117033, + "ITSVertexerParam.clusterContributorsCut": 2, + "ITSVertexerParam.seedMemberRadiusZ": 0, + "ITSVertexerParam.nSigmaCut": 0.032841}) # primary vertexing settings if 301000 <= int(args.run) and int(args.run) <= 301999: add(config, {"pvertexer.acceptableScale2" : 9, @@ -81,10 +96,197 @@ def add(cfg, flatconfig): if args.fwdmatching_cut_4_param == True: add(config, {"FwdMatching.cutFcn" : "cut3SigmaXYAngles"}) - # deal with larger combinatorics - if args.col == "PbPb" or (args.embedding and args.colBkg == "PbPb"): - add(config, {"ITSCATrackerParam.trackletsPerClusterLimit": 20, - "ITSCATrackerParam.cellsPerClusterLimit": 20, - "ITSVertexerParam.lowMultBeamDistCut": "0."}) + # FIT digitizer settings + #2023 pp + if 534125 <= int(args.run) and int(args.run) <= 543113: + if COLTYPEIR == "pp": + # central and semicentral FT0 thresholds + add(config, {"FT0DigParam.mtrg_central_trh": "40", "FT0DigParam.mtrg_semicentral_trh": "20"}) + # FV0 trigger settings + add(config, {"FV0DigParam.NchannelsLevel": "2", "FV0DigParam.InnerChargeLevel": "4", "FV0DigParam.OuterChargeLevel": "4", "FV0DigParam.ChargeLevel": "8"}) + # 2023 PbPb + if 543437 <= int(args.run) and int(args.run) <= 545367: + add(config, {"FT0DigParam.mMip_in_V": "7", "FT0DigParam.mMV_2_Nchannels": "2", "FT0DigParam.mMV_2_NchannelsInverse": "0.5"}) + add(config, {"FV0DigParam.adcChannelsPerMip": "4"}) + # central and semicentral FT0 thresholds + add(config, {"FT0DigParam.mtrg_central_trh": "1433", "FT0DigParam.mtrg_semicentral_trh": "35"}) + # FV0 trigger settings + add(config, {"FV0DigParam.NchannelsLevel": "2", "FV0DigParam.InnerChargeLevel": "4", "FV0DigParam.OuterChargeLevel": "4", "FV0DigParam.ChargeLevel": "1080"}) + # 2024 + # first and last run of 2024 + if 546088 <= int(args.run) and int(args.run) <= 560623: + # 14 ADC channels / MIP for FT0 + add(config, {"FT0DigParam.mMip_in_V": "7", "FT0DigParam.mMV_2_Nchannels": "2", "FT0DigParam.mMV_2_NchannelsInverse": "0.5"}) + # 15 ADC channels / MIP for FV0 + add(config, {"FV0DigParam.adcChannelsPerMip": "15"}) + if COLTYPEIR == "PbPb": + # 4 ADC channels / MIP + add(config, {"FV0DigParam.adcChannelsPerMip": "4"}) + # central and semicentral FT0 thresholds + add(config, {"FT0DigParam.mtrg_central_trh": "1433", "FT0DigParam.mtrg_semicentral_trh": "35"}) + # FV0 trigger settings + add(config, {"FV0DigParam.NchannelsLevel": "2", "FV0DigParam.InnerChargeLevel": "4", "FV0DigParam.OuterChargeLevel": "4", "FV0DigParam.ChargeLevel": "1080"}) + if COLTYPEIR == "pp": + # central and semicentral FT0 thresholds + add(config, {"FT0DigParam.mtrg_central_trh": "40", "FT0DigParam.mtrg_semicentral_trh": "20"}) + # FV0 trigger settings + add(config, {"FV0DigParam.NchannelsLevel": "2", "FV0DigParam.InnerChargeLevel": "4", "FV0DigParam.OuterChargeLevel": "4", "FV0DigParam.ChargeLevel": "8"}) + # 2025 + # first and last run of 2025 + if 562260 <= int(args.run) and int(args.run) <= 568721: + # 14 ADC channels / MIP for FT0 + add(config, {"FT0DigParam.mMip_in_V": "7", "FT0DigParam.mMV_2_Nchannels": "2", "FT0DigParam.mMV_2_NchannelsInverse": "0.5"}) + if COLTYPEIR == "PbPb": + # 4 ADC channels / MIP + add(config, {"FV0DigParam.adcChannelsPerMip": "4"}) + # central and semicentral FT0 thresholds + add(config, {"FT0DigParam.mtrg_central_trh": "1433", "FT0DigParam.mtrg_semicentral_trh": "35"}) + # FV0 trigger settings + add(config, {"FV0DigParam.NchannelsLevel": "2", "FV0DigParam.InnerChargeLevel": "4", "FV0DigParam.OuterChargeLevel": "4", "FV0DigParam.ChargeLevel": "1080"}) + if COLTYPEIR == "pp" or COLTYPEIR == "OO" or COLTYPEIR == "NeNe" or COLTYPEIR == "pO": + # central and semicentral FT0 thresholds + add(config, {"FT0DigParam.mtrg_central_trh": "40", "FT0DigParam.mtrg_semicentral_trh": "20"}) + # FV0 trigger settings + add(config, {"FV0DigParam.NchannelsLevel": "2", "FV0DigParam.InnerChargeLevel": "4", "FV0DigParam.OuterChargeLevel": "4", "FV0DigParam.ChargeLevel": "8"}) + if COLTYPEIR == "pp": + # 15 ADC channels / MIP + add(config, {"FV0DigParam.adcChannelsPerMip": "15"}) + if COLTYPEIR == "OO" or COLTYPEIR == "NeNe" or COLTYPEIR == "pO": + # 11 ADC channels / MIP + add(config, {"FV0DigParam.adcChannelsPerMip": "11"}) + + return config + + +def create_geant_config(args, externalConfigString): + # creates generic transport simulation config key values + # based on arguments args (run number, energy, ...) originally passed + # to o2dpg_sim_workflow.py + # + # returns a dictionary of mainkey -> dictionary of subkey : values + config = {} + def add(cfg, flatconfig): + for entry in flatconfig: + mk = entry.split(".")[0] + sk = entry.split(".")[1] + d = cfg.get(mk,{}) + d[sk] = flatconfig[entry] + cfg[mk] = d + + # ----- special setting for hepmc generator ----- + if args.gen == "hepmc": + eventSkipPresent = config.get("HepMC",{}).get("eventsToSkip") + if eventSkipPresent == None: + # add it + add(config, {"HepMC.eventsToSkip" : '${HEPMCEVENTSKIP:-0}'}) + + # ----- add default settings ----- + + add(config, {"MFTBase.buildAlignment" : "true"}) + + # Keep the ZEM calorimeters, drop the +-113 m beam line that makes the ZDC + # expensive to transport. --with-ZDC asks for the whole thing, and an explicit + # "--skipModules ZDC" still removes the module altogether. + if not getattr(args, "with_ZDC", False) and "ZDC" not in getattr(args, "skipModules", []): + add(config, {"ZDCSimParam.buildBeamLine" : "false"}) + add(config, {"GenTPCLoopers.colsys" : args.col}) + + # ----- apply external overwrites from command line ------- + for keyval in externalConfigString.split(";"): + if len(keyval) > 0: + key, val = keyval.split("=") + add(config, {key : val}) return config + +def constructConfigKeyArg(config): + # flattens dictionary constructed in create_geant_config + # and constructs the --configKeyValues options for simulation + if len(config) == 0: + return '' + arg = '--configKeyValues "' + for mainkey in config: + for subkey in config[mainkey]: + arg = arg + mainkey + '.' + subkey + '=' + config[mainkey][subkey] + ';' + arg = arg + '"' + return arg + +def load_env_file(env_file): + """Transform an environment file generated with 'export > env.txt' into a python dictionary.""" + env_vars = {} + with open(env_file, "r") as f: + for line in f: + line = line.strip() + + # Ignore empty lines or comments + if not line or line.startswith("#"): + continue + + # Remove 'declare -x ' if present + if line.startswith("declare -x "): + line = line.replace("declare -x ", "", 1) + + # Handle case: "FOO" without "=" (assign empty string) + if "=" not in line: + key, value = line.strip(), "" + else: + key, value = line.split("=", 1) + value = value.strip('"') # Remove surrounding quotes if present + + env_vars[key.strip()] = value + return env_vars + +# some functions to determine dpl option availability on the fly +def parse_dpl_help_output(executable, envfile): + """Parses the --help full output of an executable to extract available options.""" + try: + env = os.environ.copy() + if envfile != None: + print ("Loading from alternative environment") + env = load_env_file(envfile) + + # the DEVNULL is important for o2-dpl workflows not to hang on non-interactive missing tty environments + # it is cleaner that the echo | trick + output = subprocess.check_output([executable, "--help", "full"], env=env, text=True, stdin=subprocess.DEVNULL, timeout = 100) + except subprocess.CalledProcessError: + return {}, {} + + option_pattern = re.compile(r"(\-\-[\w\-]+)") + sections = {} + inverse_lookup = {} + current_section = "global" + + for line in output.split("\n"): + section_match = re.match(r"^([A-Za-z\s]+):$", line.strip()) + if section_match: + current_section = section_match.group(1).strip() + sections[current_section] = [] + continue + + option_match = option_pattern.findall(line) + if option_match: + for option in option_match: + sections.setdefault(current_section, []).append(option) + inverse_lookup.setdefault(option, []).append(current_section) + + return sections, inverse_lookup + +@lru_cache(maxsize=10) +def get_dpl_options_for_executable(executable, envfile): + """Returns available options and inverse lookup for a given executable, caching the result.""" + return parse_dpl_help_output(executable, envfile) + +def option_if_available(executable, option, envfile = None): + """Checks if an option is available for a given executable and returns it as a string. Otherwise empty string""" + _, inverse_lookup = get_dpl_options_for_executable(executable, envfile) + return ' ' + option if option in inverse_lookup else '' + + +# helper function to overwrite some values; prints out stuff that it changes +def overwrite_config(config, mainkey, subkey, value): + oldvalue = config.get(mainkey,{}).get(subkey, None) + print (f"Overwriting {mainkey}.{subkey}: {'None' if oldvalue is None else oldvalue} -> {value}") + if mainkey not in config: + # Initialize the main key in the dictionary if it does not already exist + config[mainkey] = {} + config[mainkey][subkey] = value diff --git a/MC/bin/o2dpg_sim_workflow.py b/MC/bin/o2dpg_sim_workflow.py index b6c7e0152..89d98cede 100755 --- a/MC/bin/o2dpg_sim_workflow.py +++ b/MC/bin/o2dpg_sim_workflow.py @@ -5,27 +5,34 @@ # It aims to handle the different MC possible configurations # It just creates a workflow.json txt file, to execute the workflow one must execute right after # ${O2DPG_ROOT}/MC/bin/o2_dpg_workflow_runner.py -f workflow.json +# The tests covering this script are described in test/README.md. # # Execution examples: # - pp PYTHIA jets, 2 events, triggered on high pT decay photons on all barrel calorimeters acceptance, eCMS 13 TeV -# ./o2dpg_sim_workflow.py -e TGeant3 -ns 2 -j 8 -tf 1 -mod "--skipModules ZDC" -col pp -eCM 13000 \ +# ./o2dpg_sim_workflow.py -e TGeant3 -ns 2 -j 8 -tf 1 -col pp -eCM 13000 \ # -proc "jets" -ptHatBin 3 \ # -trigger "external" -ini "\$O2DPG_ROOT/MC/config/PWGGAJE/ini/trigger_decay_gamma_allcalo_TrigPt3_5.ini" # # - pp PYTHIA ccbar events embedded into heavy-ion environment, 2 PYTHIA events into 1 bkg event, beams energy 2.510 -# ./o2dpg_sim_workflow.py -e TGeant3 -nb 1 -ns 2 -j 8 -tf 1 -mod "--skipModules ZDC" \ +# ./o2dpg_sim_workflow.py -e TGeant3 -nb 1 -ns 2 -j 8 -tf 1 \ # -col pp -eA 2.510 -proc "ccbar" --embedding # +# TODO: +# - pickup the final list of configKey values from the anchorConfig + import sys import importlib.util import argparse -from os import environ, mkdir, getcwd -from os.path import join, dirname, isdir +from os import environ, mkdir +from os.path import join, dirname, isdir, isabs, isfile import random import json import itertools +import math import requests, re +from functools import lru_cache + pandas_available = True try: import pandas as pd @@ -34,9 +41,13 @@ sys.path.append(join(dirname(__file__), '.', 'o2dpg_workflow_utils')) -from o2dpg_workflow_utils import createTask, createGlobalInitTask, dump_workflow, adjust_RECO_environment, isActive, activate_detector +from o2dpg_workflow_utils import createTask, createGlobalInitTask, dump_workflow, adjust_RECO_environment, isActive, activate_detector, deactivate_detector, compute_n_workers, merge_dicts from o2dpg_qc_finalization_workflow import include_all_QC_finalization -from o2dpg_sim_config import create_sim_config +from o2dpg_sim_config import create_sim_config, create_geant_config, constructConfigKeyArg, option_if_available, overwrite_config +from o2dpg_dpl_config_tools import dpl_option_from_config, TaskFinalizer, quote_if_needed + +# for some JAliEn interaction +from alienpy.alien import JAlien parser = argparse.ArgumentParser(description='Create an ALICE (Run3) MC simulation workflow') @@ -48,16 +59,22 @@ # - some external tool should sample it within # - we can also sample it ourselfs here parser.add_argument('--timestamp', type=int, help="Anchoring timestamp (defaults to now)", default=-1) +parser.add_argument('--conditionDB',help="CCDB url for QC workflows", default='http://alice-ccdb.cern.ch') +parser.add_argument('--qcdbHost',help="QCDB url for QC object uploading", default='http://ali-qcdbmc-gpn.cern.ch:8083') parser.add_argument('--condition-not-after', type=int, help="only consider CCDB objects not created after this timestamp (for TimeMachine)", default=3385078236000) -parser.add_argument('--orbitsPerTF', type=int, help="Timeframe size in number of LHC orbits", default=128) -parser.add_argument('--anchor-config',help="JSON file to contextualise workflow with external configs (config values etc.) for instance comping from data reco workflows.", default='') +parser.add_argument('--orbitsPerTF', type=int, help="Timeframe size in number of LHC orbits", default=32) +parser.add_argument('--anchor-config',help="JSON file to contextualise workflow with external configs (config values etc.) for instance coming from data reco workflows.", default='') +parser.add_argument('--overwrite-config',help="extra JSON file with configs (config values etc.) overwriting defaults or the config coming from --anchor-config", default='') parser.add_argument('--dump-config',help="Dump JSON file with all settings used in workflow", default='user_config.json') -parser.add_argument('-ns',help='number of signal events / timeframe', default=20) +parser.add_argument('-ns',type=int,help='number of signal events / timeframe', default=20) parser.add_argument('-gen',help='generator: pythia8, extgen', default='') -parser.add_argument('-proc',help='process type: inel, dirgamma, jets, ccbar, ...', default='') +parser.add_argument('-proc',help='process type: inel, dirgamma, jets, ccbar, ...', default='none') parser.add_argument('-trigger',help='event selection: particle, external', default='') parser.add_argument('-ini',help='generator init parameters file (full paths required), for example: ${O2DPG_ROOT}/MC/config/PWGHF/ini/GeneratorHF.ini', default='') -parser.add_argument('-confKey',help='generator or trigger configuration key values, for example: "GeneratorPythia8.config=pythia8.cfg;A.x=y"', default='') +parser.add_argument('-confKey',help='o2sim, generator or trigger configuration key values, for example: "GeneratorPythia8.config=pythia8.cfg;A.x=y"', default='') +parser.add_argument('--detectorList',help='pick which version of ALICE should be simulated', default='ALICE2') +parser.add_argument('--readoutDets',help='comma separated string of detectors readout (does not modify material budget - only hit creation)', default='all') +parser.add_argument('--make-evtpool', help='Generate workflow for event pool creation.', action='store_true') parser.add_argument('-interactionRate',help='Interaction rate, used in digitization', default=-1) parser.add_argument('-bcPatternFile',help='Bunch crossing pattern file, used in digitization (a file name or "ccdb")', default='') @@ -67,7 +84,9 @@ parser.add_argument('-eB',help='Beam B energy', default=-1) parser.add_argument('-col',help='collision system: pp, PbPb, pPb, Pbp, ..., in case of embedding collision system of signal', default='pp') parser.add_argument('-field',help='L3 field rounded to kGauss, allowed values: +-2,+-5 and 0; +-5U for uniform field; or "ccdb" to take from conditions database', default='ccdb') -parser.add_argument('--with-qed',action='store_true', help='Enable QED background contribution (for PbPb always included)') +qed_group = parser.add_mutually_exclusive_group() +qed_group.add_argument('--with-qed', dest='with_qed', action='store_true', default=False, help='Force-enable QED background contribution (on by default for same-species heavy-ion collisions such as Pb-Pb)') +qed_group.add_argument('--no-qed', dest='no_qed', action='store_true', default=False, help='Force-disable QED background contribution, even for same-species heavy-ion collisions where it is enabled by default') parser.add_argument('-ptHatMin',help='pT hard minimum when no bin requested', default=0) parser.add_argument('-ptHatMax',help='pT hard maximum when no bin requested', default=-1) @@ -81,14 +100,19 @@ parser.add_argument('-iniBkg',help='embedding background generator init parameters file (full path required)', default='${O2DPG_ROOT}/MC/config/common/ini/basic.ini') parser.add_argument('-confKeyBkg',help='embedding background configuration key values, for example: "GeneratorPythia8.config=pythia8bkg.cfg"', default='') parser.add_argument('-colBkg',help='embedding background collision system', default='PbPb') +parser.add_argument('-confKeyQED',help='Config key parameters influencing the QED background simulator', default='') -parser.add_argument('-e',help='simengine', default='TGeant4') -parser.add_argument('-tf',help='number of timeframes', default=2) +parser.add_argument('-e',help='simengine', default='TGeant4', choices=['TGeant4', 'TGeant3', 'TFluka']) +parser.add_argument('-tf',type=int,help='number of timeframes', default=2) parser.add_argument('--production-offset',help='Offset determining bunch-crossing ' + ' range within a (GRID) production. This number sets first orbit to ' + 'Offset x Number of TimeFrames x OrbitsPerTimeframe (up for further sophistication)', default=0) -parser.add_argument('-j',help='number of workers (if applicable)', default=8, type=int) -parser.add_argument('-mod',help='Active modules (deprecated)', default='--skipModules ZDC') +parser.add_argument('-j', '--n-workers', dest='n_workers', help='number of workers (if applicable)', default=8, type=int) +parser.add_argument('--force-n-workers', dest='force_n_workers', action='store_true', help='by default, number of workers is re-computed ' + 'for given interaction rate; ' + 'pass this to avoid that') +parser.add_argument('--skipModules',nargs="*", help="List of modules to skip in geometry budget (and therefore processing)", default=[]) +parser.add_argument('--skipReadout',nargs="*", help="List of modules to take out from readout", default=[""]) parser.add_argument('--with-ZDC', action='store_true', help='Enable ZDC in workflow') parser.add_argument('-seed',help='random seed number', default=None) parser.add_argument('-o',help='output workflow file', default='workflow.json') @@ -103,19 +127,25 @@ # power features (for playing) --> does not appear in help message # help='Treat smaller sensors in a single digitization') -parser.add_argument('--pregenCollContext', action='store_true', help=argparse.SUPPRESS) # the mode where we pregenerate the collision context for each timeframe (experimental) +parser.add_argument('--pregenCollContext', action='store_true', help=argparse.SUPPRESS) # Now the default, giving this option or not makes not difference. We keep it for backward compatibility +parser.add_argument('--data-anchoring', type=str, default='', help="Take collision contexts (from data) stored in this path") +parser.add_argument('--aod-output-folder', type=str, default='', help="Force this AOD folder in the AOD producer") +parser.add_argument('--aod-parent-file', type=str, default='', help="Link this as parent file in the AOD") parser.add_argument('--no-combine-smaller-digi', action='store_true', help=argparse.SUPPRESS) parser.add_argument('--no-combine-dpl-devices', action='store_true', help=argparse.SUPPRESS) parser.add_argument('--no-mc-labels', action='store_true', default=False, help=argparse.SUPPRESS) parser.add_argument('--no-tpc-digitchunking', action='store_true', help=argparse.SUPPRESS) -parser.add_argument('--with-strangeness-tracking', action='store_true', default=False, help="Enable strangeness tracking") +parser.add_argument('--no-strangeness-tracking', action='store_true', default=False, help="Disable strangeness tracking") parser.add_argument('--combine-tpc-clusterization', action='store_true', help=argparse.SUPPRESS) #<--- useful for small productions (pp, low interaction rate, small number of events) -parser.add_argument('--first-orbit', default=0, type=int, help=argparse.SUPPRESS) # to set the first orbit number of the run for HBFUtils (only used when anchoring) +parser.add_argument('--first-orbit', default=256, type=int, help=argparse.SUPPRESS) # to set the first orbit number of the run for HBFUtils (only used when anchoring); default 256 for convenience to allow for some orbits-early # (consider doing this rather in O2 digitization code directly) +parser.add_argument('--orbits-early', default=1, type=float, help=argparse.SUPPRESS) # number of orbits to start simulating earlier + # to reduce start of timeframe effects in MC --> affects collision context parser.add_argument('--sor', default=-1, type=int, help=argparse.SUPPRESS) # may pass start of run with this (otherwise it is autodetermined from run number) parser.add_argument('--run-anchored', action='store_true', help=argparse.SUPPRESS) parser.add_argument('--alternative-reco-software', default="", help=argparse.SUPPRESS) # power feature to set CVFMS alienv software version for reco steps (different from default) -parser.add_argument('--dpl-child-driver', default="", help="Child driver to use in DPL processes (export mode)") +parser.add_argument('--dpl-child-driver', default="", help="Child driver to use in DPL processes (expert mode)") +parser.add_argument('--event-gen-mode', choices=['separated', 'integrated'], default='separated', help="Whether event generation is done before (separated) or within detector simulation (integrated).") # QC related arguments parser.add_argument('--include-qc', '--include-full-qc', action='store_true', help='includes QC in the workflow, both per-tf processing and finalization') @@ -129,6 +159,10 @@ parser.add_argument('--mft-reco-full', action='store_true', help='enables complete mft reco instead of simplified misaligned version') parser.add_argument('--mft-assessment-full', action='store_true', help='enables complete assessment of mft reco') +# TPC options +parser.add_argument('--tpc-distortion-type', default=0, type=int, help='Simulate distortions in the TPC (0=no distortions, 1=distortions without scaling, 2=distortions with CTP scaling)') +parser.add_argument('--tpc-corrmap-lumi-mode', default=2, type=int, help='TPC corrections mode (0=linear, 1=derivative, 2=derivative for special MC maps') +parser.add_argument('--ctp-scaler', default=0, type=float, help='CTP raw scaler value used for distortion simulation') # Global Forward reconstruction configuration parser.add_argument('--fwdmatching-assessment-full', action='store_true', help='enables complete assessment of global forward reco') parser.add_argument('--fwdmatching-4-param', action='store_true', help='excludes q/pt from matching parameters') @@ -145,6 +179,7 @@ O2_ROOT=environ.get('O2_ROOT') QUALITYCONTROL_ROOT=environ.get('QUALITYCONTROL_ROOT') O2PHYSICS_ROOT=environ.get('O2PHYSICS_ROOT') +ccdbRemap = environ.get('ALIEN_JDL_REMAPPINGS') if O2DPG_ROOT == None: print('Error: This needs O2DPG loaded') @@ -178,13 +213,70 @@ def load_external_config(configfile): return config anchorConfig = {} +anchorConfig_generic = { "ConfigParams": create_sim_config(args) } if args.anchor_config != '': print ("** Using external config **") anchorConfig = load_external_config(args.anchor_config) + # adjust the anchorConfig with keys from the generic config, not mentioned in the external config + # (useful for instance for digitization parameters or others not usually mentioned in async reco) + for key in anchorConfig_generic["ConfigParams"]: + if not key in anchorConfig["ConfigParams"]: + print (f"Transcribing key {key} from generic config into final config") + anchorConfig["ConfigParams"][key] = anchorConfig_generic["ConfigParams"][key] + else: # we load a generic config print ("** Using generic config **") - anchorConfig = create_sim_config(args) + anchorConfig = anchorConfig_generic +# we apply additional external user choices for the configuration +# this will overwrite config from earlier stages +if args.overwrite_config != '': + # apply final JSON overwrite + config_overwrite = load_external_config(args.overwrite_config) + # let's make sure the configs follow the same format + if ("ConfigParams" in anchorConfig) != ("ConfigParams" in config_overwrite): + print ("Error: overwrite config not following same format as base config; Cannot merge") + exit (1) + + # merge the dictionaries into anchorConfig, the latter takes precedence + merge_dicts(anchorConfig, config_overwrite) + +async_envfile = 'env_async.env' if environ.get('ALIEN_JDL_O2DPG_ASYNC_RECO_TAG') is not None else None + +# Check if either ITS or MFT are in the staggered mode (either from the async reco workflow or imposed externally). +# Note thas some QC tracks at the moment need to be suppressed in this mode +staggITS = ' --enable-its-staggering ' if environ.get('ALIEN_JDL_ITS_STAGGERED') == '1' and option_if_available('o2-its-reco-workflow', '--enable-its-staggering', envfile=async_envfile) != '' else '' +# MFT still needs to update its reconstruction to account for per-layer input, uncomment line below whtb it is ready and remove "Ignoring ..." stuff. +#staggMFT = ' --enable-mft-staggering ' if environ.get('ALIEN_JDL_MFT_STAGGERED') == '1' and option_if_available('o2-mft-reco-workflow', '--enable-mft-staggering', envfile=async_envfile) != '' else '' +#if environ.get('ALIEN_JDL_MFT_STAGGERED') == '1': +# print ("Ignoring ALIEN_JDL_MFT_STAGGERED as the MFT reconstruction is not yet ready to take it") +staggMFT = '' + +# We still may need adjust configurations manually for consistency: +# +# * Force simpler TPC digitization of if TPC reco does not have the mc-time-gain option or remap to a different CCDB object if we are anchored to 2023: +tpcreco_mctimegain = option_if_available('o2-tpc-reco-workflow', '--tpc-mc-time-gain', envfile=async_envfile) +if tpcreco_mctimegain == '': + # TODO: Upload all MC time gain objects to TestReco and remove year dependence + year = environ.get('ALIEN_JDL_LPMANCHORYEAR') if environ.get('ALIEN_JDL_LPMANCHORYEAR') is not None else environ.get('ANCHORYEAR') + if year == '2023': + print("TPC reco does not support --tpc-mc-time-gain. Remapping time gain objects for 2023 MC") + # Year dependent workaround for MC anchored to 2023, solving the issue with 2tag mechanism + extra = 'https://alice-ccdb.cern.ch/TestReco/=TPC/Calib/TimeGain' + environ['ALIEN_JDL_REMAPPINGS'] = (ccdbRemap + ';' + extra) if ccdbRemap else extra + # Keep the cached variable in sync, since getDPL_global_options uses it + ccdbRemap = environ.get('ALIEN_JDL_REMAPPINGS') + else: + # this was communicated by Jens Wiechula@TPC; avoids dEdX issue https://its.cern.ch/jira/browse/O2-5486 for the 2tag mechanism + print ("TPC reco does not support --tpc-mc-time-gain. Adjusting some config for TPC digitization") + overwrite_config(anchorConfig['ConfigParams'],'TPCGasParam','OxygenCont',5e-6) + overwrite_config(anchorConfig['ConfigParams'],'TPCGEMParam','TotalGainStack',2000) + overwrite_config(anchorConfig['ConfigParams'],'GPU_global','dEdxDisableResidualGain',1) +# TODO: put into it's own function for better modularity + +# with the config, we'll create a task_finalizer functor +# this object takes care of customizing/finishing task command with externally given (anchor) config +task_finalizer = TaskFinalizer(anchorConfig, logger="o2dpg_config_replacements.log") # write this config config_key_param_path = args.dump_config @@ -192,37 +284,113 @@ def load_external_config(configfile): print(f"INFO: Written additional config key parameters to JSON {config_key_param_path}") json.dump(anchorConfig, f, indent=2) +# The ZDC geometry is the +-113 m beam line, its magnets, ZN, ZP and ZEM. What it +# costs in transport time is the beam line; ZEM sits at z = 7.6 m and carries +# 217 kg of material inside the FIT and FDD acceptance. Without --with-ZDC we +# therefore drop the beam line and keep ZEM, instead of dropping the whole module +# -- see ZDCSimParam.buildBeamLine in create_geant_config(). ZDC stays out of the +# readout either way, which the deactivate_detector('ZDC') below takes care of. + # with this we can tailor the workflow to the presence of # certain detectors -activeDetectors = anchorConfig.get('o2-ctf-reader-workflow-options',{}).get('onlyDet','all') +# these are all detectors that should be assumed active +readout_detectors = args.readoutDets +# here are all detectors that have been set in an anchored script +activeDetectors = dpl_option_from_config(anchorConfig, 'o2-ctf-reader-workflow', key='--onlyDet', default_value='all') +if activeDetectors == 'all': + # if "all" here, there was in fact nothing in the anchored script, set to what is passed to this script (which it either also "all" or a subset) + activeDetectors = readout_detectors +elif readout_detectors != 'all' and activeDetectors != 'all': + # in this case both are comma-separated lists. Take intersection + r = set(readout_detectors.split(',')) + a = set(activeDetectors.split(',')) + activeDetectors = ','.join(r & a) +# the last case: simply take what comes from the anchored config + # convert to set/hashmap -activeDetectors = { det:1 for det in activeDetectors.split(",") } +activeDetectors = { det:1 for det in activeDetectors.split(',') if det not in args.skipModules and det not in args.skipReadout} for det in activeDetectors: activate_detector(det) +for det in args.skipModules: + print(f"Skipping detector {det} in simulation") + deactivate_detector(det) + +# function to finalize detector source lists based on activeDetectors +# detector source lists are comma separated lists of DET1, DET2, DET1-DET2, ... +def cleanDetectorInputList(inputlist): + if inputlist == "all": + return inputlist + sources_list = inputlist.split(",") + # Filter the sources + filtered_sources = [ + src for src in sources_list + if all(isActive(part) for part in src.split("-")) + ] + # Recompose into a comma-separated string + return ",".join(filtered_sources) + +if not args.with_ZDC: + # deactivate to be able to use isActive consistently for ZDC + deactivate_detector('ZDC') + if 'ZDC' in activeDetectors: + del activeDetectors['ZDC'] def addWhenActive(detID, needslist, appendstring): - if isActive(detID): + """Adds item to a list if detector(s) is (are) active + + Args: + detID: str + detector to check, can also be comma-separated list of detectors + needslist: list + list, e.g. of task dicts added to workflow['stages'] + appendstring: str, dict, ... + to be appended to needslist + """ + if all(isActive(part) for part in detID.split(",")): needslist.append(appendstring) def retrieve_sor(run_number): """ retrieves start of run (sor) from the RCT/Info/RunInformation table with a simple http request - in case of problems, 0 will be returned + in case of problems, 0 will be returned. Simple http request has advantage + of not needing to initialize a Ccdb object. """ + url="http://alice-ccdb.cern.ch/browse/RCT/Info/RunInformation/"+str(run_number) ansobject=requests.get(url) tokens=ansobject.text.split("\n") + # determine start of run, earlier values take precedence (see also implementation in BasicCCDBManager::getRunDuration) + STF=0 + # extract SOR by pattern matching + for t in tokens: + match_object=re.match(r"\s*(STF\s*=\s*)([0-9]*)\s*", t) + if match_object != None: + STF=int(match_object[2]) + break + if STF > 0: + return STF + + SOX=0 + # extract SOX by pattern matching + for t in tokens: + match_object=re.match(r"\s*(STF\s*=\s*)([0-9]*)\s*", t) + if match_object != None: + SOX=int(match_object[2]) + break + if SOX > 0: + return SOX + SOR=0 # extract SOR by pattern matching for t in tokens: - match_object=re.match("\s*(SOR\s*=\s*)([0-9]*)\s*", t) + match_object=re.match(r"\s*(SOR\s*=\s*)([0-9]*)\s*", t) if match_object != None: - SOR=match_object[2] + SOR=int(match_object[2]) break - return int(SOR) + return SOR # check and sanitize config-key values (extract and remove diamond vertex arguments into finalDiamondDict) @@ -288,8 +456,10 @@ def extractVertexArgs(configKeyValuesStr, finalDiamondDict): args.timestamp = args.sor NTIMEFRAMES=int(args.tf) -NWORKERS=args.j -MODULES = "--skipModules ZDC" if not args.with_ZDC else "" +NWORKERS=args.n_workers + + +SKIPMODULES = " ".join(["--skipModules"] + args.skipModules) if len(args.skipModules) > 0 else "" SIMENGINE=args.e BFIELD=args.field RNDSEED=args.seed # typically the argument should be the jobid, but if we get None the current time is used for the initialisation @@ -297,32 +467,98 @@ def extractVertexArgs(configKeyValuesStr, finalDiamondDict): print ("Using initialisation seed: ", RNDSEED) SIMSEED = random.randint(1, 900000000 - NTIMEFRAMES - 1) # PYTHIA maximum seed is 900M for some reason +# ---- initialize global (physics variables) for signal parts ---- +ECMS=float(args.eCM) +EBEAMA=float(args.eA) +EBEAMB=float(args.eB) +NSIGEVENTS=args.ns +GENERATOR=args.gen +if GENERATOR =='': + print('o2dpg_sim_workflow: Error! generator name not provided') + exit(1) + +INIFILE='' +if args.ini!= '': + INIFILE=' --configFile ' + args.ini +PROCESS=args.proc +TRIGGER='' +if args.trigger != '': + TRIGGER=' -t ' + args.trigger + +## Pt Hat productions +WEIGHTPOW=float(args.weightPow) +PTHATMIN=float(args.ptHatMin) +PTHATMAX=float(args.ptHatMax) + +colsys = {'pp':[2212,2212], 'pPb':[2212,1000822080], 'Pbp':[1000822080,2212], 'PbPb':[1000822080,1000822080], 'pO':[2212,1000080160], 'Op':[1000080160,2212], 'HeO':[1000020040,1000080160], 'OHe':[1000080160,1000020040], 'OO':[1000080160,1000080160], 'NeNe':[1000100200,1000100200]} +# translate here collision type to PDG of allowed particles +COLTYPE=args.col +if COLTYPE in colsys.keys(): + PDGA=colsys[COLTYPE][0] + PDGB=colsys[COLTYPE][1] +else: + print('o2dpg_sim_workflow: Error! Unknown collision system %s' % COLTYPE) + exit(1) + +doembedding=True if args.embedding=='True' or args.embedding==True else False + +# If not set previously, set beam energy B equal to A +if EBEAMB < 0 and ECMS < 0: + EBEAMB=EBEAMA + print('o2dpg_sim_workflow: Set beam energy same in A and B beams') + if PDGA != PDGB: + print('o2dpg_sim_workflow: Careful! Set same energies for different particle beams!') + +if ECMS > 0: + if PDGA != PDGB: + print('o2dpg_sim_workflow: Careful! ECM set for for different particle beams!') + +if ECMS < 0 and EBEAMA < 0 and EBEAMB < 0: + print('o2dpg_sim_workflow: Error! CM or Beam Energy not set!!!') + exit(1) + +# Determine interaction rate +INTRATE=int(args.interactionRate) +if INTRATE <= 0: + print('o2dpg_sim_workflow: Error! Interaction rate not >0 !!!') + exit(1) +BCPATTERN=args.bcPatternFile + +# ----- global background specific stuff ------- +COLTYPEBKG=args.colBkg +havePbPb = (COLTYPE == 'PbPb' or (doembedding and COLTYPEBKG == "PbPb")) + workflow={} workflow['stages'] = [] ### setup global environment variables which are valid for all tasks -globalenv = {} -if args.condition_not_after: - # this is for the time-machine CCDB mechanism - globalenv['ALICEO2_CCDB_CONDITION_NOT_AFTER'] = args.condition_not_after - # this is enforcing the use of local CCDB caching - if environ.get('ALICEO2_CCDB_LOCALCACHE') == None: - print ("ALICEO2_CCDB_LOCALCACHE not set; setting to default " + getcwd() + '/ccdb') - globalenv['ALICEO2_CCDB_LOCALCACHE'] = getcwd() + "/ccdb" - else: - # fixes the workflow to use and remember externally provided path - globalenv['ALICEO2_CCDB_LOCALCACHE'] = environ.get('ALICEO2_CCDB_LOCALCACHE') - globalenv['IGNORE_VALIDITYCHECK_OF_CCDB_LOCALCACHE'] = '${ALICEO2_CCDB_LOCALCACHE:+"ON"}' - -workflow['stages'].append(createGlobalInitTask(globalenv)) +global_env = {'ALICEO2_CCDB_CONDITION_NOT_AFTER': args.condition_not_after} if args.condition_not_after else None +globalinittask = createGlobalInitTask(global_env) +globalinittask['cmd'] = 'o2-ccdb-cleansemaphores -p ${ALICEO2_CCDB_LOCALCACHE}' +workflow['stages'].append(globalinittask) #### -def getDPL_global_options(bigshm=False, ccdbbackend=True): - common=" -b --run " +usebkgcache=args.use_bkg_from!=None +includeFullQC=args.include_qc=='True' or args.include_qc==True +includeLocalQC=args.include_local_qc=='True' or args.include_local_qc==True +includeAnalysis = args.include_analysis +includeTPCResiduals=True if environ.get('ALIEN_JDL_DOTPCRESIDUALEXTRACTION') == '1' else False +includeTPCSyncMode=True if environ.get('ALIEN_JDL_DOTPCSYNCMODE') == '1' else False + +qcdir = "QC" +if (includeLocalQC or includeFullQC) and not isdir(qcdir): + mkdir(qcdir) + +def getDPL_global_options(bigshm=False, ccdbbackend=True, runcommand=True): + common=" " + if runcommand: + common=common + ' -b --run ' if len(args.dpl_child_driver) > 0: common=common + ' --child-driver ' + str(args.dpl_child_driver) if ccdbbackend: common=common + " --condition-not-after " + str(args.condition_not_after) + if ccdbRemap != None: + common=common + f" --condition-remap {quote_if_needed(ccdbRemap)} " if args.noIPC!=None: return common + " --no-IPC " if bigshm: @@ -330,22 +566,62 @@ def getDPL_global_options(bigshm=False, ccdbbackend=True): else: return common -doembedding=True if args.embedding=='True' or args.embedding==True else False -usebkgcache=args.use_bkg_from!=None -includeFullQC=args.include_qc=='True' or args.include_qc==True -includeLocalQC=args.include_local_qc=='True' or args.include_local_qc==True -includeAnalysis = args.include_analysis -qcdir = "QC" -if (includeLocalQC or includeFullQC) and not isdir(qcdir): - mkdir(qcdir) +# prefetch the aligned geometry object (for use in reconstruction) +GEOM_PREFETCH_TASK = createTask(name='geomprefetch', cpu='0') +# We need to query the config if this is done with or without parallel world. This needs to be improved +# as it could be defaulted in the ConfigKey system +with_parallel_world = 1 if args.confKey.find("useParallelWorld=1") != -1 else 0 +geom_cmd = f''' +# -- Create aligned geometry using ITS ideal alignment to avoid overlaps in geant +ENABLEPW={with_parallel_world} + +# when parallel world processing is disabled we need to switch off ITS alignment +if [ "${{ENABLEPW}}" == "0" ]; then + CCDBOBJECTS_IDEAL_MC="ITS/Calib/Align" + TIMESTAMP_IDEAL_MC=1 + ${{O2_ROOT}}/bin/o2-ccdb-downloadccdbfile --host http://alice-ccdb.cern.ch/ -p ${{CCDBOBJECTS_IDEAL_MC}} \ + -d ${{ALICEO2_CCDB_LOCALCACHE}} --timestamp ${{TIMESTAMP_IDEAL_MC}} + CCDB_RC="$?" + if [ ! "${{CCDB_RC}}" == "0" ]; then + echo "Problem during CCDB prefetching of ${{CCDBOBJECTS_IDEAL_MC}}. Exiting." + exit ${{CCDB_RC}} + fi +fi + +if [ "$ENABLEPW" == "0" ]; then + REMAP_OPT="--condition-remap=file://${{ALICEO2_CCDB_LOCALCACHE}}=ITS/Calib/Align" +else + REMAP_OPT="" +fi + +# fetch the global alignment geometry +${{O2_ROOT}}/bin/o2-create-aligned-geometry-workflow ${{ALIEN_JDL_CCDB_CONDITION_NOT_AFTER:+--condition-not-after $ALIEN_JDL_CCDB_CONDITION_NOT_AFTER}} \ + --configKeyValues "HBFUtils.startTime={args.timestamp}" -b --run ${{REMAP_OPT}} + +# copy the object into the CCDB cache +mkdir -p $ALICEO2_CCDB_LOCALCACHE/GLO/Config/GeometryAligned +ln -s -f $PWD/o2sim_geometry-aligned.root $ALICEO2_CCDB_LOCALCACHE/GLO/Config/GeometryAligned/snapshot.root +if [ "$ENABLEPW" == "0" ]; then + [[ -f $PWD/its_GeometryTGeo.root ]] && mkdir -p $ALICEO2_CCDB_LOCALCACHE/ITS/Config/Geometry && ln -s -f $PWD/its_GeometryTGeo.root $ALICEO2_CCDB_LOCALCACHE/ITS/Config/Geometry/snapshot.root +fi + +# MFT +[[ -f $PWD/mft_GeometryTGeo.root ]] && mkdir -p $ALICEO2_CCDB_LOCALCACHE/MFT/Config/Geometry && ln -s -f $PWD/mft_GeometryTGeo.root $ALICEO2_CCDB_LOCALCACHE/MFT/Config/Geometry/snapshot.root +''' + +with open("geomprefetcher_script.sh",'w') as f: + f.write(geom_cmd) +GEOM_PREFETCH_TASK['cmd'] = 'chmod +x ${PWD}/geomprefetcher_script.sh; ${PWD}/geomprefetcher_script.sh' +workflow['stages'].append(GEOM_PREFETCH_TASK) + # create/publish the GRPs and other GLO objects for consistent use further down the pipeline orbitsPerTF=int(args.orbitsPerTF) -GRP_TASK = createTask(name='grpcreate', cpu='0') +GRP_TASK = createTask(name='grpcreate', needs=["geomprefetch"], cpu='0') GRP_TASK['cmd'] = 'o2-grp-simgrp-tool createGRPs --timestamp ' + str(args.timestamp) + ' --run ' + str(args.run) + ' --publishto ${ALICEO2_CCDB_LOCALCACHE:-.ccdb} -o grp --hbfpertf ' + str(orbitsPerTF) + ' --field ' + args.field -GRP_TASK['cmd'] += ' --readoutDets ' + " ".join(activeDetectors) + ' --print ' + ('','--lhcif-CCDB')[args.run_anchored] -if (not args.run_anchored == True) and len(args.bcPatternFile) > 0: +GRP_TASK['cmd'] += ' --detectorList ' + args.detectorList + ' --readoutDets ' + " ".join(activeDetectors) + ' --print ' + ('','--lhcif-CCDB')[args.run_anchored] +if len(args.bcPatternFile) > 0: GRP_TASK['cmd'] += ' --bcPatternFile ' + str(args.bcPatternFile) if len(CONFKEYMV) > 0: # this is allowing the possibility to setup/use a different MeanVertex object than the one from CCDB @@ -353,6 +629,79 @@ def getDPL_global_options(bigshm=False, ccdbbackend=True): workflow['stages'].append(GRP_TASK) +# QED background is enabled by default only for same-species (heavy-ion) collisions. +# --with-qed forces it on; --no-qed forces it off and wins over both the default and --with-qed +# (the two flags are mutually exclusive at the argparse level, so they can never both be set). +QED_enabled = True if (PDGA==PDGB and PDGA!=2212) else False +includeQED = (QED_enabled or args.with_qed) and not args.no_qed +signalprefix='sgn' + +# No vertexing for event pool generation; otherwise the vertex comes from CCDB and later from CollContext +# (Note that the CCDB case covers the kDiamond case, since this is picked up in GRP_TASK) +vtxmode_precoll = 'kNoVertex' if args.make_evtpool else 'kCCDB' +vtxmode_sgngen = 'kNoVertex' if args.make_evtpool else 'kCollContext' + +# preproduce the collision context / timeframe structure for all timeframes at once +precollneeds=[GRP_TASK['name']] +# max number of QED events simulated per timeframe. +# A large pool of QED events (0.6*INTRATE) is needed to avoid repetition of events in the same or +# neighbouring ITS readout frames, which would fire already activated pixel, discarding the event. +# Discussed in detail in https://its.cern.ch/jira/browse/O2-5861 +NEventsQED = max(10000, int(INTRATE*0.6)) +# Hadronic cross section values are taken from Glauber MC +XSecSys = {'PbPb': 8., 'OO': 1.273, 'NeNe': 1.736} +# QED cross section values were calculated with TEPEMGEN +# OO and NeNe at 5.36 TeV, while the old PbPb value was kept as before +# If the collision energy changes these values need to be updated +# More info on the calculation can be found in the TEPEMGEN folder of AEGIS +# specifically in the epemgen.f file +QEDXSecExpected = {'PbPb': 35237.5, 'OO': 3.17289, 'NeNe': 7.74633} # expected magnitude of QED cross section from TEPEMGEN +Zsys = {'PbPb': 82, 'OO': 8, 'NeNe': 10} # atomic number of colliding species +PreCollContextTask=createTask(name='precollcontext', needs=precollneeds, cpu='1') + +# adapt timeframeID + orbits + seed + qed +# apply max-collisision offset +# apply vertexing +interactionspecification = signalprefix + ',' + str(INTRATE) + ',' + str(1000000) + ':' + str(1000000) +if doembedding: + interactionspecification = 'bkg,' + str(INTRATE) + ',' + str(NTIMEFRAMES*args.ns) + ':' + str(args.nb) + ' ' + signalprefix + ',' + args.embeddPattern + +qedspec="" +if includeQED: + if PDGA==2212 or PDGB==2212: + # QED is not enabled for pp and pA collisions + print('o2dpg_sim_workflow: Warning! QED is not enabled for pp or pA collisions') + includeQED = False + else: + qedrate = INTRATE * QEDXSecExpected[COLTYPE] / XSecSys[COLTYPE] # hadronic interaction rate * cross_section_ratio + # the QED events are reused in a round robin, so both numbers are the size of the QED pool. + # Older O2 versions wrap the QED event IDs at the first number instead of the second one and + # would otherwise put event IDs into the MC labels which are not in the QED kinematics. + # See https://its.cern.ch/jira/browse/O2-7132 + qedspec = 'qed' + ',' + str(qedrate) + ',' + str(NEventsQED) + ':' + str(NEventsQED) + +PreCollContextTask['cmd'] = task_finalizer([ + '${O2_ROOT}/bin/o2-steer-colcontexttool', + f'-i {interactionspecification}', + '--show-context', + f'--timeframeID {int(args.production_offset)*NTIMEFRAMES}', + f'--orbitsPerTF {orbitsPerTF}', + f'--orbits {NTIMEFRAMES * (orbitsPerTF)}', + f'--seed {RNDSEED}', + '--noEmptyTF', + f'--first-orbit {args.first_orbit}', + '--extract-per-timeframe tf:sgn', + f'--with-vertices {vtxmode_precoll}', + f'--maxCollsPerTF {args.ns}', + f'--orbitsEarly {args.orbits_early}', + f'--timestamp {args.timestamp}', + f'--import-external {args.data_anchoring}' if len(args.data_anchoring) > 0 else None, + '--bcPatternFile ccdb', + ' --nontrivial-mu-distribution ccdb://https://alice-ccdb.cern.ch/FT0/Calib/EventsPerBc', + f'--QEDinteraction {qedspec}' if includeQED else None + ], configname = 'precollcontext') +workflow['stages'].append(PreCollContextTask) + if doembedding: if not usebkgcache: # ---- do background transport task ------- @@ -362,19 +711,20 @@ def getDPL_global_options(bigshm=False, ccdbbackend=True): print('o2dpg_sim_workflow: Error! embedding background generator name not provided') exit(1) + # PDG translation for background + if COLTYPEBKG in colsys.keys(): + PDGABKG=colsys[COLTYPEBKG][0] + PDGBBKG=colsys[COLTYPEBKG][1] + else: + print('o2dpg_sim_workflow: Error! Unknown background collision system %s' % COLTYPEBKG) + exit(1) + PROCESSBKG=args.procBkg - COLTYPEBKG=args.colBkg ECMSBKG=float(args.eCM) EBEAMABKG=float(args.eA) EBEAMBBKG=float(args.eB) - if COLTYPEBKG == 'pp': - PDGABKG=2212 # proton - PDGBBKG=2212 # proton - if COLTYPEBKG == 'PbPb': - PDGABKG=1000822080 # Pb - PDGBBKG=1000822080 # Pb if ECMSBKG < 0: # assign 5.02 TeV to Pb-Pb print('o2dpg_sim_workflow: Set BKG CM Energy to PbPb case 5.02 TeV') ECMSBKG=5020.0 @@ -382,37 +732,25 @@ def getDPL_global_options(bigshm=False, ccdbbackend=True): PROCESSBKG = 'heavy_ion' print('o2dpg_sim_workflow: Process type not considered for Pythia8 PbPb') - if COLTYPEBKG == 'pPb': - PDGABKG=2212 # proton - PDGBBKG=1000822080 # Pb - - if COLTYPEBKG == 'Pbp': - PDGABKG=1000822080 # Pb - PDGBBKG=2212 # proton - # If not set previously, set beam energy B equal to A if EBEAMBBKG < 0 and ECMSBKG < 0: EBEAMBBKG=EBEAMABKG print('o2dpg_sim_workflow: Set beam energy same in A and B beams') - if COLTYPEBKG=="pPb" or COLTYPEBKG=="Pbp": - print('o2dpg_sim_workflow: Careful! both beam energies in bkg are the same') + if PDGABKG != PDGBBKG: + print('o2dpg_sim_workflow: Careful! Set same energies for different background beams!') if ECMSBKG > 0: - if COLTYPEBKG=="pPb" or COLTYPEBKG=="Pbp": - print('o2dpg_sim_workflow: Careful! bkg ECM set for pPb/Pbp collisions!') + if PDGABKG != PDGBBKG: + print('o2dpg_sim_workflow: Careful! ECM set for different background beams!') if ECMSBKG < 0 and EBEAMABKG < 0 and EBEAMBBKG < 0: print('o2dpg_sim_workflow: Error! bkg ECM or Beam Energy not set!!!') exit(1) - CONFKEYBKG='' - if args.confKeyBkg!= '': - CONFKEYBKG=' --configKeyValues "' + args.confKeyBkg + '"' - # Background PYTHIA configuration BKG_CONFIG_task=createTask(name='genbkgconf') BKG_CONFIG_task['cmd'] = 'echo "placeholder / dummy task"' - if GENBKG == 'pythia8' and PROCESSBKG != "": + if GENBKG == 'pythia8': print('Background generator seed: ', SIMSEED) BKG_CONFIG_task['cmd'] = '${O2DPG_ROOT}/MC/config/common/pythia8/utils/mkpy8cfg.py \ --output=pythia8bkg.cfg \ @@ -425,7 +763,7 @@ def getDPL_global_options(bigshm=False, ccdbbackend=True): --process='+str(PROCESSBKG) # if we configure pythia8 here --> we also need to adjust the configuration # TODO: we need a proper config container/manager so as to combine these local configs with external configs etc. - CONFKEYBKG='--configKeyValues "GeneratorPythia8.config=pythia8bkg.cfg;' + args.confKeyBkg + '"' + args.confKeyBkg = 'GeneratorPythia8.config=pythia8bkg.cfg;' + args.confKeyBkg workflow['stages'].append(BKG_CONFIG_task) @@ -434,14 +772,20 @@ def getDPL_global_options(bigshm=False, ccdbbackend=True): if args.iniBkg!= '': INIBKG=' --configFile ' + args.iniBkg - BKGtask=createTask(name='bkgsim', lab=["GEANT"], needs=[BKG_CONFIG_task['name'], GRP_TASK['name']], cpu=NWORKERS ) + # determine final configKey values for background transport + CONFKEYBKG = constructConfigKeyArg(create_geant_config(args, args.confKeyBkg)) + + bkgsimneeds = [BKG_CONFIG_task['name'], GRP_TASK['name'], PreCollContextTask['name']] + BKGtask=createTask(name='bkgsim', lab=["GEANT"], needs=bkgsimneeds, cpu=NWORKERS) BKGtask['cmd']='${O2_ROOT}/bin/o2-sim -e ' + SIMENGINE + ' -j ' + str(NWORKERS) + ' -n ' + str(NBKGEVENTS) \ - + ' -g ' + str(GENBKG) + ' ' + str(MODULES) + ' -o bkg ' + str(INIBKG) \ - + ' --field ' + str(BFIELD) + ' ' + str(CONFKEYBKG) \ + + ' -g ' + str(GENBKG) + ' ' + str(SKIPMODULES) + ' -o bkg ' + str(INIBKG) \ + + ' --field ccdb ' + str(CONFKEYBKG) \ + ('',' --timestamp ' + str(args.timestamp))[args.timestamp!=-1] + ' --run ' + str(args.run) \ - + ' --vertexMode kCCDB' + + ' --vertexMode ' + vtxmode_sgngen \ + + ' --fromCollContext collisioncontext.root:bkg ' \ + + ' --detectorList ' + args.detectorList - if not "all" in activeDetectors: + if not isActive('all'): BKGtask['cmd'] += ' --readoutDetectors ' + " ".join(activeDetectors) workflow['stages'].append(BKGtask) @@ -472,11 +816,12 @@ def getDPL_global_options(bigshm=False, ccdbbackend=True): workflow['stages'].append(BKG_HEADER_task) # a list of smaller sensors (used to construct digitization tasks in a parametrized way) -smallsensorlist = [ "ITS", "TOF", "FDD", "MCH", "MID", "MFT", "HMP", "EMC", "PHS", "CPV" ] -if args.with_ZDC: - smallsensorlist += [ "ZDC" ] +smallsensorlist = [ "ITS", "TOF", "FDD", "MCH", "MID", "MFT", "HMP", "PHS", "CPV", "ZDC" ] +if args.detectorList == 'ALICE2.1': + smallsensorlist = ['IT3' if sensor == 'ITS' else sensor for sensor in smallsensorlist] + # a list of detectors that serve as input for the trigger processor CTP --> these need to be processed together for now -ctp_trigger_inputlist = [ "FT0", "FV0" ] +ctp_trigger_inputlist = [ "FT0", "FV0", "EMC" ] BKG_HITDOWNLOADER_TASKS={} for det in [ 'TPC', 'TRD' ] + smallsensorlist + ctp_trigger_inputlist: @@ -495,195 +840,187 @@ def getDPL_global_options(bigshm=False, ccdbbackend=True): # We download some binary files, necessary for processing # Eventually, these files/objects should be queried directly from within these tasks? -MATBUD_DOWNLOADER_TASK = createTask(name='matbuddownloader', cpu='0') -MATBUD_DOWNLOADER_TASK['cmd'] = '[ -f matbud.root ] || ${O2_ROOT}/bin/o2-ccdb-downloadccdbfile --host http://alice-ccdb.cern.ch/ -p GLO/Param/MatLUT -o matbud.root --no-preserve-path --timestamp ' + str(args.timestamp) + ' --created-not-after ' + str(args.condition_not_after) -workflow['stages'].append(MATBUD_DOWNLOADER_TASK) -# We download trivial TPC space charge corrections to be applied during -# reco. This is necessary to have consistency (decalibration and calibration) between digitization and reconstruction ... until digitization can -# also apply this effect via CCDB. -TPC_SPACECHARGE_DOWNLOADER_TASK = createTask(name='tpc_spacecharge_downloader', cpu='0') -TPC_SPACECHARGE_DOWNLOADER_TASK['cmd'] = '[ "${O2DPG_ENABLE_TPC_DISTORTIONS}" ] || { ${O2_ROOT}/bin/o2-ccdb-downloadccdbfile --host http://alice-ccdb.cern.ch -p TPC/Calib/CorrectionMapRef --timestamp 1 --created-not-after ' + str(args.condition_not_after) + ' -d ${ALICEO2_CCDB_LOCALCACHE} ; ' \ - '${O2_ROOT}/bin/o2-ccdb-downloadccdbfile --host http://alice-ccdb.cern.ch -p TPC/Calib/CorrectionMap --timestamp 1 --created-not-after ' + str(args.condition_not_after) + ' -d ${ALICEO2_CCDB_LOCALCACHE} ; }' -workflow['stages'].append(TPC_SPACECHARGE_DOWNLOADER_TASK) +# Fix (residual) geometry alignment for simulation stage +# Detectors that prefer to apply special alignments (for example residual effects) should be listed here and download these files. +# These object will take precedence over ordinary align objects **and** will only be applied in transport simulation +# and digitization (Det/Calib/Align is only read in simulation since reconstruction tasks use GLO/Config/AlignedGeometry automatically). +SIM_ALIGNMENT_PREFETCH_TASK = createTask(name='sim_alignment', cpu='0') +SIM_ALIGNMENT_PREFETCH_TASK['cmd'] = '${O2_ROOT}/bin/o2-ccdb-downloadccdbfile --host http://alice-ccdb.cern.ch -p MID/MisCalib/Align --timestamp ' + str(args.timestamp) + ' --created-not-after ' \ + + str(args.condition_not_after) + ' -d ${ALICEO2_CCDB_LOCALCACHE}/MID/Calib/Align --no-preserve-path ; ' +SIM_ALIGNMENT_PREFETCH_TASK['cmd'] += '${O2_ROOT}/bin/o2-ccdb-downloadccdbfile --host http://alice-ccdb.cern.ch -p MCH/MisCalib/Align --timestamp ' + str(args.timestamp) + ' --created-not-after ' \ + + str(args.condition_not_after) + ' -d ${ALICEO2_CCDB_LOCALCACHE}/MCH/Calib/Align --no-preserve-path ' +workflow['stages'].append(SIM_ALIGNMENT_PREFETCH_TASK) +# query initial configKey args for signal transport; mainly used to setup generators +simInitialConfigKeys = create_geant_config(args, args.confKey) # loop over timeframes for tf in range(1, NTIMEFRAMES + 1): TFSEED = SIMSEED + tf print("Timeframe " + str(tf) + " seed: ", TFSEED) - timeframeworkdir='tf'+str(tf) # ---- transport task ------- - # function encapsulating the signal sim part - # first argument is timeframe id - ECMS=float(args.eCM) - EBEAMA=float(args.eA) - EBEAMB=float(args.eB) - NSIGEVENTS=args.ns - GENERATOR=args.gen - if GENERATOR =='': - print('o2dpg_sim_workflow: Error! generator name not provided') - exit(1) - - INIFILE='' - if args.ini!= '': - INIFILE=' --configFile ' + args.ini - CONFKEY='' - if args.confKey!= '': - CONFKEY=' --configKeyValues "' + args.confKey + '"' - PROCESS=args.proc - TRIGGER='' - if args.trigger != '': - TRIGGER=' -t ' + args.trigger - - ## Pt Hat productions - WEIGHTPOW=float(args.weightPow) - PTHATMIN=float(args.ptHatMin) - PTHATMAX=float(args.ptHatMax) - - # translate here collision type to PDG - COLTYPE=args.col - - if COLTYPE == 'pp': - PDGA=2212 # proton - PDGB=2212 # proton - - if COLTYPE == 'PbPb': - PDGA=1000822080 # Pb - PDGB=1000822080 # Pb - if ECMS < 0: # assign 5.02 TeV to Pb-Pb - print('o2dpg_sim_workflow: Set CM Energy to PbPb case 5.02 TeV') - ECMS=5020.0 - - if COLTYPE == 'pPb': - PDGA=2212 # proton - PDGB=1000822080 # Pb - - if COLTYPE == 'Pbp': - PDGA=1000822080 # Pb - PDGB=2212 # proton - - # If not set previously, set beam energy B equal to A - if EBEAMB < 0 and ECMS < 0: - EBEAMB=EBEAMA - print('o2dpg_sim_workflow: Set beam energy same in A and B beams') - if COLTYPE=="pPb" or COLTYPE=="Pbp": - print('o2dpg_sim_workflow: Careful! both beam energies are the same') - - if ECMS > 0: - if COLTYPE=="pPb" or COLTYPE=="Pbp": - print('o2dpg_sim_workflow: Careful! ECM set for pPb/Pbp collisions!') - - if ECMS < 0 and EBEAMA < 0 and EBEAMB < 0: - print('o2dpg_sim_workflow: Error! CM or Beam Energy not set!!!') - exit(1) - - # Determine interation rate - # it should be taken from CDB, meanwhile some default values - signalprefix='sgn_' + str(tf) - INTRATE=int(args.interactionRate) - BCPATTERN=args.bcPatternFile - includeQED = (COLTYPE == 'PbPb' or (doembedding and COLTYPEBKG == "PbPb")) or (args.with_qed == True) - - # preproduce the collision context - precollneeds=[GRP_TASK['name']] - NEventsQED=10000 # max number of QED events to simulate per timeframe - PbPbXSec=8. # expected PbPb cross section - QEDXSecExpected=35237.5 # expected magnitude of QED cross section - PreCollContextTask=createTask(name='precollcontext_' + str(tf), needs=precollneeds, tf=tf, cwd=timeframeworkdir, cpu='1') - PreCollContextTask['cmd']='${O2_ROOT}/bin/o2-steer-colcontexttool -i ' + signalprefix + ',' + str(INTRATE) + ',' + str(args.ns) + ':' + str(args.ns) + ' --show-context ' + ' --timeframeID ' + str(tf-1 + int(args.production_offset)*NTIMEFRAMES) + ' --orbitsPerTF ' + str(orbitsPerTF) + ' --orbits ' + str(orbitsPerTF) + ' --seed ' + str(TFSEED) + ' --noEmptyTF --first-orbit ' + str(args.first_orbit) - PreCollContextTask['cmd'] += ' --bcPatternFile ccdb' # <--- the object should have been set in (local) CCDB - if includeQED: - qedrate = INTRATE * QEDXSecExpected / PbPbXSec # hadronic interaction rate * cross_section_ratio - qedspec = 'qed_' + str(tf) + ',' + str(qedrate) + ',10000000:' + str(NEventsQED) - PreCollContextTask['cmd'] += ' --QEDinteraction ' + qedspec - workflow['stages'].append(PreCollContextTask) - # produce QED background for PbPb collissions QEDdigiargs = "" if includeQED: - NEventsQED=10000 # 35K for a full timeframe? - qedneeds=[GRP_TASK['name']] - if args.pregenCollContext == True: - qedneeds.append(PreCollContextTask['name']) + qedneeds=[GRP_TASK['name'], PreCollContextTask['name']] QED_task=createTask(name='qedsim_'+str(tf), needs=qedneeds, tf=tf, cwd=timeframeworkdir, cpu='1') ######################################################################################################## # # ATTENTION: CHANGING THE PARAMETERS/CUTS HERE MIGHT INVALIDATE THE QED INTERACTION RATES USED ELSEWHERE # ######################################################################################################## - QED_task['cmd'] = 'o2-sim -e TGeant3 --field ' + str(BFIELD) + ' \ - -j ' + str('1') + ' -o qed_' + str(tf) + ' \ - -n ' + str(NEventsQED) + ' -m PIPE ITS MFT FT0 FV0 FDD ' \ + + # determine final conf key for QED simulation + QEDBaseConfig = "GeneratorExternal.fileName=$O2_ROOT/share/Generators/external/QEDLoader.C;QEDGenParam.yMin=-7;QEDGenParam.yMax=7;QEDGenParam.ptMin=0.001;QEDGenParam.ptMax=1.;QEDGenParam.xSectionHad="+str(XSecSys[COLTYPE])+";QEDGenParam.Z="+str(Zsys[COLTYPE])+";QEDGenParam.cmEnergy="+str(ECMS)+";Diamond.width[2]=6.;" + QEDCONFKEY = constructConfigKeyArg(create_geant_config(args, QEDBaseConfig + args.confKeyQED)) + qed_detectorlist = ' ITS MFT FT0 FV0 FDD ' + if args.detectorList == 'ALICE2.1': + qed_detectorlist = qed_detectorlist.replace('ITS', 'IT3') + QED_task['cmd'] = 'o2-sim -e TGeant3 --field ccdb -j ' + str('1') + ' -o qed' \ + + ' -n ' + str(NEventsQED) + ' -m ' + qed_detectorlist \ + ('', ' --timestamp ' + str(args.timestamp))[args.timestamp!=-1] + ' --run ' + str(args.run) \ + ' --seed ' + str(TFSEED) \ - + ' -g extgen --configKeyValues \"GeneratorExternal.fileName=$O2_ROOT/share/Generators/external/QEDLoader.C;QEDGenParam.yMin=-7;QEDGenParam.yMax=7;QEDGenParam.ptMin=0.001;QEDGenParam.ptMax=1.;Diamond.width[2]=6.\"' # + (' ',' --fromCollContext collisioncontext.root')[args.pregenCollContext] + + ' -g extgen ' \ + + ' --detectorList ' + args.detectorList + ' ' \ + + QEDCONFKEY QED_task['cmd'] += '; RC=$?; QEDXSecCheck=`grep xSectionQED qedgenparam.ini | sed \'s/xSectionQED=//\'`' - QED_task['cmd'] += '; echo "CheckXSection ' + str(QEDXSecExpected) + ' = $QEDXSecCheck"; [[ ${RC} == 0 ]]' + QED_task['cmd'] += '; echo "CheckXSection ' + str(QEDXSecExpected[COLTYPE]) + ' = $QEDXSecCheck"; [[ ${RC} == 0 ]]' # TODO: propagate the Xsecion ratio dynamically - QEDdigiargs=' --simPrefixQED qed_' + str(tf) + ' --qed-x-section-ratio ' + str(QEDXSecExpected/PbPbXSec) + QEDdigiargs=' --simPrefixQED qed' + ' --qed-x-section-ratio ' + str(QEDXSecExpected[COLTYPE]/XSecSys[COLTYPE]) workflow['stages'].append(QED_task) + # recompute the number of workers to increase CPU efficiency + NWORKERS_TF = compute_n_workers(INTRATE, COLTYPE, n_workers_user = NWORKERS) if (not args.force_n_workers) else NWORKERS + # produce the signal configuration SGN_CONFIG_task=createTask(name='gensgnconf_'+str(tf), tf=tf, cwd=timeframeworkdir) SGN_CONFIG_task['cmd'] = 'echo "placeholder / dummy task"' - if GENERATOR == 'pythia8' and PROCESS!='': - SGN_CONFIG_task['cmd'] = '${O2DPG_ROOT}/MC/config/common/pythia8/utils/mkpy8cfg.py \ - --output=pythia8.cfg \ - --seed='+str(TFSEED)+' \ - --idA='+str(PDGA)+' \ - --idB='+str(PDGB)+' \ - --eCM='+str(ECMS)+' \ - --eA='+str(EBEAMA)+' \ - --eB='+str(EBEAMB)+' \ - --process='+str(PROCESS)+' \ - --ptHatMin='+str(PTHATMIN)+' \ - --ptHatMax='+str(PTHATMAX) - if WEIGHTPOW > 0: - SGN_CONFIG_task['cmd'] = SGN_CONFIG_task['cmd'] + ' --weightPow=' + str(WEIGHTPOW) + if GENERATOR == 'pythia8': + # see if config is given externally + externalPythia8Config = simInitialConfigKeys.get("GeneratorPythia8", {}).get("config", None) + if externalPythia8Config != None: + # check if this refers to a file with ABSOLUTE path + if not isabs(externalPythia8Config): + print ('Error: Argument to GeneratorPythia8.config must be absolute path') + exit (1) + # in this case, we copy the external config to the local dir (maybe not even necessary) + SGN_CONFIG_task['cmd'] = 'cp ' + externalPythia8Config + ' pythia8.cfg' + else: + SGN_CONFIG_task['cmd'] = '${O2DPG_ROOT}/MC/config/common/pythia8/utils/mkpy8cfg.py \ + --output=pythia8.cfg \ + --seed='+str(TFSEED)+' \ + --idA='+str(PDGA)+' \ + --idB='+str(PDGB)+' \ + --eCM='+str(ECMS)+' \ + --eA='+str(EBEAMA)+' \ + --eB='+str(EBEAMB)+' \ + --process='+str(PROCESS)+' \ + --ptHatMin='+str(PTHATMIN)+' \ + --ptHatMax='+str(PTHATMAX) + if WEIGHTPOW > 0: + SGN_CONFIG_task['cmd'] = SGN_CONFIG_task['cmd'] + ' --weightPow=' + str(WEIGHTPOW) # if we configure pythia8 here --> we also need to adjust the configuration # TODO: we need a proper config container/manager so as to combine these local configs with external configs etc. - CONFKEY='--configKeyValues "GeneratorPythia8.config=pythia8.cfg'+';'+args.confKey+'"' + args.confKey = args.confKey + ";GeneratorPythia8.config=pythia8.cfg" # elif GENERATOR == 'extgen': what do we do if generator is not pythia8? # NOTE: Generator setup might be handled in a different file or different files (one per # possible generator) - #if CONFKEY=='': - # print('o2dpg_sim_workflow: Error! configuration file not provided') - # exit(1) - workflow['stages'].append(SGN_CONFIG_task) + # default flags for extkinO2 signal simulation (no transport) + extkinO2Config = '' + if GENERATOR == 'extkinO2': + extkinO2Config = ';GeneratorFromO2Kine.randomize=true;GeneratorFromO2Kine.rngseed=' + str(TFSEED) + + # determine final conf key for signal simulation + CONFKEY = constructConfigKeyArg(create_geant_config(args, args.confKey + extkinO2Config)) # ----------------- # transport signals # ----------------- signalneeds=[ SGN_CONFIG_task['name'], GRP_TASK['name'] ] - if (args.pregenCollContext == True): - signalneeds.append(PreCollContextTask['name']) + signalneeds.append(PreCollContextTask['name']) # add embedIntoFile only if embeddPattern does contain a '@' embeddinto= "--embedIntoFile ../bkg_MCHeader.root" if (doembedding & ("@" in args.embeddPattern)) else "" - #embeddinto= "--embedIntoFile ../bkg_MCHeader.root" if doembedding else "" if doembedding: if not usebkgcache: signalneeds = signalneeds + [ BKGtask['name'] ] else: signalneeds = signalneeds + [ BKG_HEADER_task['name'] ] - SGNtask=createTask(name='sgnsim_'+str(tf), needs=signalneeds, tf=tf, cwd='tf'+str(tf), lab=["GEANT"], relative_cpu=7/8, n_workers=NWORKERS, mem='2000') - SGNtask['cmd']='${O2_ROOT}/bin/o2-sim -e ' + str(SIMENGINE) + ' ' + str(MODULES) + ' -n ' + str(NSIGEVENTS) + ' --seed ' + str(TFSEED) \ - + ' --field ' + str(BFIELD) + ' -j ' + str(NWORKERS) + ' -g ' + str(GENERATOR) \ - + ' ' + str(TRIGGER) + ' ' + str(CONFKEY) + ' ' + str(INIFILE) \ - + ' -o ' + signalprefix + ' ' + embeddinto \ - + ('', ' --timestamp ' + str(args.timestamp))[args.timestamp!=-1] + ' --run ' + str(args.run) \ - + ' --vertexMode kCCDB' - if not "all" in activeDetectors: + + # (separate) event generation task + sep_event_mode = args.event_gen_mode == 'separated' + sgngenneeds=signalneeds + # for HepMC we need some special treatment since we need + # to ensure that different timeframes read different events from this file + if GENERATOR=="hepmc" and tf > 1: + sgngenneeds=signalneeds + ['sgngen_' + str(tf-1)] # we serialize event generation + SGNGENtask=createTask(name='sgngen_'+str(tf), needs=sgngenneeds, tf=tf, cwd='tf'+str(tf), lab=["GEN"], + cpu=8 if args.make_evtpool else 1, mem=1000) + + SGNGENtask['cmd']='' + if GENERATOR=="hepmc": + if tf == 1: + # determine the offset number + eventOffset = environ.get('HEPMCOFFSET') + print("HEPMCOFFSET: ", eventOffset) + if eventOffset == None: + eventOffset = 0 + cmd = 'export HEPMCEVENTSKIP=$(${O2DPG_ROOT}/UTILS/InitHepMCEventSkip.sh ../HepMCEventSkip.json ' + str(eventOffset) + ');' + elif tf > 1: + # determine the skip number + cmd = 'export HEPMCEVENTSKIP=$(${O2DPG_ROOT}/UTILS/ReadHepMCEventSkip.sh ../HepMCEventSkip.json ' + str(tf) + ');' + SGNGENtask['cmd'] = cmd + + generationtimeout = -1 # possible timeout for event pool generation + if args.make_evtpool: + JOBTTL=environ.get('JOBTTL', None) + if JOBTTL != None: + generationtimeout = 0.95*int(JOBTTL) # for GRID jobs, determine timeout automatically + SGNGENtask['cmd'] +=('','timeout ' + str(generationtimeout) + ' ')[args.make_evtpool and generationtimeout>0] \ + + '${O2_ROOT}/bin/o2-sim --noGeant -j 1 --field ccdb --vertexMode ' + vtxmode_sgngen \ + + ' --run ' + str(args.run) + ' ' + str(CONFKEY) + str(TRIGGER) \ + + ' -g ' + str(GENERATOR) + ' ' + str(INIFILE) + ' -o genevents ' + embeddinto \ + + ('', ' --timestamp ' + str(args.timestamp))[args.timestamp!=-1] \ + + ' --seed ' + str(TFSEED) + ' -n ' + str(NSIGEVENTS) \ + + ' --detectorList ' + args.detectorList \ + + ' --fromCollContext collisioncontext.root:' + signalprefix + if GENERATOR=="hepmc": + SGNGENtask['cmd'] += "; RC=$?; ${O2DPG_ROOT}/UTILS/UpdateHepMCEventSkip.sh ../HepMCEventSkip.json " + str(tf) + '; [[ ${RC} == 0 ]]' + if sep_event_mode == True: + workflow['stages'].append(SGNGENtask) + signalneeds = signalneeds + [SGNGENtask['name']] + if args.make_evtpool: + if generationtimeout > 0: + # final adjustment of command for event pools and timeout --> we need to analyse the return code + # if we have a timeout then we finish what we can and are also happy with return code 124 + SGNGENtask['cmd'] += ' ; RC=$? ; [[ ${RC} == 0 || ${RC} == 124 ]]' + continue + + # GeneratorFromO2Kine parameters are needed only before the transport + CONFKEY = re.sub(r'GeneratorFromO2Kine.*?;', '', CONFKEY) + + sgnmem = 6000 if COLTYPE == 'PbPb' else 4000 + SGNtask=createTask(name='sgnsim_'+str(tf), needs=signalneeds, tf=tf, cwd='tf'+str(tf), lab=["GEANT"], + relative_cpu=7/8, n_workers=NWORKERS_TF, mem=str(sgnmem)) + sgncmdbase = '${O2_ROOT}/bin/o2-sim -e ' + str(SIMENGINE) + ' ' + str(SKIPMODULES) + ' -n ' + str(NSIGEVENTS) + ' --seed ' + str(TFSEED) \ + + ' --field ccdb -j ' + str(NWORKERS_TF) + ' ' + str(CONFKEY) + ' ' + str(INIFILE) + ' -o ' + signalprefix + ' ' + embeddinto \ + + ' --detectorList ' + args.detectorList \ + + ('', ' --timestamp ' + str(args.timestamp))[args.timestamp!=-1] + ' --run ' + str(args.run) + if sep_event_mode: + SGNtask['cmd'] = sgncmdbase + ' -g extkinO2 --extKinFile genevents_Kine.root ' + ' --vertexMode kNoVertex' + else: + SGNtask['cmd'] = sgncmdbase + ' -g ' + str(GENERATOR) + ' ' + str(TRIGGER) + ' --vertexMode kCCDB ' + if not isActive('all'): SGNtask['cmd'] += ' --readoutDetectors ' + " ".join(activeDetectors) - if args.pregenCollContext == True: - SGNtask['cmd'] += ' --fromCollContext collisioncontext.root' + + SGNtask['cmd'] += ' --fromCollContext collisioncontext.root' workflow['stages'].append(SGNtask) # some tasks further below still want geometry + grp in fixed names, so we provide it here @@ -745,26 +1082,7 @@ def getDPL_global_options(bigshm=False, ccdbbackend=True): if (args.sor != -1): globalTFConfigValues["HBFUtils.startTime"] = args.sor - def putConfigValues(localCF = {}): - """ - Creates the final --configValues string to be passed to the workflows. - Uses the globalTFConfigValues and merges/overrides them with the local settings. - localCF is supposed to be a dictionary mapping key to param - """ - returnstring = ' --configKeyValues "' - cf = globalTFConfigValues.copy() - isfirst=True - for e in localCF: - cf[e] = localCF[e] - - for e in cf: - returnstring += (';','')[isfirst] + str(e) + "=" + str(cf[e]) - isfirst=False - - returnstring = returnstring + '"' - return returnstring - - def putConfigValuesNew(listOfMainKeys=[], localCF = {}): + def putConfigValues(listOfMainKeys=[], localCF = {}, globalTFConfig = True): """ Creates the final --configValues string to be passed to the workflows. Uses the globalTFConfigValues and applies other parameters on top @@ -772,14 +1090,17 @@ def putConfigValuesNew(listOfMainKeys=[], localCF = {}): localCF: a dictionary mapping key to param - possibly overrides settings taken from global config """ returnstring = ' --configKeyValues "' - cf = globalTFConfigValues.copy() + cf = globalTFConfigValues.copy() if globalTFConfig else {} isfirst=True # now bring in the relevant keys # from the external config for key in listOfMainKeys: - # it this key exists + # try to find key flat in dict (backward compatible) keydict = anchorConfig.get(key) + if keydict == None: + # try to find under the ConfigurableKey entry (standard) + keydict = anchorConfig.get("ConfigParams",{}).get(key) if keydict != None: for k in keydict: cf[key+"."+k] = keydict[k] @@ -799,57 +1120,87 @@ def putConfigValuesNew(listOfMainKeys=[], localCF = {}): PASSNAME='${ALIEN_JDL_LPMANCHORPASSNAME:-unanchored}' # This task creates the basic setup for all digitizers! all digitization configKeyValues need to be given here + # The purpose of this short task is to generate the digi INI file which all other tasks may use contextneeds = [LinkGRPFileTask['name'], SGNtask['name']] if includeQED: contextneeds += [QED_task['name']] ContextTask = createTask(name='digicontext_'+str(tf), needs=contextneeds, tf=tf, cwd=timeframeworkdir, lab=["DIGI"], cpu='1') # this is just to have the digitizer ini file - ContextTask['cmd'] = '${O2_ROOT}/bin/o2-sim-digitizer-workflow --only-context --interactionRate ' + str(INTRATE) \ - + ' ' + getDPL_global_options(ccdbbackend=False) + ' -n ' + str(args.ns) + simsoption \ - + ' --seed ' + str(TFSEED) \ - + ' ' + putConfigValuesNew({"DigiParams.maxOrbitsToDigitize" : str(orbitsPerTF)},{"DigiParams.passName" : str(PASSNAME)}) + ('',' --incontext ' + CONTEXTFILE)[args.pregenCollContext] + QEDdigiargs + ContextTask['cmd'] = '${O2_ROOT}/bin/o2-sim-digitizer-workflow --only-context --interactionRate ' + str(INTRATE) \ + + ' ' + getDPL_global_options(ccdbbackend=False) + ' -n ' + str(args.ns) + simsoption \ + + ' --seed ' + str(TFSEED) \ + + ' ' + putConfigValues({"DigiParams.maxOrbitsToDigitize" : str(orbitsPerTF)},{"DigiParams.passName" : str(PASSNAME)}) \ + + ' --incontext ' + CONTEXTFILE + QEDdigiargs ContextTask['cmd'] += ' --bcPatternFile ccdb' - - # in case of embedding we engineer the context directly and allow the user to provide an embedding pattern - # The :r flag means to shuffle the background events randomly - if doembedding: - ContextTask['cmd'] += ';ln -nfs ../bkg_Kine.root .;${O2_ROOT}/bin/o2-steer-colcontexttool -i bkg,' + str(INTRATE) + ',' + str(args.ns) + ':' + str(args.nb) + ' ' + signalprefix + ',' + args.embeddPattern + ' --show-context ' + ' --timeframeID ' + str(tf-1 + int(args.production_offset)*NTIMEFRAMES) + ' --orbitsPerTF ' + str(orbitsPerTF) + ' --use-existing-kine' - ContextTask['cmd'] += ' --bcPatternFile ccdb --seed ' + str(TFSEED) - workflow['stages'].append(ContextTask) - tpcdigineeds=[ContextTask['name'], LinkGRPFileTask['name'], TPC_SPACECHARGE_DOWNLOADER_TASK['name']] + # ===| TPC digi part |=== + CTPSCALER = args.ctp_scaler + tpcDistortionType=args.tpc_distortion_type + print(f"TPC distortion simulation: type = {tpcDistortionType}, CTP scaler value {CTPSCALER}"); + tpcdigineeds=[ContextTask['name'], LinkGRPFileTask['name']] if usebkgcache: tpcdigineeds += [ BKG_HITDOWNLOADER_TASKS['TPC']['name'] ] + tpcLocalCF={"DigiParams.maxOrbitsToDigitize" : str(orbitsPerTF), "DigiParams.seed" : str(TFSEED)} + + # force TPC common mode correction in all cases to avoid issues the CMk values stored in the CCDB + tpcLocalCF['TPCEleParam.DigiMode'] = str(2) # 2 = o2::tpc::DigitzationMode::ZeroSuppressionCMCorr from TPCBase/ParameterElectronics.h + + # handle distortions and scaling using MC maps + # this assumes the lumi inside the maps is stored in FT0 (pp) scalers + # in case of PbPb the conversion factor ZDC ->FT0 (pp) must be taken into account in the scalers + if tpcDistortionType == 2 and CTPSCALER <= 0: + print('Warning: lumi scaling requested, but no ctp scaler value set. Full map will be applied at face value.') + tpcDistortionType=1 + lumiInstFactor=1 + if COLTYPE == 'PbPb': + lumiInstFactor=2.414 + if tpcDistortionType == 2: + tpcLocalCF['TPCCorrMap.lumiInst'] = str(CTPSCALER * lumiInstFactor) + + tpcdigimem = 12000 if havePbPb else 9000 TPCDigitask=createTask(name='tpcdigi_'+str(tf), needs=tpcdigineeds, - tf=tf, cwd=timeframeworkdir, lab=["DIGI"], cpu=NWORKERS, mem='9000') + tf=tf, cwd=timeframeworkdir, lab=["DIGI"], cpu=NWORKERS_TF, mem=str(tpcdigimem)) TPCDigitask['cmd'] = ('','ln -nfs ../bkg_HitsTPC.root . ;')[doembedding] - TPCDigitask['cmd'] += '${O2_ROOT}/bin/o2-sim-digitizer-workflow ' + getDPL_global_options() + ' -n ' + str(args.ns) + simsoption + ' --onlyDet TPC --TPCuseCCDB --interactionRate ' + str(INTRATE) + ' --tpc-lanes ' + str(NWORKERS) + ' --incontext ' + str(CONTEXTFILE) + ' --disable-write-ini --early-forward-policy always ' + putConfigValuesNew(["TPCGasParam","TPCGEMParam","TPCEleParam","TPCITCorr","TPCDetParam"],localCF={"DigiParams.maxOrbitsToDigitize" : str(orbitsPerTF)}) + TPCDigitask['cmd'] += '${O2_ROOT}/bin/o2-ccdb-downloadccdbfile --host http://alice-ccdb.cern.ch -p TPC/Config/RunInfoV2 --timestamp ' \ + + str(args.timestamp) + ' --created-not-after ' + str(args.condition_not_after) + ' -d ${ALICEO2_CCDB_LOCALCACHE} ; ' + TPCDigitask['cmd'] += '${O2_ROOT}/bin/o2-sim-digitizer-workflow ' + getDPL_global_options(bigshm=True) + ' -n ' + str(args.ns) + simsoption \ + + ' --onlyDet TPC --TPCuseCCDB --interactionRate ' + str(INTRATE) + ' --tpc-lanes ' + str(NWORKERS_TF) \ + + ' --incontext ' + str(CONTEXTFILE) + ' --disable-write-ini --early-forward-policy always --forceSelectedDets ' \ + + ' --tpc-distortion-type ' + str(tpcDistortionType) \ + + ' --n-threads-distortions 1 ' \ + + putConfigValues(["TPCGasParam","TPCGEMParam","TPCEleParam","TPCITCorr","TPCDetParam"], + localCF=tpcLocalCF) TPCDigitask['cmd'] += (' --tpc-chunked-writer','')[args.no_tpc_digitchunking] TPCDigitask['cmd'] += ('',' --disable-mc')[args.no_mc_labels] # we add any other extra command line options (power user customization) with an environment variable if environ.get('O2DPG_TPC_DIGIT_EXTRA') != None: TPCDigitask['cmd'] += ' ' + environ['O2DPG_TPC_DIGIT_EXTRA'] workflow['stages'].append(TPCDigitask) + # END TPC digi part trddigineeds = [ContextTask['name']] if usebkgcache: trddigineeds += [ BKG_HITDOWNLOADER_TASKS['TRD']['name'] ] TRDDigitask=createTask(name='trddigi_'+str(tf), needs=trddigineeds, - tf=tf, cwd=timeframeworkdir, lab=["DIGI"], cpu=NWORKERS, mem='8000') + tf=tf, cwd=timeframeworkdir, lab=["DIGI"], cpu=NWORKERS_TF, mem='8000') TRDDigitask['cmd'] = ('','ln -nfs ../bkg_HitsTRD.root . ;')[doembedding] - TRDDigitask['cmd'] += '${O2_ROOT}/bin/o2-sim-digitizer-workflow ' + getDPL_global_options() + ' -n ' + str(args.ns) + simsoption + ' --onlyDet TRD --interactionRate ' + str(INTRATE) + putConfigValuesNew(localCF={"TRDSimParams.digithreads" : NWORKERS}) + ' --incontext ' + str(CONTEXTFILE) + ' --disable-write-ini' + TRDDigitask['cmd'] += '${O2_ROOT}/bin/o2-sim-digitizer-workflow ' + getDPL_global_options() + ' -n ' + str(args.ns) + simsoption \ + + ' --onlyDet TRD --interactionRate ' + str(INTRATE) + ' --incontext ' + str(CONTEXTFILE) + ' --disable-write-ini' \ + + putConfigValues(localCF={"TRDSimParams.digithreads" : NWORKERS_TF, "DigiParams.seed" : str(TFSEED)}) + " --forceSelectedDets" TRDDigitask['cmd'] += ('',' --disable-mc')[args.no_mc_labels] - if isActive("TRD"): - workflow['stages'].append(TRDDigitask) + addWhenActive("TRD", workflow['stages'], TRDDigitask) # these are digitizers which are single threaded def createRestDigiTask(name, det='ALLSMALLER'): tneeds =[ContextTask['name']] if includeQED == True: tneeds += [QED_task['name']] - commondigicmd = '${O2_ROOT}/bin/o2-sim-digitizer-workflow ' + getDPL_global_options() + ' -n ' + str(args.ns) + simsoption + ' --interactionRate ' + str(INTRATE) + ' --incontext ' + str(CONTEXTFILE) + ' --disable-write-ini' + putConfigValuesNew(["MFTAlpideParam", "ITSAlpideParam", "ITSDigitizerParam"]) + QEDdigiargs + commondigicmd = '${O2_ROOT}/bin/o2-sim-digitizer-workflow ' + getDPL_global_options() + ' -n ' + str(args.ns) + simsoption \ + + ' --interactionRate ' + str(INTRATE) + ' --incontext ' + str(CONTEXTFILE) + ' --disable-write-ini' \ + + putConfigValues(["MFTAlpideParam", "ITSAlpideParam", "ITSDigitizerParam" if args.detectorList == 'ALICE2' else "IT3DigitizerParam"], + localCF={"DigiParams.seed" : str(TFSEED), "MCHDigitizer.seed" : str(TFSEED)}) + QEDdigiargs if det=='ALLSMALLER': # here we combine all smaller digits in one DPL workflow if usebkgcache: @@ -859,31 +1210,34 @@ def createRestDigiTask(name, det='ALLSMALLER'): tf=tf, cwd=timeframeworkdir, lab=["DIGI","SMALLDIGI"], cpu='1') t['cmd'] = ('','ln -nfs ../bkg_Hits*.root . ;')[doembedding] detlist = '' - for d in smallsensorlist: - if isActive(d): - if len(detlist) > 0: - detlist += ',' - detlist += d + detlist = ','.join(smallsensorlist) + detlist = cleanDetectorInputList(detlist) t['cmd'] += commondigicmd + ' --onlyDet ' + detlist - t['cmd'] += ' --ccdb-tof-sa ' + if 'ITS' in detlist: + t['cmd'] += staggITS + if 'MFT' in detlist: + t['cmd'] += staggMFT + t['cmd'] += ' --ccdb-tof-sa --forceSelectedDets ' t['cmd'] += (' --combine-devices ','')[args.no_combine_dpl_devices] t['cmd'] += ('',' --disable-mc')[args.no_mc_labels] workflow['stages'].append(t) return t else: # here we create individual digitizers - if isActive(det): - if usebkgcache: - tneeds += [ BKG_HITDOWNLOADER_TASKS[det]['name'] ] - t = createTask(name=name, needs=tneeds, - tf=tf, cwd=timeframeworkdir, lab=["DIGI","SMALLDIGI"], cpu='1') - t['cmd'] = ('','ln -nfs ../bkg_Hits' + str(det) + '.root . ;')[doembedding] - t['cmd'] += commondigicmd + ' --onlyDet ' + str(det) - t['cmd'] += ('',' --disable-mc')[args.no_mc_labels] - if det == 'TOF': - t['cmd'] += ' --ccdb-tof-sa' - workflow['stages'].append(t) - return t + if usebkgcache: + tneeds += [ BKG_HITDOWNLOADER_TASKS[det]['name'] ] + t = createTask(name=name, needs=tneeds, tf=tf, cwd=timeframeworkdir, lab=["DIGI","SMALLDIGI"], cpu='1') + t['cmd'] = ('','ln -nfs ../bkg_Hits' + str(det) + '.root . ;')[doembedding] + t['cmd'] += commondigicmd + ' --onlyDet ' + str(det) + if det == 'ITS': + t['cmd'] += staggITS + if det == 'MFT': + t['cmd'] += staggMFT + t['cmd'] += ('',' --disable-mc')[args.no_mc_labels] + if det == 'TOF': + t['cmd'] += ' --ccdb-tof-sa' + workflow['stages'].append(t) + return t det_to_digitask={} @@ -896,17 +1250,34 @@ def createRestDigiTask(name, det='ALLSMALLER'): det_to_digitask[det]=t # detectors serving CTP need to be treated somewhat special since CTP needs - # these inputs at the same time --> still need to be made better + # these inputs at the same time --> still need to be made better. tneeds = [ContextTask['name']] if includeQED: tneeds += [QED_task['name']] - t = createTask(name="ft0fv0ctp_digi_" + str(tf), needs=tneeds, - tf=tf, cwd=timeframeworkdir, lab=["DIGI","SMALLDIGI"], cpu='1') - t['cmd'] = ('','ln -nfs ../bkg_HitsFT0.root . ; ln -nfs ../bkg_HitsFV0.root . ;')[doembedding] - t['cmd'] += '${O2_ROOT}/bin/o2-sim-digitizer-workflow ' + getDPL_global_options() + ' -n ' + str(args.ns) + simsoption + ' --onlyDet FT0,FV0,CTP --interactionRate ' + str(INTRATE) + ' --incontext ' + str(CONTEXTFILE) + ' --disable-write-ini' + putConfigValuesNew() + (' --combine-devices','')[args.no_combine_dpl_devices] + ('',' --disable-mc')[args.no_mc_labels] + QEDdigiargs - workflow['stages'].append(t) - det_to_digitask["FT0"]=t - det_to_digitask["FV0"]=t + FT0FV0EMCCTPDIGItask = createTask(name="ft0fv0emcctp_digi_" + str(tf), needs=tneeds, + tf=tf, cwd=timeframeworkdir, lab=["DIGI","SMALLDIGI"], cpu='1') + FT0FV0EMCCTPDIGItask['cmd'] = ('','ln -nfs ../bkg_HitsFT0.root . ; ln -nfs ../bkg_HitsFV0.root . ; ln -nfs ../bkg_HitsEMC.root; ln -nfs ../bkg_Kine.root; ')[doembedding] + FT0FV0EMCCTPDIGItask['cmd'] += task_finalizer([ + '${O2_ROOT}/bin/o2-sim-digitizer-workflow', + getDPL_global_options(), + f'-n {args.ns}', + simsoption, + '--onlyDet ' + ','.join([det for det in ['FT0', 'FV0', 'EMC', 'CTP'] if isActive(det)]), + f'--interactionRate {INTRATE}', + f'--incontext {CONTEXTFILE}', + f'--store-ctp-lumi {CTPSCALER}', + '--disable-write-ini', + putConfigValues(listOfMainKeys=['EMCSimParam','FV0DigParam','FT0DigParam'], localCF={"DigiParams.seed" : str(TFSEED)}), + ('--combine-devices','')[args.no_combine_dpl_devices], + ('',' --disable-mc')[args.no_mc_labels], + QEDdigiargs, + '--forceSelectedDets'], configname = 'ft0fv0emcctp_digi') + + workflow['stages'].append(FT0FV0EMCCTPDIGItask) + det_to_digitask["FT0"]=FT0FV0EMCCTPDIGItask + det_to_digitask["FV0"]=FT0FV0EMCCTPDIGItask + det_to_digitask["EMC"]=FT0FV0EMCCTPDIGItask + det_to_digitask["CTP"]=FT0FV0EMCCTPDIGItask def getDigiTaskName(det): t = det_to_digitask.get(det) @@ -917,19 +1288,39 @@ def getDigiTaskName(det): # ----------- # reco # ----------- - tpcreconeeds=[] + tpcreconeeds=[FT0FV0EMCCTPDIGItask['name']] + tpcclusterneed=[TPCDigitask['name'], FT0FV0EMCCTPDIGItask['name']] if not args.combine_tpc_clusterization: # We treat TPC clusterization in multiple (sector) steps in order to # stay within the memory limit or to parallelize over sector from outside (not yet supported within cluster algo) + # For the clusterization we disable the TPC SC corrections tpcclustertasks=[] sectorpertask=18 for s in range(0,35,sectorpertask): taskname = 'tpcclusterpart' + str((int)(s/sectorpertask)) + '_' + str(tf) tpcclustertasks.append(taskname) - tpcclussect = createTask(name=taskname, needs=[TPCDigitask['name']], tf=tf, cwd=timeframeworkdir, lab=["RECO"], cpu='2', mem='8000') - digitmergerstr = '${O2_ROOT}/bin/o2-tpc-chunkeddigit-merger --tpc-sectors ' + str(s)+'-'+str(s+sectorpertask-1) + ' --tpc-lanes ' + str(NWORKERS) + ' | ' - tpcclussect['cmd'] = (digitmergerstr,'')[args.no_tpc_digitchunking] + ' ${O2_ROOT}/bin/o2-tpc-reco-workflow ' + getDPL_global_options(bigshm=True) + ' --input-type ' + ('digitizer','digits')[args.no_tpc_digitchunking] + ' --output-type clusters,send-clusters-per-sector --tpc-native-cluster-writer \" --outfile tpc-native-clusters-part'+ str((int)(s/sectorpertask)) + '.root\" --tpc-sectors ' + str(s)+'-'+str(s+sectorpertask-1) + ' ' + putConfigValuesNew(["GPU_global"], {"GPU_proc.ompThreads" : 4}) + ('',' --disable-mc')[args.no_mc_labels] - tpcclussect['env'] = { "OMP_NUM_THREADS" : "4", "SHMSIZE" : "16000000000" } + tpcclussect = createTask(name=taskname, needs=tpcclusterneed, tf=tf, cwd=timeframeworkdir, lab=["RECO"], cpu='2', mem='8000') + digitmergerstr = '${O2_ROOT}/bin/o2-tpc-chunkeddigit-merger --tpc-sectors ' + str(s)+'-'+str(s+sectorpertask-1) + ' --tpc-lanes ' + str(NWORKERS_TF) + ' | ' + tpcclussect['cmd'] = (digitmergerstr,'')[args.no_tpc_digitchunking] + task_finalizer( + [ + '${O2_ROOT}/bin/o2-tpc-reco-workflow', + getDPL_global_options(bigshm=True), + '--input-type ' + ('digitizer','digits')[args.no_tpc_digitchunking], + '--output-type clusters,send-clusters-per-sector', + '--corrmap-lumi-mode 3', + '--disable-ctp-lumi-request', + f'--tpc-native-cluster-writer \" --outfile tpc-native-clusters-part{(int)(s/sectorpertask)}.root\"', + f'--tpc-sectors {s}-{s+sectorpertask-1}', + putConfigValues(["GPU_global", + "GPU_proc_nn", + "TPCCorrMap", + "TPCGasParam"], {"GPU_proc.ompThreads" : 4}), + '--disable-mc' if args.no_mc_labels else None + ], configname="tpcclusterizertask" + ) + # RS: note that --disable-IDC-scalers should be added to options above once it is added by the CorrectionMapsOptions::addGlobalOptions) + + tpcclussect['env'] = { "OMP_NUM_THREADS" : "4" , "TBB_NUM_THREADS" : "4" } tpcclussect['semaphore'] = "tpctriggers.root" tpcclussect['retry_count'] = 2 # the task has a race condition --> makes sense to retry workflow['stages'].append(tpcclussect) @@ -939,229 +1330,639 @@ def getDigiTaskName(det): workflow['stages'].append(TPCCLUSMERGEtask) tpcreconeeds.append(TPCCLUSMERGEtask['name']) else: - tpcclus = createTask(name='tpccluster_' + str(tf), needs=[TPCDigitask['name']], tf=tf, cwd=timeframeworkdir, lab=["RECO"], cpu=NWORKERS, mem='2000') - tpcclus['cmd'] = '${O2_ROOT}/bin/o2-tpc-chunkeddigit-merger --tpc-lanes ' + str(NWORKERS) - tpcclus['cmd'] += ' | ${O2_ROOT}/bin/o2-tpc-reco-workflow ' + getDPL_global_options() + ' --input-type digitizer --output-type clusters,send-clusters-per-sector ' + putConfigValuesNew(["GPU_global","TPCGasParam"],{"GPU_proc.ompThreads" : 1}) + ('',' --disable-mc')[args.no_mc_labels] + # TODO: adapt this to the case above and merge code / avoid code duplication + tpcclus = createTask(name='tpccluster_' + str(tf), needs=tpcclusterneed, tf=tf, cwd=timeframeworkdir, lab=["RECO"], cpu=NWORKERS_TF, mem='2000') + tpcclus['cmd'] = '${O2_ROOT}/bin/o2-tpc-chunkeddigit-merger --tpc-lanes ' + str(NWORKERS_TF) + tpcclus['cmd'] += ' | ${O2_ROOT}/bin/o2-tpc-reco-workflow ' + getDPL_global_options() + ' --input-type digitizer --output-type clusters,send-clusters-per-sector --corrmap-lumi-mode 3 --disable-ctp-lumi-request ' + putConfigValues(["GPU_global","TPCGasParam","TPCCorrMap"],{"GPU_proc.ompThreads" : 1}) + ('',' --disable-mc')[args.no_mc_labels] workflow['stages'].append(tpcclus) tpcreconeeds.append(tpcclus['name']) - tpc_corr_scaling_options = anchorConfig.get('tpc-corr-scaling','') + # ===| TPC reco |=== + tpcLocalCFreco=dict() + + # handle distortion corrections and scaling using MC maps + # this assumes the lumi inside the maps is stored in FT0 (pp) scalers + # in case of PbPb the conversion factor ZDC ->FT0 (pp) must be set + tpc_corr_options_mc='' + + tpcCorrmapLumiMode = args.tpc_corrmap_lumi_mode + + if tpcDistortionType == 0: # disable distortion corrections + tpc_corr_options_mc=' --corrmap-lumi-mode 0 ' + tpcLocalCFreco['TPCCorrMap.lumiMean'] = '-1'; + elif tpcDistortionType == 1: # disable scaling + tpc_corr_options_mc=' --corrmap-lumi-mode ' + str(tpcCorrmapLumiMode) + ' ' + tpcLocalCFreco['TPCCorrMap.lumiInst'] = str(CTPSCALER) + tpcLocalCFreco['TPCCorrMap.lumiMean'] = str(CTPSCALER) + elif tpcDistortionType == 2: # full scaling with CTP values + if COLTYPE == 'PbPb': + tpcLocalCFreco['TPCCorrMap.lumiInstFactor'] = str(lumiInstFactor) + tpc_corr_options_mc=' --corrmap-lumi-mode ' + str(tpcCorrmapLumiMode) + ' ' + tpcLocalCFreco['TPCCorrMap.lumiInst'] = str(CTPSCALER) + + # Setup the TPC correction scaling options for reco; They come from the anchoring setup + # Some useful comments from Ruben: + # - lumi-type == 0 means no-scaling of corrections with any measure of the lumi rather than no corrections at all. + # - The "no corrections" mode is imposed by the TPCCorrMap.lumiMean configurable being negative, in this case all other options in the corrections treatment are ignored. + # - But if the MC simulation was done with distortions, then the reco needs --lumy-type 1 (i.e. scale with the CTP lumi) even if the corresponding anchor run reco was using --lumy-type 2 + # (i.e. scaling according to the TPC IDC, which don't exist in the MC). + + anchor_lumi_type = dpl_option_from_config(anchorConfig, 'o2-tpcits-match-workflow', '--lumi-type', section = 'full', default_value = '') + if anchor_lumi_type != '': + anchor_lumi_type = '--lumi-type ' + anchor_lumi_type + anchor_corrmaplumi_mode = dpl_option_from_config(anchorConfig, 'o2-tpcits-match-workflow', '--corrmap-lumi-mode', section = 'full', default_value = '') + if anchor_corrmaplumi_mode != '': + anchor_corrmaplumi_mode = '--corrmap-lumi-mode ' + anchor_corrmaplumi_mode + + tpc_corr_scaling_options = anchor_lumi_type + ' ' + anchor_corrmaplumi_mode + if not isActive('CTP'): + # CTP digits won't be produced for this timeframe (CTP not in the readout detector list) + tpc_corr_scaling_options += ' --disable-ctp-lumi-request' + + # why not simply? + # tpc_corr_scaling_options = ('--lumi-type 1', '')[tpcDistortionType != 0] + + # at the moment MC is not ready for the sector edge fluctuations (RS: and does not use IDC but --disable-IDC-scalers can be added only once it is added by the CorrectionMapsOptions::addGlobalOptions) + tpc_corr_scaling_options += option_if_available('o2-tpc-reco-workflow', '--disable-sec-edge-fluc-correction', envfile=async_envfile) + + #<--------- TPC reco task + if includeTPCSyncMode: + tpcSyncreconeeds = tpcreconeeds.copy() + TPCSyncRECOtask=createTask(name='tpcSyncreco_'+str(tf), needs=tpcSyncreconeeds, tf=tf, cwd=timeframeworkdir, lab=["RECO"], relative_cpu=3/8, mem='16000') + TPCSyncRECOtask['cmd'] = '${O2_ROOT}/bin/o2-tpc-reco-workflow ' + getDPL_global_options(bigshm=True, ccdbbackend=False, runcommand=False) \ + + '--input-type clusters --output-type clusters,disable-writer ' \ + + putConfigValues() + TPCSyncRECOtask['cmd'] += ' | ${O2_ROOT}/bin/o2-gpu-reco-workflow ' + getDPL_global_options(bigshm=True, ccdbbackend=True, runcommand=False) \ + + '--input-type clusters --output-type compressed-clusters-flat,clusters,send-clusters-per-sector --filtered-output-specs ' \ + + tpc_corr_scaling_options + ' ' + tpc_corr_options_mc \ + + putConfigValues(["TPCGasParam", "TPCCorrMap", "trackTuneParams"], + localCF={"GPU_proc.ompThreads":NWORKERS_TF, \ + "GPU_proc.tpcWriteClustersAfterRejection":1, \ + "GPU_rec_tpc.compressionTypeMask":0, \ + "GPU_global.synchronousProcessing":1, \ + "GPU_proc.tpcIncreasedMinClustersPerRow":500000}, + globalTFConfig=False) + TPCSyncRECOtask['cmd'] += ' | ${O2_ROOT}/bin/o2-tpc-reco-workflow ' + getDPL_global_options(bigshm=True, ccdbbackend=False, runcommand=True) + ' --filtered-input --input-type pass-through --output-type clusters,send-clusters-per-sector ' + TPCSyncRECOtask['cmd'] += ' ; mv tpc-filtered-native-clusters.root tpc-native-clusters.root' + workflow['stages'].append(TPCSyncRECOtask) + tpcreconeeds.append(TPCSyncRECOtask['name']) + TPCRECOtask=createTask(name='tpcreco_'+str(tf), needs=tpcreconeeds, tf=tf, cwd=timeframeworkdir, lab=["RECO"], relative_cpu=3/8, mem='16000') - TPCRECOtask['cmd'] = '${O2_ROOT}/bin/o2-tpc-reco-workflow ' + getDPL_global_options(bigshm=True) + ' --input-type clusters --output-type tracks,send-clusters-per-sector ' \ - + putConfigValuesNew(["GPU_global","TPCGasParam", "GPU_rec_tpc", "trackTuneParams"], {"GPU_proc.ompThreads":NWORKERS}) + ('',' --disable-mc')[args.no_mc_labels] \ - + tpc_corr_scaling_options + TPCRECOtask['cmd'] = task_finalizer([ + '${O2_ROOT}/bin/o2-tpc-reco-workflow', + getDPL_global_options(bigshm=True), + '--input-type clusters', + '--output-type tracks,send-clusters-per-sector', + putConfigValues(["GPU_global", + "TPCGasParam", + "TPCCorrMap", + "GPU_rec_tpc", + "trackTuneParams"], + {"GPU_proc.ompThreads":NWORKERS_TF} | tpcLocalCFreco), + ('',' --disable-mc')[args.no_mc_labels], + tpc_corr_scaling_options, + tpc_corr_options_mc, + tpcreco_mctimegain]) workflow['stages'].append(TPCRECOtask) - havePbPb = (COLTYPE == 'PbPb' or (doembedding and COLTYPEBKG == "PbPb")) + #<--------- ITS reco task ITSMemEstimate = 12000 if havePbPb else 2000 # PbPb has much large mem requirement for now (in worst case) - ITSRECOtask=createTask(name='itsreco_'+str(tf), needs=[getDigiTaskName("ITS"), MATBUD_DOWNLOADER_TASK['name']], + ITSRECOtask=createTask(name='itsreco_'+str(tf), needs=[getDigiTaskName("ITS" if args.detectorList == 'ALICE2' else "IT3")], tf=tf, cwd=timeframeworkdir, lab=["RECO"], cpu='1', mem=str(ITSMemEstimate)) - ITSRECOtask['cmd'] = '${O2_ROOT}/bin/o2-its-reco-workflow --trackerCA --tracking-mode async ' + getDPL_global_options(bigshm=havePbPb) \ - + putConfigValuesNew(["ITSVertexerParam", "ITSAlpideParam", - "ITSClustererParam", "ITSCATrackerParam"], {"NameConf.mDirMatLUT" : ".."}) - ITSRECOtask['cmd'] += ('',' --disable-mc')[args.no_mc_labels] + ITSRECOtask['cmd'] = task_finalizer([ + "${O2_ROOT}/bin/o2-its-reco-workflow" if args.detectorList == 'ALICE2' else "${O2_ROOT}/bin/o2-its3-reco-workflow", + getDPL_global_options(bigshm=havePbPb), + staggITS, + '--tracking-mode async', + putConfigValues(["ITSVertexerParam", + "ITSAlpideParam", + "ITSClustererParam", + "ITSCATrackerParam"], + {"NameConf.mDirMatLUT" : ".."}), + ('',' --disable-mc')[args.no_mc_labels] + ]) workflow['stages'].append(ITSRECOtask) - FT0RECOtask=createTask(name='ft0reco_'+str(tf), needs=[getDigiTaskName("FT0")], tf=tf, cwd=timeframeworkdir, lab=["RECO"], mem='1000') - FT0RECOtask['cmd'] = '${O2_ROOT}/bin/o2-ft0-reco-workflow --disable-time-offset-calib ' + getDPL_global_options() + putConfigValues() - workflow['stages'].append(FT0RECOtask) - - ITSTPCMATCHtask=createTask(name='itstpcMatch_'+str(tf), needs=[TPCRECOtask['name'], ITSRECOtask['name'], FT0RECOtask['name']], tf=tf, cwd=timeframeworkdir, lab=["RECO"], mem='8000', relative_cpu=3/8) - ITSTPCMATCHtask['cmd'] = '${O2_ROOT}/bin/o2-tpcits-match-workflow ' + getDPL_global_options(bigshm=True) + ' --tpc-track-reader \"tpctracks.root\" --tpc-native-cluster-reader \"--infile tpc-native-clusters.root\" --use-ft0' \ - + putConfigValuesNew(['MFTClustererParam', 'ITSCATrackerParam', 'tpcitsMatch', 'TPCGasParam', 'ITSClustererParam, GPU_rec_tpc, trackTuneParams'], {"NameConf.mDirMatLUT" : ".."}) \ - + tpc_corr_scaling_options + #<--------- FT0 reco task + FT0RECOtask = createTask(name='ft0reco_'+str(tf), needs=[getDigiTaskName("FT0")], tf=tf, cwd=timeframeworkdir, lab=["RECO"], mem='1000') + FT0RECOtask["cmd"] = task_finalizer([ + '${O2_ROOT}/bin/o2-ft0-reco-workflow', + getDPL_global_options(ccdbbackend=False), # note: when calibrations (or CCDB objects) are reenabled, we need to say ccdbbackend=True + '--disable-time-offset-calib', # because effect not simulated in MC + '--disable-slewing-calib', # because effect not simulated in MC + putConfigValues() + ]) + addWhenActive("FT0", workflow['stages'], FT0RECOtask) + + #<--------- ITS-TPC track matching task + ITSTPCMATCHtask=createTask(name='itstpcMatch_'+str(tf), needs=[TPCRECOtask['name'], ITSRECOtask['name'], FT0RECOtask['name'] if isActive("FT0") else None], tf=tf, cwd=timeframeworkdir, lab=["RECO"], mem='8000', relative_cpu=3/8) + ITSTPCMATCHtask["cmd"] = task_finalizer([ + '${O2_ROOT}/bin/o2-tpcits-match-workflow', + staggITS, + getDPL_global_options(bigshm=True), + ' --tpc-track-reader tpctracks.root', + '--tpc-native-cluster-reader \"--infile tpc-native-clusters.root\"', + '--use-ft0' if isActive("FT0") else None, + putConfigValues(['MFTClustererParam', + 'ITSCATrackerParam', + 'tpcitsMatch', + 'TPCGasParam', + 'TPCCorrMap', + 'ITSClustererParam', + 'GPU_rec_tpc', + 'trackTuneParams', + 'GlobalParams', + 'ft0tag'], + {"NameConf.mDirMatLUT" : ".."} | tpcLocalCFreco), + tpc_corr_scaling_options, + tpc_corr_options_mc + ]) workflow['stages'].append(ITSTPCMATCHtask) + #<--------- ITS-TPC track matching task TRDTRACKINGtask = createTask(name='trdreco_'+str(tf), needs=[TRDDigitask['name'], ITSTPCMATCHtask['name'], TPCRECOtask['name'], ITSRECOtask['name']], tf=tf, cwd=timeframeworkdir, lab=["RECO"], cpu='1', mem='2000') - TRDTRACKINGtask['cmd'] = '${O2_ROOT}/bin/o2-trd-tracklet-transformer ' + getDPL_global_options() + putConfigValues() + ('',' --disable-mc')[args.no_mc_labels] - workflow['stages'].append(TRDTRACKINGtask) + TRDTRACKINGtask['cmd'] = task_finalizer(['${O2_ROOT}/bin/o2-trd-tracklet-transformer', + getDPL_global_options(), + putConfigValues(), + ('',' --disable-mc')[args.no_mc_labels]]) + addWhenActive("TRD", workflow['stages'], TRDTRACKINGtask) + #<--------- TRD global tracking # FIXME This is so far a workaround to avoud a race condition for trdcalibratedtracklets.root - TRDTRACKINGtask2 = createTask(name='trdreco2_'+str(tf), needs=[TRDTRACKINGtask['name'],MATBUD_DOWNLOADER_TASK['name']], tf=tf, cwd=timeframeworkdir, lab=["RECO"], cpu='1', mem='2000') - TRDTRACKINGtask2['cmd'] = '${O2_ROOT}/bin/o2-trd-global-tracking ' + getDPL_global_options(bigshm=True) + ('',' --disable-mc')[args.no_mc_labels] \ - + putConfigValuesNew(['ITSClustererParam', - 'ITSCATrackerParam', - 'trackTuneParams', - 'GPU_rec_tpc', - 'TPCGasParam'], {"NameConf.mDirMatLUT" : ".."}) \ - + " --track-sources " + anchorConfig.get("o2-trd-global-tracking-options",{}).get("track-sources","all") \ - + tpc_corr_scaling_options - workflow['stages'].append(TRDTRACKINGtask2) - + TRDTRACKINGtask2 = createTask(name='trdreco2_'+str(tf), needs=[TRDTRACKINGtask['name']], tf=tf, cwd=timeframeworkdir, lab=["RECO"], cpu='1', mem='2000') + trd_track_sources = cleanDetectorInputList(dpl_option_from_config(anchorConfig, 'o2-trd-global-tracking', '--track-sources', default_value='TPC,ITS-TPC')) + TRDTRACKINGtask2['cmd'] = task_finalizer([ + '${O2_ROOT}/bin/o2-trd-global-tracking', + getDPL_global_options(bigshm=True), + staggITS, + ('',' --disable-mc')[args.no_mc_labels], + putConfigValues(['ITSClustererParam', + 'ITSCATrackerParam', + 'trackTuneParams', + 'GPU_rec_tpc', + 'TPCGasParam', + 'GlobalParams', + 'TPCCorrMap'], {"NameConf.mDirMatLUT" : ".."} | tpcLocalCFreco), + '--track-sources ' + trd_track_sources, + tpc_corr_scaling_options, + tpc_corr_options_mc]) + addWhenActive("TRD", workflow['stages'], TRDTRACKINGtask2) + + #<--------- TOF reco task TOFRECOtask = createTask(name='tofmatch_'+str(tf), needs=[ITSTPCMATCHtask['name'], getDigiTaskName("TOF")], tf=tf, cwd=timeframeworkdir, lab=["RECO"], mem='1500') - TOFRECOtask['cmd'] = '${O2_ROOT}/bin/o2-tof-reco-workflow --use-ccdb ' + getDPL_global_options() + putConfigValuesNew() + ('',' --disable-mc')[args.no_mc_labels] - workflow['stages'].append(TOFRECOtask) - - toftpcmatchneeds = [TOFRECOtask['name'], TPCRECOtask['name']] - toftracksrcdefault = "TPC,ITS-TPC" - if isActive('TRD'): - toftpcmatchneeds.append(TRDTRACKINGtask2['name']) - toftracksrcdefault+=",TPC-TRD,ITS-TPC-TRD" + TOFRECOtask["cmd"] = task_finalizer([ + '${O2_ROOT}/bin/o2-tof-reco-workflow', + getDPL_global_options(), + '--use-ccdb', + putConfigValues(), + ('',' --disable-mc')[args.no_mc_labels] + ]) + addWhenActive("TOF", workflow['stages'], TOFRECOtask) + + #<--------- TOF-TPC(-ITS) global track matcher workflow + toftpcmatchneeds = [TOFRECOtask['name'], + TPCRECOtask['name'], + ITSTPCMATCHtask['name'], + TRDTRACKINGtask2['name'] if isActive("TRD") else None] + toftracksrcdefault = cleanDetectorInputList(dpl_option_from_config(anchorConfig, 'o2-tof-matcher-workflow', '--track-sources', default_value='TPC,ITS-TPC,TPC-TRD,ITS-TPC-TRD')) + tofusefit = option_if_available('o2-tof-matcher-workflow', '--use-fit', envfile=async_envfile) TOFTPCMATCHERtask = createTask(name='toftpcmatch_'+str(tf), needs=toftpcmatchneeds, tf=tf, cwd=timeframeworkdir, lab=["RECO"], mem='1000') - TOFTPCMATCHERtask['cmd'] = '${O2_ROOT}/bin/o2-tof-matcher-workflow ' + getDPL_global_options() \ - + putConfigValuesNew(["ITSClustererParam", - 'TPCGasParam', - 'ITSCATrackerParam', - 'MFTClustererParam', - 'GPU_rec_tpc', - 'trackTuneParams']) \ - + " --track-sources " + anchorConfig.get("o2-tof-matcher-workflow-options",{}).get("track-sources",toftracksrcdefault) + (' --combine-devices','')[args.no_combine_dpl_devices] \ - + tpc_corr_scaling_options - workflow['stages'].append(TOFTPCMATCHERtask) + tofmatcher_cmd_parts = [ + '${O2_ROOT}/bin/o2-tof-matcher-workflow', + getDPL_global_options(), + putConfigValues(['ITSClustererParam', + 'TPCGasParam', + 'TPCCorrMap', + 'ITSCATrackerParam', + 'MFTClustererParam', + 'GPU_rec_tpc', + 'ft0tag', + 'trackTuneParams'], tpcLocalCFreco), + ' --track-sources ' + toftracksrcdefault, + (' --combine-devices','')[args.no_combine_dpl_devices], + tofusefit if isActive("FT0") else None, + tpc_corr_scaling_options, + tpc_corr_options_mc + ] + TOFTPCMATCHERtask['cmd'] = task_finalizer(tofmatcher_cmd_parts) + addWhenActive("TOF", workflow['stages'], TOFTPCMATCHERtask) # MFT reco: needing access to kinematics (when assessment enabled) mftreconeeds = [getDigiTaskName("MFT")] if usebkgcache: mftreconeeds += [ BKG_KINEDOWNLOADER_TASK['name'] ] + #<--------- MFT reco workflow MFTRECOtask = createTask(name='mftreco_'+str(tf), needs=mftreconeeds, tf=tf, cwd=timeframeworkdir, lab=["RECO"], mem='1500') MFTRECOtask['cmd'] = ('','ln -nfs ../bkg_Kine.root . ;')[doembedding] - MFTRECOtask['cmd'] += '${O2_ROOT}/bin/o2-mft-reco-workflow ' + getDPL_global_options() + putConfigValuesNew(['MFTTracking', 'MFTAlpideParam', 'ITSClustererParam','MFTClustererParam']) - MFTRECOtask['cmd'] += ('',' --disable-mc')[args.no_mc_labels] - if args.mft_assessment_full == True: - MFTRECOtask['cmd']+= ' --run-assessment ' - workflow['stages'].append(MFTRECOtask) + MFTRECOtask['cmd'] += task_finalizer([ + '${O2_ROOT}/bin/o2-mft-reco-workflow', + getDPL_global_options(), + staggMFT, + putConfigValues(['MFTTracking', + 'MFTAlpideParam', + 'ITSClustererParam', + 'MFTClustererParam']), + ('','--disable-mc')[args.no_mc_labels], + ('','--run-assessment')[args.mft_assessment_full]]) + addWhenActive("MFT", workflow['stages'], MFTRECOtask) # MCH reco: needing access to kinematics ... so some extra logic needed here mchreconeeds = [getDigiTaskName("MCH")] if usebkgcache: mchreconeeds += [ BKG_KINEDOWNLOADER_TASK['name'] ] + #<--------- MCH reco workflow MCHRECOtask = createTask(name='mchreco_'+str(tf), needs=mchreconeeds, tf=tf, cwd=timeframeworkdir, lab=["RECO"], mem='1500') MCHRECOtask['cmd'] = ('','ln -nfs ../bkg_Kine.root . ;')[doembedding] - MCHRECOtask['cmd'] += '${O2_ROOT}/bin/o2-mch-reco-workflow ' + getDPL_global_options() + putConfigValues() - MCHRECOtask['cmd'] += ('',' --disable-mc')[args.no_mc_labels] - workflow['stages'].append(MCHRECOtask) - + MCHRECOtask['cmd'] += task_finalizer( + ['${O2_ROOT}/bin/o2-mch-reco-workflow', + getDPL_global_options(), + putConfigValues(), + ('',' --disable-mc')[args.no_mc_labels], + '--enable-clusters-root-output']) + addWhenActive("MCH", workflow['stages'], MCHRECOtask) + + #<--------- MID reco workflow MIDRECOtask = createTask(name='midreco_'+str(tf), needs=[getDigiTaskName("MID")], tf=tf, cwd=timeframeworkdir, lab=["RECO"], mem='1500') - MIDRECOtask['cmd'] = '${O2_ROOT}/bin/o2-mid-digits-reader-workflow ' + ('',' --disable-mc')[args.no_mc_labels] + ' | ${O2_ROOT}/bin/o2-mid-reco-workflow ' + getDPL_global_options() + putConfigValues() - MIDRECOtask['cmd'] += ('',' --disable-mc')[args.no_mc_labels] - workflow['stages'].append(MIDRECOtask) - + MIDRECOtask['cmd'] = task_finalizer( + ['${O2_ROOT}/bin/o2-mid-digits-reader-workflow', + ('',' --disable-mc')[args.no_mc_labels]]) + MIDRECOtask['cmd'] += ' | ' + MIDRECOtask['cmd'] += task_finalizer(['${O2_ROOT}/bin/o2-mid-reco-workflow', + getDPL_global_options(), + putConfigValues(),('',' --disable-mc')[args.no_mc_labels]]) + addWhenActive("MID", workflow['stages'], MIDRECOtask) + + #<--------- FDD reco workflow FDDRECOtask = createTask(name='fddreco_'+str(tf), needs=[getDigiTaskName("FDD")], tf=tf, cwd=timeframeworkdir, lab=["RECO"], mem='1500') - FDDRECOtask['cmd'] = '${O2_ROOT}/bin/o2-fdd-reco-workflow ' + getDPL_global_options(ccdbbackend=False) + putConfigValues() - FDDRECOtask['cmd'] += ('',' --disable-mc')[args.no_mc_labels] - workflow['stages'].append(FDDRECOtask) + FDDRECOtask['cmd'] = task_finalizer(['${O2_ROOT}/bin/o2-fdd-reco-workflow', + getDPL_global_options(ccdbbackend=False), + putConfigValues(), + ('',' --disable-mc')[args.no_mc_labels]]) + addWhenActive("FDD", workflow['stages'], FDDRECOtask) + #<--------- FV0 reco workflow FV0RECOtask = createTask(name='fv0reco_'+str(tf), needs=[getDigiTaskName("FV0")], tf=tf, cwd=timeframeworkdir, lab=["RECO"], mem='1500') - FV0RECOtask['cmd'] = '${O2_ROOT}/bin/o2-fv0-reco-workflow ' + getDPL_global_options() + putConfigValues() - FV0RECOtask['cmd'] += ('',' --disable-mc')[args.no_mc_labels] - workflow['stages'].append(FV0RECOtask) + FV0RECOtask['cmd'] = task_finalizer(['${O2_ROOT}/bin/o2-fv0-reco-workflow', + getDPL_global_options(), + putConfigValues(), + ('',' --disable-mc')[args.no_mc_labels]]) + addWhenActive("FV0", workflow['stages'], FV0RECOtask) # calorimeters + #<--------- EMC reco workflow EMCRECOtask = createTask(name='emcalreco_'+str(tf), needs=[getDigiTaskName("EMC")], tf=tf, cwd=timeframeworkdir, lab=["RECO"], mem='1500') - EMCRECOtask['cmd'] = '${O2_ROOT}/bin/o2-emcal-reco-workflow --input-type digits --output-type cells --infile emcaldigits.root ' + getDPL_global_options(ccdbbackend=False) + putConfigValues() - EMCRECOtask['cmd'] += ('',' --disable-mc')[args.no_mc_labels] - workflow['stages'].append(EMCRECOtask) - + # first part + EMCRECOtask['cmd'] = task_finalizer([ + '${O2_ROOT}/bin/o2-emcal-reco-workflow', + putConfigValues(), + '--input-type digits', + '--output-type cells', + '--infile emcaldigits.root', + '--disable-root-output', + '--subspecificationOut 1', + ('',' --disable-mc')[args.no_mc_labels]]) + # second part + EMCRECOtask['cmd'] += ' | ' + EMCRECOtask['cmd'] += task_finalizer([ + '${O2_ROOT}/bin/o2-emcal-cell-recalibrator-workflow', + putConfigValues(), + '--input-subspec 1', + '--output-subspec 0', + '--no-timecalib', + '--no-gaincalib', + (' --isMC','')[args.no_mc_labels]]) + # third part + EMCRECOtask['cmd'] += ' | ' + EMCRECOtask['cmd'] += task_finalizer([ + '${O2_ROOT}/bin/o2-emcal-cell-writer-workflow', + getDPL_global_options(), + '--subspec 0', + ('',' --disable-mc')[args.no_mc_labels]]) + addWhenActive("EMC", workflow['stages'], EMCRECOtask) + + #<--------- PHS reco workflow PHSRECOtask = createTask(name='phsreco_'+str(tf), needs=[getDigiTaskName("PHS")], tf=tf, cwd=timeframeworkdir, lab=["RECO"], mem='1500') - PHSRECOtask['cmd'] = '${O2_ROOT}/bin/o2-phos-reco-workflow ' + getDPL_global_options() + putConfigValues() - PHSRECOtask['cmd'] += ('',' --disable-mc')[args.no_mc_labels] - workflow['stages'].append(PHSRECOtask) - + PHSRECOtask['cmd'] = task_finalizer([ + '${O2_ROOT}/bin/o2-phos-reco-workflow', + getDPL_global_options(), + putConfigValues(), + ('',' --disable-mc')[args.no_mc_labels]]) + addWhenActive("PHS", workflow['stages'], PHSRECOtask) + + #<--------- CPV reco workflow CPVRECOtask = createTask(name='cpvreco_'+str(tf), needs=[getDigiTaskName("CPV")], tf=tf, cwd=timeframeworkdir, lab=["RECO"], mem='1500') - CPVRECOtask['cmd'] = '${O2_ROOT}/bin/o2-cpv-reco-workflow ' + getDPL_global_options() + putConfigValues() - CPVRECOtask['cmd'] += ('',' --disable-mc')[args.no_mc_labels] - workflow['stages'].append(CPVRECOtask) - - if args.with_ZDC: - ZDCRECOtask = createTask(name='zdcreco_'+str(tf), needs=[getDigiTaskName("ZDC")], tf=tf, cwd=timeframeworkdir, lab=["RECO", "ZDC"]) - ZDCRECOtask['cmd'] = '${O2_ROOT}/bin/o2-zdc-digits-reco ' + getDPL_global_options() + putConfigValues() - ZDCRECOtask['cmd'] += ('',' --disable-mc')[args.no_mc_labels] - workflow['stages'].append(ZDCRECOtask) + CPVRECOtask['cmd'] = task_finalizer( + ['${O2_ROOT}/bin/o2-cpv-reco-workflow', + getDPL_global_options(), + putConfigValues(), + ('',' --disable-mc')[args.no_mc_labels]]) + addWhenActive("CPV", workflow['stages'], CPVRECOtask) + + #<--------- ZDC reco workflow + ZDCRECOtask = createTask(name='zdcreco_'+str(tf), needs=[getDigiTaskName("ZDC")], tf=tf, cwd=timeframeworkdir, lab=["RECO", "ZDC"]) + ZDCRECOtask['cmd'] = task_finalizer( + ['${O2_ROOT}/bin/o2-zdc-digits-reco', + getDPL_global_options(), + putConfigValues(), + ('',' --disable-mc')[args.no_mc_labels]]) + addWhenActive("ZDC", workflow['stages'], ZDCRECOtask) ## forward matching + #<--------- MCH-MID forward matching MCHMIDMATCHtask = createTask(name='mchmidMatch_'+str(tf), needs=[MCHRECOtask['name'], MIDRECOtask['name']], tf=tf, cwd=timeframeworkdir, lab=["RECO"], mem='1500') - MCHMIDMATCHtask['cmd'] = '${O2_ROOT}/bin/o2-muon-tracks-matcher-workflow ' + getDPL_global_options(ccdbbackend=False) - MCHMIDMATCHtask['cmd'] += ('',' --disable-mc')[args.no_mc_labels] - workflow['stages'].append(MCHMIDMATCHtask) + MCHMIDMATCHtask['cmd'] = task_finalizer( + ['${O2_ROOT}/bin/o2-muon-tracks-matcher-workflow', + getDPL_global_options(ccdbbackend=False), + putConfigValues(), + ('',' --disable-mc')[args.no_mc_labels]]) + addWhenActive("MID,MCH", workflow['stages'], MCHMIDMATCHtask) + + #<--------- MFT-MCH forward matching + forwardmatchneeds = [MCHRECOtask['name'], + MFTRECOtask['name'], + MCHMIDMATCHtask['name'] if isActive("MID") else None] + MFTMCHMATCHtask = createTask(name='mftmchMatch_'+str(tf), needs=forwardmatchneeds, tf=tf, cwd=timeframeworkdir, lab=["RECO"], mem='1500') + MFTMCHMATCHtask['cmd'] = task_finalizer( + ['${O2_ROOT}/bin/o2-globalfwd-matcher-workflow', + staggMFT, + putConfigValues(['ITSAlpideConfig', + 'MFTAlpideConfig', + 'FwdMatching'],{"FwdMatching.useMIDMatch": "true" if isActive("MID") else "false"}), + ('',' --disable-mc')[args.no_mc_labels]]) - MFTMCHMATCHtask = createTask(name='mftmchMatch_'+str(tf), needs=[MCHMIDMATCHtask['name'], MFTRECOtask['name']], tf=tf, cwd=timeframeworkdir, lab=["RECO"], mem='1500') - MFTMCHMATCHtask['cmd'] = '${O2_ROOT}/bin/o2-globalfwd-matcher-workflow ' + putConfigValuesNew(['ITSAlpideConfig','MFTAlpideConfig','FwdMatching'],{"FwdMatching.useMIDMatch":"true"}) if args.fwdmatching_assessment_full == True: - MFTMCHMATCHtask['cmd']+= ' | o2-globalfwd-assessment-workflow ' - MFTMCHMATCHtask['cmd']+= getDPL_global_options() + ('',' --disable-mc')[args.no_mc_labels] - workflow['stages'].append(MFTMCHMATCHtask) + MFTMCHMATCHtask['cmd'] += ' | ' + MFTMCHMATCHtask['cmd'] += task_finalizer( + ['${O2_ROOT}/bin/o2-globalfwd-assessment-workflow', + getDPL_global_options(), + ('',' --disable-mc')[args.no_mc_labels]]) + addWhenActive("MFT,MCH", workflow['stages'], MFTMCHMATCHtask) if args.fwdmatching_save_trainingdata == True: MFTMCHMATCHTraintask = createTask(name='mftmchMatchTrain_'+str(tf), needs=[MCHMIDMATCHtask['name'], MFTRECOtask['name']], tf=tf, cwd=timeframeworkdir, lab=["RECO"], mem='1500') - MFTMCHMATCHTraintask['cmd'] = '${O2_ROOT}/bin/o2-globalfwd-matcher-workflow ' + putConfigValuesNew(['ITSAlpideConfig','MFTAlpideConfig'],{"FwdMatching.useMIDMatch":"true"}) + MFTMCHMATCHTraintask['cmd'] = '${O2_ROOT}/bin/o2-globalfwd-matcher-workflow ' + staggMFT + putConfigValues(['ITSAlpideConfig','MFTAlpideConfig'],{"FwdMatching.useMIDMatch":"true"}) MFTMCHMATCHTraintask['cmd']+= getDPL_global_options() workflow['stages'].append(MFTMCHMATCHTraintask) # HMP tasks + #<--------- HMP forward matching HMPRECOtask = createTask(name='hmpreco_'+str(tf), needs=[getDigiTaskName('HMP')], tf=tf, cwd=timeframeworkdir, lab=["RECO"], mem='1000') - HMPRECOtask['cmd'] = '${O2_ROOT}/bin/o2-hmpid-digits-to-clusters-workflow ' + getDPL_global_options(ccdbbackend=False) + putConfigValuesNew() - workflow['stages'].append(HMPRECOtask) - - HMPMATCHtask = createTask(name='hmpmatch_'+str(tf), needs=[HMPRECOtask['name'],ITSTPCMATCHtask['name'],TOFTPCMATCHERtask['name']], tf=tf, cwd=timeframeworkdir, lab=["RECO"], mem='1000') - HMPMATCHtask['cmd'] = '${O2_ROOT}/bin/o2-hmpid-matcher-workflow ' + getDPL_global_options() + putConfigValuesNew() - workflow['stages'].append(HMPMATCHtask) - - # Take None as default, we only add more if nothing from anchorConfig - pvfinder_sources = anchorConfig.get("o2-primary-vertexing-workflow-options",{}).get("vertexing-sources", None) - pvfinder_matching_sources = anchorConfig.get("o2-primary-vertexing-workflow-options",{}).get("vertex-track-matching-sources", None) - pvfinderneeds = [ITSTPCMATCHtask['name']] - if not pvfinder_sources: - pvfinder_sources = "ITS,ITS-TPC,ITS-TPC-TRD,ITS-TPC-TOF,ITS-TPC-TRD-TOF" - if not pvfinder_matching_sources: - pvfinder_matching_sources = "ITS,MFT,TPC,ITS-TPC,MCH,MFT-MCH,TPC-TOF,TPC-TRD,ITS-TPC-TRD,ITS-TPC-TOF,ITS-TPC-TRD-TOF" - if isActive("MID"): - pvfinder_matching_sources += ",MID" - pvfinderneeds += [MIDRECOtask['name']] - if isActive('MCH') and isActive('MID'): - pvfinder_matching_sources += ",MCH-MID" - pvfinderneeds += [MCHMIDMATCHtask['name']] - - if isActive('FT0'): - pvfinderneeds += [FT0RECOtask['name']] - pvfinder_matching_sources += ",FT0" - if isActive('FV0'): - pvfinderneeds += [FV0RECOtask['name']] - pvfinder_matching_sources += ",FV0" - if isActive('FDD'): - pvfinderneeds += [FT0RECOtask['name']] - pvfinder_matching_sources += ",FDD" - if isActive('EMC'): - pvfinderneeds += [EMCRECOtask['name']] - pvfinder_matching_sources += ",EMC" - if isActive('PHS'): - pvfinderneeds += [PHSRECOtask['name']] - pvfinder_matching_sources += ",PHS" - if isActive('CPV'): - pvfinderneeds += [CPVRECOtask['name']] - pvfinder_matching_sources += ",CPV" - if isActive('TOF'): - pvfinderneeds += [TOFTPCMATCHERtask['name']] - if isActive('MFT'): - pvfinderneeds += [MFTRECOtask['name']] - if isActive('MCH'): - pvfinderneeds += [MCHRECOtask['name']] - if isActive('TRD'): - pvfinderneeds += [TRDTRACKINGtask2['name']] - if isActive('FDD'): - pvfinderneeds += [FDDRECOtask['name']] - if isActive('MFT') and isActive('MCH'): - pvfinderneeds += [MFTMCHMATCHtask['name']] - - - PVFINDERtask = createTask(name='pvfinder_'+str(tf), needs=pvfinderneeds, tf=tf, cwd=timeframeworkdir, lab=["RECO"], cpu=NWORKERS, mem='4000') - PVFINDERtask['cmd'] = '${O2_ROOT}/bin/o2-primary-vertexing-workflow ' \ - + getDPL_global_options() + putConfigValuesNew(['ITSAlpideParam','MFTAlpideParam', 'pvertexer', 'TPCGasParam'], {"NameConf.mDirMatLUT" : ".."}) - PVFINDERtask['cmd'] += ' --vertexing-sources ' + pvfinder_sources + ' --vertex-track-matching-sources ' + pvfinder_matching_sources + (' --combine-source-devices','')[args.no_combine_dpl_devices] - PVFINDERtask['cmd'] += ('',' --disable-mc')[args.no_mc_labels] + HMPRECOtask['cmd'] = task_finalizer( + ['${O2_ROOT}/bin/o2-hmpid-digits-to-clusters-workflow', + getDPL_global_options(ccdbbackend=False), + putConfigValues()]) + addWhenActive("HMP", workflow['stages'], HMPRECOtask) + + #<--------- HMP forward matching + hmpmatchneeds = [HMPRECOtask['name'], + ITSTPCMATCHtask['name'], + TOFTPCMATCHERtask['name'] if isActive("TOF") else None, + TRDTRACKINGtask2['name'] if isActive("TRD") else None] + hmpmatchneeds = [ n for n in hmpmatchneeds if n != None ] + hmp_match_sources = cleanDetectorInputList(dpl_option_from_config(anchorConfig, 'o2-hmpid-matcher-workflow', '--track-sources', default_value='ITS-TPC,ITS-TPC-TRD,TPC-TRD')) + HMPMATCHtask = createTask(name='hmpmatch_'+str(tf), needs=hmpmatchneeds, tf=tf, cwd=timeframeworkdir, lab=["RECO"], mem='1000') + HMPMATCHtask['cmd'] = task_finalizer( + ['${O2_ROOT}/bin/o2-hmpid-matcher-workflow', + '--track-sources ' + hmp_match_sources, + getDPL_global_options(), + putConfigValues() + ]) + addWhenActive("HMP", workflow['stages'], HMPMATCHtask) + + #<---------- primary vertex finding + pvfinder_sources = dpl_option_from_config(anchorConfig, + 'o2-primary-vertexing-workflow', + '--vertexing-sources', + default_value='ITS-TPC,TPC-TRD,ITS-TPC-TRD,TPC-TOF,ITS-TPC-TOF,TPC-TRD-TOF,ITS-TPC-TRD-TOF,MFT-MCH,MCH-MID,ITS,MFT,TPC,TOF,FT0,MID,EMC,PHS,CPV,FDD,HMP,FV0,TRD,MCH,CTP') + pvfinder_sources = cleanDetectorInputList(pvfinder_sources) + + pvfinder_matching_sources = dpl_option_from_config(anchorConfig, + 'o2-primary-vertexing-workflow', + '--vertex-track-matching-sources', + default_value='ITS-TPC,TPC-TRD,ITS-TPC-TRD,TPC-TOF,ITS-TPC-TOF,TPC-TRD-TOF,ITS-TPC-TRD-TOF,MFT-MCH,MCH-MID,ITS,MFT,TPC,TOF,FT0,MID,EMC,PHS,CPV,FDD,HMP,FV0,TRD,MCH,CTP') + pvfinder_matching_sources = cleanDetectorInputList(pvfinder_matching_sources) + + pvfinderneeds = [TRDTRACKINGtask2['name'] if isActive("TRD") else None, + ITSRECOtask['name'] if isActive("ITS") else None, + TPCRECOtask['name'] if isActive("TPC") else None, + MFTRECOtask['name'] if isActive("MFT") else None, + MCHRECOtask['name'] if isActive("MCH") else None, + FT0RECOtask['name'] if isActive("FT0") else None, + FV0RECOtask['name'] if isActive("FV0") else None, + EMCRECOtask['name'] if isActive("EMC") else None, + PHSRECOtask['name'] if isActive("PHS") else None, + CPVRECOtask['name'] if isActive("CPV") else None, + FDDRECOtask['name'] if isActive("FDD") else None, + ITSTPCMATCHtask['name'] if isActive("ITS") and isActive("TPC") else None, + TOFTPCMATCHERtask['name'] if isActive("TPC") and isActive("TOF") else None, + MFTMCHMATCHtask['name'] if isActive("MFT") and isActive("MCH") else None, + MCHMIDMATCHtask['name'] if isActive("MCH") and isActive("MID") else None] + pvfinderneeds = [p for p in pvfinderneeds if p != None] + + PVFINDERtask = createTask(name='pvfinder_'+str(tf), needs=pvfinderneeds, tf=tf, cwd=timeframeworkdir, lab=["RECO"], cpu=NWORKERS_TF, mem='4000') + PVFINDERtask['cmd'] = task_finalizer( + [ '${O2_ROOT}/bin/o2-primary-vertexing-workflow', + getDPL_global_options(), + putConfigValues(['ITSAlpideParam', + 'MFTAlpideParam', + 'pvertexer', + 'TPCGasParam', + 'TPCCorrMap', + 'ft0tag'], + {"NameConf.mDirMatLUT" : ".."}), + '--vertexing-sources ' + pvfinder_sources, + '--vertex-track-matching-sources ' + pvfinder_matching_sources, + (' --combine-source-devices','')[args.no_combine_dpl_devices], + ('',' --disable-mc')[args.no_mc_labels] + ]) workflow['stages'].append(PVFINDERtask) + #<------------- secondary vertexer + svfinder_threads = ' --threads 1 ' + svfinder_cpu = 1 + if COLTYPE == "PbPb" or (doembedding and COLTYPEBKG == "PbPb"): + svfinder_threads = ' --threads 8 ' + svfinder_cpu = 8 + + svfinder_sources = dpl_option_from_config(anchorConfig, + 'o2-primary-vertexing-workflow', + '--vertex-track-matching-sources', + default_value='ITS-TPC,TPC-TRD,ITS-TPC-TRD,TPC-TOF,ITS-TPC-TOF,TPC-TRD-TOF,ITS-TPC-TRD-TOF,MFT-MCH,MCH-MID,ITS,MFT,TPC,TOF,FT0,MID,EMC,PHS,CPV,ZDC,FDD,HMP,FV0,TRD,MCH,CTP') + svfinder_sources = cleanDetectorInputList(svfinder_sources) + SVFINDERtask = createTask(name='svfinder_'+str(tf), needs=[PVFINDERtask['name'], FT0FV0EMCCTPDIGItask['name']], tf=tf, cwd=timeframeworkdir, lab=["RECO"], cpu=svfinder_cpu, mem='5000') + SVFINDERtask['cmd'] = task_finalizer( + [ '${O2_ROOT}/bin/o2-secondary-vertexing-workflow', + staggITS, + getDPL_global_options(bigshm=True), + svfinder_threads, + putConfigValues(['svertexer', 'TPCCorrMap', 'GlobalParams'], {"NameConf.mDirMatLUT" : ".."} | tpcLocalCFreco), + tpc_corr_scaling_options, + tpc_corr_options_mc, + '--vertexing-sources ' + svfinder_sources, + ('--combine-source-devices','')[args.no_combine_dpl_devices], + ('',' --disable-strangeness-tracker')[args.no_strangeness_tracking], + ('',' --disable-mc')[args.no_mc_labels and not args.no_strangeness_tracking] # strangeness tracking may require MC labels + ]) + workflow['stages'].append(SVFINDERtask) + + #<------------- AOD producer + # TODO This needs further refinement, sources and dependencies should be constructed dynamically + aod_info_souces_default = 'ITS-TPC,TPC-TRD,ITS-TPC-TRD,TPC-TOF,ITS-TPC-TOF,TPC-TRD-TOF,ITS-TPC-TRD-TOF,MFT-MCH,MCH-MID,ITS,MFT,TPC,TOF,FT0,MID,EMC,PHS,CPV,ZDC,FDD,HMP,FV0,TRD,MCH,CTP' + aodinfosources = dpl_option_from_config(anchorConfig, 'o2-aod-producer-workflow', '--info-sources', default_value=aod_info_souces_default) + aodinfosources = cleanDetectorInputList(aodinfosources) + aodneeds = [PVFINDERtask['name'], SVFINDERtask['name'], + HMPMATCHtask['name'] if isActive("HMP") else None, + ZDCRECOtask['name'] if isActive("ZDC") else None, + MIDRECOtask['name'] if isActive("MID") else None] + + if usebkgcache: + aodneeds += [ BKG_KINEDOWNLOADER_TASK['name'] ] + + aod_df_id = '{0:03}'.format(tf) + + import os + aod_creator = os.getenv("JALIEN_USER") + if aod_creator == None: + # we use JAliEn to determine the user and capture it's output into a variable via redirect_stdout + import io + from contextlib import redirect_stdout + f = io.StringIO() + with redirect_stdout(f): + if JAlien(['whoami']) == 0: + aod_creator = f.getvalue().strip() + print (f"Determined GRID username {aod_creator}") + + # this option might not be universally available + created_by_option = option_if_available('o2-aod-producer-workflow', '--created-by', envfile=async_envfile) + if created_by_option != '': + created_by_option += ' ' + aod_creator + + aod_parent_option = option_if_available('o2-aod-producer-workflow', '--aod-parent', envfile=async_envfile) + if aod_parent_option != '' and len(args.aod_parent_file) > 0: + aod_parent_option += ' ' + args.aod_parent_file + else: + aod_parent_option = '' + + aod_timeframe_id = f"${{ALIEN_PROC_ID}}{aod_df_id}" if not args.run_anchored else "" + if len(args.aod_output_folder) > 0: + aod_timeframe_id = args.aod_output_folder + + AODtask = createTask(name='aod_'+str(tf), needs=aodneeds, tf=tf, cwd=timeframeworkdir, lab=["AOD"], mem='4000', cpu='1') + AODtask['cmd'] = ('','ln -nfs ../bkg_Kine.root . ;')[doembedding] + AODtask['cmd'] += '[ -f AO2D.root ] && rm AO2D.root; ' + AODtask['cmd'] += task_finalizer([ + "${O2_ROOT}/bin/o2-aod-producer-workflow", + staggITS, + staggMFT, + "--reco-mctracks-only 1", + "--aod-writer-keep dangling", + "--aod-writer-resfile AO2D", + '--aod-writer-resmode "UPDATE"', + f"--run-number {args.run}", + getDPL_global_options(bigshm=True), + f"--info-sources {aodinfosources}", + f"--lpmp-prod-tag {args.productionTag}", + "--anchor-pass ${ALIEN_JDL_LPMANCHORPASSNAME:-unknown}", + "--anchor-prod ${ALIEN_JDL_LPMANCHORPRODUCTION:-unknown}", + "--reco-pass ${ALIEN_JDL_LPMPASSNAME:-unknown}", + aod_parent_option, + created_by_option, + "--combine-source-devices" if not args.no_combine_dpl_devices else "", + "--disable-mc" if args.no_mc_labels else "", + "--enable-truncation 0" if environ.get("O2DPG_AOD_NOTRUNCATE") or environ.get("ALIEN_JDL_O2DPG_AOD_NOTRUNCATE") else "", + "--disable-strangeness-tracker" if args.no_strangeness_tracking else "", + f"--aod-timeframe-id {aod_timeframe_id}" if len(aod_timeframe_id) > 0 else "" + ]) + # Consider in future: AODtask['disable_alternative_reco_software'] = True # do not apply reco software here (we prefer latest aod converter) + workflow['stages'].append(AODtask) + + if includeTPCResiduals: + print ("Adding TPC residuals extraction and aggregation") + + #<------------- TPC residuals extraction + scdcalib_vertex_sources = cleanDetectorInputList(dpl_option_from_config(anchorConfig, + 'o2-tpc-scdcalib-interpolation-workflow', + '--vtx-sources', + default_value='ITS-TPC,TPC-TRD,ITS-TPC-TRD,TPC-TOF,ITS-TPC-TOF,TPC-TRD-TOF,ITS-TPC-TRD-TOF,MFT-MCH,MCH-MID,ITS,MFT,TPC,TOF,FT0,MID,EMC,PHS,CPV,FDD,HMP,FV0,TRD,MCH,CTP')) + + scdcalib_track_sources = cleanDetectorInputList(dpl_option_from_config(anchorConfig, + 'o2-tpc-scdcalib-interpolation-workflow', + '--tracking-sources', + default_value='ITS-TPC,TPC-TRD,ITS-TPC-TRD,TPC-TOF,ITS-TPC-TOF,TPC-TRD-TOF,ITS-TPC-TRD-TOF,MFT-MCH,MCH-MID,ITS,MFT,TPC,TOF,FT0,MID,EMC,PHS,CPV,FDD,HMP,FV0,TRD,MCH,CTP')) + + scdcalib_track_extraction = cleanDetectorInputList(dpl_option_from_config(anchorConfig, + 'o2-tpc-scdcalib-interpolation-workflow', + '--tracking-sources-map-extraction', + default_value='ITS-TPC')) + + # store the MC truth of the residual tracks (branch trkMC of the trackData tree) whenever MC labels are available + scdcalib_mc_option = '' if args.no_mc_labels else option_if_available('o2-tpc-scdcalib-interpolation-workflow', '--enable-mc', envfile=async_envfile) + + SCDCALIBtask = createTask(name='scdcalib_'+str(tf), needs=[PVFINDERtask['name']], tf=tf, cwd=timeframeworkdir, lab=["CALIB"], mem='4000') + SCDCALIBtask['cmd'] = task_finalizer( + [ '${O2_ROOT}/bin/o2-tpc-scdcalib-interpolation-workflow', + getDPL_global_options(bigshm=True), + putConfigValues(['scdcalib']), + '--vtx-sources ' + scdcalib_vertex_sources, + '--tracking-sources ' + scdcalib_track_sources, + '--tracking-sources-map-extraction ' + scdcalib_track_extraction, + '--sec-per-slot 1 ', + '--send-track-data', + scdcalib_mc_option + ]) + workflow['stages'].append(SCDCALIBtask) + + #<------------- TPC residuals aggregator + scdaggreg_secperslot = dpl_option_from_config(anchorConfig, + 'o2-calibration-residual-aggregator', + '--sec-per-slot', + default_value='600') + scdaggreg_outputtype = dpl_option_from_config(anchorConfig, + 'o2-calibration-residual-aggregator', + '--output-type', + default_value='trackParams,unbinnedResid') + + SCDAGGREGtask = createTask(name='scdaggreg_'+str(tf), needs=[SCDCALIBtask['name']], tf=tf, cwd=timeframeworkdir, lab=["CALIB"], mem='1500') + SCDAGGREGtask['cmd'] = task_finalizer( + [ '${O2_ROOT}/bin/o2-calibration-residual-aggregator', + getDPL_global_options(bigshm=True), + '--sec-per-slot ' + scdaggreg_secperslot, + '--enable-ctp ', + '--enable-track-input', + '--output-dir ./', + '--output-type ' + scdaggreg_outputtype, + '--meta-output-dir /dev/null', + scdcalib_mc_option + ]) + workflow['stages'].append(SCDAGGREGtask) + + # conditional + # + # QC tasks follow + # + if includeFullQC or includeLocalQC: def addQCPerTF(taskName, needs, readerCommand, configFilePath, objectsFile=''): - task = createTask(name=taskName + '_local' + str(tf), needs=needs, tf=tf, cwd=timeframeworkdir, lab=["QC"], cpu=1, mem='2000') + task = createTask(name=taskName + '_local_' + str(tf), needs=needs, tf=tf, cwd=timeframeworkdir, lab=["QC"], cpu=1, mem='2000') objectsFile = objectsFile if len(objectsFile) > 0 else taskName + '.root' - # the --local-batch argument will make QC Tasks store their results in a file and merge with any existing objects - task['cmd'] = f'{readerCommand} | o2-qc --config {configFilePath}' + \ + + def remove_json_prefix(path): + return re.sub(r'^json://', '', path) + + configFilePathOnDisk = remove_json_prefix(configFilePath) + # we check if the configFilePath actually exists in the currently loaded software. Otherwise we exit immediately and gracefully + task['cmd'] = ' if [ -f ' + configFilePathOnDisk + ' ]; then { ' + # The actual QC command + # the --local-batch argument will make QC Tasks store their results in a file and merge with any existing objects + task['cmd'] += f'{readerCommand} | o2-qc --config {configFilePath}' + \ f' --local-batch ../{qcdir}/{objectsFile}' + \ - f' --override-values "qc.config.Activity.number={args.run};qc.config.Activity.periodName={args.productionTag};qc.config.Activity.start={args.timestamp}"' + \ + f' --override-values "qc.config.database.host={args.qcdbHost};qc.config.Activity.number={args.run};qc.config.Activity.type=PHYSICS;qc.config.Activity.periodName={args.productionTag};qc.config.Activity.beamType={args.col};qc.config.Activity.start={args.timestamp};qc.config.conditionDB.url={args.conditionDB}"' + \ ' ' + getDPL_global_options(ccdbbackend=False) + task['cmd'] += ' ;} else { echo "Task ' + taskName + ' not performed due to config file not found "; } fi' + # Prevents this task from being run for multiple TimeFrames at the same time, thus trying to modify the same file. task['semaphore'] = objectsFile workflow['stages'].append(task) @@ -1169,20 +1970,26 @@ def addQCPerTF(taskName, needs, readerCommand, configFilePath, objectsFile=''): ### MFT # to be enabled once MFT Digits should run 5 times with different configurations - for flp in range(5): - addQCPerTF(taskName='mftDigitsQC' + str(flp), + # At the moment QC does not support per-layer digits/clusters, disable these tasks if staggering is requested + if isActive("MFT") and not staggMFT: + for flp in range(5): + addQCPerTF(taskName='mftDigitsQC' + str(flp), needs=[getDigiTaskName("MFT")], - readerCommand='o2-qc-mft-digits-root-file-reader --mft-digit-infile=mftdigits.root', + readerCommand='o2-qc-mft-digits-root-file-reader --mft-digit-infile=mftdigits.root' + staggMFT, configFilePath='json://${O2DPG_ROOT}/MC/config/QC/json/mft-digits-' + str(flp) + '.json', objectsFile='mftDigitsQC.root') - addQCPerTF(taskName='mftClustersQC', + addQCPerTF(taskName='mftClustersQC', needs=[MFTRECOtask['name']], - readerCommand='o2-global-track-cluster-reader --track-types none --cluster-types MFT', + readerCommand='o2-global-track-cluster-reader --track-types none --cluster-types MFT' + staggMFT, configFilePath='json://${O2DPG_ROOT}/MC/config/QC/json/mft-clusters.json') - addQCPerTF(taskName='mftTracksQC', + addQCPerTF(taskName='mftTracksQC', needs=[MFTRECOtask['name']], - readerCommand='o2-global-track-cluster-reader --track-types MFT --cluster-types MFT', + readerCommand='o2-global-track-cluster-reader --track-types MFT --cluster-types MFT' + staggMFT, configFilePath='json://${O2DPG_ROOT}/MC/config/QC/json/mft-tracks.json') + addQCPerTF(taskName='mftMCTracksQC', + needs=[MFTRECOtask['name']], + readerCommand='o2-global-track-cluster-reader --track-types MFT --cluster-types MFT' + staggMFT, + configFilePath='json://${O2DPG_ROOT}/MC/config/QC/json/mft-tracks-mc.json') ### TPC # addQCPerTF(taskName='tpcTrackingQC', @@ -1196,52 +2003,80 @@ def addQCPerTF(taskName, needs, readerCommand, configFilePath, objectsFile=''): configFilePath='json://${O2DPG_ROOT}/MC/config/QC/json/tpc-qc-standard-direct.json') ### TRD - addQCPerTF(taskName='trdDigitsQC', + # TODO: check if the readerCommand also reperforms tracklet construction (which already done in digitization) + if isActive('TRD'): + addQCPerTF(taskName='trdDigitsQC', needs=[TRDDigitask['name']], - readerCommand='o2-trd-trap-sim', - configFilePath='json://${O2DPG_ROOT}/MC/config/QC/json/trd-digits-task.json') + readerCommand='o2-trd-trap-sim --disable-root-output true', + configFilePath='json://${O2DPG_ROOT}/MC/config/QC/json/trd-standalone-task.json') + + addQCPerTF(taskName='trdTrackingQC', + needs=[TRDTRACKINGtask2['name']], + readerCommand='o2-global-track-cluster-reader --track-types "ITS-TPC-TRD,TPC-TRD" --cluster-types none', + configFilePath='json://${O2DPG_ROOT}/MC/config/QC/json/trd-tracking-task.json') ### TOF - addQCPerTF(taskName='tofDigitsQC', - needs=[getDigiTaskName("TOF")], - readerCommand='${O2_ROOT}/bin/o2-tof-reco-workflow --delay-1st-tf 3 --input-type digits --output-type none', - configFilePath='json://${O2DPG_ROOT}/MC/config/QC/json/tofdigits.json', - objectsFile='tofDigitsQC.root') - - # depending if TRD and FT0 are available - if isActive('FT0') and isActive('TRD'): - addQCPerTF(taskName='tofft0PIDQC', + if isActive('TOF'): + addQCPerTF(taskName='tofDigitsQC', + needs=[getDigiTaskName("TOF")], + readerCommand='${O2_ROOT}/bin/o2-tof-reco-workflow --delay-1st-tf 3 --input-type digits --output-type none', + configFilePath='json://${O2DPG_ROOT}/MC/config/QC/json/tofdigits.json', + objectsFile='tofDigitsQC.root') + + # depending if TRD and FT0 are available + if isActive('FT0') and isActive('TRD'): + addQCPerTF(taskName='tofft0PIDQC', needs=[TOFTPCMATCHERtask['name'], FT0RECOtask['name']], readerCommand='o2-global-track-cluster-reader --track-types "ITS-TPC-TOF,TPC-TOF,TPC,ITS-TPC-TRD,ITS-TPC-TRD-TOF,TPC-TRD,TPC-TRD-TOF" --cluster-types FT0', configFilePath='json://${O2DPG_ROOT}/MC/config/QC/json/pidft0tof.json') - elif isActive('FT0'): - addQCPerTF(taskName='tofft0PIDQC', + elif isActive('FT0'): + addQCPerTF(taskName='tofft0PIDQC', needs=[TOFTPCMATCHERtask['name']], readerCommand='o2-global-track-cluster-reader --track-types "ITS-TPC-TOF,TPC-TOF,TPC" --cluster-types FT0', - configFilePath='json://${O2DPG_ROOT}/MC/config/QC/json/pidft0tofNoTRD.json') - elif isActive('TRD'): - addQCPerTF(taskName='tofPIDQC', + configFilePath='json://${O2DPG_ROOT}/MC/config/QC/json/pidft0tofNoTRD.json') + elif isActive('TRD'): + addQCPerTF(taskName='tofPIDQC', needs=[TOFTPCMATCHERtask['name']], readerCommand='o2-global-track-cluster-reader --track-types "ITS-TPC-TOF,TPC-TOF,TPC,ITS-TPC-TRD,ITS-TPC-TRD-TOF,TPC-TRD,TPC-TRD-TOF" --cluster-types none', - configFilePath='json://${O2DPG_ROOT}/MC/config/QC/json/pidtof.json') - else: - addQCPerTF(taskName='tofPIDQC', + configFilePath='json://${O2DPG_ROOT}/MC/config/QC/json/pidtof.json') + else: + addQCPerTF(taskName='tofPIDQC', needs=[TOFTPCMATCHERtask['name']], readerCommand='o2-global-track-cluster-reader --track-types "ITS-TPC-TOF,TPC-TOF,TPC" --cluster-types none', - configFilePath='json://${O2DPG_ROOT}/MC/config/QC/json/pidtofNoTRD.json') + configFilePath='json://${O2DPG_ROOT}/MC/config/QC/json/pidtofNoTRD.json') ### EMCAL if isActive('EMC'): - addQCPerTF(taskName='emcCellQC', + addQCPerTF(taskName='emcRecoQC', needs=[EMCRECOtask['name']], readerCommand='o2-emcal-cell-reader-workflow --infile emccells.root', - configFilePath='json://${O2DPG_ROOT}/MC/config/QC/json/emc-cell-task.json') + configFilePath='json://${O2DPG_ROOT}/MC/config/QC/json/emc-reco-tasks.json') + if isActive('CTP'): + addQCPerTF(taskName='emcBCQC', + needs=[EMCRECOtask['name'], getDigiTaskName("CTP")], + readerCommand='o2-emcal-cell-reader-workflow --infile emccells.root | o2-ctp-digit-reader --inputfile ctpdigits.root --disable-mc', + configFilePath='json://${O2DPG_ROOT}/MC/config/QC/json/emc-bc-task.json') ### FT0 - addQCPerTF(taskName='RecPointsQC', + if isActive("FT0"): + addQCPerTF(taskName='RecPointsQC', needs=[FT0RECOtask['name']], - readerCommand='o2-ft0-recpoints-reader-workflow --infile o2reco_ft0.root', + readerCommand='o2-ft0-recpoints-reader-workflow --infile o2reco_ft0.root', configFilePath='json://${O2DPG_ROOT}/MC/config/QC/json/ft0-reconstruction-config.json') + ### FV0 + if isActive("FV0"): + addQCPerTF(taskName='FV0DigitsQC', + needs=[getDigiTaskName("FV0")], + readerCommand='o2-fv0-digit-reader-workflow --fv0-digit-infile fv0digits.root', + configFilePath='json://${O2DPG_ROOT}/MC/config/QC/json/fv0-digits.json') + + ### FDD + if isActive("FDD"): + addQCPerTF(taskName='FDDRecPointsQC', + needs=[FDDRECOtask['name']], + readerCommand='o2-fdd-recpoints-reader-workflow --fdd-recpoints-infile o2reco_fdd.root', + configFilePath='json://${O2DPG_ROOT}/MC/config/QC/json/fdd-recpoints.json') + ### GLO + RECO addQCPerTF(taskName='vertexQC', needs=[PVFINDERtask['name']], @@ -1262,15 +2097,17 @@ def addQCPerTF(taskName, needs, readerCommand, configFilePath, objectsFile=''): readerCommand='o2-global-track-cluster-reader --track-types "ITS-TPC-TOF,TPC-TOF,TPC,ITS-TPC-TRD,ITS-TPC-TRD-TOF,TPC-TRD,TPC-TRD-TOF" --cluster-types none', configFilePath='json://${O2DPG_ROOT}/MC/config/QC/json/tofMatchedTracks_AllTypes_direct_MC.json') ### ITS - addQCPerTF(taskName='ITSTrackSimTask', + addQCPerTF(taskName='ITSTrackSimTaskQC', needs=[ITSRECOtask['name']], - readerCommand='o2-global-track-cluster-reader --track-types "ITS" --cluster-types "ITS"', + readerCommand='o2-global-track-cluster-reader --track-types "ITS" --cluster-types "ITS"' + staggITS, configFilePath='json://${O2DPG_ROOT}/MC/config/QC/json/its-mc-tracks-qc.json') - addQCPerTF(taskName='ITSTracksClusters', - needs=[ITSRECOtask['name']], - readerCommand='o2-global-track-cluster-reader --track-types "ITS" --cluster-types "ITS"', - configFilePath='json://${O2DPG_ROOT}/MC/config/QC/json/its-clusters-tracks-qc.json') + # At the moment QC does not support per-layer digits/clusters, disable these tasks if staggering is requested + if not staggITS: + addQCPerTF(taskName='ITSTracksClustersQC', + needs=[ITSRECOtask['name']], + readerCommand='o2-global-track-cluster-reader --track-types "ITS" --cluster-types "ITS"' + staggITS, + configFilePath='json://${O2DPG_ROOT}/MC/config/QC/json/its-clusters-tracks-qc.json') ### CPV if isActive('CPV'): @@ -1290,116 +2127,98 @@ def addQCPerTF(taskName, needs, readerCommand, configFilePath, objectsFile=''): readerCommand='o2-phos-reco-workflow --input-type cells --output-type clusters --disable-mc --disable-root-output', configFilePath='json://${O2DPG_ROOT}/MC/config/QC/json/phs-cells-clusters-task.json') - #secondary vertexer - svfinder_threads = ' --threads 1 ' - svfinder_cpu = 1 - if COLTYPE == "PbPb" or (doembedding and COLTYPEBKG == "PbPb"): - svfinder_threads = ' --threads 8 ' - svfinder_cpu = 8 - SVFINDERtask = createTask(name='svfinder_'+str(tf), needs=[PVFINDERtask['name']], tf=tf, cwd=timeframeworkdir, lab=["RECO"], cpu=svfinder_cpu, mem='5000') - SVFINDERtask['cmd'] = '${O2_ROOT}/bin/o2-secondary-vertexing-workflow ' - SVFINDERtask['cmd'] += getDPL_global_options(bigshm=True) + svfinder_threads + putConfigValuesNew(['svertexer'], {"NameConf.mDirMatLUT" : ".."}) - # Take None as default, we only add more if nothing from anchorConfig - svfinder_sources = anchorConfig.get("o2-secondary-vertexing-workflow-options",{}).get("vertexing-sources", None) - if not svfinder_sources: - svfinder_sources = "ITS,ITS-TPC,TPC-TRD,TPC-TOF,ITS-TPC-TRD,ITS-TPC-TOF,ITS-TPC-TRD-TOF" - if isActive("MID"): - svfinder_sources += ",MID" - SVFINDERtask['cmd'] += ' --vertexing-sources ' + svfinder_sources + (' --combine-source-devices','')[args.no_combine_dpl_devices] - # strangeness tracking is now called from the secondary vertexer - if not args.with_strangeness_tracking: - SVFINDERtask['cmd'] += ' --disable-strangeness-tracker' - # if enabled, it may require MC labels + ### MID + if isActive('MID'): + addQCPerTF(taskName='MIDTaskQC', + needs=[MIDRECOtask['name']], + readerCommand='o2-mid-digits-reader-workflow | o2-mid-tracks-reader-workflow', + configFilePath='json://${O2DPG_ROOT}/MC/config/QC/json/mid-task.json') + + ### MCH + if isActive('MCH'): + addQCPerTF(taskName='MCHDigitsTaskQC', + needs=[MCHRECOtask['name']], + readerCommand='o2-mch-digits-reader-workflow', + configFilePath='json://${O2DPG_ROOT}/MC/config/QC/json/mch-digits-task.json') + addQCPerTF(taskName='MCHErrorsTaskQC', + needs=[MCHRECOtask['name']], + readerCommand='o2-mch-errors-reader-workflow', + configFilePath='json://${O2DPG_ROOT}/MC/config/QC/json/mch-errors-task.json') + addQCPerTF(taskName='MCHRecoTaskQC', + needs=[MCHRECOtask['name']], + readerCommand='o2-mch-reco-workflow --disable-root-output', + configFilePath='json://${O2DPG_ROOT}/MC/config/QC/json/mch-reco-task.json') + addQCPerTF(taskName='MCHTracksTaskQC', + needs=[MCHRECOtask['name']], + readerCommand='o2-global-track-cluster-reader --track-types MCH --cluster-types MCH', + configFilePath='json://${O2DPG_ROOT}/MC/config/QC/json/mch-tracks-task.json') + + ### MCH + MID + if isActive('MCH') and isActive('MID'): + addQCPerTF(taskName='MCHMIDTracksTaskQC', + needs=[MCHMIDMATCHtask['name']], + readerCommand='o2-global-track-cluster-reader --track-types "MCH,MID,MCH-MID" --cluster-types "MCH,MID"', + configFilePath='json://${O2DPG_ROOT}/MC/config/QC/json/mchmid-tracks-task.json') + + ### MCH && MID && MFT || MCH && MFT + if isActive('MCH') and isActive('MID') and isActive('MFT') : + addQCPerTF(taskName='MUONTracksMFTTaskQC', + needs=[MFTMCHMATCHtask['name'], MCHMIDMATCHtask['name']], + readerCommand='o2-global-track-cluster-reader --track-types "MFT,MCH,MID,MCH-MID,MFT-MCH,MFT-MCH-MID" --cluster-types "MCH,MID,MFT"' + staggMFT, + configFilePath='json://${O2DPG_ROOT}/MC/config/QC/json/mftmchmid-tracks-task.json') + elif isActive('MCH') and isActive('MFT') : + addQCPerTF(taskName='MCHMFTTaskQC', + needs=[MFTMCHMATCHtask['name']], + readerCommand='o2-global-track-cluster-reader --track-types "MCH,MFT,MFT-MCH" --cluster-types "MCH,MFT"' + staggMFT, + configFilePath='json://${O2DPG_ROOT}/MC/config/QC/json/mftmch-tracks-task.json') + + + #<------------------ TPC - time-series objects + # initial implementation taken from comments in https://its.cern.ch/jira/browse/O2-4612 + # the inputs follow the active detectors where the time series accepts --track-sources + tpcts_sources = '' + tpcts_ownreaders = '' + if isActive("ITS") and isActive("TPC") and isActive("FT0"): + tpcts_sources = option_if_available('o2-tpc-time-series-workflow', '--track-sources', envfile=async_envfile) + tpcts_ownreaders = option_if_available('o2-tpc-time-series-workflow', '--disable-root-input', envfile=async_envfile) + if not tpcts_sources and tf == 1: + print("WARNING: o2-tpc-time-series-workflow has no --track-sources; the TPC time series runs only with TOF and TRD active") + if tpcts_sources: + # only the tracks which the TRD and TOF matching produce with the anchored configuration + trdsrc = trd_track_sources.split(',') + tofsrc = toftracksrcdefault.split(',') + tpcts_tracklist = ["ITS", "TPC", "ITS-TPC"] + tpcts_tracklist += ["ITS-TPC-TRD"] if "ITS-TPC" in trdsrc else [] + tpcts_tracklist += ["ITS-TPC-TOF"] if "ITS-TPC" in tofsrc else [] + tpcts_tracklist += ["ITS-TPC-TRD-TOF"] if "ITS-TPC" in trdsrc and "ITS-TPC-TRD" in tofsrc else [] + tpcts_tracktypes = cleanDetectorInputList(",".join(tpcts_tracklist)) + tpcts_clustertypes = "FT0,TPC" + tpcts_clustertypes += ",TOF" if "TOF" in tpcts_tracktypes else "" + tpcts_clustertypes += ",TRD" if "TRD" in tpcts_tracktypes else "" + tpcts_detectors = "ITS,TPC,FT0" else: - SVFINDERtask['cmd'] += ('',' --disable-mc')[args.no_mc_labels] - workflow['stages'].append(SVFINDERtask) - - # ----------- - # produce AOD - # ----------- - # TODO This needs further refinement, sources and dependencies should be constructed dynamically - aodinfosources = 'ITS,MFT,MCH,TPC,ITS-TPC,MFT-MCH,ITS-TPC-TOF,TPC-TOF,FT0,FDD,TPC-TRD,ITS-TPC-TRD,ITS-TPC-TRD-TOF' - aodneeds = [PVFINDERtask['name'], SVFINDERtask['name']] - - if isActive('CTP'): - aodinfosources += ',CTP' - if isActive('FV0'): - aodneeds += [ FV0RECOtask['name'] ] - aodinfosources += ',FV0' - if isActive('TOF'): - aodneeds += [ TOFRECOtask['name'] ] - if isActive('TRD'): - aodneeds += [ TRDTRACKINGtask2['name'] ] - if isActive('EMC'): - aodneeds += [ EMCRECOtask['name'] ] - aodinfosources += ',EMC' - if isActive('CPV'): - aodneeds += [ CPVRECOtask['name'] ] - aodinfosources += ',CPV' - if isActive('PHS'): - aodneeds += [ PHSRECOtask['name'] ] - aodinfosources += ',PHS' - if isActive('MID'): - aodneeds += [ MIDRECOtask['name'] ] - aodinfosources += ',MID' - if isActive('MID') and isActive('MCH'): - aodneeds += [ MCHMIDMATCHtask['name'] ] - aodinfosources += ',MCH-MID' - if isActive('HMP'): - aodneeds += [ HMPMATCHtask['name'] ] - aodinfosources += ',HMP' - if args.with_ZDC and isActive('ZDC'): - aodneeds += [ ZDCRECOtask['name'] ] - aodinfosources += ',ZDC' - if usebkgcache: - aodneeds += [ BKG_KINEDOWNLOADER_TASK['name'] ] - - aod_df_id = '{0:03}'.format(tf) - - AODtask = createTask(name='aod_'+str(tf), needs=aodneeds, tf=tf, cwd=timeframeworkdir, lab=["AOD"], mem='4000', cpu='1') - AODtask['cmd'] = ('','ln -nfs ../bkg_Kine.root . ;')[doembedding] - AODtask['cmd'] += '[ -f AO2D.root ] && rm AO2D.root; ${O2_ROOT}/bin/o2-aod-producer-workflow --reco-mctracks-only 1 --aod-writer-keep dangling --aod-writer-resfile AO2D' - # next line needed for meta data writing (otherwise lost) - AODtask['cmd'] += ' --aod-writer-resmode "UPDATE"' - AODtask['cmd'] += ' --run-number ' + str(args.run) - # only in non-anchored runs - if args.run_anchored == False: - AODtask['cmd'] += ' --aod-timeframe-id ${ALIEN_PROC_ID}' + aod_df_id - AODtask['cmd'] += ' ' + getDPL_global_options(bigshm=True) - AODtask['cmd'] += ' --info-sources ' + anchorConfig.get("o2-aod-producer-workflow-options",{}).get("info-sources",str(aodinfosources)) - AODtask['cmd'] += ' --lpmp-prod-tag ${ALIEN_JDL_LPMPRODUCTIONTAG:-unknown}' - AODtask['cmd'] += ' --anchor-pass ${ALIEN_JDL_LPMANCHORPASSNAME:-unknown}' - AODtask['cmd'] += ' --anchor-prod ${ALIEN_JDL_MCANCHOR:-unknown}' - AODtask['cmd'] += (' --combine-source-devices ','')[args.no_combine_dpl_devices] - AODtask['cmd'] += ('',' --disable-mc')[args.no_mc_labels] - if environ.get('O2DPG_AOD_NOTRUNCATE') != None or environ.get('ALIEN_JDL_O2DPG_AOD_NOTRUNCATE') != None: - AODtask['cmd'] += ' --enable-truncation 0' # developer option to suppress precision truncation - - if not args.with_strangeness_tracking: - AODtask['cmd'] += ' --disable-strangeness-tracker' - - workflow['stages'].append(AODtask) - - # AOD merging / combination step (as individual stages) --> for the moment deactivated in favor or more stable global merging - """ - aodmergerneeds = [ AODtask['name'] ] - if tf > 1: - # we can only merge this if the previous timeframe was already merged in order - # to keep time ordering of BCs intact - aodmergerneeds += [ 'aodmerge_' + str(tf-1) ] - - AOD_merge_task = createTask(name='aodmerge_'+str(tf), needs = aodmergerneeds, tf=tf, cwd=timeframeworkdir, lab=["AOD"], mem='2000', cpu='1') - AOD_merge_task['cmd'] = ' root -q -b -l ${O2DPG_ROOT}/UTILS/repairAOD.C\\(\\"AO2D.root\\",\\"AO2D_repaired.root\\"\\); ' - # AOD_merge_task['cmd'] += ' mv AO2D.root AO2D_old.root && mv AO2D_repaired.root AO2D.root ; ' - AOD_merge_task['cmd'] += '[ -f ../AO2D.root ] && mv ../AO2D.root ../AO2D_old.root;' - AOD_merge_task['cmd'] += ' [ -f input.txt ] && rm input.txt; ' - AOD_merge_task['cmd'] += ' [ -f ../AO2D_old.root ] && echo "../AO2D_old.root" > input.txt;' - AOD_merge_task['cmd'] += ' echo "./AO2D_repaired.root" >> input.txt;' - AOD_merge_task['cmd'] += ' o2-aod-merger --output ../AO2D.root;' - AOD_merge_task['cmd'] += ' rm ../AO2D_old.root || true' - AOD_merge_task['semaphore'] = 'aodmerge' #<---- this is making sure that only one merge is running at any time - workflow['stages'].append(AOD_merge_task) - """ + # older async tags (e.g. 2023 apass4, 2024 pp apass1) always ask for TOF and TRD inputs + tpcts_tracktypes = "ITS,TPC,ITS-TPC,ITS-TPC-TOF,ITS-TPC-TRD-TOF" + tpcts_clustertypes = "FT0,TOF,TPC" + tpcts_detectors = "ITS,TPC,FT0,TOF,TRD" + tpctsneeds = [ TPCRECOtask['name'], + ITSTPCMATCHtask['name'], + FT0RECOtask['name'], + PVFINDERtask['name'] + ] + addWhenActive("TOF", tpctsneeds, TOFTPCMATCHERtask['name']) + addWhenActive("TRD", tpctsneeds, TRDTRACKINGtask2['name']) + TPCTStask = createTask(name='tpctimeseries_'+str(tf), needs=tpctsneeds, tf=tf, cwd=timeframeworkdir, lab=["RECO"], mem='2000', cpu='1') + TPCTStask['cmd'] = f'o2-global-track-cluster-reader --disable-mc --cluster-types "{tpcts_clustertypes}" --track-types "{tpcts_tracktypes}"' + TPCTStask['cmd'] += ' --primary-vertices ' + TPCTStask['cmd'] += staggITS + # newer builds have their own readers; switch them off so that only the reader above runs + TPCTStask['cmd'] += f' | o2-tpc-time-series-workflow{tpcts_ownreaders}' + TPCTStask['cmd'] += f'{tpcts_sources} "{tpcts_tracktypes},FT0"' if tpcts_sources else '' + TPCTStask['cmd'] += ' --enable-unbinned-root-output --sample-unbinned-tsallis --sampling-factor 0.01 ' + TPCTStask['cmd'] += staggITS + TPCTStask['cmd'] += putConfigValues() + ' ' + getDPL_global_options(bigshm=True) + addWhenActive(tpcts_detectors, workflow['stages'], TPCTStask) # cleanup # -------- @@ -1408,33 +2227,60 @@ def addQCPerTF(taskName, needs, readerCommand, configFilePath, objectsFile=''): # taking away digits, clusters and other stuff as soon as possible. # TODO: cleanup by labels or task names if args.early_tf_cleanup == True: - TFcleanup = createTask(name='tfcleanup_'+str(tf), needs= [ AOD_merge_task['name'] ], tf=tf, cwd=timeframeworkdir, lab=["CLEANUP"], mem='0', cpu='1') + TFcleanup = createTask(name='tfcleanup_'+str(tf), needs= [ AODtask['name'] ], tf=tf, cwd=timeframeworkdir, lab=["CLEANUP"], mem='0', cpu='1') TFcleanup['cmd'] = 'rm *digi*.root;' TFcleanup['cmd'] += 'rm *cluster*.root' - workflow['stages'].append(TFcleanup); - -# AOD merging as one global final step -aodmergerneeds = ['aod_' + str(tf) for tf in range(1, NTIMEFRAMES + 1)] -AOD_merge_task = createTask(name='aodmerge', needs = aodmergerneeds, lab=["AOD"], mem='2000', cpu='1') -AOD_merge_task['cmd'] = ' [ -f aodmerge_input.txt ] && rm aodmerge_input.txt; ' -AOD_merge_task['cmd'] += ' for i in `seq 1 ' + str(NTIMEFRAMES) + '`; do echo "tf${i}/AO2D.root" >> aodmerge_input.txt; done; ' -AOD_merge_task['cmd'] += ' o2-aod-merger --input aodmerge_input.txt --output AO2D.root' -workflow['stages'].append(AOD_merge_task) + workflow['stages'].append(TFcleanup) + +if not args.make_evtpool: + # AOD merging as one global final step + aodmergerneeds = ['aod_' + str(tf) for tf in range(1, NTIMEFRAMES + 1)] + AOD_merge_task = createTask(name='aodmerge', needs = aodmergerneeds, lab=["AOD"], mem='2000', cpu='1') + AOD_merge_task['cmd'] = ' set -e ; [ -f aodmerge_input.txt ] && rm aodmerge_input.txt; ' + AOD_merge_task['cmd'] += ' for i in `seq 1 ' + str(NTIMEFRAMES) + '`; do echo "tf${i}/AO2D.root" >> aodmerge_input.txt; done; ' + AOD_merge_task['cmd'] += ' o2-aod-merger --input aodmerge_input.txt --output AO2D_pre.root' + # reindex the BC + connected tables because it there could be duplicate BC entries due to the orbit-early treatment + # see https://its.cern.ch/jira/browse/O2-6227 + AOD_merge_task['cmd'] += ' ; root -q -b -l "${O2DPG_ROOT}/MC/utils/AODBcRewriter.C(\\\"AO2D_pre.root\\\",\\\"AO2D.root\\\")"' + # produce MonaLisa event stat file + AOD_merge_task['cmd'] += ' ; ${O2DPG_ROOT}/MC/bin/o2dpg_determine_eventstat.py' + AOD_merge_task['alternative_alienv_package'] = "None" # we want latest software for this step + workflow['stages'].append(AOD_merge_task) -job_merging = False -if includeFullQC: - workflow['stages'].extend(include_all_QC_finalization(ntimeframes=NTIMEFRAMES, standalone=False, run=args.run, productionTag=args.productionTag)) + job_merging = False + if includeFullQC: + workflow['stages'].extend(include_all_QC_finalization(ntimeframes=NTIMEFRAMES, standalone=False, run=args.run, productionTag=args.productionTag, conditionDB=args.conditionDB, qcdbHost=args.qcdbHost, beamType=args.col)) + if includeAnalysis: + # include analyses and potentially final QC upload tasks + add_analysis_tasks(workflow["stages"], needs=[AOD_merge_task["name"]], is_mc=True, collision_system=COLTYPE) + if QUALITYCONTROL_ROOT: + add_analysis_qc_upload_tasks(workflow["stages"], args.productionTag, args.run, "passMC") +else: + wfneeds=['sgngen_' + str(tf) for tf in range(1, NTIMEFRAMES + 1)] + tfpool=['tf' + str(tf) + '/genevents_Kine.root' for tf in range(1, NTIMEFRAMES + 1)] + POOL_merge_task = createTask(name='poolmerge', needs=wfneeds, lab=["POOL"], mem='2000', cpu='1') + POOL_merge_task['cmd'] = '${O2DPG_ROOT}/UTILS/root_merger.py -o evtpool.root -i ' + ','.join(tfpool) + # also create the stat file with the event count + POOL_merge_task['cmd'] += '; RC=$?; root -l -q -b -e "auto f=TFile::Open(\\\"evtpool.root\\\"); auto t=(TTree*)f->Get(\\\"o2sim\\\"); int n=t->GetEntries(); std::ofstream((\\\"0_0_0_\\\"+std::to_string(n)+\\\".stat\\\").c_str()) << \\\"# MonaLisa stat file for event pools\\\";" ; [[ ${RC} == 0 ]]' + workflow['stages'].append(POOL_merge_task) + +# if TPC residuals extraction was requested, we have to merge per-tf trees +if includeTPCResiduals: + tpcResidMergingNeeds = ['scdaggreg_' + str(tf) for tf in range(1, NTIMEFRAMES + 1)] + TPCResid_merge_task = createTask(name='tpcresidmerge', needs = tpcResidMergingNeeds, lab=["CALIB"], mem='2000', cpu='1') + TPCResid_merge_task['cmd'] = ' set -e ; [ -f tpcresidmerge_input.txt ] && rm tpcresidmerge_input.txt; ' + TPCResid_merge_task['cmd'] += ' for i in `seq 1 ' + str(NTIMEFRAMES) + '`; do find tf${i} -name "o2tpc_residuals_*.root" >> tpcresidmerge_input.txt; done; ' + TPCResid_merge_task['cmd'] += '${O2DPG_ROOT}/UTILS/root_merger.py -o o2tpc_residuals.root -i $(grep -v \"^$\" tpcresidmerge_input.txt | paste -sd, -)' + workflow['stages'].append(TPCResid_merge_task) -if includeAnalysis: - # include analyses and potentially final QC upload tasks - add_analysis_tasks(workflow["stages"], needs=[AOD_merge_task["name"]], is_mc=True, collision_system=COLTYPE) - if QUALITYCONTROL_ROOT: - add_analysis_qc_upload_tasks(workflow["stages"], args.productionTag, args.run, "passMC") # adjust for alternate (RECO) software environments adjust_RECO_environment(workflow, args.alternative_reco_software) dump_workflow(workflow['stages'], args.o, meta=vars(args)) +# dump a config that can be used to reproduce this workflow +task_finalizer.dump_collected_config("final_config.json") + exit (0) diff --git a/MC/bin/o2dpg_sim_workflow_anchored.py b/MC/bin/o2dpg_sim_workflow_anchored.py index ad9ba4a0a..56d11a0c9 100755 --- a/MC/bin/o2dpg_sim_workflow_anchored.py +++ b/MC/bin/o2dpg_sim_workflow_anchored.py @@ -13,6 +13,18 @@ import re import json import math +import pandas as pd +import subprocess +import shlex + +# hack to find the script for meta upload +o2dpg_root = os.environ.get("O2DPG_ROOT") +if o2dpg_root is None: + raise EnvironmentError("O2DPG_ROOT is not set in the environment.") +mc_prodinfo_path = os.path.abspath(os.path.join(o2dpg_root, "MC", "prodinfo")) +sys.path.append(mc_prodinfo_path) +from mcprodinfo_ccdb_upload import MCProdInfo, publish_MCProdInfo +import dataclasses # Creates a time anchored MC workflow; positioned within a given run-number (as function of production size etc) @@ -50,16 +62,6 @@ def __init__(self, url): # we allow nullptr responsens and will treat it ourselves o2.ccdb.BasicCCDBManager.instance().setFatalWhenNull(False) - def list(self, path, dump_path=None): - ret = self.api.list(path, False, "application/json") - ret = json.loads(ret) - if ret and "objects" in ret: - ret = ret["objects"] - if ret and dump_path: - print(f"CCDB object information for path {path} stored in {dump_path}") - dump_json(ret, dump_path) - return ret - def fetch(self, path, obj_type, timestamp=None, meta_info=None): """ TODO We could use CcdbApi::snapshot at some point, needs revision @@ -77,6 +79,9 @@ def fetch(self, path, obj_type, timestamp=None, meta_info=None): return timestamp, obj + def get_run_duration(self, run_number): + return o2.ccdb.BasicCCDBManager.instance().getRunDuration(run_number) + def fetch_header(self, path, timestamp=None): meta_info = std.map["std::string", "std::string"]() if timestamp is None: @@ -85,22 +90,6 @@ def fetch_header(self, path, timestamp=None): return header - -def retrieve_sor_eor(ccdbreader, run_number): - """ - retrieves start of run (sor) and end of run (eor) given a run number - from the RCT/Info/RunInformation table - """ - - path_run_info = "RCT/Info/RunInformation" - header = ccdbreader.fetch_header(path_run_info, run_number) - if not header: - print(f"WARNING: Cannot find run information for run number {r}") - return None - # return this a dictionary - return {"SOR": int(header["SOR"]), "EOR": int(header["EOR"])} - - def retrieve_CCDBObject_asJSON(ccdbreader, path, timestamp, objtype_external = None): """ Retrieves a CCDB object as a JSON/dictionary. @@ -121,13 +110,72 @@ def retrieve_CCDBObject_asJSON(ccdbreader, path, timestamp, objtype_external = N jsonTString = TBufferJSON.ConvertToJSON(obj, TClass.GetClass(objtype)) return json.loads(jsonTString.Data()) -def retrieve_sor_eor_fromGRPECS(ccdbreader, run_number, rct = None): + +def retrieve_Aggregated_RunInfos(run_number): + """ + Retrieves start of run (sor), end of run (eor) and other global parameters by using the + AggregatedRunInfo object in O2 which takes care of building the information consistently. + This is the prefered way over older function "retrieve_params_fromGRPECS_and_OrbitReset". + """ + + runInfo = o2.parameters.AggregatedRunInfo.buildAggregatedRunInfo(o2.ccdb.BasicCCDBManager.instance(), run_number) + detList = o2.detectors.DetID.getNames(runInfo.grpECS.getDetsReadOut()) + assert (run_number == runInfo.runNumber) + assert (run_number == runInfo.grpECS.getRun()) + + print (f"Detector list from RunInfo/GRPECS is {detList}") + # potential overwrite in detector list if special env variable ALIEN_JDL_WORKFLOWDETECTORS is set + detlist_overwrite = os.getenv("ALIEN_JDL_WORKFLOWDETECTORS") + if detlist_overwrite: + detList = detlist_overwrite + print (f"Detector list is overwritten to {detList} via JDL") + + return {"SOR" : runInfo.sor, + "EOR" : runInfo.eor, + "FirstOrbit" : runInfo.orbitSOR, + "LastOrbit" : runInfo.orbitEOR, + "OrbitsPerTF" : int(runInfo.orbitsPerTF), + "detList" : detList} + + +def parse_orbits_per_tf(orbitsPerTF, intRate): + """ + Function to determine the number of orbits per TF to be used as + a function of interaction rate. + """ + if intRate == None or intRate < 0: + return -1 + + # Check if the argument is a single integer, in which case we just use it + if orbitsPerTF.isdigit(): + return int(orbitsPerTF) + + # Otherwise we assume that the argument is a string of the form + # a1:b1:o1,a2:b2:o2,... + # where we apply orbit o2 if the intRate falls between a2 <= intRate < b2. + ranges = orbitsPerTF.split(',') + for entry in ranges: + try: + a, b, x = map(int, entry.split(':')) + if a <= intRate < b: + return x + except ValueError: + raise ValueError(f"Invalid format in entry: {entry}") + + # if we didn't find a valid range we return -1 which means + # that the orbit number will be determined from GRPECS + return -1 + + +def retrieve_params_fromGRPECS_and_OrbitReset(ccdbreader, run_number, run_start, run_end): """ Retrieves start of run (sor), end of run (eor) and other global parameters from the GRPECS object, given a run number. We first need to find the right object ... but this is possible with a browsing request and meta_data filtering. Optionally, we can pass an existing result from RCT/Info/RunInformation to check for consistency. In this case and when information is inconsistent we will take time from RCT and issue a warning message. + + NOTE: This function is deprecated; It should no longer be used and might be removed soon. """ # make a simple HTTP request on the "browsing" endpoint @@ -151,48 +199,41 @@ def retrieve_sor_eor_fromGRPECS(ccdbreader, run_number, rct = None): match_object=re.match("\s*([0-9]*)\s*-\s*([0-9]*)\s*.*", VALIDITY) SOV = -1 # start of object validity (not necessarily the same as actual run-start) - EOV = -1 # end of object validity (not the same as actual run-end) if match_object != None: SOV=match_object[1] - EOV=match_object[2] # we make a suitable request (at the start time) --> this gives the actual # object, with which we can query the end time as well grp=retrieve_CCDBObject_asJSON(ccdbreader, "/GLO/Config/GRPECS" + "/runNumber=" + str(run_number) + "/", int(SOV)) - # check that this object is really the one we wanted based on run-number assert(int(grp["mRun"]) == int(run_number)) - SOR=int(grp["mTimeStart"]) # in milliseconds - EOR=int(grp["mTimeEnd"]) - # cross check with RCT if available - if rct != None: - # verify that the variaous sor_eor information are the same - if SOR != rct["SOR"]: - print ("WARNING: Inconsistent SOR information on CCDB (divergence between GRPECS and RCT) ... will take RCT one") - SOR=rct["SOR"] - - if EOR != rct["EOR"]: - print ("WARNING: Inconsistent EOR information on CCDB (divergence between GRPECS and RCT) ... will take RCT one") - EOR=rct["EOR"] - # fetch orbit reset to calculate orbitFirst - ts, oreset = ccdbreader.fetch("CTP/Calib/OrbitReset", "vector", timestamp = SOR) + _, oreset = ccdbreader.fetch("CTP/Calib/OrbitReset", "vector", timestamp = run_start) print ("All orbit resets") for i in range(len(oreset)): print (" oreset " + str(i) + " " + str(oreset[i])) print ("OrbitReset:", int(oreset[0])) - print ("RunStart:", SOR) - - orbitFirst = int((1000*SOR - oreset[0])//LHCOrbitMUS) # calc done in microseconds - orbitLast = int((1000*EOR - oreset[0])//LHCOrbitMUS) + orbitFirst = int((1000*run_start - oreset[0])//LHCOrbitMUS) # calc done in microseconds + orbitLast = int((1000*run_end - oreset[0])//LHCOrbitMUS) print ("OrbitFirst", orbitFirst) # first orbit of this run print ("LastOrbit of run", orbitLast) + # Now fetch the detector list + print ("DetsReadout-Mask: ", grp["mDetsReadout"]['v']) + detList = o2.detectors.DetID.getNames(grp["mDetsReadout"]['v']) + print ("Detector list is ", detList) + + # potential reduction in detector list if special env variable ALIEN_JDL_WORKFLOWDETECTORS is set + detlist_overwrite = os.getenv("ALIEN_JDL_WORKFLOWDETECTORS") + if detlist_overwrite: + detList = detlist_overwrite + print ("Detector list is overwritten to ", detList) + # orbitReset.get(run_number) - return {"SOR": SOR, "EOR": EOR, "FirstOrbit" : orbitFirst, "LastOrbit" : orbitLast, "OrbitsPerTF" : int(grp["mNHBFPerTF"])} + return {"FirstOrbit" : orbitFirst, "LastOrbit" : orbitLast, "OrbitsPerTF" : int(grp["mNHBFPerTF"]), "detList" : detList} def retrieve_GRP(ccdbreader, timestamp): """ @@ -206,65 +247,339 @@ def retrieve_GRP(ccdbreader, timestamp): ts, grp = ccdbreader.fetch(grp_path, "o2::parameters::GRPObject", timestamp = timestamp) return grp -def retrieve_MinBias_CTPScaler_Rate(ccdbreader, timestamp, run_number, finaltime, ft0_eff): +def retrieve_GRPLHCIF(ccdbreader, timestamp): + """ + retrieves the GRPLHCIF object for a given time stamp + """ + _, grplhcif = ccdbreader.fetch("GLO/Config/GRPLHCIF", "o2::parameters::GRPLHCIFData", timestamp = timestamp) + return grplhcif + +def retrieve_CTPScalers(ccdbreader, run_number, timestamp=None): """ retrieves the CTP scalers object for a given timestamp and run_number and calculates the interation rate to be applied in Monte Carlo digitizers """ path = "CTP/Calib/Scalers/runNumber=" + str(run_number) - ts, ctpscaler = ccdbreader.fetch(path, "o2::ctp::CTPRunScalers", timestamp = timestamp) - if ctpscaler != None: + _, ctpscaler = ccdbreader.fetch(path, "o2::ctp::CTPRunScalers", timestamp = timestamp) + if ctpscaler is not None: ctpscaler.convertRawToO2() - rate = ctpscaler.getRateGivenT(finaltime,0,0) # the simple FT0 rate from the counters - # print("Global rate " + str(rate.first) + " local rate " + str(rate.second)) - - # now get the bunch filling object which is part of GRPLHCIF and calculate - # true rate (input from Chiara Zampolli) - ts, grplhcif = ccdbreader.fetch("GLO/Config/GRPLHCIF", "o2::parameters::GRPLHCIFData", timestamp = timestamp) - coll_bunches = grplhcif.getBunchFilling().getNBunches() - mu = - math.log(1. - rate.first / 11245 / coll_bunches) / ft0_eff - finalRate = coll_bunches * mu * 11245 - return finalRate - - print (f"[ERROR]: Could not determine interaction rate; Some (external) default used") + return ctpscaler return None +def make_CTP_raw_rate_source(run_number, timestamp, ColSystem): + """ + A function giving the raw CTP counting rate in Hz at a timestamp in milliseconds, and the name + of the source it reads. + + Which class to count, at which scaler index and with which scaler type, and how that changed + over the years, is CTP's business: o2::ctp::CTPRateFetcher knows it, a fixed scaler index does + not. Only the raw rate is taken from it; the pile-up correction is done below. + """ + ccdb = o2.ccdb.BasicCCDBManager.instance() + fetcher = o2.ctp.CTPRateFetcher() + fetcher.setupRun(run_number, ccdb, int(timestamp), True) + # "ZNC" without "hadronic" so that the fetcher does not apply the factor 28 itself + source = "ZNC" if ColSystem == "PbPb" else "T0VTX" + + def raw_rate_at(timestamp_ms): + return fetcher.fetchNoPuCorr(ccdb, int(timestamp_ms), run_number, source) + + return raw_rate_at, source + +def determine_last_collision_time(raw_rate_at, ctpscaler, run_end): + """ + The end of the run as far as collisions are concerned, in milliseconds. + + Data taking goes on after the collisions stop and the time frames written in between hold + nothing to simulate: in run 571043 the CTP counts zero over the last 256 s. The run record only + knows the end of data taking, so ask the scalers. Mirrors the ITS ramp-up adjustment of the + start of the run. + """ + times = [float(r.epochTime) for r in ctpscaler.getScalerRecordO2()] + for i in range(len(times) - 1, 0, -1): + # interval i spans records i-1 and i; ask in the middle so that getRateGivenT brackets it + if raw_rate_at(1000. * 0.5 * (times[i - 1] + times[i])) > 0: + if i == len(times) - 1: + return run_end + # counting stopped inside interval i, so the last moment we know of is where it began + return min(int(1000. * times[i - 1]), run_end) + return run_end + +def retrieve_ITS_RampDuration(ccdbreader, timestamp): + """ + Retrieves the ITS ramp-up duration for a given timestamp and + returns it in milliseconds. + ITS does not deliver digits during a certain ramp-up period so + the start of run is adjusted accordingly using this value. + """ + _, ramp_duration = ccdbreader.fetch("ITS/Calib/RampDuration", "vector", timestamp=timestamp) + if ramp_duration and len(ramp_duration) > 0: + # The vector contains the duration in seconds, convert to milliseconds + duration_ms = int(ramp_duration[0] * 1000) + return duration_ms + print("WARNING: ITS ramp duration vector is empty, using 0") + return 0 + +def milliseconds_to_orbits(milliseconds): + """ + Number of complete LHC orbits contained in a time span given in milliseconds. + Rounded up, so that a span used to skip something is never cut short. + """ + return int(math.ceil(1000. * milliseconds / LHCOrbitMUS)) + +def shift_anchor_past_ITS_rampup(run_start, first_orbit, orbitsPerTF, ITS_rampup): + """ + Moves the anchoring point past the ITS ramp-up period, in both of its + coordinates, and returns the pair (start of run in ms, first orbit). + The two have to move together: the timeframes a job simulates are placed by + orbit (first orbit plus the production offset), so shifting only the + timestamp leaves a job at production offset 0 inside the ramp, where the ITS + time-dead map masks every chip. + """ + # round the ramp-up up to whole milliseconds, then to a whole number of timeframes; + # the truncated millisecond shift then still ends after the ramp and before the orbit + rampupOrbits = -(-milliseconds_to_orbits(math.ceil(ITS_rampup)) // orbitsPerTF) * orbitsPerTF + return run_start + int(rampupOrbits * LHCOrbitMUS / 1000.), first_orbit + rampupOrbits + +def retrieve_MinBias_CTPScaler_Rate(raw_rate_at, finaltime, trig_eff_arg, NBunches, ColSystem, eCM, run_number = -1): + """ + Turns the raw CTP counting rate at finaltime (in milliseconds) into the interaction rate for + the digitizers. Uses trig_eff_arg when positive, otherwise the efficiency of the collision + system. + + Returns (interaction rate, raw rate). No collisions at that timestamp gives (None, 0.0) so the + caller can drop it; anything else unusable gives (None, None), which leaves the externally + given -interactionRate in place. + """ + # Fraction of inelastic collisions firing the class counted below: FT0 vertex for pp and the + # light systems, ZDC for PbPb, where the ratio is above one because the ZDC also sees + # electromagnetic dissociation. + trigger_effs = { + "pp": { + "1000": 0.68, + "6000": 0.737, + "default": 0.759 + }, + "pO": { + "default": 0.8222 + }, + "Op": { + "default": 0.8222 + }, + "OO": { + "default": 0.8677 + }, + "NeNe": { + "default": 0.9147 + }, + "PbPb": { + "default": 28.0 # this is ZDC + } + } + + # determine first of all the trigger efficiency + effTrigger = trig_eff_arg + if effTrigger < 0: + # Check if ColSystem is defined in trigger_effs + if ColSystem in trigger_effs: + if ColSystem == "pp": + if eCM < 1000: + effTrigger = trigger_effs["pp"]["1000"] + elif eCM < 6000: + effTrigger = trigger_effs["pp"]["6000"] + else: + effTrigger = trigger_effs["pp"]["default"] + else: + effTrigger = trigger_effs[ColSystem]["default"] + else: + effTrigger = 0.759 # The simulation will fail later if the collision system is not defined + + raw_rate = raw_rate_at(finaltime) + print(f"CTP raw rate at timestamp {finaltime} is {raw_rate} Hz") + + if raw_rate == 0: + print(f"[WARNING]: The CTP counted no collisions in run {run_number} at timestamp " + f"{finaltime}; there is nothing to simulate at this moment") + return None, 0.0 + + if not raw_rate > 0: + print(f"[ERROR]: Could not determine interaction rate for run {run_number} at timestamp " + f"{finaltime} (raw rate {raw_rate}); Some (external) default used") + return None, None + + # calculate true rate (input from Chiara Zampolli) using number of bunches + coll_bunches = NBunches + if coll_bunches <= 0: + print(f"[ERROR]: Number of colliding bunches is {coll_bunches} for run {run_number}; " + f"cannot determine interaction rate; Some (external) default used") + return None, None + + # invert the Poisson zero-probability to get the mean number of triggering interactions per + # colliding crossing, then divide by the efficiency to get all of them + trigger_prob_per_bc = raw_rate / 11245 / coll_bunches + if trigger_prob_per_bc >= 1.: + print(f"[ERROR]: Raw scaler rate {raw_rate} Hz is not below the {coll_bunches} * 11245 Hz of " + f"colliding bunch crossings of run {run_number}; cannot determine interaction rate; " + f"Some (external) default used") + return None, None + + mu = - math.log(1. - trigger_prob_per_bc) / effTrigger + finalRate = coll_bunches * mu * 11245 + return finalRate, raw_rate + def determine_timestamp(sor, eor, splitinfo, cycle, ntf, HBF_per_timeframe = 256): """ Determines the timestamp and production offset variable based on the global properties of the production (MC split, etc) and the properties of the run. ntf is the number of timeframes per MC job + Args: + sor: int + start-of-run in milliseconds since epoch + eor: int + end-of-run in milliseconds since epoch + splitinfo: tuple (int, int) + splitinfo[0]: split ID of this job + splitinfo[1]: total number of jobs to split into + cycle: int + cycle of this productions. Typically a run is not entirely filled by and anchored simulation + but only a proportion of events is simulated. + With increasing number of cycles, the data run is covered more and more. + ntf: int + number of timeframes + HBF_per_timeframe: int + number of orbits per timeframe + Returns: + int: timestamp in milliseconds + int: production offset aka "which timeslot in this production to simulate" """ totaljobs = splitinfo[1] thisjobID = splitinfo[0] - print (f"Start-of-run : {sor}") - print (f"End-of-run : {eor}") - time_length_inmus = 1000*(eor - sor) # time length in micro seconds - timestamp_delta = time_length_inmus / totaljobs + # length of this run in micro seconds, since we use the orbit duration in micro seconds + time_length_inmus = 1000 * (eor - sor) + # figure out how many timeframes fit into this run range + # take the number of orbits per timeframe and multiply by orbit duration to calculate how many timeframes fit into this run ntimeframes = time_length_inmus / (HBF_per_timeframe * LHCOrbitMUS) - norbits = time_length_inmus / LHCOrbitMUS + # also calculate how many orbits fit into the run range print (f"This run has space for {ntimeframes} timeframes") - print (f"This run has {norbits} orbits") - # ntimeframes is the total number of timeframes possible - # if we have totaljobs number of jobs - maxtimeframesperjob = ntimeframes // totaljobs - orbitsperjob = norbits // totaljobs + # figure out how many timeframes can maximally be covered by one job + maxtimeframesperjob = ntimeframes / totaljobs print (f"Each job can do {maxtimeframesperjob} maximally at a prod split of {totaljobs}") - print (f"With each job doing {ntf} timeframes, this corresponds to a filling rate of ", ntf/maxtimeframesperjob) + print (f"With each job doing {ntf} timeframes, this corresponds to a filling rate of {ntf / maxtimeframesperjob}") # filling rate should be smaller than 100% assert(ntf <= maxtimeframesperjob) - maxcycles = maxtimeframesperjob // ntf - print (f"We can do this amount of cycle iterations to achieve 100%: ", maxcycles) - + # each cycle populates more and more run range. The maximum number of cycles to populate the run fully is: + maxcycles = maxtimeframesperjob / ntf + print (f"We can do this amount of cycle iterations to achieve 100%: {maxcycles}") + + # overall, we have maxcycles * totaljobs slots to fill the run range with ntf timeframes per slot + # figure out in which slot to simulate production_offset = int(thisjobID * maxcycles) + cycle + # add the time difference of this slot to start-of-run to get the final timestamp timestamp_of_production = sor + production_offset * ntf * HBF_per_timeframe * LHCOrbitMUS / 1000 + # this is a closure test. If we had perfect floating point precision everywhere, it wouldn't fail. + # But since we don't have that and there are some int casts as well, better check again. assert (timestamp_of_production >= sor) assert (timestamp_of_production <= eor) return int(timestamp_of_production), production_offset +def determine_timestamp_from_timeframeID(sor, eor, timeframeID, HBF_per_timeframe = 256): + """ + Determines the timestamp based on the given timeframeID within a run + Args: + sor: int + start-of-run in milliseconds since epoch + eor: int + end-of-run in milliseconds since epoch + timeframeID: int + timeframe id + HBF_per_timeframe: int + number of orbits per timeframe + Returns: + int: timestamp in milliseconds + """ + # length of this run in micro seconds, since we use the orbit duration in micro seconds + + timestamp_of_production = sor + timeframeID * HBF_per_timeframe * LHCOrbitMUS / 1000 + # this is a closure test. If we had perfect floating point precision everywhere, it wouldn't fail. + # But since we don't have that and there are some int casts as well, better check again. + assert (timestamp_of_production >= sor) + assert (timestamp_of_production <= eor) + return int(timestamp_of_production) + +def exclude_timestamp(ts, orbit, run, filename, global_run_params): + """ + Checks if timestamp ts (or orbit) falls within a bad data period. + Returns true if this timestamp should be excluded; false otherwise + + ts is supposed to be in milliseconds + orbit is some orbit after the orbitreset of the run + """ + if len(filename) == 0: + return False + + if not os.path.isfile(filename): + return False + + def parse_file(filename): + parsed_data = [] + with open(filename, 'r') as file: + for line in file: + # Split the line into exactly 4 parts (first three numbers + comment) + columns = re.split(r'[,\s;\t]+', line.strip(), maxsplit=3) + + if len(columns) < 3: + continue # Skip lines with insufficient columns + + try: + # Extract the first three columns as numbers + num1, num2, num3 = map(int, columns[:3]) # Assuming integers in the data + comment = columns[3] if len(columns) > 3 else "" + parsed_data.append({"Run" : num1, "From" : num2, "To" : num3, "Message" : comment}) + except ValueError: + continue # Skip lines where first three columns are not numeric + return parsed_data + + data = parse_file(filename) + # print (data) + df = pd.DataFrame(data) # convert to data frame for easy handling + + # extract data for this run number + filtered = df[df['Run'] == run] + + # now extract from and to lists + exclude_list = list(zip(filtered["From"].to_list() , filtered["To"].to_list())) + + print("Exclusion list has " + str(len(exclude_list)) + " entries") + if len(exclude_list) == 0: + return False + + timeframelength_intime = global_run_params["EOR"] - global_run_params["SOR"] + timeframelength_inorbits = global_run_params["LastOrbit"] - global_run_params["FirstOrbit"] + total_excluded_fraction = 0 + excluded = False + for exclusion_entry in exclude_list: + # + data_is_in_orbits = exclusion_entry[0] < 1514761200000 + print ("Checking data", exclusion_entry) + if data_is_in_orbits: + total_excluded_fraction = total_excluded_fraction + (exclusion_entry[1] - exclusion_entry[0]) / (1.*timeframelength_inorbits) + if exclusion_entry[0] <= orbit and orbit <= exclusion_entry[1]: + print ("Excluding orbit ", str(orbit)) + excluded = True + else: + total_excluded_fraction = total_excluded_fraction + (exclusion_entry[1] - exclusion_entry[0]) / (1.*timeframelength_intime) + if exclusion_entry[0] <= ts and ts <= exclusion_entry[1]: + print ("Excluding timestamp ", str(ts)) + excluded = True + + print(f"This run as globally {total_excluded_fraction} of it's data marked to be exluded") + return excluded + + + def main(): parser = argparse.ArgumentParser(description='Creates an O2DPG simulation workflow, anchored to a given LHC run. The workflows are time anchored at regular positions within a run as a function of production size, split-id and cycle.') @@ -275,57 +590,141 @@ def main(): parser.add_argument("--split-id", type=int, help="The split id of this job within the whole production --prod-split)", default=0) parser.add_argument("-tf", type=int, help="number of timeframes per job", default=1) parser.add_argument("--ccdb-IRate", type=bool, help="whether to try fetching IRate from CCDB/CTP", default=True) - parser.add_argument("--ft0-eff", type=float, dest="ft0_eff", help="FT0 eff needed for IR", default=-1.0) + parser.add_argument("--trig-eff", type=float, dest="trig_eff", help="Trigger eff needed for IR (default is automatic mode)", default=-1.0) + parser.add_argument("--run-time-span-file", type=str, dest="run_span_file", help="Run-time-span-file for exclusions of timestamps (bad data periods etc.)", default="") + parser.add_argument("--invert-irframe-selection", action='store_true', help="Inverts the logic of --run-time-span-file") + parser.add_argument("--orbitsPerTF", type=str, help="Force a certain orbits-per-timeframe number; Automatically taken from CCDB if not given.", default="") + parser.add_argument('--publish-mcprodinfo', action='store_true', default=False, help="Publish MCProdInfo metadata to CCDB") + parser.add_argument('--timeframeID', type=int, help="If given, anchor to this specific timeframe id within a run. Takes precendence over determination based on (split-id, prod-split, cycle)", default=-1) parser.add_argument('forward', nargs=argparse.REMAINDER) # forward args passed to actual workflow creation - parser.add_argument("-eCM", type=float, dest="eCM", help="Energy", default=13600) - parser.add_argument("-col", type=str, dest="col", help="Collision System", default="pp") args = parser.parse_args() + print (args) # split id should not be larger than production id - assert(args.split_id < args.prod_split) + assert(args.split_id <= args.prod_split) # make a CCDB accessor object ccdbreader = CCDBAccessor(args.ccdb_url) - # fetch the EOR/SOR - rct_sor_eor = retrieve_sor_eor(ccdbreader, args.run_number) # <-- from RCT/Info - GLOparams = retrieve_sor_eor_fromGRPECS(ccdbreader, args.run_number, rct=rct_sor_eor) - if not GLOparams: - print ("No time info found") - sys.exit(1) - - # determine timestamp, and production offset for the final - # MC job to run - timestamp, prod_offset = determine_timestamp(GLOparams["SOR"], GLOparams["EOR"], [args.split_id, args.prod_split], args.cycle, args.tf, GLOparams["OrbitsPerTF"]) + + # fetch the EOR/SOR/FirstOrbit and other important run parameters + GLOparams = retrieve_Aggregated_RunInfos(args.run_number) + run_start = GLOparams["SOR"] + run_end = GLOparams["EOR"] + + # Adjust the anchoring point using the ITS ramp-up period + ITS_rampup = retrieve_ITS_RampDuration(ccdbreader, run_start) + print(f"ITS ramp-up time: {ITS_rampup} ms") + effective_run_start, effective_first_orbit = shift_anchor_past_ITS_rampup(run_start, GLOparams["FirstOrbit"], GLOparams["OrbitsPerTF"], ITS_rampup) + mid_run_timestamp = (effective_run_start + run_end) // 2 + + # -------- + # fetch other important global properties needed further below + # -------- + ctp_scalers = retrieve_CTPScalers(ccdbreader, args.run_number, timestamp=mid_run_timestamp) + if ctp_scalers is None: + print(f"ERROR: Cannot retrive scalers for run number {args.run_number}") + exit (1) + + # retrieve the GRPHCIF object (using mid-run timestamp) + grplhcif = retrieve_GRPLHCIF(ccdbreader, int(mid_run_timestamp)) + + # determine some fundamental physics quantities + eCM = grplhcif.getSqrtS() + eA = grplhcif.getBeamEnergyPerNucleonInGeV(o2.constants.lhc.BeamDirection.BeamC) + eB = grplhcif.getBeamEnergyPerNucleonInGeV(o2.constants.lhc.BeamDirection.BeamA) + A1 = grplhcif.getAtomicNumberB1() + A2 = grplhcif.getAtomicNumberB2() + + # determine collision system and energy + print ("Determined eCM ", eCM) + print ("Determined eA ", eA) + print ("Determined eB ", eB) + print ("Determined atomic number A1 ", A1) + print ("Determined atomic number A2 ", A2) + ColSystem = "" + col_systems = { + "pp": (1, 1), + "pO": (1, 8), + "Op": (8, 1), + "OO": (8, 8), + "NeNe": (10, 10), + "PbPb": (82, 82) + } + # check if we have a known collision system + for system, (a1, a2) in col_systems.items(): + if A1 == a1 and A2 == a2: + ColSystem = system + break + if ColSystem == "": + print(f"ERROR: Unknown collision system for A1={A1}, A2={A2}. Check the GRPLHCIF object.") + exit(1) + + print ("Collision system ", ColSystem) + + # how to read the raw CTP counting rate in this run, and where the collisions actually end + raw_rate_at, ctp_source = make_CTP_raw_rate_source(args.run_number, mid_run_timestamp, ColSystem) + print("Reading the CTP rate from source " + ctp_source) + collisions_end = determine_last_collision_time(raw_rate_at, ctp_scalers, run_end) + if collisions_end < run_end: + print(f"Adjusting end-of-run from {run_end} to {collisions_end}: the CTP counted no " + f"collisions over the last {(run_end - collisions_end)/1000.:.1f} s of this run") + run_end = collisions_end + mid_run_timestamp = (effective_run_start + run_end) // 2 + + # possibly overwrite the orbitsPerTF with some external choices + if args.orbitsPerTF!="": + # we actually need the interaction rate for this calculation + # let's use the one provided from IR.txt (async reco) as quick way to make the decision + run_rate, _ = retrieve_MinBias_CTPScaler_Rate(raw_rate_at, mid_run_timestamp, args.trig_eff, grplhcif.getBunchFilling().getNBunches(), ColSystem, eCM, args.run_number) + determined_orbits = parse_orbits_per_tf(args.orbitsPerTF, run_rate) + if determined_orbits != -1: + print("Adjusting orbitsPerTF from " + str(GLOparams["OrbitsPerTF"]) + " to " + str(determined_orbits)) + GLOparams["OrbitsPerTF"] = determined_orbits + + # determine timestamp, and production offset for the final MC job to run + timestamp = 0 + prod_offset = 0 + if args.timeframeID != -1: + # an explicit timeframe is a fixed position in the run, so bound it by the run as recorded; + # if it falls past the collisions the rate below is zero and the job is excluded + timestamp = determine_timestamp_from_timeframeID(effective_run_start, GLOparams["EOR"], args.timeframeID, GLOparams["OrbitsPerTF"]) + prod_offset = args.timeframeID + else: + timestamp, prod_offset = determine_timestamp(effective_run_start, run_end, [args.split_id - 1, args.prod_split], args.cycle, args.tf, GLOparams["OrbitsPerTF"]) + + # determine orbit corresponding to timestamp (mainly used in exclude_timestamp function) + orbit = GLOparams["FirstOrbit"] + int((timestamp - GLOparams["SOR"]) / ( LHCOrbitMUS / 1000)) + # this is anchored to - print ("Determined start-of-run to be: ", GLOparams["SOR"]) + print ("Determined start-of-run to be: ", effective_run_start) + print ("Determined first orbit to be: ", effective_first_orbit) + print ("Determined end-of-run to be: ", run_end) print ("Determined timestamp to be : ", timestamp) print ("Determined offset to be : ", prod_offset) - print ("Determined start of run to be : ", GLOparams["SOR"]) - - currentorbit = GLOparams["FirstOrbit"] + prod_offset * GLOparams["OrbitsPerTF"] # orbit number at production start - currenttime = GLOparams["SOR"] + prod_offset * GLOparams["OrbitsPerTF"] * LHCOrbitMUS // 1000 # timestamp in milliseconds - print ("Production put at time : " + str(currenttime)) + print ("SOR ", GLOparams["SOR"]) + print ("EOR ", GLOparams["EOR"]) + print ("TIM ", timestamp) # this timestamp + print ("OS ", GLOparams["FirstOrbit"]) + print ("OE ", GLOparams["LastOrbit"]) + print ("TO ", orbit) # this orbit + + # check if timestamp is to be excluded + job_is_exluded = exclude_timestamp(timestamp, orbit, args.run_number, args.run_span_file, GLOparams) + # possibly invert the selection + if args.invert_irframe_selection: + job_is_exluded = not job_is_exluded forwardargs = " ".join([ a for a in args.forward if a != '--' ]) # retrieve interaction rate rate = None + ctp_local_rate_raw = None if args.ccdb_IRate == True: - effT0 = args.ft0_eff - if effT0 < 0: - if args.col == "pp": - if args.eCM < 1000: - effT0 = 0.68 - elif args.eCM < 6000: - effT0 = 0.737 - else: - effT0 = 0.759 - elif args.col == "PbPb": - effT0 = 4.0 - else: - effT0 = 0.759 - - rate = retrieve_MinBias_CTPScaler_Rate(ccdbreader, timestamp, args.run_number, currenttime/1000., effT0) + rate, ctp_local_rate_raw = retrieve_MinBias_CTPScaler_Rate(raw_rate_at, timestamp, args.trig_eff, grplhcif.getBunchFilling().getNBunches(), ColSystem, eCM, args.run_number) + + # no collisions here, so nothing to simulate: treat it like any other excluded timestamp + if rate == None and ctp_local_rate_raw == 0: + job_is_exluded = True if rate != None: # if the rate calculation was successful we will use it, otherwise we fall back to some rate given as part @@ -335,14 +734,60 @@ def main(): # Use re.sub() to replace the pattern with an empty string forwardargs = re.sub(pattern, " ", forwardargs) forwardargs += ' -interactionRate ' + str(int(rate)) - + if ctp_local_rate_raw != None and ctp_local_rate_raw > 0: + forwardargs += ' --ctp-scaler ' + str(ctp_local_rate_raw) + # we finally pass forward to the unanchored MC workflow creation - # TODO: this needs to be done in a pythonic way clearly - forwardargs += " -tf " + str(args.tf) + " --sor " + str(GLOparams["SOR"]) + " --timestamp " + str(timestamp) + " --production-offset " + str(prod_offset) + " -run " + str(args.run_number) + " --run-anchored --first-orbit " + str(GLOparams["FirstOrbit"]) + " -field ccdb -bcPatternFile ccdb" + " --orbitsPerTF " + str(GLOparams["OrbitsPerTF"]) + " -col " + str(args.col) + " -eCM " + str(args.eCM) + # NOTE: forwardargs can - in principle - contain some of the arguments that are appended here. + # However, the last passed argument wins, so they would be overwritten. If this should not happen, the option + # needs to be handled as further below: + energyarg = (" -eCM " + str(eCM)) if A1 == A2 else (" -eA " + str(eA) + " -eB " + str(eB)) + forwardargs += " -tf " + str(args.tf) + " --sor " + str(effective_run_start) + " --timestamp " + str(timestamp) + " --production-offset " + str(prod_offset) + " -run " + str(args.run_number) + " --run-anchored --first-orbit " \ + + str(effective_first_orbit) + " --orbitsPerTF " + str(GLOparams["OrbitsPerTF"]) + str(energyarg) + # the following options can be overwritten/influenced from the outside + if not re.search(r'(? /dev/null" + os.system(command) + +# A fallback solution to getting all child procs +# in case psutil has problems (PermissionError). +# It returns the same list as psutil.children(recursive=True). +def getChildProcs(basepid): + cmd=''' + childprocs() { + local parent=$1 + if [ ! "$2" ]; then + child_pid_list="" + fi + if [ "$parent" ] ; then + child_pid_list="$child_pid_list $parent" + for childpid in $(pgrep -P ${parent}); do + childprocs $childpid "nottoplevel" + done; + fi + # return via a string list (only if toplevel) + if [ ! "$2" ]; then + echo "${child_pid_list}" + fi + } + ''' + cmd = cmd + '\n' + 'childprocs ' + str(basepid) + output = subprocess.check_output(cmd, shell=True) + plist = [] + for p in output.strip().split(): + try: + proc=psutil.Process(int(p)) + except psutil.NoSuchProcess: + continue + + plist.append(proc) + return plist + +# +# Code section to find all topological orderings +# of a DAG. This is used to know when we can schedule +# things in parallel. +# Taken from https://www.geeksforgeeks.org/all-topological-sorts-of-a-directed-acyclic-graph/ + +# class to represent a graph object +class Graph: + + # Constructor + def __init__(self, edges, N): + + # A List of Lists to represent an adjacency list + self.adjList = [[] for _ in range(N)] + + # stores in-degree of a vertex + # initialize in-degree of each vertex by 0 + self.indegree = [0] * N + + # add edges to the undirected graph + for (src, dest) in edges: + + # add an edge from source to destination + self.adjList[src].append(dest) + + # increment in-degree of destination vertex by 1 + self.indegree[dest] = self.indegree[dest] + 1 + +# Recursive function to find all topological orderings of a given DAG +def findAllTopologicalOrders(graph, path, discovered, N, allpaths, maxnumber=1): + if len(allpaths) >= maxnumber: + return + + # do for every vertex + for v in range(N): + + # proceed only if in-degree of current node is 0 and + # current node is not processed yet + if graph.indegree[v] == 0 and not discovered[v]: + + # for every adjacent vertex u of v, reduce in-degree of u by 1 + for u in graph.adjList[v]: + graph.indegree[u] = graph.indegree[u] - 1 + + # include current node in the path and mark it as discovered + path.append(v) + discovered[v] = True + + # recur + findAllTopologicalOrders(graph, path, discovered, N, allpaths) + + # backtrack: reset in-degree information for the current node + for u in graph.adjList[v]: + graph.indegree[u] = graph.indegree[u] + 1 + + # backtrack: remove current node from the path and + # mark it as undiscovered + path.pop() + discovered[v] = False + + # record valid ordering + if len(path) == N: + allpaths.append(path.copy()) + + +# get all topological orderings of a given DAG as a list +def printAllTopologicalOrders(graph, maxnumber=1): + # get number of nodes in the graph + N = len(graph.adjList) + + # create an auxiliary space to keep track of whether vertex is discovered + discovered = [False] * N + + # list to store the topological order + path = [] + allpaths = [] + # find all topological ordering and print them + findAllTopologicalOrders(graph, path, discovered, N, allpaths, maxnumber=maxnumber) + return allpaths + +# <--- end code section for topological sorts + +# find all tasks that depend on a given task (id); when a cache +# dict is given we can fill for the whole graph in one pass... +def find_all_dependent_tasks(possiblenexttask, tid, cache=None): + c=cache.get(tid) if cache else None + if c!=None: + return c + + daughterlist=[tid] + # possibly recurse + for n in possiblenexttask[tid]: + c = cache.get(n) if cache else None + if c == None: + c = find_all_dependent_tasks(possiblenexttask, n, cache) + daughterlist = daughterlist + c + if cache is not None: + cache[n]=c + + if cache is not None: + cache[tid]=daughterlist + return list(set(daughterlist)) + + +# wrapper taking some edges, constructing the graph, +# obtain all topological orderings and some other helper data structures +def analyseGraph(edges, nodes): + # Number of nodes in the graph + N = len(nodes) + + # candidate list trivial + nextjobtrivial = { n:[] for n in nodes } + # startnodes + nextjobtrivial[-1] = nodes + for e in edges: + nextjobtrivial[e[0]].append(e[1]) + if nextjobtrivial[-1].count(e[1]): + nextjobtrivial[-1].remove(e[1]) + + # find topological orderings of the graph + # create a graph from edges + graph = Graph(edges, N) + orderings = printAllTopologicalOrders(graph) + + return (orderings, nextjobtrivial) + + +def draw_workflow(workflowspec): + if not havegraphviz: + print('graphviz not installed, cannot draw workflow') + return + + dot = Digraph(comment='MC workflow') + nametoindex={} + index=0 + # nodes + for node in workflowspec['stages']: + name=node['name'] + nametoindex[name]=index + dot.node(str(index), name) + index=index+1 + + # edges + for node in workflowspec['stages']: + toindex = nametoindex[node['name']] + for req in node['needs']: + fromindex = nametoindex[req] + dot.edge(str(fromindex), str(toindex)) + + dot.render('workflow.gv') + +# builds the graph given a "taskuniverse" list +# builds accompagnying structures tasktoid and idtotask +def build_graph(taskuniverse, workflowspec): + tasktoid={ t[0]['name']:i for i, t in enumerate(taskuniverse, 0) } + # print (tasktoid) + + nodes = [] + edges = [] + for t in taskuniverse: + nodes.append(tasktoid[t[0]['name']]) + for n in t[0]['needs']: + edges.append((tasktoid[n], tasktoid[t[0]['name']])) + + return (edges, nodes) + + +# loads json into dict, e.g. for workflow specification +def load_json(workflowfile): + fp=open(workflowfile) + workflowspec=json.load(fp) + return workflowspec + + +# filters the original workflowspec according to wanted targets or labels +# returns a new workflowspec and the list of "final" workflowtargets +def filter_workflow(workflowspec, targets=[], targetlabels=[]): + if len(targets)==0: + return workflowspec, [] + if len(targetlabels)==0 and len(targets)==1 and targets[0]=="*": + return workflowspec, [] + + transformedworkflowspec = workflowspec + + def task_matches(t): + for filt in targets: + if filt=="*": + return True + if re.match(filt, t) != None: + return True + return False + + def task_matches_labels(t): + # when no labels are given at all it's ok + if len(targetlabels)==0: + return True + + for l in t['labels']: + if targetlabels.count(l)!=0: + return True + return False + + # The following sequence of operations works and is somewhat structured. + # However, it builds lookups used elsewhere as well, so some CPU might be saved by reusing + # some structures across functions or by doing less passes on the data. + + # helper lookup + tasknametoid = { t['name']:i for i, t in enumerate(workflowspec['stages'],0) } + + # check if a task can be run at all + # or not due to missing requirements + def canBeDone(t,cache={}): + ok = True + c = cache.get(t['name']) + if c != None: + return c + for r in t['needs']: + taskid = tasknametoid.get(r) + if taskid != None: + if not canBeDone(workflowspec['stages'][taskid], cache): + ok = False + break + else: + ok = False + break + cache[t['name']] = ok + if ok == False: + print (f"Disabling target {t['name']} due to unsatisfied requirements") + return ok + + okcache = {} + # build full target list + full_target_list = [ t for t in workflowspec['stages'] if task_matches(t['name']) and task_matches_labels(t) and canBeDone(t,okcache) ] + full_target_name_list = [ t['name'] for t in full_target_list ] + + # build full dependency list for a task t + def getallrequirements(t): + _l=[] + for r in t['needs']: + fulltask = workflowspec['stages'][tasknametoid[r]] + _l.append(fulltask) + _l=_l+getallrequirements(fulltask) + return _l + + full_requirements_list = [ getallrequirements(t) for t in full_target_list ] + + # make flat and fetch names only + full_requirements_name_list = list(set([ item['name'] for sublist in full_requirements_list for item in sublist ])) + + # inner "lambda" helper answering if a task "name" is needed by given targets + def needed_by_targets(name): + if full_target_name_list.count(name)!=0: + return True + if full_requirements_name_list.count(name)!=0: + return True + return False + + # we finaly copy everything matching the targets as well + # as all their requirements + transformedworkflowspec['stages']=[ l for l in workflowspec['stages'] if needed_by_targets(l['name']) ] + return transformedworkflowspec, full_target_name_list + + +# builds topological orderings (for each timeframe) +def build_dag_properties(workflowspec): + globaltaskuniverse = [ (l, i) for i, l in enumerate(workflowspec['stages'], 1) ] + timeframeset = set( l['timeframe'] for l in workflowspec['stages'] ) + + edges, nodes = build_graph(globaltaskuniverse, workflowspec) + tup = analyseGraph(edges, nodes.copy()) + # + global_next_tasks = tup[1] + + + dependency_cache = {} + # weight influences scheduling order can be anything user defined ... for the moment we just prefer to stay within a timeframe + # then take the number of tasks that depend on a task as further weight + # TODO: bring in resource estimates from runtime, CPU, MEM + # TODO: make this a policy of the runner to study different strategies + def getweight(tid): + return (globaltaskuniverse[tid][0]['timeframe'], len(find_all_dependent_tasks(global_next_tasks, tid, dependency_cache))) + + task_weights = [ getweight(tid) for tid in range(len(globaltaskuniverse)) ] + + for tid in range(len(globaltaskuniverse)): + actionlogger.info("Score for " + str(globaltaskuniverse[tid][0]['name']) + " is " + str(task_weights[tid])) + + # print (global_next_tasks) + return { 'nexttasks' : global_next_tasks, 'weights' : task_weights, 'topological_ordering' : tup[0] } + + +# update the resource estimates of a workflow based on resources given via JSON +def update_resource_estimates(workflow, resource_json): + # the resource_dict here is generated by tool o2dpg_sim_metrics.py json-stat + resource_dict = load_json(resource_json) + stages = workflow["stages"] + + for task in stages: + if task["timeframe"] >= 1: + name = "_".join(task["name"].split("_")[:-1]) + else: + name = task["name"] + + if name not in resource_dict: + continue + + new_resources = resource_dict[name] + + # memory + newmem = new_resources.get("pss", {}).get("max", None) + if newmem is not None: + oldmem = task["resources"]["mem"] + actionlogger.info("Updating mem estimate for " + task["name"] + " from " + str(oldmem) + " to " + str(newmem)) + task["resources"]["mem"] = newmem + + # cpu + newcpu = new_resources.get("cpu", {}).get("mean", None) + if newcpu is not None: + oldcpu = task["resources"]["cpu"] + rel_cpu = task["resources"]["relative_cpu"] + # TODO: No longer sure about this since we inject numbers from actually measured workloads + if rel_cpu is not None: + # respect the relative CPU settings + # By default, the CPU value in the workflow is already scaled if relative_cpu is given. + # The new estimate on the other hand is not yet scaled so it needs to be done here. + newcpu *= rel_cpu + actionlogger.info("Updating cpu estimate for " + task["name"] + " from " + str(oldcpu) + " to " + str(newcpu)) + task["resources"]["cpu"] = newcpu + +# a function to read a software environment determined by alienv into +# a python dictionary +def get_alienv_software_environment(packagestring): + """ + packagestring is something like O2::v202298081-1,O2Physics::xxx representing packages + published on CVMFS ... or ... a file containing directly the software environment to apply + """ + + # the trivial cases do nothing + if packagestring == None or packagestring == "" or packagestring == "None": + return {} + + def load_env_file(env_file): + """Transform an environment file generated with 'export > env.txt' into a python dictionary.""" + env_vars = {} + with open(env_file, "r") as f: + for line in f: + line = line.strip() + + # Ignore empty lines or comments + if not line or line.startswith("#"): + continue + + # Remove 'declare -x ' if present + if line.startswith("declare -x "): + line = line.replace("declare -x ", "", 1) + + # Handle case: "FOO" without "=" (assign empty string) + if "=" not in line: + key, value = line.strip(), "" + else: + key, value = line.split("=", 1) + value = value.strip('"') # Remove surrounding quotes if present + + env_vars[key.strip()] = value + return env_vars + + # see if this is a file + if os.path.exists(packagestring) and os.path.isfile(packagestring): + actionlogger.info("Taking software environment from file " + packagestring) + return load_env_file(packagestring) + + # alienv printenv packagestring --> dictionary + # for the moment this works with CVMFS only + cmd="/cvmfs/alice.cern.ch/bin/alienv printenv " + packagestring + proc = subprocess.Popen([cmd], stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) + + envstring, err = proc.communicate() + # see if the printenv command was successful + if len(err.decode()) > 0: + print (err.decode()) + raise Exception + + # the software environment is now in the evnstring + # split it on semicolon + envstring=envstring.decode() + tokens=envstring.split(";") + # build envmap + envmap = {} + for t in tokens: + # check if assignment + if t.count("=") > 0: + assignment = t.rstrip().split("=") + envmap[assignment[0]] = assignment[1] + elif t.count("export") > 0: + # the case when we export or a simple variable + # need to consider the case when this has not been previously assigned + variable = t.split()[1] + if not variable in envmap: + envmap[variable]="" + + return envmap + +# +# functions for execution; encapsulated in a WorkflowExecutor class +# + +class Semaphore: + """ + Object that can be used as semaphore + """ + def __init__(self): + self.locked = False + def lock(self): + self.locked = True + def unlock(self): + self.locked = False + + +class ResourceBoundaries: + """ + Container holding global resource properties + """ + def __init__(self, cpu_limit, mem_limit, dynamic_resources=False, optimistic_resources=False): + self.cpu_limit = cpu_limit + self.mem_limit = mem_limit + self.dynamic_resources = dynamic_resources + # if this is set, tasks that would normally go beyond the resource limits will tried to be run in any case + self.optimistic_resources = optimistic_resources + + +class TaskResources: + """ + Container holding resources of a single task + """ + def __init__(self, tid, name, cpu, cpu_relative, mem, resource_boundaries): + # the task ID belonging to these resources + self.tid = tid + self.name = name + # original CPUs/MEM assigned (persistent) + self.cpu_assigned_original = cpu + self.mem_assigned_original = mem + # relative CPU, to be multiplied with sampled CPU; set by the user, e.g. to allow to backfill tasks + # only takes effect when sampling resources; persistent + self.cpu_relative = cpu_relative if cpu_relative else 1 + # CPUs/MEM assigned (transient) + self.cpu_assigned = cpu + self.mem_assigned = mem + # global resource settings + self.resource_boundaries = resource_boundaries + # sampled resources of this + self.cpu_sampled = None + self.mem_sampled = None + # Set these after a task has finished to compute new estimates for related tasks + self.walltime = None + self.cpu_taken = None + self.mem_taken = None + # collected during monitoring + self.time_collect = [] + self.cpu_collect = [] + self.mem_collect = [] + # linked to other resources of task that are of the same type as this one + self.related_tasks = None + # can assign a semaphore + self.semaphore = None + # the task's nice value + self.nice_value = None + # whether or not the task's resources are currently booked + self.booked = False + + @property + def is_done(self): + return self.time_collect and not self.booked + + def is_within_limits(self): + """ + Check if assigned resources respect limits + """ + cpu_within_limits = True + mem_within_limits = True + if self.cpu_assigned > self.resource_boundaries.cpu_limit: + cpu_within_limits = False + actionlogger.warning("CPU of task %s exceeds limits %d > %d", self.name, self.cpu_assigned, self.resource_boundaries.cpu_limit) + if self.cpu_assigned > self.resource_boundaries.mem_limit: + mem_within_limits = False + actionlogger.warning("MEM of task %s exceeds limits %d > %d", self.name, self.cpu_assigned, self.resource_boundaries.cpu_limit) + return cpu_within_limits and mem_within_limits + + def limit_resources(self, cpu_limit=None, mem_limit=None): + """ + Limit resources of this specific task + """ + if not cpu_limit: + cpu_limit = self.resource_boundaries.cpu_limit + if not mem_limit: + mem_limit = self.resource_boundaries.mem_limit + self.cpu_assigned = min(self.cpu_assigned, cpu_limit) + self.mem_assigned = min(self.mem_assigned, mem_limit) + + def add(self, time_passed, cpu, mem): + """ + Brief interface to add resources that were measured after time_passed + """ + self.time_collect.append(time_passed) + self.cpu_collect.append(cpu) + self.mem_collect.append(mem) + + def sample_resources(self): + """ + If this task is done, sample CPU and MEM for all related tasks that have not started yet + """ + if not self.is_done: + return + + if len(self.time_collect) < 3: + # Consider at least 3 points to sample from + self.cpu_sampled = self.cpu_assigned + self.mem_sampled = self.mem_assigned + actionlogger.debug("Task %s has not enough points (< 3) to sample resources, setting to previosuly assigned values.", self.name) + else: + # take the time deltas and leave out the very first CPU measurent which is not meaningful, + # at least when it domes from psutil.Proc.cpu_percent(interval=None) + time_deltas = [self.time_collect[i+1] - self.time_collect[i] for i in range(len(self.time_collect) - 1)] + cpu = sum([cpu * time_delta for cpu, time_delta in zip(self.cpu_collect[1:], time_deltas) if cpu >= 0]) + self.cpu_sampled = cpu / sum(time_deltas) + self.mem_sampled = max(self.mem_collect) + + mem_sampled = 0 + cpu_sampled = [] + for res in self.related_tasks: + if res.is_done: + mem_sampled = max(mem_sampled, res.mem_sampled) + cpu_sampled.append(res.cpu_sampled) + cpu_sampled = sum(cpu_sampled) / len(cpu_sampled) + + # This task ran already with the assigned resources, so let's set it to the limit + if cpu_sampled > self.resource_boundaries.cpu_limit: + actionlogger.warning("Sampled CPU (%.2f) exceeds assigned CPU limit (%.2f)", cpu_sampled, self.resource_boundaries.cpu_limit) + elif cpu_sampled < 0: + actionlogger.debug("Sampled CPU for %s is %.2f < 0, setting to previously assigned value %.2f", self.name, cpu_sampled, self.cpu_assigned) + cpu_sampled = self.cpu_assigned + + if mem_sampled > self.resource_boundaries.mem_limit: + actionlogger.warning("Sampled MEM (%.2f) exceeds assigned MEM limit (%.2f)", mem_sampled, self.resource_boundaries.mem_limit) + elif mem_sampled <= 0: + actionlogger.debug("Sampled memory for %s is %.2f <= 0, setting to previously assigned value %.2f", self.name, mem_sampled, self.mem_assigned) + mem_sampled = self.mem_assigned + + for res in self.related_tasks: + if res.is_done or res.booked: + continue + res.cpu_assigned = cpu_sampled * res.cpu_relative + res.mem_assigned = mem_sampled + # This task has been run before, stay optimistic and limit the resources in case the sampled ones exceed limits + res.limit_resources() + + +class ResourceManager: + """ + Central class to manage resources + + - CPU limits + - MEM limits + - Semaphores + + Entrypoint to set and to query for resources to be updated. + + Can be asked whether a certain task can be run under current resource usage. + Book and unbook resources. + """ + def __init__(self, cpu_limit, mem_limit, procs_parallel_max=100, dynamic_resources=False, optimistic_resources=False): + """ + Initialise members with defaults + """ + # hold TaskResources of all tasks + self.resources = [] + + # helper dictionaries holding common objects which will be distributed to single TaskResources objects + # to avoid further lookup and at the same time to share the same common objects + self.resources_related_tasks_dict = {} + self.semaphore_dict = {} + + # one common object that holds global resource settings such as CPU and MEM limits + self.resource_boundaries = ResourceBoundaries(cpu_limit, mem_limit, dynamic_resources, optimistic_resources) + + # register resources that are booked under default nice value + self.cpu_booked = 0 + self.mem_booked = 0 + # number of tasks currently booked + self.n_procs = 0 + + # register resources that are booked under high nice value + self.cpu_booked_backfill = 0 + self.mem_booked_backfill = 0 + # number of tasks currently booked under high nice value + self.n_procs_backfill = 0 + + # the maximum number of tasks that run at the same time + self.procs_parallel_max = procs_parallel_max + + # get the default nice value of this python script + self.nice_default = os.nice(0) + # add 19 to get nice value of low-priority tasks + self.nice_backfill = self.nice_default + 19 + + def add_task_resources(self, name, related_tasks_name, cpu, cpu_relative, mem, semaphore_string=None): + """ + Construct and Add a new TaskResources object + """ + resources = TaskResources(len(self.resources), name, cpu, cpu_relative, mem, self.resource_boundaries) + if not resources.is_within_limits() and not self.resource_boundaries.optimistic_resources: + # exit if we don't dare to try + print(f"Resources of task {name} are exceeding the boundaries.\nCPU: {cpu} (estimate) vs. {self.resource_boundaries.cpu_limit} (boundary)\nMEM: {mem} (estimated) vs. {self.resource_boundaries.mem_limit} (boundary).") + print("Pass --optimistic-resources to the runner to attempt the run anyway.") + exit(1) + # if we get here, either all is good or the user decided to be optimistic and we limit the resources, by default to the given CPU and mem limits. + resources.limit_resources() + + self.resources.append(resources) + # do the following to have the same Semaphore object for all corresponding TaskResources so that we do not need a lookup + if semaphore_string: + if semaphore_string not in self.semaphore_dict: + self.semaphore_dict[semaphore_string] = Semaphore() + resources.semaphore = self.semaphore_dict[semaphore_string] + + # do the following to give each TaskResources a list of the related tasks so we do not need an additional lookup + if related_tasks_name: + if related_tasks_name not in self.resources_related_tasks_dict: + # assigned list is [valid top be used, list of CPU, list of MEM, list of walltimes of each related task, list of processes that ran in parallel on average, list of taken CPUs, list of assigned CPUs, list of tasks finished in the meantime] + self.resources_related_tasks_dict[related_tasks_name] = [] + self.resources_related_tasks_dict[related_tasks_name].append(resources) + resources.related_tasks = self.resources_related_tasks_dict[related_tasks_name] + + def add_monitored_resources(self, tid, time_delta_since_start, cpu, mem): + self.resources[tid].add(time_delta_since_start, cpu, mem) + + def book(self, tid, nice_value): + """ + Book the resources of this task with given nice value + + The final nice value is determined by the final submission and could be different. + This can happen if the nice value should have been changed while that is not allowed by the system. + """ + res = self.resources[tid] + # take the nice value that was previously assigned when resources where checked last time + previous_nice_value = res.nice_value + + if previous_nice_value is None: + # this has not been checked ever if it was ok to be submitted + actionlogger.warning("Task ID %d has never been checked for resources. Treating as backfill", tid) + nice_value = self.nice_backfill + elif res.nice_value != nice_value: + actionlogger.warning("Task ID %d has was last time checked for a different nice value (%d) but is now submitted with (%d).", tid, res.nice_value, nice_value) + + res.nice_value = nice_value + res.booked = True + if res.semaphore is not None: + res.semaphore.lock() + if nice_value != self.nice_default: + self.n_procs_backfill += 1 + self.cpu_booked_backfill += res.cpu_assigned + self.mem_booked_backfill += res.mem_assigned + return + self.n_procs += 1 + self.cpu_booked += res.cpu_assigned + self.mem_booked += res.mem_assigned + + def unbook(self, tid): + """ + Unbook the reources of this task + """ + res = self.resources[tid] + res.booked = False + if self.resource_boundaries.dynamic_resources: + res.sample_resources() + if res.semaphore is not None: + res.semaphore.unlock() + if res.nice_value != self.nice_default: + self.cpu_booked_backfill -= res.cpu_assigned + self.mem_booked_backfill -= res.mem_assigned + self.n_procs_backfill -= 1 + if self.n_procs_backfill <= 0: + self.cpu_booked_backfill = 0 + self.mem_booked_backfill = 0 + return + self.n_procs -= 1 + self.cpu_booked -= res.cpu_assigned + self.mem_booked -= res.mem_assigned + if self.n_procs <= 0: + self.cpu_booked = 0 + self.mem_booked = 0 + + def ok_to_submit(self, tids): + """ + This generator yields the tid and nice value tuple from the list of task ids that should be checked + """ + tids_copy = tids.copy() + + def ok_to_submit_default(res): + """ + Return default nice value if conditions are met, None otherwise + """ + # analyse CPU + okcpu = (self.cpu_booked + res.cpu_assigned <= self.resource_boundaries.cpu_limit) + # analyse MEM + okmem = (self.mem_booked + res.mem_assigned <= self.resource_boundaries.mem_limit) + actionlogger.debug ('Condition check --normal-- for ' + str(res.tid) + ':' + res.name + ' CPU ' + str(okcpu) + ' MEM ' + str(okmem)) + return self.nice_default if (okcpu and okmem) else None + + def ok_to_submit_backfill(res, backfill_cpu_factor=1.5, backfill_mem_factor=1.5): + """ + Return backfill nice value if conditions are met, None otherwise + """ + if self.n_procs_backfill >= args.n_backfill: + return None + + if res.cpu_assigned > 0.9 * self.resource_boundaries.cpu_limit or res.mem_assigned / self.resource_boundaries.cpu_limit >= 1900: + return None + + # analyse CPU + okcpu = (self.cpu_booked_backfill + res.cpu_assigned <= self.resource_boundaries.cpu_limit) + okcpu = okcpu and (self.cpu_booked + self.cpu_booked_backfill + res.cpu_assigned <= backfill_cpu_factor * self.resource_boundaries.cpu_limit) + # analyse MEM + okmem = (self.mem_booked + self.mem_booked_backfill + res.mem_assigned <= backfill_mem_factor * self.resource_boundaries.mem_limit) + actionlogger.debug ('Condition check --backfill-- for ' + str(res.tid) + ':' + res.name + ' CPU ' + str(okcpu) + ' MEM ' + str(okmem)) + + return self.nice_backfill if (okcpu and okmem) else None + + if self.n_procs + self.n_procs_backfill >= self.procs_parallel_max: + # in this case, nothing can be done + return + + for ok_to_submit_impl, should_break in ((ok_to_submit_default, True), (ok_to_submit_backfill, False)): + tid_index = 0 + while tid_index < len(tids_copy): + + tid = tids_copy[tid_index] + res = self.resources[tid] + + actionlogger.info("Setup resources for task %s, cpu: %f, mem: %f", res.name, res.cpu_assigned, res.mem_assigned) + tid_index += 1 + + if (res.semaphore is not None and res.semaphore.locked) or res.booked: + continue + + nice_value = ok_to_submit_impl(res) + if nice_value is not None: + # if we get a non-None nice value, it means that this task is good to go + res.nice_value = nice_value + # yield the tid and its assigned nice value + yield tid, nice_value + + elif should_break: + # break here if resources of the next task do not fit + break + + +def filegraph_expand_timeframes(data: dict, timeframes: set, target_namelist) -> dict: + """ + A utility function for the fileaccess logic. Takes a template and duplicates + for the multi-timeframe structure. + """ + tf_entries = [ + entry for entry in data.get("file_report", []) + if re.match(r"^\./tf\d+/", entry["file"]) + ] + + result = {} + for i in timeframes: + if i == -1: + continue + # Deepcopy to avoid modifying original + new_entries = deepcopy(tf_entries) + for entry in new_entries: + # Fix filepath + entry["file"] = re.sub(r"^\./tf\d+/", f"./tf{i}/", entry["file"]) + # Fix written_by and read_by (preserve prefix, change numeric suffix) + entry["written_by"] = [ + re.sub(r"_\d+$", f"_{i}", w) for w in entry["written_by"] + ] + # for now we mark some files as keep if they are written + # by a target in the runner targetlist. TODO: Add other mechanisms + # to ask for file keeping (such as via regex or the like) + for e in entry["written_by"]: + if e in target_namelist: + entry["keep"] = True + entry["read_by"] = [ + re.sub(r"_\d+$", f"_{i}", r) for r in entry["read_by"] + ] + result[f"timeframe-{i}"] = new_entries + + return result + + + +class WorkflowExecutor: + # Constructor + def __init__(self, workflowfile, args, jmax=100): + self.args=args + self.is_productionmode = args.production_mode == True # os.getenv("ALIEN_PROC_ID") != None + self.workflowfile = workflowfile + self.workflowspec = load_json(workflowfile) + self.globalinit = self.extract_global_environment(self.workflowspec) # initialize global environment settings + for e in self.globalinit['env']: + if os.environ.get(e, None) == None: + value = self.globalinit['env'][e] + actionlogger.info("Applying global environment from init section " + str(e) + " : " + str(value)) + os.environ[e] = str(value) + + # only keep those tasks that are necessary to be executed based on user's filters + self.full_target_namelist = [] + self.workflowspec, self.full_target_namelist = filter_workflow(self.workflowspec, args.target_tasks, args.target_labels) + + if not self.workflowspec['stages']: + if args.target_tasks: + print ('Apparently some of the chosen target tasks are not in the workflow') + exit (0) + print ('Workflow is empty. Nothing to do') + exit (0) + + # construct the DAG, compute task weights + workflow = build_dag_properties(self.workflowspec) + if args.visualize_workflow: + draw_workflow(self.workflowspec) + self.possiblenexttask = workflow['nexttasks'] + self.taskweights = workflow['weights'] + self.topological_orderings = workflow['topological_ordering'] + self.taskuniverse = [ l['name'] for l in self.workflowspec['stages'] ] + # construct task ID <-> task name lookup + self.idtotask = [ 0 for _ in self.taskuniverse ] + self.tasktoid = {} + self.idtotf = [ l['timeframe'] for l in self.workflowspec['stages'] ] + for i, name in enumerate(self.taskuniverse): + self.tasktoid[name]=i + self.idtotask[i]=name + + if args.update_resources: + update_resource_estimates(self.workflowspec, args.update_resources) + + # construct the object that is in charge of resource management... + self.resource_manager = ResourceManager(args.cpu_limit, args.mem_limit, args.maxjobs, args.dynamic_resources, args.optimistic_resources) + for task in self.workflowspec['stages']: + # ...and add all initial resource estimates + global_task_name = self.get_global_task_name(task["name"]) + try: + cpu_relative = float(task["resources"]["relative_cpu"]) + except TypeError: + cpu_relative = 1 + self.resource_manager.add_task_resources(task["name"], global_task_name, float(task["resources"]["cpu"]), cpu_relative, float(task["resources"]["mem"]), task.get("semaphore")) + + self.procstatus = { tid:'ToDo' for tid in range(len(self.workflowspec['stages'])) } + self.taskneeds= { t:set(self.getallrequirements(t)) for t in self.taskuniverse } + self.stoponfailure = not args.keep_going + print ("Stop on failure ",self.stoponfailure) + + self.scheduling_iteration = 0 # count how often it was tried to schedule new tasks + self.process_list = [] # list of currently scheduled tasks with normal priority + self.backfill_process_list = [] # list of curently scheduled tasks with low backfill priority (not sure this is needed) + self.pid_to_psutilsproc = {} # cache of putilsproc for resource monitoring + self.pid_to_files = {} # we can auto-detect what files are produced by which task (at least to some extent) + self.pid_to_connections = {} # we can auto-detect what connections are opened by which task (at least to some extent) + signal.signal(signal.SIGINT, self.SIGHandler) + signal.siginterrupt(signal.SIGINT, False) + self.internalmonitorcounter = 0 # internal use + self.internalmonitorid = 0 # internal use + self.tids_marked_toretry = [] # sometimes we might want to retry a failed task (simply because it was "unlucky") and we put them here + self.retry_counter = [ 0 for tid in range(len(self.taskuniverse)) ] # we keep track of many times retried already + self.task_retries = [ self.workflowspec['stages'][tid].get('retry_count',0) for tid in range(len(self.taskuniverse)) ] # the per task specific "retry" number -> needs to be parsed from the JSON + + self.alternative_envs = {} # mapping of taskid to alternative software envs (to be applied on a per-task level) + # init alternative software environments + self.init_alternative_software_environments() + + # initialize container to keep track of file-task relationsships + self.file_removal_candidates = {} + self.do_early_file_removal = False + self.timeframeset = set([ task["timeframe"] for task in self.workflowspec['stages'] ]) + if args.remove_files_early != "": + with open(args.remove_files_early) as f: + filegraph_data = json.load(f) + self.do_early_file_removal = True + self.file_removal_candidates = filegraph_expand_timeframes(filegraph_data, self.timeframeset, self.full_target_namelist) + + def apply_global_env(self, environ_dict): + for e in self.globalinit['env']: + if environ_dict.get(e, None) == None: + value = self.globalinit['env'][e] + actionlogger.info("Applying global environment from init section " + str(e) + " : " + str(value)) + environ_dict[e] = str(value) + + def perform_early_file_removal(self, taskids): + """ + This function checks which files can be deleted upon completion of task + and optionally does so. + """ + + def remove_if_exists(filepath: str) -> None: + """ + Check if a file exists, and remove it if found. + """ + if os.path.exists(filepath): + fsize = os.path.getsize(filepath) + os.remove(filepath) + actionlogger.info(f"Removing {filepath} since no longer needed. Freeing {fsize/1024./1024.} MB.") + return True + + return False + + def remove_for_task_id(taskname, file_dict, timeframe_id, listofalltimeframes): + marked_for_removal = [] + + timeframestoscan = [ timeframe_id ] + if timeframe_id == -1: + timeframestoscan = [ i for i in listofalltimeframes if i != -1 ] + + # TODO: Note that this traversal of files is not certainly not optimal + # We should (and will) keep an mapping of tasks->potential files and just + # scan these. This is already provided by the FileIOGraph analysis tool. + for tid in timeframestoscan: + for i,file_entry in enumerate(file_dict[f"timeframe-{tid}"]): + filename = file_entry['file'] + read_by = file_entry['read_by'] + written_by = file_entry['written_by'] + if taskname in read_by: + file_entry['read_by'].remove(taskname) + if taskname in written_by: + file_entry['written_by'].remove(taskname) + + # TODO: in principle the written_by criterion might not be needed + if len(file_entry['read_by']) == 0 and len(file_entry['written_by']) == 0 and file_entry.get('keep', False) == False: + # the filename mentioned here is no longer needed and we can remove it + # make sure it is there and then delete it + if remove_if_exists(filename): + # also take out the file entry from the dict altogether + marked_for_removal.append(file_entry) + + #for k in marked_for_removal: + # file_dict[f"timeframe-{tid}"].remove(k) + + for tid in taskids: + taskname = self.idtotask[tid] + timeframe_id = self.idtotf[tid] + remove_for_task_id(taskname, self.file_removal_candidates, timeframe_id, self.timeframeset) + + + def SIGHandler(self, signum, frame): + """ + basically forcing shut down of all child processes + """ + actionlogger.info("Signal " + str(signum) + " caught") + try: + procs = psutil.Process().children(recursive=True) + except (psutil.NoSuchProcess): + pass + except (psutil.AccessDenied, PermissionError): + procs = getChildProcs(os.getpid()) + + for p in procs: + actionlogger.info("Terminating " + str(p)) + try: + p.terminate() + except (psutil.NoSuchProcess, psutil.AccessDenied): + pass + + _, alive = psutil.wait_procs(procs, timeout=3) + for p in alive: + try: + actionlogger.info("Killing " + str(p)) + p.kill() + except (psutil.NoSuchProcess, psutil.AccessDenied): + pass + + exit (1) + + def extract_global_environment(self, workflowspec): + """ + Checks if the workflow contains a dedicated init task + defining a global environment. Extract information and remove from workflowspec. + """ + init_index = 0 # this has to be the first task in the workflow + globalenv = {} + initcmd = None + if workflowspec['stages'][init_index]['name'] == '__global_init_task__': + env = workflowspec['stages'][init_index].get('env', None) + if env != None: + globalenv = { e : env[e] for e in env } + cmd = workflowspec['stages'][init_index].get('cmd', None) + if cmd != 'NO-COMMAND': + initcmd = cmd + + del workflowspec['stages'][init_index] + + return {"env" : globalenv, "cmd" : initcmd } + + def execute_globalinit_cmd(self, cmd): + actionlogger.info("Executing global setup cmd " + str(cmd)) + # perform the global init command (think of cleanup/setup things to be done in any case) + p = subprocess.Popen(['/bin/bash','-c', cmd], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + stdout, stderr = p.communicate() + + # Check if the command was successful (return code 0) + if p.returncode == 0: + actionlogger.info(stdout.decode()) + else: + # this should be an error + actionlogger.error("Error executing global init function") + return False + return True + + def get_global_task_name(self, name): + """ + Get the global task name + + Tasks are related if only the suffix _ is different + """ + tokens = name.split("_") + try: + int(tokens[-1]) + return "_".join(tokens[:-1]) + except ValueError: + pass + return name + + def getallrequirements(self, task_name): + """ + get all requirement of a task by its name + """ + l=[] + for required_task_name in self.workflowspec['stages'][self.tasktoid[task_name]]['needs']: + l.append(required_task_name) + l=l+self.getallrequirements(required_task_name) + return l + + def get_logfile(self, tid): + """ + O2 taskwrapper logs task stdout and stderr to logfile .log + Get its exact path based on task ID + """ + # determines the logfile name for this task + name = self.workflowspec['stages'][tid]['name'] + workdir = self.workflowspec['stages'][tid]['cwd'] + return os.path.join(workdir, f"{name}.log") + + def get_done_filename(self, tid): + """ + O2 taskwrapper leaves .log_done after a task has successfully finished + Get its exact path based on task ID + """ + return f"{self.get_logfile(tid)}_done" + + def get_resources_filename(self, tid): + """ + O2 taskwrapper leaves .log_time after a task is done + Get its exact path based on task ID + """ + return f"{self.get_logfile(tid)}_time" + + # removes the done flag from tasks that need to be run again + def remove_done_flag(self, listoftaskids): + """ + Remove .log_done files to given task IDs + """ + for tid in listoftaskids: + done_filename = self.get_done_filename(tid) + name=self.workflowspec['stages'][tid]['name'] + if args.dry_run: + print ("Would mark task " + name + " as to be done again") + else: + print ("Marking task " + name + " as to be done again") + if os.path.exists(done_filename) and os.path.isfile(done_filename): + os.remove(done_filename) + + # submits a task as subprocess and records Popen instance + def submit(self, tid, nice): + """ + Submit a task + + 1. if needed, construct working directory if it does not yet exist + 2. update lookup structures flagging the task as being run + 3. set specific environment if requested for task + 4. construct psutil.Process from command line + 4.1 adjust the niceness of that process if requested + 5. return psutil.Process object + """ + actionlogger.debug("Submitting task " + str(self.idtotask[tid]) + " with nice value " + str(nice)) + c = self.workflowspec['stages'][tid]['cmd'] + workdir = self.workflowspec['stages'][tid]['cwd'] + if workdir: + if os.path.exists(workdir) and not os.path.isdir(workdir): + actionlogger.error('Cannot create working dir ... some other resource exists already') + return None + + if not os.path.isdir(workdir): + os.makedirs(workdir) + + self.procstatus[tid]='Running' + if args.dry_run: + drycommand="echo \' " + str(self.scheduling_iteration) + " : would do " + str(self.workflowspec['stages'][tid]['name']) + "\'" + return psutil.Popen(['/bin/bash','-c',drycommand], cwd=workdir) + + taskenv = os.environ.copy() + # apply specific (non-default) software version, if any + # (this was setup earlier) + alternative_env = self.alternative_envs.get(tid, None) + if alternative_env != None and len(alternative_env) > 0: + actionlogger.info('Applying alternative software environment to task ' + self.idtotask[tid]) + if alternative_env.get('TERM') != None: + # the environment is a complete environment + taskenv = {} + taskenv = alternative_env + else: + for entry in alternative_env: + # overwrite what is present in default + taskenv[entry] = alternative_env[entry] + + # add task specific environment + if self.workflowspec['stages'][tid].get('env')!=None: + taskenv.update(self.workflowspec['stages'][tid]['env']) + + # add global workflow environment + self.apply_global_env(taskenv) + + if os.environ.get('PIPELINE_RUNNER_DUMP_TASKENVS') != None: + envfilename = "taskenv_" + str(tid) + ".log" + with open(envfilename, "w") as file: + json.dump(taskenv, file, indent=2) + + p = psutil.Popen(['/bin/bash','-c',c], cwd=workdir, env=taskenv) + try: + p.nice(nice) + except (psutil.NoSuchProcess, psutil.AccessDenied): + actionlogger.error('Couldn\'t set nice value of ' + str(p.pid) + ' to ' + str(nice)) + + return p + + def ok_to_skip(self, tid): + """ + Decide if task can be skipped based on existence of .log_done + """ + done_filename = self.get_done_filename(tid) + if os.path.exists(done_filename) and os.path.isfile(done_filename): + return True + return False + + def try_job_from_candidates(self, taskcandidates, finished): + """ + Try to schedule next tasks + + Args: + taskcandidates: list + list of possible tasks that can be submitted + finished: list + empty list that will be filled with IDs of tasks that were finished in the meantime + """ + self.scheduling_iteration = self.scheduling_iteration + 1 + + # remove "done / skippable" tasks immediately + for tid in taskcandidates.copy(): # <--- the copy is important !! otherwise this loop is not doing what you think + if self.ok_to_skip(tid): + finished.append(tid) + taskcandidates.remove(tid) + actionlogger.info("Skipping task " + str(self.idtotask[tid])) + + # if tasks_skipped: + # return # ---> we return early in order to preserve some ordering (the next candidate tried should be daughters of skipped jobs) + # get task ID and proposed niceness from generator + for (tid, nice_value) in self.resource_manager.ok_to_submit(taskcandidates): + actionlogger.debug ("trying to submit " + str(tid) + ':' + str(self.idtotask[tid])) + if p := self.submit(tid, nice_value): + # explicitly set the nice value here from the process again because it might happen that submit could not change the niceness + # so we let the ResourceManager know what the final niceness is + self.resource_manager.book(tid, p.nice()) + self.process_list.append((tid,p)) + taskcandidates.remove(tid) + # minimal delay + time.sleep(0.1) + + def stop_pipeline_and_exit(self, process_list): + # kill all remaining jobs + for p in process_list: + p[1].kill() + + exit(1) + + + def monitor(self, process_list): + """ + Go through all running tasks and get their current resources + + Resources are summed up for tasks and all their children + + Pass CPU, PSS, USS, niceness, current time to metriclogger + + Warn if overall PSS exceeds assigned memory limit + """ + self.internalmonitorcounter+=1 + if self.internalmonitorcounter % 5 != 0: + return + + self.internalmonitorid+=1 + + globalCPU=0. + globalPSS=0. + resources_per_task = {} + + # On a global level, we are interested in total disc space used (not differential in tasks) + # We can call system "du" as the fastest impl + def disk_usage_du(path: str) -> int: + """Use system du to get total size in bytes.""" + out = subprocess.check_output(['du', '-sb', path], text=True) + return int(out.split()[0]) + + disc_usage = -1 + if os.getenv("MONITOR_DISC_USAGE"): + disc_usage = disk_usage_du(os.getcwd()) / 1024. / 1024 # in MB + + for tid, proc in process_list: + + # proc is Popen object + pid=proc.pid + if self.pid_to_files.get(pid)==None: + self.pid_to_files[pid]=set() + self.pid_to_connections[pid]=set() + try: + psutilProcs = [ proc ] + # use psutil for CPU measurement + psutilProcs = psutilProcs + proc.children(recursive=True) + except (psutil.NoSuchProcess): + continue + + except (psutil.AccessDenied, PermissionError): + psutilProcs = psutilProcs + getChildProcs(pid) + + # accumulate total metrics (CPU, memory) + totalCPU = 0. + totalPSS = 0. + totalSWAP = 0. + totalUSS = 0. + for p in psutilProcs: + """ + try: + for f in p.open_files(): + self.pid_to_files[pid].add(str(f.path)+'_'+str(f.mode)) + for f in p.connections(kind="all"): + remote=f.raddr + if remote==None: + remote='none' + self.pid_to_connections[pid].add(str(f.type)+"_"+str(f.laddr)+"_"+str(remote)) + except Exception: + pass + """ + thispss=0 + thisuss=0 + # MEMORY part + try: + fullmem=p.memory_full_info() + thispss=getattr(fullmem,'pss',0) #<-- pss not available on MacOS + totalPSS=totalPSS + thispss + totalSWAP=totalSWAP + fullmem.swap + thisuss=fullmem.uss + totalUSS=totalUSS + thisuss + except (psutil.NoSuchProcess, psutil.AccessDenied): + pass + + # CPU part + # fetch existing proc or insert + cachedproc = self.pid_to_psutilsproc.get(p.pid) + if cachedproc!=None: + try: + thiscpu = cachedproc.cpu_percent(interval=None) + except (psutil.NoSuchProcess, psutil.AccessDenied): + thiscpu = 0. + totalCPU = totalCPU + thiscpu + # thisresource = {'iter':self.internalmonitorid, 'pid': p.pid, 'cpu':thiscpu, 'uss':thisuss/1024./1024., 'pss':thispss/1024./1024.} + # metriclogger.info(thisresource) + else: + self.pid_to_psutilsproc[p.pid] = p + try: + self.pid_to_psutilsproc[p.pid].cpu_percent() + except (psutil.NoSuchProcess, psutil.AccessDenied): + pass + + time_delta = int((time.perf_counter() - self.start_time) * 1000) + totalUSS = totalUSS / 1024 / 1024 + totalPSS = totalPSS / 1024 / 1024 + nice_value = proc.nice() + resources_per_task[tid]={'iter':self.internalmonitorid, + 'name':self.idtotask[tid], + 'cpu':totalCPU, + 'uss':totalUSS, + 'pss':totalPSS, + 'nice':nice_value, + 'swap':totalSWAP, + 'label':self.workflowspec['stages'][tid]['labels'], + 'disc': disc_usage} + self.resource_manager.add_monitored_resources(tid, time_delta, totalCPU / 100, totalPSS) + if nice_value == self.resource_manager.nice_default: + globalCPU += totalCPU + globalPSS += totalPSS + + metriclogger.info(resources_per_task[tid]) + send_webhook(self.args.webhook, resources_per_task) + + if globalPSS > self.resource_manager.resource_boundaries.mem_limit: + metriclogger.info('*** MEMORY LIMIT PASSED !! ***') + # --> We could use this for corrective actions such as killing jobs currently back-filling + # (or better hibernating) + + def waitforany(self, process_list, finished, failingtasks): + """ + Loop through all submitted tasks and check if they are finished + + 1. If process is still running, do nothing + 2. If process is finished, get its return value, update finished and failingtasks lists + 2.1 unbook resources + 2.2 add taken resources and pass the to ResourceManager + """ + failuredetected = False + failingpids = [] + if len(process_list)==0: + return False + + for p in list(process_list): + pid = p[1].pid + tid = p[0] # the task id of this process + returncode = 0 + if not self.args.dry_run: + returncode = p[1].poll() + if returncode!=None: + actionlogger.info ('Task ' + str(pid) + ' ' + str(tid)+':'+str(self.idtotask[tid]) + ' finished with status ' + str(returncode)) + # account for cleared resources + self.resource_manager.unbook(tid) + self.procstatus[tid]='Done' + finished.append(tid) + #self.validate_resources_running(tid) + process_list.remove(p) + if returncode != 0: + print (str(self.idtotask[tid]) + ' failed ... checking retry') + # we inspect if this is something "unlucky" which could be resolved by a simple resubmit + if self.is_worth_retrying(tid) and ((self.retry_counter[tid] < int(args.retry_on_failure)) or (self.retry_counter[tid] < int(self.task_retries[tid]))): + print (str(self.idtotask[tid]) + ' to be retried') + actionlogger.info ('Task ' + str(self.idtotask[tid]) + ' failed but marked to be retried ') + self.tids_marked_toretry.append(tid) + self.retry_counter[tid] += 1 + + else: + failuredetected = True + failingpids.append(pid) + failingtasks.append(tid) + + if failuredetected and self.stoponfailure: + actionlogger.info('Stoping pipeline due to failure in stages with PID ' + str(failingpids)) + # self.analyse_files_and_connections() + if self.args.stdout_on_failure: + self.cat_logfiles_tostdout(failingtasks) + self.send_checkpoint(failingtasks, self.args.checkpoint_on_failure) + self.stop_pipeline_and_exit(process_list) + + # empty finished means we have to wait more + return len(finished)==0 + + def is_worth_retrying(self, tid): + # This checks for some signatures in logfiles that indicate that a retry of this task + # might have a chance. + # Ideally, this should be made user configurable. Either the user could inject a lambda + # or a regular expression to use. For now we just put a hard coded list + logfile = self.get_logfile(tid) + + return True #! --> for now we just retry tasks a few times + + # 1) ZMQ_EVENT + interrupted system calls (DPL bug during shutdown) + # Not sure if grep is faster than native Python text search ... + # status = os.system('grep "failed setting ZMQ_EVENTS" ' + logfile + ' &> /dev/null') + # if os.WEXITSTATUS(status) == 0: + # return True + + # return False + + + def cat_logfiles_tostdout(self, taskids): + # In case of errors we can cat the logfiles for this taskname + # to stdout. Assuming convention that "taskname" translates to "taskname.log" logfile. + for tid in taskids: + logfile = self.get_logfile(tid) + if os.path.exists(logfile): + print (' ----> START OF LOGFILE ', logfile, ' -----') + os.system('cat ' + logfile) + print (' <---- END OF LOGFILE ', logfile, ' -----') + + def send_checkpoint(self, taskids, location): + # Makes a tarball containing all files in the base dir + # (timeframe independent) and the dir with corrupted timeframes + # and copies it to a specific ALIEN location. Not a core function + # just some tool get hold on error conditions appearing on the GRID. + + def get_tar_command(dir='./', flags='cf', findtype='f', filename='checkpoint.tar'): + return 'find ' + str(dir) + ' -maxdepth 1 -type ' + str(findtype) + ' -print0 | xargs -0 tar ' + str(flags) + ' ' + str(filename) + + if location != None: + print ('Making a failure checkpoint') + # let's determine a filename from ALIEN_PROC_ID - hostname - and PID + + aliprocid=os.environ.get('ALIEN_PROC_ID') + if aliprocid == None: + aliprocid = 0 + + fn='pipeline_checkpoint_ALIENPROC' + str(aliprocid) + '_PID' + str(os.getpid()) + '_HOST' + socket.gethostname() + '.tar' + actionlogger.info("Checkpointing to file " + fn) + tarcommand = get_tar_command(filename=fn) + actionlogger.info("Taring " + tarcommand) + + # create a README file with instruction on how to use checkpoint + readmefile=open('README_CHECKPOINT_PID' + str(os.getpid()) + '.txt','w') + + for tid in taskids: + taskspec = self.workflowspec['stages'][tid] + name = taskspec['name'] + readmefile.write('Checkpoint created because of failure in task ' + name + '\n') + readmefile.write('In order to reproduce with this checkpoint, do the following steps:\n') + readmefile.write('a) setup the appropriate O2sim environment using alienv\n') + readmefile.write('b) run: $O2DPG_ROOT/MC/bin/o2_dpg_workflow_runner.py -f workflow.json -tt ' + name + '$ --retry-on-failure 0\n') + readmefile.close() + + # first of all the base directory + os.system(tarcommand) + + # then we add stuff for the specific timeframes ids if any + for tid in taskids: + taskspec = self.workflowspec['stages'][tid] + directory = taskspec['cwd'] + if directory != "./": + tarcommand = get_tar_command(dir=directory, flags='rf', filename=fn) + actionlogger.info("Tar command is " + tarcommand) + os.system(tarcommand) + # same for soft links + tarcommand = get_tar_command(dir=directory, flags='rf', findtype='l', filename=fn) + actionlogger.info("Tar command is " + tarcommand) + os.system(tarcommand) + + # prepend file:/// to denote local file + fn = "file://" + fn + actionlogger.info("Local checkpoint file is " + fn) + + # location needs to be an alien path of the form alien:///foo/bar/ + copycommand='alien.py cp ' + fn + ' ' + str(location) + '@disk:1' + actionlogger.info("Copying to alien " + copycommand) + os.system(copycommand) + + def init_alternative_software_environments(self): + """ + Initialises alternative software environments for specific tasks, if there + is an annotation in the workflow specificiation. + """ + + environment_cache = {} + # go through all the tasks once and setup environment + for taskid in range(len(self.workflowspec['stages'])): + packagestr = self.workflowspec['stages'][taskid].get("alternative_alienv_package") + if packagestr == None: + continue + + if environment_cache.get(packagestr) == None: + environment_cache[packagestr] = get_alienv_software_environment(packagestr) + + self.alternative_envs[taskid] = environment_cache[packagestr] + + + def analyse_files_and_connections(self): + for p,s in self.pid_to_files.items(): + for f in s: + print("F" + str(f) + " : " + str(p)) + for p,s in self.pid_to_connections.items(): + for c in s: + print("C" + str(c) + " : " + str(p)) + #print(str(p) + " CONS " + str(c)) + try: + # check for intersections + for p1, s1 in self.pid_to_files.items(): + for p2, s2 in self.pid_to_files.items(): + if p1!=p2: + if type(s1) is set and type(s2) is set: + if len(s1)>0 and len(s2)>0: + try: + inters = s1.intersection(s2) + except Exception: + print ('Exception during intersect inner') + pass + if (len(inters)>0): + print ('FILE Intersection ' + str(p1) + ' ' + str(p2) + ' ' + str(inters)) + # check for intersections + for p1, s1 in self.pid_to_connections.items(): + for p2, s2 in self.pid_to_connections.items(): + if p1!=p2: + if type(s1) is set and type(s2) is set: + if len(s1)>0 and len(s2)>0: + try: + inters = s1.intersection(s2) + except Exception: + print ('Exception during intersect inner') + pass + if (len(inters)>0): + print ('CON Intersection ' + str(p1) + ' ' + str(p2) + ' ' + str(inters)) + + # check for intersections + #for p1, s1 in slf.pid_to_files.items(): + # for p2, s2 in self.pid_to_files.items(): + # if p1!=p2 and len(s1.intersection(s2))!=0: + # print ('Intersection found files ' + str(p1) + ' ' + str(p2) + ' ' + s1.intersection(s2)) + except Exception as e: + exc_type, exc_obj, exc_tb = sys.exc_info() + fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] + print(exc_type, fname, exc_tb.tb_lineno) + print('Exception during intersect outer') + pass + + def is_good_candidate(self, candid, finishedtasks): + if self.procstatus[candid] != 'ToDo': + return False + needs = set([self.tasktoid[t] for t in self.taskneeds[self.idtotask[candid]]]) + if set(finishedtasks).intersection(needs) == needs: + return True + return False + + def emit_code_for_task(self, tid, lines): + actionlogger.debug("Submitting task " + str(self.idtotask[tid])) + taskspec = self.workflowspec['stages'][tid] + c = taskspec['cmd'] + workdir = taskspec['cwd'] + env = taskspec.get('env') + # in general: + # try to make folder + lines.append('[ ! -d ' + workdir + ' ] && mkdir ' + workdir + '\n') + # cd folder + lines.append('cd ' + workdir + '\n') + # set local environment + if env!=None: + for e in env.items(): + lines.append('export ' + e[0] + '=' + str(e[1]) + '\n') + # do command + lines.append(c + '\n') + # unset local environment + if env!=None: + for e in env.items(): + lines.append('unset ' + e[0] + '\n') + + # cd back + lines.append('cd $OLDPWD\n') + + + # produce a bash script that runs workflow standalone + def produce_script(self, filename): + # pick one of the correct task orderings + taskorder = self.topological_orderings[0] + outF = open(filename, "w") + + lines=[] + # header + lines.append('#!/usr/bin/env bash\n') + lines.append('#THIS FILE IS AUTOGENERATED\n') + lines.append('export JOBUTILS_SKIPDONE=ON\n') + + # we record the global environment setting + # in particular to capture global workflow initialization + lines.append('#-- GLOBAL INIT SECTION FROM WORKFLOW --\n') + for e in self.globalinit['env']: + lines.append('export ' + str(e) + '=' + str(self.globalinit['env'][e]) + '\n') + lines.append('#-- TASKS FROM WORKFLOW --\n') + for tid in taskorder: + print ('Doing task ' + self.idtotask[tid]) + self.emit_code_for_task(tid, lines) + + outF.writelines(lines) + outF.close() + + def production_endoftask_hook(self, tid): + # Executes a hook at end of a successful task, meant to be used in GRID productions. + # For the moment, archiving away log files, done + time files from jobutils. + # TODO: In future this may be much more generic tasks such as dynamic cleanup of intermediate + # files (when they are no longer needed). + # TODO: Care must be taken with the continue feature as `_done` files are stored elsewhere now + actionlogger.info("Cleaning up log files for task " + str(tid)) + logf = self.get_logfile(tid) + donef = self.get_done_filename(tid) + timef = logf + "_time" + + # add to tar file archive + tf = tarfile.open(name="pipeline_log_archive.log.tar", mode='a') + if tf != None: + tf.add(logf) + tf.add(donef) + tf.add(timef) + tf.close() + + # remove original file + os.remove(logf) + os.remove(donef) + os.remove(timef) + + # print error message when no progress can be made + def noprogress_errormsg(self): + # TODO: rather than writing this out here; refer to the documentation discussion this? + msg = """Scheduler runtime error: The scheduler is not able to make progress although we have a non-zero candidate set. + +Explanation: This is typically the case because the **ESTIMATED** resource requirements for some tasks +in the workflow exceed the available number of CPU cores or the memory (as explicitely or implicitely determined from the +--cpu-limit and --mem-limit options). Often, this might be the case on laptops with <=16GB of RAM if one of the tasks +is demanding ~16GB. In this case, one could try to tell the scheduler to use a slightly higher memory limit +with an explicit --mem-limit option (for instance `--mem-limit 20000` to set to 20GB). This might work whenever the +**ACTUAL** resource usage of the tasks is smaller than anticipated (because only small test cases are run). + +In addition it might be worthwile running the workflow without this resource aware, dynamic scheduler. +This is possible by converting the json workflow into a linearized shell script and by directly executing the shell script. +Use the `--produce-script myscript.sh` option for this. +""" + print (msg, file=sys.stderr) + + def execute(self): + self.start_time = time.perf_counter() + psutil.cpu_percent(interval=None) + os.environ['JOBUTILS_SKIPDONE'] = "ON" + errorencountered = False + + def speedup_ROOT_Init(): + """initialize some env variables that speed up ROOT init + and prevent ROOT from spawning many short-lived child + processes""" + + # only do it on Linux + if platform.system() != 'Linux': + return + + if os.environ.get('ROOT_LDSYSPATH')!=None and os.environ.get('ROOT_CPPSYSINCL')!=None: + # do nothing if already defined + return + + # a) the PATH for system libraries + # search taken from ROOT TUnixSystem + cmd='LD_DEBUG=libs LD_PRELOAD=DOESNOTEXIST ls /tmp/DOESNOTEXIST 2>&1 | grep -m 1 "system search path" | sed \'s/.*=//g\' | awk \'//{print $1}\'' + proc = subprocess.Popen([cmd], stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) + libpath, err = proc.communicate() + if not (args.no_rootinit_speedup == True): + print ("setting up ROOT system") + os.environ['ROOT_LDSYSPATH'] = libpath.decode() + os.environ['CLING_LDSYSPATH'] = libpath.decode() + + # b) the PATH for compiler includes needed by Cling + cmd = "LC_ALL=C c++ -xc++ -E -v /dev/null 2>&1 | sed -n '/^#include/,${/^ \\/.*++/{p}}'" + proc = subprocess.Popen([cmd], stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) + incpath, err = proc.communicate() + incpaths = [ line.lstrip() for line in incpath.decode().splitlines() ] + joined = ':'.join(incpaths) + if not (args.no_rootinit_speedup == True): + actionlogger.info("Determined ROOT_CPPSYSINCL=" + joined) + os.environ['ROOT_CPPSYSINCL'] = joined + os.environ['CLING_CPPSYSINCL'] = joined + + speedup_ROOT_Init() + + # we make our own "tmp" folder + # where we can put stuff such as tmp socket files etc (for instance DPL FAIR-MQ sockets) + # (In case of running within docker/singularity, this may not be so important) + if not os.path.isdir("./.tmp"): + os.mkdir("./.tmp") + if os.environ.get('FAIRMQ_IPC_PREFIX')==None: + socketpath = os.getcwd() + "/.tmp" + actionlogger.info("Setting FAIRMQ socket path to " + socketpath) + os.environ['FAIRMQ_IPC_PREFIX'] = socketpath + + # some maintenance / init work + if args.list_tasks: + print ('List of tasks in this workflow:') + for i,t in enumerate(self.workflowspec['stages'],0): + print (t['name'] + ' (' + str(t['labels']) + ')' + ' ToDo: ' + str(not self.ok_to_skip(i))) + exit (0) + + if args.produce_script != None: + self.produce_script(args.produce_script) + exit (0) + + # execute the user-given global init cmd for this workflow + globalinitcmd = self.globalinit.get("cmd", None) + if globalinitcmd != None: + if not self.execute_globalinit_cmd(globalinitcmd): + exit (1) + + if args.rerun_from: + reruntaskfound=False + for task in self.workflowspec['stages']: + taskname=task['name'] + if re.match(args.rerun_from, taskname): + reruntaskfound=True + taskid=self.tasktoid[taskname] + self.remove_done_flag(find_all_dependent_tasks(self.possiblenexttask, taskid)) + if not reruntaskfound: + print('No task matching ' + args.rerun_from + ' found; cowardly refusing to do anything ') + exit (1) + + # ***************** + # main control loop + # ***************** + candidates = [ tid for tid in self.possiblenexttask[-1] ] + + self.process_list=[] # list of tuples of nodes ids and Popen subprocess instances + + finishedtasks=[] # global list of finished tasks + + try: + + while True: + # sort candidate list according to task weights + candidates = [ (tid, self.taskweights[tid]) for tid in candidates ] + candidates.sort(key=lambda tup: (tup[1][0],-tup[1][1])) # prefer small and same timeframes first then prefer important tasks within frameframe + # remove weights + candidates = [ tid for tid,_ in candidates ] + + finished = [] # --> to account for finished because already done or skipped + actionlogger.debug('Sorted current candidates: ' + str([(c,self.idtotask[c]) for c in candidates])) + self.try_job_from_candidates(candidates, finished) + if len(candidates) > 0 and len(self.process_list) == 0: + self.noprogress_errormsg() + send_webhook(self.args.webhook,"Unable to make further progress: Quitting") + errorencountered = True + break + + finished_from_started = [] # to account for finished when actually started + failing = [] + while self.waitforany(self.process_list, finished_from_started, failing): + if not args.dry_run: + self.monitor(self.process_list) # ---> make async to normal operation? + time.sleep(1) # <--- make this incremental (small wait at beginning) + else: + time.sleep(0.001) + + finished = finished + finished_from_started + actionlogger.debug("finished now :" + str(finished_from_started)) + finishedtasks = finishedtasks + finished + + # perform file cleanup + if self.do_early_file_removal: + self.perform_early_file_removal(finished_from_started) + + if self.is_productionmode: + # we can do some generic cleanup of finished tasks in non-interactive/GRID mode + # TODO: this can run asynchronously + for _t in finished_from_started: + self.production_endoftask_hook(_t) + + # if a task was marked "failed" and we come here (because + # we use --keep-going) ... we need to take out the pid from finished + if len(failing) > 0: + # remove these from those marked finished in order + # not to continue with their children + errorencountered = True + for t in failing: + finished = [ x for x in finished if x != t ] + finishedtasks = [ x for x in finishedtasks if x != t ] + + # if a task was marked as "retry" we simply put it back into the candidate list + if len(self.tids_marked_toretry) > 0: + # we need to remove these first of all from those marked finished + for t in self.tids_marked_toretry: + finished = [ x for x in finished if x != t ] + finishedtasks = [ x for x in finishedtasks if x != t ] + + candidates = candidates + self.tids_marked_toretry + self.tids_marked_toretry = [] + + + # new candidates + for tid in finished: + if self.possiblenexttask.get(tid)!=None: + potential_candidates=list(self.possiblenexttask[tid]) + for candid in potential_candidates: + # try to see if this is really a candidate: + if self.is_good_candidate(candid, finishedtasks) and candidates.count(candid)==0: + candidates.append(candid) + + actionlogger.debug("New candidates " + str( candidates)) + send_webhook(self.args.webhook, "New candidates " + str(candidates)) + + if len(candidates)==0 and len(self.process_list)==0: + break + except Exception as e: + exc_type, exc_obj, exc_tb = sys.exc_info() + fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] + print(exc_type, fname, exc_tb.tb_lineno) + traceback.print_exc() + print ('Cleaning up ') + + self.SIGHandler(0,0) + + endtime = time.perf_counter() + statusmsg = "success" + if errorencountered: + statusmsg = "with failures" + + print ('\n**** Pipeline done ' + statusmsg + ' (global_runtime : {:.3f}s) *****\n'.format(endtime-self.start_time)) + actionlogger.debug("global_runtime : {:.3f}s".format(endtime-self.start_time)) + return errorencountered + + +if args.cgroup!=None: + myPID=os.getpid() + # cgroups such as /sys/fs/cgroup/cpuset//tasks + # or /sys/fs/cgroup/cpu//tasks + command="echo " + str(myPID) + f" > {args.cgroup}" + actionlogger.info(f"Try running in cgroup {args.cgroup}") + waitstatus = os.system(command) + if code := os.waitstatus_to_exitcode(waitstatus): + actionlogger.error(f"Could not apply cgroup") + exit(code) + actionlogger.info("Running in cgroup") + + +# This starts the fanotify fileaccess monitoring process +# if asked for +o2dpg_filegraph_exec = os.getenv("O2DPG_PRODUCE_FILEGRAPH") # switches filegraph monitoring on and contains the executable name +if o2dpg_filegraph_exec: + env = os.environ.copy() + env["FILEACCESS_MON_ROOTPATH"] = os.getcwd() + env["MAXMOTHERPID"] = f"{os.getpid()}" + + fileaccess_log_file_name = f"pipeline_fileaccess_{os.getpid()}.log" + fileaccess_log_file = open(fileaccess_log_file_name, "w") + fileaccess_monitor_proc = subprocess.Popen( + [o2dpg_filegraph_exec], + stdout=fileaccess_log_file, + stderr=subprocess.STDOUT, + env=env) +else: + fileaccess_monitor_proc = None + +try: + # This is core workflow runner invocation + executor=WorkflowExecutor(args.workflowfile,jmax=int(args.maxjobs),args=args) + rc = executor.execute() +finally: + if fileaccess_monitor_proc: + fileaccess_monitor_proc.terminate() # sends SIGTERM + try: + fileaccess_monitor_proc.wait(timeout=5) + except subprocess.TimeoutExpired: + fileaccess_monitor_proc.kill() # force kill if not stopping + # now produce the final filegraph output + o2dpg_root = os.getenv("O2DPG_ROOT") + analyse_cmd = [ + sys.executable, # runs with same Python interpreter + f"{o2dpg_root}/UTILS/FileIOGraph/analyse_FileIO.py", + "--actionFile", actionlogger_file, + "--monitorFile", fileaccess_log_file_name, + "-o", f"pipeline_fileaccess_report_{os.getpid()}.json", + "--basedir", os.getcwd() ] + print (f"Producing FileIOGraph with command {analyse_cmd}") + subprocess.run(analyse_cmd, check=True) + +sys.exit(rc) \ No newline at end of file diff --git a/MC/bin/o2dpg_workflow_utils.py b/MC/bin/o2dpg_workflow_utils.py index 304bb8234..763c472ab 100755 --- a/MC/bin/o2dpg_workflow_utils.py +++ b/MC/bin/o2dpg_workflow_utils.py @@ -1,11 +1,13 @@ #!/usr/bin/env python3 +from os import environ, getcwd from copy import deepcopy import json # List of active detectors ACTIVE_DETECTORS = ["all"] +INACTIVE_DETECTORS = [] def activate_detector(det): try: @@ -16,8 +18,39 @@ def activate_detector(det): pass ACTIVE_DETECTORS.append(det) +def deactivate_detector(det): + INACTIVE_DETECTORS.append(det) + def isActive(det): - return "all" in ACTIVE_DETECTORS or det in ACTIVE_DETECTORS + def check(detector): + return detector not in INACTIVE_DETECTORS and ("all" in ACTIVE_DETECTORS or detector in ACTIVE_DETECTORS) + + if det == "ITS": # special remapping for upgrade only needed in one direction since IT3 output pretends to be ITS + return check("ITS") or check("IT3") + else: + return check(det) + +def compute_n_workers(interaction_rate, collision_system, n_workers_user=8, n_workers_min=1, interaction_rate_linear_below=300000): + """ + Compute number of workers + + n_workers = m * IR + b + + based on + https://indico.cern.ch/event/1395900/contributions/5868567/attachments/2823967/4932440/20240320_slides_cpu_eff.pdf, slide 3 + + Assume n_workers_in=8 to be ideal for pp IR > interaction_rate_linear_below + + Start with 1 worker at IR=0 + Go linearly until interaction_rate_linear_below + """ + if collision_system == "PbPb" or interaction_rate >= interaction_rate_linear_below: + return n_workers_user + + n_workers_min = max(1, n_workers_min) + m = (n_workers_user - n_workers_min) / interaction_rate_linear_below + # at least 1 worker + return max(1, round(m * interaction_rate + n_workers_min)) def relativeCPU(n_rel, n_workers): # compute number of CPUs from a given number of workers @@ -80,18 +113,44 @@ def createTask(name='', needs=[], tf=-1, cwd='./', lab=[], cpu=1, relative_cpu=N 'cwd' : cwd } -def createGlobalInitTask(envdict): +def createGlobalInitTask(keys_values=None, set_defaults=True): """Returns a special task that is recognized by the executor as a task whose environment section is to be globally applied to all tasks of a workflow. - envdict: dictionary of environment variables and values to be globally applied to all tasks + Args: + keys_values: dict or None + dictionary of environment variables and values to be globally applied to all tasks + if sharing keys with defaults, keys_values takes precedence + set_defaults: bool + whether or not some default values will be added + + Returns: + dict: task dictionary """ + + # dictionary holding global environment to be passed to task + env_dict = {} + + if set_defaults: + if environ.get('ALICEO2_CCDB_LOCALCACHE') is None: + print ("ALICEO2_CCDB_LOCALCACHE not set; setting to default " + getcwd() + '/ccdb') + env_dict['ALICEO2_CCDB_LOCALCACHE'] = getcwd() + "/ccdb" + else: + # fixes the workflow to use and remember externally provided path + env_dict['ALICEO2_CCDB_LOCALCACHE'] = environ.get('ALICEO2_CCDB_LOCALCACHE') + env_dict['IGNORE_VALIDITYCHECK_OF_CCDB_LOCALCACHE'] = '${ALICEO2_CCDB_LOCALCACHE:+"ON"}' + + if keys_values: + # keys_values takes priority in case of same keys + env_dict |= keys_values + t = createTask(name = '__global_init_task__') t['cmd'] = 'NO-COMMAND' - t['env'] = envdict + t['env'] = env_dict return t + def summary_workflow(workflow): print("=== WORKFLOW SUMMARY ===\n") print(f"-> There are {len(workflow)} tasks") @@ -114,11 +173,14 @@ def dump_workflow(workflow, filename, meta=None): to_dump = deepcopy(workflow) for s in to_dump: - if s["cmd"] and taskwrapper_string not in s["cmd"]: + if s["cmd"] and s["name"] != '__global_init_task__' and taskwrapper_string not in s["cmd"]: # insert taskwrapper stuff if not there already, only do it if cmd string is not empty s['cmd'] = '. ' + taskwrapper_string + ' ' + s['name']+'.log \'' + s['cmd'] + '\'' # remove unnecessary whitespaces for better readibility s['cmd'] = trimString(s['cmd']) + # remove None entries from needs list + s['needs'] = [ n for n in s['needs'] if n != None ] + # make the final dict to be dumped to_dump = {"stages": to_dump} filename = make_workflow_filename(filename) @@ -221,6 +283,14 @@ def adjust_RECO_environment(workflowspec, package = ""): if len(package) == 0: return + # we try to extract the stage from the path (can be given via '@' separation) + # example O2sim::daily-xxx@DIGI ---> apply this environment from the DIGI phase + # example O2sim::daily-xxx@RECO ---> apply this environment from the RECO phase + # example O2sim::daily-xxx ---> apply this environment from the RECO phase == default case + from_stage = "RECO" + if package.count('@') == 1: + package, from_stage = package.split('@') + # We essentially need to go through the graph and apply the mapping # so take the workflow spec and see if the task itself or any child # is labeled RECO ---> typical graph traversal with caching @@ -252,6 +322,20 @@ def matches_or_inherits_label(taskid, label, cache): # fills the matches_label dictionary for taskid in range(len(workflowspec['stages'])): - if (matches_or_inherits_label(taskid, "RECO", matches_label)): + if (matches_or_inherits_label(taskid, from_stage, matches_label)): # now we do the final adjust (as annotation) in the workflow itself - workflowspec['stages'][taskid]["alternative_alienv_package"] = package + if workflowspec['stages'][taskid].get('disable_alternative_reco_software', False) != True: + if workflowspec['stages'][taskid].get("alternative_alienv_package") == None: + workflowspec['stages'][taskid]["alternative_alienv_package"] = package + +def merge_dicts(dict1, dict2): + """ + merges dict2 into dict1 (potentially overwriting values) + """ + for key, value in dict2.items(): + if key in dict1 and isinstance(dict1[key], dict) and isinstance(value, dict): + # If both are dictionaries, merge them recursively + merge_dicts(dict1[key], value) + else: + # Otherwise, overwrite dict1's value with dict2's value + dict1[key] = value diff --git a/MC/bin/tests/test_anchoring_rampup.py b/MC/bin/tests/test_anchoring_rampup.py new file mode 100644 index 000000000..4b3c1f20b --- /dev/null +++ b/MC/bin/tests/test_anchoring_rampup.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python3 +"""Offline test: the ITS ramp-up shift must move the first orbit, not only the timestamp.""" +import os +import sys +import unittest + +HERE = os.path.dirname(os.path.abspath(__file__)) +sys.path.insert(0, os.path.dirname(HERE)) + +os.environ.setdefault("O2DPG_ROOT", os.path.dirname(os.path.dirname(os.path.dirname(HERE)))) + +try: + import o2dpg_sim_workflow_anchored as anchored # needs ROOT +except ImportError as exc: # pragma: no cover + anchored = None + IMPORT_ERROR = exc + +# Run 571781 of LHC26e9, whose SPLITID=1 job reconstructed no collisions at all: +# the ITS time-dead map masks all 24120 chips up to orbit 20539968 and the job was +# placed at the run's first orbit. See https://its.cern.ch/jira/browse/O2-6894 +FIRST_ORBIT = 20505888 +SOR = 1778806732526 +ITS_RAMPUP_MS = 5000 +ORBITS_PER_TF = 128 +FIRST_ALIVE_ORBIT = 20539968 + + +@unittest.skipIf(anchored is None, "o2dpg_sim_workflow_anchored not importable (needs ROOT)") +class TestRampUpShift(unittest.TestCase): + + def test_orbits_from_milliseconds(self): + orbit_ms = anchored.LHCOrbitMUS / 1000. + self.assertEqual(anchored.milliseconds_to_orbits(0), 0) + # a span of one orbit minus an epsilon still has to cover a full orbit + self.assertEqual(anchored.milliseconds_to_orbits(0.5 * orbit_ms), 1) + self.assertEqual(anchored.milliseconds_to_orbits(orbit_ms), 1) + + def test_both_coordinates_move(self): + """A ramp-up of a few seconds must move the orbit as well as the timestamp.""" + start, orbit = anchored.shift_anchor_past_ITS_rampup(SOR, FIRST_ORBIT, ORBITS_PER_TF, ITS_RAMPUP_MS) + self.assertGreaterEqual(start, SOR + ITS_RAMPUP_MS) + self.assertGreater(orbit, FIRST_ORBIT) + self.assertEqual((orbit - FIRST_ORBIT) % ORBITS_PER_TF, 0) + + def test_nothing_moves_without_a_ramp(self): + self.assertEqual(anchored.shift_anchor_past_ITS_rampup(SOR, FIRST_ORBIT, ORBITS_PER_TF, 0), + (SOR, FIRST_ORBIT)) + + def test_shifted_orbit_is_never_inside_the_ramp(self): + """The shifted orbit must sit at or after the shifted timestamp, never before.""" + for ramp_ms in (0, 1, 500, ITS_RAMPUP_MS, 30000): + start, orbit = anchored.shift_anchor_past_ITS_rampup(SOR, FIRST_ORBIT, ORBITS_PER_TF, ramp_ms) + time_of_orbit = SOR + (orbit - FIRST_ORBIT) * anchored.LHCOrbitMUS / 1000. + self.assertGreaterEqual(time_of_orbit, start, + f"orbit shift falls short of the ramp for {ramp_ms} ms") + + def test_shift_agrees_with_the_timestamp_to_orbit_conversion(self): + """Closure: the shifted orbit is what main() derives from the shifted timestamp.""" + start, orbit = anchored.shift_anchor_past_ITS_rampup(SOR, FIRST_ORBIT, ORBITS_PER_TF, ITS_RAMPUP_MS) + # this is the conversion main() uses for the exclude_timestamp() check + derived = FIRST_ORBIT + int((start - SOR) / (anchored.LHCOrbitMUS / 1000.)) + # the timestamp has millisecond resolution, which is about 11 orbits + self.assertLessEqual(abs(orbit - derived), anchored.milliseconds_to_orbits(1)) + + def test_timestamp_is_not_inside_the_ramp_at_a_tf_boundary(self): + """Regression: a ramp ending just below a TF boundary must not be cut short by the ms truncation.""" + ramp_ms = 12928 * anchored.LHCOrbitMUS / 1000. - 0.001 + start, _ = anchored.shift_anchor_past_ITS_rampup(SOR, FIRST_ORBIT, ORBITS_PER_TF, ramp_ms) + self.assertGreaterEqual(start - SOR, ramp_ms) + + def test_split_id_one_clears_the_its_dead_window(self): + """Regression: the first job of a production must not sample the dead window.""" + _, orbit = anchored.shift_anchor_past_ITS_rampup(SOR, FIRST_ORBIT, ORBITS_PER_TF, ITS_RAMPUP_MS) + self.assertGreater(orbit, FIRST_ALIVE_ORBIT) + + +if __name__ == "__main__": + unittest.main() diff --git a/MC/bin/tests/wf_test_pp.sh b/MC/bin/tests/wf_test_pp.sh index c5213a20f..2d7d04a63 100644 --- a/MC/bin/tests/wf_test_pp.sh +++ b/MC/bin/tests/wf_test_pp.sh @@ -10,14 +10,12 @@ [ ! "${O2DPG_ROOT}" ] && echo "Error: This needs O2DPG loaded" && exit 1 [ ! "${O2_ROOT}" ] && echo "Error: This needs O2 loaded" && exit 1 -# ----------- LOAD UTILITY FUNCTIONS -------------------------- -. ${O2_ROOT}/share/scripts/jobutils.sh # ----------- START ACTUAL JOB ----------------------------- -NSIGEVENTS=${NSIGEVENTS:-1} +NSIGEVENTS=${NSIGEVENTS:-5} SIGPROC=${SIGPROC:-cdiff} -NTIMEFRAMES=${NTIMEFRAMES:-1} +NTIMEFRAMES=${NTIMEFRAMES:-2} SIMENGINE=${SIMENGINE:-TGeant3} NWORKERS=${NWORKERS:-1} SEED=${SEED:-624} @@ -26,4 +24,5 @@ INTERACTIONRATE=${INTERACTIONRATE:-50000} # create workflow ${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM 5020 -col pp -gen pythia8 -proc ${SIGPROC} -ns ${NSIGEVENTS} \ -tf ${NTIMEFRAMES} -e ${SIMENGINE} -j ${NWORKERS} -seed ${SEED} \ - --include-analysis -run 310000 -interactionRate ${INTERACTIONRATE} + --include-analysis -run 310000 -interactionRate ${INTERACTIONRATE} \ + --include-local-qc diff --git a/MC/bin/tests/wf_test_pp_staggered.sh b/MC/bin/tests/wf_test_pp_staggered.sh new file mode 100755 index 000000000..6d3b7a0a6 --- /dev/null +++ b/MC/bin/tests/wf_test_pp_staggered.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +# +# A pp workflow MC->RECO->AOD with ITS and MFT staggering enabled +# + +# make sure O2DPG + O2 is loaded +[ ! "${O2DPG_ROOT}" ] && echo "Error: This needs O2DPG loaded" && exit 1 +[ ! "${O2_ROOT}" ] && echo "Error: This needs O2 loaded" && exit 1 + + +# ----------- START ACTUAL JOB ----------------------------- + +NSIGEVENTS=${NSIGEVENTS:-5} +SIGPROC=${SIGPROC:-cdiff} +NTIMEFRAMES=${NTIMEFRAMES:-2} +SIMENGINE=${SIMENGINE:-TGeant3} +NWORKERS=${NWORKERS:-1} +SEED=${SEED:-624} +INTERACTIONRATE=${INTERACTIONRATE:-50000} + +# enable ITS and MFT staggering in all consumers +export ALIEN_JDL_ITS_STAGGERED=1 +export ALIEN_JDL_MFT_STAGGERED=1 + +# create workflow +${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM 5020 -col pp -gen pythia8 -proc ${SIGPROC} -ns ${NSIGEVENTS} \ + -tf ${NTIMEFRAMES} -e ${SIMENGINE} -j ${NWORKERS} -seed ${SEED} \ + --include-analysis -run 310000 -interactionRate ${INTERACTIONRATE} \ + --include-local-qc diff --git a/MC/config/ALICE3/README.md b/MC/config/ALICE3/README.md new file mode 100644 index 000000000..2bd77e940 --- /dev/null +++ b/MC/config/ALICE3/README.md @@ -0,0 +1,3 @@ +# ALICE3 configurations +This directory contains configurations used for ALICE3 on-the-fly studies on Hyperloop. + diff --git a/MC/config/ALICE3/ini/central_strangeness_PbPb_552tev.ini b/MC/config/ALICE3/ini/central_strangeness_PbPb_552tev.ini new file mode 100644 index 000000000..30c665813 --- /dev/null +++ b/MC/config/ALICE3/ini/central_strangeness_PbPb_552tev.ini @@ -0,0 +1,9 @@ +[Diamond] +width[2]=6.0 + +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator_pythia8_central_strangeness_gun_PbPb.C +funcName=generatePYTHIA() + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator/pythia8_central_hi.cfg diff --git a/MC/config/ALICE3/ini/ditto_oo_536tev.ini b/MC/config/ALICE3/ini/ditto_oo_536tev.ini new file mode 100644 index 000000000..3fe23499a --- /dev/null +++ b/MC/config/ALICE3/ini/ditto_oo_536tev.ini @@ -0,0 +1,4 @@ +#---> pythia8_pp_136tev +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator_DittoMC.C +funcName=generator_DittoMC("https://github.com/njacazio/DittoMC/releases/download/v1.0.1/Ditto_tune_pythia8_OO_536.root") diff --git a/MC/config/ALICE3/ini/ditto_pbpb_536tev.ini b/MC/config/ALICE3/ini/ditto_pbpb_536tev.ini new file mode 100644 index 000000000..70d1ddbab --- /dev/null +++ b/MC/config/ALICE3/ini/ditto_pbpb_536tev.ini @@ -0,0 +1,4 @@ +#---> pythia8_pp_136tev +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator_DittoMC.C +funcName=generator_DittoMC("https://github.com/njacazio/DittoMC/releases/download/v1.0.1/Ditto_tune_pythia8_PbPb_536tev.root") diff --git a/MC/config/ALICE3/ini/ditto_pbpb_536tev_inj.ini b/MC/config/ALICE3/ini/ditto_pbpb_536tev_inj.ini new file mode 100644 index 000000000..4ae5c8b87 --- /dev/null +++ b/MC/config/ALICE3/ini/ditto_pbpb_536tev_inj.ini @@ -0,0 +1,4 @@ +#---> pythia8_pp_136tev +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator_DittoMC.C +funcName=generator_DittoMC("https://github.com/njacazio/DittoMC/releases/download/v1.0.1/Ditto_tune_pythia8_PbPb_536tev.root", "https://github.com/njacazio/DittoMC/releases/download/v1.0.1/signal_pbpb.root") diff --git a/MC/config/ALICE3/ini/ditto_pp_136tev.ini b/MC/config/ALICE3/ini/ditto_pp_136tev.ini new file mode 100644 index 000000000..a6ac925af --- /dev/null +++ b/MC/config/ALICE3/ini/ditto_pp_136tev.ini @@ -0,0 +1,4 @@ +#---> pythia8_pp_136tev +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator_DittoMC.C +funcName=generator_DittoMC("https://github.com/njacazio/DittoMC/releases/download/v1.0.1/Ditto_tune_pythia8_inel_136tev.root") diff --git a/MC/config/ALICE3/ini/ditto_pp_136tev_inj.ini b/MC/config/ALICE3/ini/ditto_pp_136tev_inj.ini new file mode 100644 index 000000000..068718d16 --- /dev/null +++ b/MC/config/ALICE3/ini/ditto_pp_136tev_inj.ini @@ -0,0 +1,4 @@ +#---> pythia8_pp_136tev +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator_DittoMC.C +funcName=generator_DittoMC("https://github.com/njacazio/DittoMC/releases/download/v1.0.1/Ditto_tune_pythia8_inel_136tev.root", "https://github.com/njacazio/DittoMC/releases/download/v1.0.1/signal.root") diff --git a/MC/config/ALICE3/ini/pythia8_ArAr.ini b/MC/config/ALICE3/ini/pythia8_ArAr.ini new file mode 100644 index 000000000..e47a32d73 --- /dev/null +++ b/MC/config/ALICE3/ini/pythia8_ArAr.ini @@ -0,0 +1,9 @@ +[Diamond] +width[2]=6.0 + +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator_pythia8_ALICE3.C +funcName=generator_pythia8_ALICE3() + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator/pythia8_ArAr.cfg diff --git a/MC/config/ALICE3/ini/pythia8_KrKr.ini b/MC/config/ALICE3/ini/pythia8_KrKr.ini new file mode 100644 index 000000000..9812255cd --- /dev/null +++ b/MC/config/ALICE3/ini/pythia8_KrKr.ini @@ -0,0 +1,9 @@ +[Diamond] +width[2]=6.0 + +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator_pythia8_ALICE3.C +funcName=generator_pythia8_ALICE3() + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator/pythia8_KrKr.cfg \ No newline at end of file diff --git a/MC/config/ALICE3/ini/pythia8_OO.ini b/MC/config/ALICE3/ini/pythia8_OO.ini new file mode 100644 index 000000000..f9fd8c177 --- /dev/null +++ b/MC/config/ALICE3/ini/pythia8_OO.ini @@ -0,0 +1,9 @@ +[Diamond] +width[2]=6.0 + +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator_pythia8_ALICE3.C +funcName=generator_pythia8_ALICE3() + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator/pythia8_OO.cfg \ No newline at end of file diff --git a/MC/config/ALICE3/ini/pythia8_PbPb.ini b/MC/config/ALICE3/ini/pythia8_PbPb.ini new file mode 100644 index 000000000..53dbbeb89 --- /dev/null +++ b/MC/config/ALICE3/ini/pythia8_PbPb.ini @@ -0,0 +1,9 @@ +[Diamond] +width[2]=6.0 + +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator_pythia8_ALICE3.C +funcName=generator_pythia8_ALICE3() + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator/pythia8_PbPb.cfg diff --git a/MC/config/ALICE3/ini/pythia8_PbPb_502tev.ini b/MC/config/ALICE3/ini/pythia8_PbPb_502tev.ini new file mode 100644 index 000000000..8a8641bdf --- /dev/null +++ b/MC/config/ALICE3/ini/pythia8_PbPb_502tev.ini @@ -0,0 +1,10 @@ +#---> pythia8_PbPb_536tev +[Diamond] +width[2]=6.0 + +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator_pythia8_ALICE3.C +funcName=generator_pythia8_ALICE3() + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator/pythia8_PbPb_502tev.cfg diff --git a/MC/config/ALICE3/ini/pythia8_PbPb_536tev.ini b/MC/config/ALICE3/ini/pythia8_PbPb_536tev.ini new file mode 100644 index 000000000..379ba3e2f --- /dev/null +++ b/MC/config/ALICE3/ini/pythia8_PbPb_536tev.ini @@ -0,0 +1,9 @@ +[Diamond] +width[2]=6.0 + +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator_pythia8_ALICE3.C +funcName=generator_pythia8_ALICE3() + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator/pythia8_PbPb_536tev.cfg diff --git a/MC/config/ALICE3/ini/pythia8_PbPb_D0toKpi.ini b/MC/config/ALICE3/ini/pythia8_PbPb_D0toKpi.ini new file mode 100644 index 000000000..04f617fa0 --- /dev/null +++ b/MC/config/ALICE3/ini/pythia8_PbPb_D0toKpi.ini @@ -0,0 +1,9 @@ +[Diamond] +width[2]=6.0 + +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator_pythia8_ALICE3.C +funcName=generator_pythia8_ALICE3() + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator/pythia8_PbPb_D0toKpi.cfg diff --git a/MC/config/ALICE3/ini/pythia8_XeXe.ini b/MC/config/ALICE3/ini/pythia8_XeXe.ini new file mode 100644 index 000000000..df7691404 --- /dev/null +++ b/MC/config/ALICE3/ini/pythia8_XeXe.ini @@ -0,0 +1,9 @@ +[Diamond] +width[2]=6.0 + +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator_pythia8_ALICE3.C +funcName=generator_pythia8_ALICE3() + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator/pythia8_XeXe.cfg diff --git a/MC/config/ALICE3/ini/pythia8_pp.ini b/MC/config/ALICE3/ini/pythia8_pp.ini new file mode 100644 index 000000000..aba3c7605 --- /dev/null +++ b/MC/config/ALICE3/ini/pythia8_pp.ini @@ -0,0 +1,9 @@ +[Diamond] +width[2]=6.0 + +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator_pythia8_ALICE3.C +funcName=generator_pythia8_ALICE3() + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator/pythia8_pp.cfg diff --git a/MC/config/ALICE3/ini/pythia8_pp_136tev.ini b/MC/config/ALICE3/ini/pythia8_pp_136tev.ini new file mode 100644 index 000000000..0abdd4c40 --- /dev/null +++ b/MC/config/ALICE3/ini/pythia8_pp_136tev.ini @@ -0,0 +1,9 @@ +[Diamond] +width[2]=6.0 + +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator_pythia8_ALICE3.C +funcName=generator_pythia8_ALICE3() + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator/pythia8_pp_136tev.cfg diff --git a/MC/config/ALICE3/ini/pythia8_pp_136tev_inel_difftuned.ini b/MC/config/ALICE3/ini/pythia8_pp_136tev_inel_difftuned.ini new file mode 100644 index 000000000..564329d38 --- /dev/null +++ b/MC/config/ALICE3/ini/pythia8_pp_136tev_inel_difftuned.ini @@ -0,0 +1,10 @@ +#---> pythia8_pp_136tev +[Diamond] +width[2]=6.0 + +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator_pythia8_ALICE3.C +funcName=generator_pythia8_ALICE3() + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/generator/pythia8_inel_difftuned.cfg diff --git a/MC/config/ALICE3/ini/pythia8_pp_13tev.ini b/MC/config/ALICE3/ini/pythia8_pp_13tev.ini new file mode 100644 index 000000000..df119b101 --- /dev/null +++ b/MC/config/ALICE3/ini/pythia8_pp_13tev.ini @@ -0,0 +1,9 @@ +[Diamond] +width[2]=6.0 + +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator_pythia8_ALICE3.C +funcName=generator_pythia8_ALICE3() + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator/pythia8_pp_13tev.cfg diff --git a/MC/config/ALICE3/ini/pythia8_pp_502tev.ini b/MC/config/ALICE3/ini/pythia8_pp_502tev.ini new file mode 100644 index 000000000..3c73b4e1a --- /dev/null +++ b/MC/config/ALICE3/ini/pythia8_pp_502tev.ini @@ -0,0 +1,9 @@ +[Diamond] +width[2]=6.0 + +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator_pythia8_ALICE3.C +funcName=generator_pythia8_ALICE3() + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator/pythia8_pp_502tev.cfg diff --git a/MC/config/ALICE3/ini/pythia8_pp_536tev.ini b/MC/config/ALICE3/ini/pythia8_pp_536tev.ini new file mode 100644 index 000000000..528de37c2 --- /dev/null +++ b/MC/config/ALICE3/ini/pythia8_pp_536tev.ini @@ -0,0 +1,9 @@ +[Diamond] +width[2]=6.0 + +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator_pythia8_ALICE3.C +funcName=generator_pythia8_ALICE3() + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator/pythia8_pp_536tev.cfg diff --git a/MC/config/ALICE3/ini/pythia8_pp_7tev.ini b/MC/config/ALICE3/ini/pythia8_pp_7tev.ini new file mode 100644 index 000000000..5c4b1336e --- /dev/null +++ b/MC/config/ALICE3/ini/pythia8_pp_7tev.ini @@ -0,0 +1,9 @@ +[Diamond] +width[2]=6.0 + +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator_pythia8_ALICE3.C +funcName=generator_pythia8_ALICE3() + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator/pythia8_pp_7tev.cfg diff --git a/MC/config/ALICE3/ini/pythia8_pp_clr-blc0_13tev.ini b/MC/config/ALICE3/ini/pythia8_pp_clr-blc0_13tev.ini new file mode 100644 index 000000000..a08d45738 --- /dev/null +++ b/MC/config/ALICE3/ini/pythia8_pp_clr-blc0_13tev.ini @@ -0,0 +1,9 @@ +[Diamond] +width[2]=6.0 + +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator_pythia8_ALICE3.C +funcName=generator_pythia8_ALICE3() + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator/pythia8_pp_clr-blc0_13tev.cfg diff --git a/MC/config/ALICE3/ini/pythia8_pp_clr-blc2_13tev.ini b/MC/config/ALICE3/ini/pythia8_pp_clr-blc2_13tev.ini new file mode 100644 index 000000000..6572e3d11 --- /dev/null +++ b/MC/config/ALICE3/ini/pythia8_pp_clr-blc2_13tev.ini @@ -0,0 +1,10 @@ +#---> pythia8_pp_clr-blc0_13tev +[Diamond] +width[2]=6.0 + +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator_pythia8_ALICE3.C +funcName=generator_pythia8_ALICE3() + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator/pythia8_pp_clr-blc2_13tev.cfg diff --git a/MC/config/ALICE3/ini/pythia8_pp_clr-blc3_13tev.ini b/MC/config/ALICE3/ini/pythia8_pp_clr-blc3_13tev.ini new file mode 100644 index 000000000..6ebad106e --- /dev/null +++ b/MC/config/ALICE3/ini/pythia8_pp_clr-blc3_13tev.ini @@ -0,0 +1,10 @@ +#---> pythia8_pp_clr-blc0_13tev +[Diamond] +width[2]=6.0 + +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator_pythia8_ALICE3.C +funcName=generator_pythia8_ALICE3() + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator/pythia8_pp_clr-blc3_13tev.cfg diff --git a/MC/config/ALICE3/ini/pythia8_pp_cr_136tev.ini b/MC/config/ALICE3/ini/pythia8_pp_cr_136tev.ini new file mode 100644 index 000000000..420f99495 --- /dev/null +++ b/MC/config/ALICE3/ini/pythia8_pp_cr_136tev.ini @@ -0,0 +1,9 @@ +[Diamond] +width[2]=6.0 + +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator_pythia8_ALICE3.C +funcName=generator_pythia8_ALICE3() + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator/pythia8_pp_cr_136tev.cfg diff --git a/MC/config/ALICE3/ini/pythia8_pp_hardQCD_136tev.ini b/MC/config/ALICE3/ini/pythia8_pp_hardQCD_136tev.ini new file mode 100644 index 000000000..57419d9ee --- /dev/null +++ b/MC/config/ALICE3/ini/pythia8_pp_hardQCD_136tev.ini @@ -0,0 +1,10 @@ +[Diamond] +width[2]=6.0 + +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator_pythia8_ALICE3.C +funcName=generator_pythia8_ALICE3() + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator/pythia8_pp_hardQCD_136tev.cfg +includePartonEvent=true diff --git a/MC/config/ALICE3/ini/pythia8_pp_hf_hardQCD_136tev.ini b/MC/config/ALICE3/ini/pythia8_pp_hf_hardQCD_136tev.ini new file mode 100644 index 000000000..c30de94f3 --- /dev/null +++ b/MC/config/ALICE3/ini/pythia8_pp_hf_hardQCD_136tev.ini @@ -0,0 +1,10 @@ +[Diamond] +width[2]=6.0 + +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator_pythia8_ALICE3.C +funcName=generator_pythia8_ALICE3() + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator/pythia8_pp_hf_hardQCD_136tev.cfg +includePartonEvent=true \ No newline at end of file diff --git a/MC/config/ALICE3/ini/pythia8_pp_rescattering_136tev.ini b/MC/config/ALICE3/ini/pythia8_pp_rescattering_136tev.ini new file mode 100644 index 000000000..4189c775a --- /dev/null +++ b/MC/config/ALICE3/ini/pythia8_pp_rescattering_136tev.ini @@ -0,0 +1,9 @@ +[Diamond] +width[2]=6.0 + +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator_pythia8_ALICE3.C +funcName=generator_pythia8_ALICE3() + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator/pythia8_pp_rescattering_136tev.cfg diff --git a/MC/config/ALICE3/ini/pythia8_pp_ropes.ini b/MC/config/ALICE3/ini/pythia8_pp_ropes.ini new file mode 100644 index 000000000..fed05526e --- /dev/null +++ b/MC/config/ALICE3/ini/pythia8_pp_ropes.ini @@ -0,0 +1,9 @@ +[Diamond] +width[2]=6.0 + +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator_pythia8_ALICE3.C +funcName=generator_pythia8_ALICE3() + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator/pythia8_pp_ropes.cfg diff --git a/MC/config/ALICE3/ini/pythia8_pp_ropes_136tev.ini b/MC/config/ALICE3/ini/pythia8_pp_ropes_136tev.ini new file mode 100644 index 000000000..b979fae04 --- /dev/null +++ b/MC/config/ALICE3/ini/pythia8_pp_ropes_136tev.ini @@ -0,0 +1,9 @@ +[Diamond] +width[2]=6.0 + +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator_pythia8_ALICE3.C +funcName=generator_pythia8_ALICE3() + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator/pythia8_pp_ropes_136tev.cfg diff --git a/MC/config/ALICE3/ini/pythia8_pp_ropes_13tev.ini b/MC/config/ALICE3/ini/pythia8_pp_ropes_13tev.ini new file mode 100644 index 000000000..f3622c4de --- /dev/null +++ b/MC/config/ALICE3/ini/pythia8_pp_ropes_13tev.ini @@ -0,0 +1,9 @@ +[Diamond] +width[2]=6.0 + +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator_pythia8_ALICE3.C +funcName=generator_pythia8_ALICE3() + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator/pythia8_pp_ropes_13tev.cfg diff --git a/MC/config/ALICE3/ini/pythia8_pp_shoving.ini b/MC/config/ALICE3/ini/pythia8_pp_shoving.ini new file mode 100644 index 000000000..21f30d6b7 --- /dev/null +++ b/MC/config/ALICE3/ini/pythia8_pp_shoving.ini @@ -0,0 +1,9 @@ +[Diamond] +width[2]=6.0 + +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator_pythia8_ALICE3.C +funcName=generator_pythia8_ALICE3() + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator/pythia8_pp_shoving.cfg diff --git a/MC/config/ALICE3/ini/pythia8_pp_shoving_136tev.ini b/MC/config/ALICE3/ini/pythia8_pp_shoving_136tev.ini new file mode 100644 index 000000000..8a10618fb --- /dev/null +++ b/MC/config/ALICE3/ini/pythia8_pp_shoving_136tev.ini @@ -0,0 +1,9 @@ +[Diamond] +width[2]=6.0 + +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator_pythia8_ALICE3.C +funcName=generator_pythia8_ALICE3() + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator/pythia8_pp_shoving_136tev.cfg diff --git a/MC/config/ALICE3/ini/pythia8_pp_shoving_13tev.ini b/MC/config/ALICE3/ini/pythia8_pp_shoving_13tev.ini new file mode 100644 index 000000000..1704e944c --- /dev/null +++ b/MC/config/ALICE3/ini/pythia8_pp_shoving_13tev.ini @@ -0,0 +1,9 @@ +[Diamond] +width[2]=6.0 + +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator_pythia8_ALICE3.C +funcName=generator_pythia8_ALICE3() + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator/pythia8_pp_shoving_13tev.cfg diff --git a/MC/config/ALICE3/ini/tests/central_strangeness_PbPb_552tev.C b/MC/config/ALICE3/ini/tests/central_strangeness_PbPb_552tev.C new file mode 100644 index 000000000..bdb092295 --- /dev/null +++ b/MC/config/ALICE3/ini/tests/central_strangeness_PbPb_552tev.C @@ -0,0 +1,36 @@ +int External() +{ + std::string path{"o2sim_Kine.root"}; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + auto nEvents = tree->GetEntries(); + int nTracks = 0; + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + nTracks += tracks->size(); + } + + const int meanNTracksPerEvent = nTracks / nEvents; + + // Expecting only events with a 0-10% centrality + // 0-100% gives a mean of ~1350 + // 2300 should be enough + if (meanNTracksPerEvent < 2300) { + return 1; + } + + return 0; +} \ No newline at end of file diff --git a/MC/config/ALICE3/ini/tests/pythia8_ArAr.C b/MC/config/ALICE3/ini/tests/pythia8_ArAr.C new file mode 100644 index 000000000..0676e4c22 --- /dev/null +++ b/MC/config/ALICE3/ini/tests/pythia8_ArAr.C @@ -0,0 +1,8 @@ +int External() { + return 0; +} + +int pythia8() +{ + return External(); +} \ No newline at end of file diff --git a/MC/config/ALICE3/ini/tests/pythia8_KrKr.C b/MC/config/ALICE3/ini/tests/pythia8_KrKr.C new file mode 100644 index 000000000..0676e4c22 --- /dev/null +++ b/MC/config/ALICE3/ini/tests/pythia8_KrKr.C @@ -0,0 +1,8 @@ +int External() { + return 0; +} + +int pythia8() +{ + return External(); +} \ No newline at end of file diff --git a/MC/config/ALICE3/ini/tests/pythia8_OO.C b/MC/config/ALICE3/ini/tests/pythia8_OO.C new file mode 100644 index 000000000..0676e4c22 --- /dev/null +++ b/MC/config/ALICE3/ini/tests/pythia8_OO.C @@ -0,0 +1,8 @@ +int External() { + return 0; +} + +int pythia8() +{ + return External(); +} \ No newline at end of file diff --git a/MC/config/ALICE3/ini/tests/pythia8_PbPb.C b/MC/config/ALICE3/ini/tests/pythia8_PbPb.C new file mode 100644 index 000000000..0676e4c22 --- /dev/null +++ b/MC/config/ALICE3/ini/tests/pythia8_PbPb.C @@ -0,0 +1,8 @@ +int External() { + return 0; +} + +int pythia8() +{ + return External(); +} \ No newline at end of file diff --git a/MC/config/ALICE3/ini/tests/pythia8_PbPb_536tev.C b/MC/config/ALICE3/ini/tests/pythia8_PbPb_536tev.C new file mode 100644 index 000000000..0676e4c22 --- /dev/null +++ b/MC/config/ALICE3/ini/tests/pythia8_PbPb_536tev.C @@ -0,0 +1,8 @@ +int External() { + return 0; +} + +int pythia8() +{ + return External(); +} \ No newline at end of file diff --git a/MC/config/ALICE3/ini/tests/pythia8_XeXe.C b/MC/config/ALICE3/ini/tests/pythia8_XeXe.C new file mode 100644 index 000000000..0676e4c22 --- /dev/null +++ b/MC/config/ALICE3/ini/tests/pythia8_XeXe.C @@ -0,0 +1,8 @@ +int External() { + return 0; +} + +int pythia8() +{ + return External(); +} \ No newline at end of file diff --git a/MC/config/ALICE3/ini/tests/pythia8_pp.C b/MC/config/ALICE3/ini/tests/pythia8_pp.C new file mode 100644 index 000000000..0676e4c22 --- /dev/null +++ b/MC/config/ALICE3/ini/tests/pythia8_pp.C @@ -0,0 +1,8 @@ +int External() { + return 0; +} + +int pythia8() +{ + return External(); +} \ No newline at end of file diff --git a/MC/config/ALICE3/ini/tests/pythia8_pp_136tev.C b/MC/config/ALICE3/ini/tests/pythia8_pp_136tev.C new file mode 100644 index 000000000..0676e4c22 --- /dev/null +++ b/MC/config/ALICE3/ini/tests/pythia8_pp_136tev.C @@ -0,0 +1,8 @@ +int External() { + return 0; +} + +int pythia8() +{ + return External(); +} \ No newline at end of file diff --git a/MC/config/ALICE3/ini/tests/pythia8_pp_13tev.C b/MC/config/ALICE3/ini/tests/pythia8_pp_13tev.C new file mode 100644 index 000000000..8fb911b19 --- /dev/null +++ b/MC/config/ALICE3/ini/tests/pythia8_pp_13tev.C @@ -0,0 +1,3 @@ +int External() { + return 0; +} diff --git a/MC/config/ALICE3/ini/tests/pythia8_pp_clr-blc0_13tev.C b/MC/config/ALICE3/ini/tests/pythia8_pp_clr-blc0_13tev.C new file mode 100644 index 000000000..d258500c3 --- /dev/null +++ b/MC/config/ALICE3/ini/tests/pythia8_pp_clr-blc0_13tev.C @@ -0,0 +1,19 @@ +int External() +{ + std::string path{"o2sim_Kine.root"}; + + TFile file(path.c_str(), "read"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << std::endl; + return 1; + } + + TTree* tree = (TTree*)file.Get("o2sim"); + + if (!tree) { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/ALICE3/ini/tests/pythia8_pp_hardQCD_136tev.C b/MC/config/ALICE3/ini/tests/pythia8_pp_hardQCD_136tev.C new file mode 100644 index 000000000..0676e4c22 --- /dev/null +++ b/MC/config/ALICE3/ini/tests/pythia8_pp_hardQCD_136tev.C @@ -0,0 +1,8 @@ +int External() { + return 0; +} + +int pythia8() +{ + return External(); +} \ No newline at end of file diff --git a/MC/config/ALICE3/ini/tests/pythia8_pp_hf_hardQCD_136tev.C b/MC/config/ALICE3/ini/tests/pythia8_pp_hf_hardQCD_136tev.C new file mode 100644 index 000000000..0676e4c22 --- /dev/null +++ b/MC/config/ALICE3/ini/tests/pythia8_pp_hf_hardQCD_136tev.C @@ -0,0 +1,8 @@ +int External() { + return 0; +} + +int pythia8() +{ + return External(); +} \ No newline at end of file diff --git a/MC/config/ALICE3/ini/tests/pythia8_pp_rescattering_136tev.C b/MC/config/ALICE3/ini/tests/pythia8_pp_rescattering_136tev.C new file mode 100644 index 000000000..9b21ec151 --- /dev/null +++ b/MC/config/ALICE3/ini/tests/pythia8_pp_rescattering_136tev.C @@ -0,0 +1,24 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + auto nEvents = tree->GetEntries(); + if (nEvents == 0) { + std::cerr << "No event of interest\n"; + return 1; + } + return 0; +} \ No newline at end of file diff --git a/MC/config/ALICE3/ini/tests/pythia8_pp_ropes.C b/MC/config/ALICE3/ini/tests/pythia8_pp_ropes.C new file mode 100644 index 000000000..0676e4c22 --- /dev/null +++ b/MC/config/ALICE3/ini/tests/pythia8_pp_ropes.C @@ -0,0 +1,8 @@ +int External() { + return 0; +} + +int pythia8() +{ + return External(); +} \ No newline at end of file diff --git a/MC/config/ALICE3/ini/tests/pythia8_pp_ropes_136tev.C b/MC/config/ALICE3/ini/tests/pythia8_pp_ropes_136tev.C new file mode 100644 index 000000000..0676e4c22 --- /dev/null +++ b/MC/config/ALICE3/ini/tests/pythia8_pp_ropes_136tev.C @@ -0,0 +1,8 @@ +int External() { + return 0; +} + +int pythia8() +{ + return External(); +} \ No newline at end of file diff --git a/MC/config/ALICE3/ini/tests/pythia8_pp_ropes_13tev.C b/MC/config/ALICE3/ini/tests/pythia8_pp_ropes_13tev.C new file mode 100644 index 000000000..8fb911b19 --- /dev/null +++ b/MC/config/ALICE3/ini/tests/pythia8_pp_ropes_13tev.C @@ -0,0 +1,3 @@ +int External() { + return 0; +} diff --git a/MC/config/ALICE3/ini/tests/pythia8_pp_shoving.C b/MC/config/ALICE3/ini/tests/pythia8_pp_shoving.C new file mode 100644 index 000000000..0676e4c22 --- /dev/null +++ b/MC/config/ALICE3/ini/tests/pythia8_pp_shoving.C @@ -0,0 +1,8 @@ +int External() { + return 0; +} + +int pythia8() +{ + return External(); +} \ No newline at end of file diff --git a/MC/config/ALICE3/ini/tests/pythia8_pp_shoving_136tev.C b/MC/config/ALICE3/ini/tests/pythia8_pp_shoving_136tev.C new file mode 100644 index 000000000..0676e4c22 --- /dev/null +++ b/MC/config/ALICE3/ini/tests/pythia8_pp_shoving_136tev.C @@ -0,0 +1,8 @@ +int External() { + return 0; +} + +int pythia8() +{ + return External(); +} \ No newline at end of file diff --git a/MC/config/ALICE3/ini/tests/pythia8_pp_shoving_13tev.C b/MC/config/ALICE3/ini/tests/pythia8_pp_shoving_13tev.C new file mode 100644 index 000000000..8fb911b19 --- /dev/null +++ b/MC/config/ALICE3/ini/tests/pythia8_pp_shoving_13tev.C @@ -0,0 +1,3 @@ +int External() { + return 0; +} diff --git a/MC/config/ALICE3/ini/tests/xi_PbPb.C b/MC/config/ALICE3/ini/tests/xi_PbPb.C new file mode 100644 index 000000000..5d55db370 --- /dev/null +++ b/MC/config/ALICE3/ini/tests/xi_PbPb.C @@ -0,0 +1,46 @@ +int External() +{ + std::string path{"o2sim_Kine.root"}; + int pdgToCheck = 3312; + + TFile file(path.c_str(), "read"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << std::endl; + return 1; + } + + int nInjectedParticles = 0; + TTree* tree = (TTree*)file.Get("o2sim"); + + if (!tree) { + std::cerr << "Cannot find tree o2sim in file " << path << std::endl; + return 1; + } + + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int nEvents = tree->GetEntries(); + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + for (auto& track : *tracks) { + auto pdgCode = track.GetPdgCode(); + if (pdgCode == pdgToCheck) { + // not injecting anti-particle + nInjectedParticles++; + } + } + } + + if (nInjectedParticles < nEvents) { + // Check that we are correctly injecting 15 + // particle per event + return 1; + } + + return 0; +} + + + + diff --git a/MC/config/ALICE3/ini/tests/xi_pp.C b/MC/config/ALICE3/ini/tests/xi_pp.C new file mode 100644 index 000000000..9a09b788e --- /dev/null +++ b/MC/config/ALICE3/ini/tests/xi_pp.C @@ -0,0 +1,46 @@ +int External() +{ + std::string path{"o2sim_Kine.root"}; + int pdgToCheck = 3312; + + TFile file(path.c_str(), "read"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << std::endl; + return 1; + } + + int nInjectedParticles = 0; + TTree* tree = (TTree*)file.Get("o2sim"); + + if (!tree) { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int nEvents = tree->GetEntries(); + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + for (auto& track : *tracks) { + auto pdgCode = track.GetPdgCode(); + if (pdgCode == pdgToCheck) { + // not injecting anti-particle + nInjectedParticles++; + } + } + } + + if (nInjectedParticles < nEvents) { + // Check that we are correctly injecting one + // particle per event + return 1; + } + + return 0; +} + + + + diff --git a/MC/config/ALICE3/ini/tests/xic_PbPb.C b/MC/config/ALICE3/ini/tests/xic_PbPb.C new file mode 100644 index 000000000..8286b733b --- /dev/null +++ b/MC/config/ALICE3/ini/tests/xic_PbPb.C @@ -0,0 +1,46 @@ +int External() +{ + std::string path{"o2sim_Kine.root"}; + int pdgToCheck = 4232; + + TFile file(path.c_str(), "read"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << std::endl; + return 1; + } + + int nInjectedParticles = 0; + TTree* tree = (TTree*)file.Get("o2sim"); + + if (!tree) { + std::cerr << "Cannot find tree o2sim in file " << path << std::endl; + return 1; + } + + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int nEvents = tree->GetEntries(); + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + for (auto& track : *tracks) { + auto pdgCode = track.GetPdgCode(); + if (pdgCode == pdgToCheck) { + // not injecting anti-particle + nInjectedParticles++; + } + } + } + + if (nInjectedParticles < nEvents) { + // Check that we are correctly injecting 15 + // particle per event + return 1; + } + + return 0; +} + + + + diff --git a/MC/config/ALICE3/ini/tests/xic_pp.C b/MC/config/ALICE3/ini/tests/xic_pp.C new file mode 100644 index 000000000..5b531f7b8 --- /dev/null +++ b/MC/config/ALICE3/ini/tests/xic_pp.C @@ -0,0 +1,46 @@ +int External() +{ + std::string path{"o2sim_Kine.root"}; + int pdgToCheck = 4232; + + TFile file(path.c_str(), "read"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << std::endl; + return 1; + } + + int nInjectedParticles = 0; + TTree* tree = (TTree*)file.Get("o2sim"); + + if (!tree) { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int nEvents = tree->GetEntries(); + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + for (auto& track : *tracks) { + auto pdgCode = track.GetPdgCode(); + if (pdgCode == pdgToCheck) { + // not injecting anti-particle + nInjectedParticles++; + } + } + } + + if (nInjectedParticles < nEvents) { + // Check that we are correctly injecting one + // particle per event + return 1; + } + + return 0; +} + + + + diff --git a/MC/config/ALICE3/ini/tests/xicc_PbPb.C b/MC/config/ALICE3/ini/tests/xicc_PbPb.C new file mode 100644 index 000000000..7e846624c --- /dev/null +++ b/MC/config/ALICE3/ini/tests/xicc_PbPb.C @@ -0,0 +1,26 @@ +int External() +{ + std::string path{"o2sim_Kine.root"}; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + auto nEvents = tree->GetEntries(); + auto nSelected = tree->Scan("MCTrack.GetPdgCode()", "MCTrack.GetPdgCode() == 4422"); + if (nSelected == 0) { + std::cerr << "No event of interest\n"; + return 1; + } + return 0; +} \ No newline at end of file diff --git a/MC/config/ALICE3/ini/tests/xicc_background_PbPb.C b/MC/config/ALICE3/ini/tests/xicc_background_PbPb.C new file mode 100644 index 000000000..63ce4a977 --- /dev/null +++ b/MC/config/ALICE3/ini/tests/xicc_background_PbPb.C @@ -0,0 +1,38 @@ +int External() +{ + std::string path{"o2sim_Kine.root"}; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + auto nEvents = tree->GetEntries(); + int nInjected = 0; + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + for (auto& track : *tracks) { + auto pdgCode = std::fabs(track.GetPdgCode()); + if (pdgCode == 3312) { + nInjected++; + } + } + } + + // Check if we are above typical Angantyr numbers + if (nInjected < 5 * nEvents) { + std::cerr << "Too few particles injected\n"; + return 1; + } + + return 0; +} \ No newline at end of file diff --git a/MC/config/ALICE3/ini/tests/xicc_pp.C b/MC/config/ALICE3/ini/tests/xicc_pp.C new file mode 100644 index 000000000..7e846624c --- /dev/null +++ b/MC/config/ALICE3/ini/tests/xicc_pp.C @@ -0,0 +1,26 @@ +int External() +{ + std::string path{"o2sim_Kine.root"}; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + auto nEvents = tree->GetEntries(); + auto nSelected = tree->Scan("MCTrack.GetPdgCode()", "MCTrack.GetPdgCode() == 4422"); + if (nSelected == 0) { + std::cerr << "No event of interest\n"; + return 1; + } + return 0; +} \ No newline at end of file diff --git a/MC/config/ALICE3/ini/xi_PbPb.ini b/MC/config/ALICE3/ini/xi_PbPb.ini new file mode 100755 index 000000000..efa324ead --- /dev/null +++ b/MC/config/ALICE3/ini/xi_PbPb.ini @@ -0,0 +1,6 @@ +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator_pythia8_gun_PbPb.C +funcName=generateNativeXi() + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator/config_custom_xicc.cfg \ No newline at end of file diff --git a/MC/config/ALICE3/ini/xi_pp.ini b/MC/config/ALICE3/ini/xi_pp.ini new file mode 100755 index 000000000..90039ae12 --- /dev/null +++ b/MC/config/ALICE3/ini/xi_pp.ini @@ -0,0 +1,6 @@ +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator_pythia8_gun_pp.C +funcName=generateNativeXi() + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator/config_custom_xicc.cfg \ No newline at end of file diff --git a/MC/config/ALICE3/ini/xic_PbPb.ini b/MC/config/ALICE3/ini/xic_PbPb.ini new file mode 100755 index 000000000..4e8c02141 --- /dev/null +++ b/MC/config/ALICE3/ini/xic_PbPb.ini @@ -0,0 +1,6 @@ +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator_pythia8_gun_PbPb.C +funcName=generateNativeXiC() + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator/config_custom_xicc.cfg \ No newline at end of file diff --git a/MC/config/ALICE3/ini/xic_pp.ini b/MC/config/ALICE3/ini/xic_pp.ini new file mode 100755 index 000000000..df8a3d220 --- /dev/null +++ b/MC/config/ALICE3/ini/xic_pp.ini @@ -0,0 +1,6 @@ +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator_pythia8_gun_pp.C +funcName=generateNativeXiC() + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator/config_custom_xicc.cfg \ No newline at end of file diff --git a/MC/config/ALICE3/ini/xicc_PbPb.ini b/MC/config/ALICE3/ini/xicc_PbPb.ini new file mode 100755 index 000000000..2802dc52b --- /dev/null +++ b/MC/config/ALICE3/ini/xicc_PbPb.ini @@ -0,0 +1,6 @@ +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator_pythia8_gun_PbPb.C +funcName=generateNativeXiCC() + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator/config_custom_xicc.cfg \ No newline at end of file diff --git a/MC/config/ALICE3/ini/xicc_background_PbPb.ini b/MC/config/ALICE3/ini/xicc_background_PbPb.ini new file mode 100644 index 000000000..cc6c54be5 --- /dev/null +++ b/MC/config/ALICE3/ini/xicc_background_PbPb.ini @@ -0,0 +1,9 @@ +[Diamond] +width[2]=6.0 + +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator_pythia8_strangeness_gun_PbPb.C +funcName=generatePYTHIA() + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator/pythia8_PbPb.cfg diff --git a/MC/config/ALICE3/ini/xicc_pp.ini b/MC/config/ALICE3/ini/xicc_pp.ini new file mode 100755 index 000000000..69ac65c84 --- /dev/null +++ b/MC/config/ALICE3/ini/xicc_pp.ini @@ -0,0 +1,6 @@ +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator_pythia8_gun_pp.C +funcName=generateNativeXiCC() + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator/config_custom_xicc.cfg diff --git a/MC/config/ALICE3/pythia8/generator/config_custom_xicc.cfg b/MC/config/ALICE3/pythia8/generator/config_custom_xicc.cfg new file mode 100755 index 000000000..9b937de6e --- /dev/null +++ b/MC/config/ALICE3/pythia8/generator/config_custom_xicc.cfg @@ -0,0 +1,51 @@ +### Xi_c +#4232:all = Xic+ Xic- 2 3 0 2.46793 0. 0. 0. 10 +4422:all = XiCC AntiXiCC 2 6 0 3.6212 0. 0. 0. 10 + +### Omega_c +#4332:all = Omega_c0 Omega_cbar0 2 0 0 2.69520 0. 0. 0. 10 +4432:all = Omega_cc Omega_ccbar 2 3 0 3.746 0. 0. 0. 10 +4444:all = Omega_ccc Omega_cccbar 4 6 0 4.797 0. 0. 0. 10 + +ProcessLevel:all = off + +### changing the ctau value in mm/c +### use scaling based on Lambda, Xi, Omega progression +4332:tau0=0.08000000000 +4432:tau0=0.04980000000 +4444:tau0=0.02496000000 +4422:tau0=0.07675 +4232:tau0=0.132 + +### add OmegaC decay absent in PYTHIA8 decay table +4332:addChannel = 1 1 0 3334 211 +4432:addChannel = 1 1 0 4332 211 +4444:oneChannel = 1 1 0 4432 211 + +4422:addChannel = 1 1 0 4232 211 +4422:onMode = off +4422:onIfMatch = 4232 211 + +### add OmegaC decay absent in PYTHIA8 decay table +4232:addChannel = 1 0.0001 0 3312 211 211 +4232:onMode = off +4232:onIfMatch = 3312 211 211 + +### force the OmegaC to decay in the Omega_c -> Omega pi channel +4332:onMode = off +4332:onIfMatch = 3334 211 +4432:onMode = off +4432:onIfMatch = 4332 211 +4444:onMode = off +4444:onIfMatch = 4432 211 + +#boundless decays +ParticleDecays:limitTau0 off + +#do other decays via geant3, etc +310:onMode = off +3122:onMode = off +3112:onMode = off +3222:onMode = off +3312:onMode = off +3334:onMode = off diff --git a/MC/config/ALICE3/pythia8/generator/pythia8_ArAr.cfg b/MC/config/ALICE3/pythia8/generator/pythia8_ArAr.cfg new file mode 100644 index 000000000..60555ee13 --- /dev/null +++ b/MC/config/ALICE3/pythia8/generator/pythia8_ArAr.cfg @@ -0,0 +1,20 @@ +### ArAr beams +Beams:idA = 1000180400 +Beams:idB = 1000180400 +Beams:eCM = 6300.0 ### energy + +### Add Ar +1000180400:all = 40Ar 40Arbar 8 54 0 39.963 + +Beams:frameType = 1 +ParticleDecays:limitTau0 = on +ParticleDecays:tau0Max = 10. ### match alice: 1cm/c = 10.0mm/c + +### Initialize the Angantyr model to fit the total and semi-includive +### cross sections in Pythia within some tolerance. +HeavyIon:SigFitErr = 0.02,0.02,0.1,0.05,0.05,0.0,0.1,0.0 + +### These parameters are typicall suitable for sqrt(S_NN)=5TeV +HeavyIon:SigFitDefPar = 17.24,2.15,0.33,0.0,0.0,0.0,0.0,0.0 + +Random:setSeed = on diff --git a/MC/config/ALICE3/pythia8/generator/pythia8_KrKr.cfg b/MC/config/ALICE3/pythia8/generator/pythia8_KrKr.cfg new file mode 100644 index 000000000..858d97d79 --- /dev/null +++ b/MC/config/ALICE3/pythia8/generator/pythia8_KrKr.cfg @@ -0,0 +1,20 @@ +### KrKr beams +Beams:idA = 1000360840 +Beams:idB = 1000360840 +Beams:eCM = 6460.0 ### energy + +### Add Kr +1000360840:all = 84Kr 84Krbar 10 108 0 83.798 + +Beams:frameType = 1 +ParticleDecays:limitTau0 = on +ParticleDecays:tau0Max = 10. ### match alice: 1cm/c = 10.0mm/c + +### Initialize the Angantyr model to fit the total and semi-includive +### cross sections in Pythia within some tolerance. +HeavyIon:SigFitErr = 0.02,0.02,0.1,0.05,0.05,0.0,0.1,0.0 + +### These parameters are typicall suitable for sqrt(S_NN)=5TeV +HeavyIon:SigFitDefPar = 17.24,2.15,0.33,0.0,0.0,0.0,0.0,0.0 + +Random:setSeed = on diff --git a/MC/config/ALICE3/pythia8/generator/pythia8_OO.cfg b/MC/config/ALICE3/pythia8/generator/pythia8_OO.cfg new file mode 100644 index 000000000..39c81053c --- /dev/null +++ b/MC/config/ALICE3/pythia8/generator/pythia8_OO.cfg @@ -0,0 +1,17 @@ +### OO beams +Beams:idA = 1000080160 +Beams:idB = 1000080160 +Beams:eCM = 7000.0 ### energy + +Beams:frameType = 1 +ParticleDecays:limitTau0 = on +ParticleDecays:tau0Max = 10. ### match alice: 1cm/c = 10.0mm/c + +### Initialize the Angantyr model to fit the total and semi-includive +### cross sections in Pythia within some tolerance. +HeavyIon:SigFitErr = 0.02,0.02,0.1,0.05,0.05,0.0,0.1,0.0 + +### These parameters are typicall suitable for sqrt(S_NN)=5TeV +HeavyIon:SigFitDefPar = 17.24,2.15,0.33,0.0,0.0,0.0,0.0,0.0 + +Random:setSeed = on diff --git a/MC/config/ALICE3/pythia8/generator/pythia8_PbPb.cfg b/MC/config/ALICE3/pythia8/generator/pythia8_PbPb.cfg new file mode 100644 index 000000000..b146e0be4 --- /dev/null +++ b/MC/config/ALICE3/pythia8/generator/pythia8_PbPb.cfg @@ -0,0 +1,17 @@ +### Specify beams +Beams:idA = 1000822080 +Beams:idB = 1000822080 +Beams:eCM = 5520.0 ### energy + +Beams:frameType = 1 +ParticleDecays:limitTau0 = on +ParticleDecays:tau0Max = 10. ### match alice: 1cm/c = 10.0mm/c + +### Initialize the Angantyr model to fit the total and semi-includive +### cross sections in Pythia within some tolerance. +HeavyIon:SigFitErr = 0.02,0.02,0.1,0.05,0.05,0.0,0.1,0.0 + +### These parameters are typicall suitable for sqrt(S_NN)=5TeV +HeavyIon:SigFitDefPar = 17.24,2.15,0.33,0.0,0.0,0.0,0.0,0.0 + +Random:setSeed = on diff --git a/MC/config/ALICE3/pythia8/generator/pythia8_PbPb_502tev.cfg b/MC/config/ALICE3/pythia8/generator/pythia8_PbPb_502tev.cfg new file mode 100644 index 000000000..5d0843644 --- /dev/null +++ b/MC/config/ALICE3/pythia8/generator/pythia8_PbPb_502tev.cfg @@ -0,0 +1,17 @@ +### Specify beams +Beams:idA = 1000822080 +Beams:idB = 1000822080 +Beams:eCM = 5020.0 ### energy + +Beams:frameType = 1 +ParticleDecays:limitTau0 = on +ParticleDecays:tau0Max = 10. ### match alice: 1cm/c = 10.0mm/c + +### Initialize the Angantyr model to fit the total and semi-includive +### cross sections in Pythia within some tolerance. +HeavyIon:SigFitErr = 0.02,0.02,0.1,0.05,0.05,0.0,0.1,0.0 + +### These parameters are typicall suitable for sqrt(S_NN)=5TeV +HeavyIon:SigFitDefPar = 17.24,2.15,0.33,0.0,0.0,0.0,0.0,0.0 + +Random:setSeed = on diff --git a/MC/config/ALICE3/pythia8/generator/pythia8_PbPb_536tev.cfg b/MC/config/ALICE3/pythia8/generator/pythia8_PbPb_536tev.cfg new file mode 100644 index 000000000..be69adf17 --- /dev/null +++ b/MC/config/ALICE3/pythia8/generator/pythia8_PbPb_536tev.cfg @@ -0,0 +1,17 @@ +### Specify beams +Beams:idA = 1000822080 +Beams:idB = 1000822080 +Beams:eCM = 5360.0 ### energy + +Beams:frameType = 1 +ParticleDecays:limitTau0 = on +ParticleDecays:tau0Max = 10. ### match alice: 1cm/c = 10.0mm/c + +### Initialize the Angantyr model to fit the total and semi-includive +### cross sections in Pythia within some tolerance. +HeavyIon:SigFitErr = 0.02,0.02,0.1,0.05,0.05,0.0,0.1,0.0 + +### These parameters are typicall suitable for sqrt(S_NN)=5TeV +HeavyIon:SigFitDefPar = 17.24,2.15,0.33,0.0,0.0,0.0,0.0,0.0 + +Random:setSeed = on diff --git a/MC/config/ALICE3/pythia8/generator/pythia8_PbPb_D0toKpi.cfg b/MC/config/ALICE3/pythia8/generator/pythia8_PbPb_D0toKpi.cfg new file mode 100644 index 000000000..039d501a5 --- /dev/null +++ b/MC/config/ALICE3/pythia8/generator/pythia8_PbPb_D0toKpi.cfg @@ -0,0 +1,20 @@ +### Specify beams +Beams:idA = 1000822080 +Beams:idB = 1000822080 +Beams:eCM = 5520.0 ### energy + +Beams:frameType = 1 +ParticleDecays:limitTau0 = on +ParticleDecays:tau0Max = 10. ### match alice: 1cm/c = 10.0mm/c + +### Initialize the Angantyr model to fit the total and semi-includive +### cross sections in Pythia within some tolerance. +HeavyIon:SigFitErr = 0.02,0.02,0.1,0.05,0.05,0.0,0.1,0.0 + +### These parameters are typicall suitable for sqrt(S_NN)=5TeV +HeavyIon:SigFitDefPar = 17.24,2.15,0.33,0.0,0.0,0.0,0.0,0.0 + +### Force decay of D0 into Kpi +421:oneChannel = 1 0.0389 0 -321 211 + +Random:setSeed = on diff --git a/MC/config/ALICE3/pythia8/generator/pythia8_XeXe.cfg b/MC/config/ALICE3/pythia8/generator/pythia8_XeXe.cfg new file mode 100644 index 000000000..45552676e --- /dev/null +++ b/MC/config/ALICE3/pythia8/generator/pythia8_XeXe.cfg @@ -0,0 +1,17 @@ +### XeXe beams +Beams:idA = 1000541290 +Beams:idB = 1000541290 +Beams:eCM = 5860.0 ### energy + +Beams:frameType = 1 +ParticleDecays:limitTau0 = on +ParticleDecays:tau0Max = 10. ### match alice: 1cm/c = 10.0mm/c + +### Initialize the Angantyr model to fit the total and semi-includive +### cross sections in Pythia within some tolerance. +HeavyIon:SigFitErr = 0.02,0.02,0.1,0.05,0.05,0.0,0.1,0.0 + +### These parameters are typicall suitable for sqrt(S_NN)=5TeV +HeavyIon:SigFitDefPar = 17.24,2.15,0.33,0.0,0.0,0.0,0.0,0.0 + +Random:setSeed = on diff --git a/MC/config/ALICE3/pythia8/generator/pythia8_central_hi.cfg b/MC/config/ALICE3/pythia8/generator/pythia8_central_hi.cfg new file mode 100755 index 000000000..cb7e7cec0 --- /dev/null +++ b/MC/config/ALICE3/pythia8/generator/pythia8_central_hi.cfg @@ -0,0 +1,20 @@ +### Specify beams +Beams:idA = 1000822080 +Beams:idB = 1000822080 +Beams:eCM = 5520.0 ### energy + +Beams:frameType = 1 +ParticleDecays:limitTau0 = on +ParticleDecays:tau0Max = 10. ### match alice: 1cm/c = 10.0mm/c + +### Initialize the Angantyr model to fit the total and semi-includive +### cross sections in Pythia within some tolerance. +HeavyIon:SigFitErr = 0.02,0.02,0.1,0.05,0.05,0.0,0.1,0.0 + +### These parameters are typicall suitable for sqrt(S_NN)=5TeV +HeavyIon:SigFitNGen = 0 +HeavyIon:SigFitDefPar = 13.88,1.84,0.22,0.0,0.0,0.0,0.0,0.0 +HeavyIon:bWidth = 4.43 + + +Random:setSeed = on \ No newline at end of file diff --git a/MC/config/ALICE3/pythia8/generator/pythia8_central_hi.cmnd b/MC/config/ALICE3/pythia8/generator/pythia8_central_hi.cmnd new file mode 100755 index 000000000..713f1b36d --- /dev/null +++ b/MC/config/ALICE3/pythia8/generator/pythia8_central_hi.cmnd @@ -0,0 +1,21 @@ +### beams +Beams:idA 1000822080 # Pb +Beams:idB 1000822080 # Pb +Beams:eCM 5520. # GeV + +### heavy-ion settings (valid for Pb-Pb 5520 only) +HeavyIon:SigFitNGen = 0 +HeavyIon:SigFitDefPar = 13.88,1.84,0.22,0.0,0.0,0.0,0.0,0.0 +HeavyIon:bWidth = 4.43 + +### processes (apparently not to be defined) + +### decays +ParticleDecays:limitTau0 on +ParticleDecays:tau0Max 0.001 + +! 2) Seed settings +! Seed is set inside the generator +! If run on the grid, seed is set to job id +! If run locally, seed is set to 0 +Random:setSeed = on ! Random seed on diff --git a/MC/config/ALICE3/pythia8/generator/pythia8_hi.cmnd b/MC/config/ALICE3/pythia8/generator/pythia8_hi.cmnd new file mode 100755 index 000000000..75dbb2383 --- /dev/null +++ b/MC/config/ALICE3/pythia8/generator/pythia8_hi.cmnd @@ -0,0 +1,21 @@ +### beams +Beams:idA 1000822080 # Pb +Beams:idB 1000822080 # Pb +Beams:eCM 5520. # GeV + +### heavy-ion settings (valid for Pb-Pb 5520 only) +HeavyIon:SigFitNGen = 0 +HeavyIon:SigFitDefPar = 13.88,1.84,0.22,0.0,0.0,0.0,0.0,0.0 +HeavyIon:bWidth = 14.48 + +### processes (apparently not to be defined) + +### decays +ParticleDecays:limitTau0 on +ParticleDecays:tau0Max 0.001 + +! 2) Seed settings +! Seed is set inside the generator +! If run on the grid, seed is set to job id +! If run locally, seed is set to 0 +Random:setSeed = on ! Random seed on diff --git a/MC/config/ALICE3/pythia8/generator/pythia8_pp.cfg b/MC/config/ALICE3/pythia8/generator/pythia8_pp.cfg new file mode 100644 index 000000000..a8adfd677 --- /dev/null +++ b/MC/config/ALICE3/pythia8/generator/pythia8_pp.cfg @@ -0,0 +1,16 @@ +### Specify beams +Beams:idA = 2212 +Beams:idB = 2212 +Beams:eCM = 14000. ### energy + +Beams:frameType = 1 +ParticleDecays:limitTau0 = on +ParticleDecays:tau0Max = 10. ### match alice: 1cm/c = 10.0mm/c + +### processes +SoftQCD:inelastic = on # all inelastic processes + +# default: do nothing, Monash 2013 will do its thing +Tune:pp = 14 + +Random:setSeed = on diff --git a/MC/config/ALICE3/pythia8/generator/pythia8_pp.cmnd b/MC/config/ALICE3/pythia8/generator/pythia8_pp.cmnd new file mode 100755 index 000000000..fa35a6dcc --- /dev/null +++ b/MC/config/ALICE3/pythia8/generator/pythia8_pp.cmnd @@ -0,0 +1,61 @@ +! WithMPI_MPIBased.cmnd. +! This file contains the More QCD scheme colour reconnection settings scenario +! to be read in for a Pythia8 run. +! Lines beginning with a "!" are comments. +! Author: Renan Acconcia + +!pythia default +!Last update: 01/08/2018 +!Only Inelastic events: INEL + +! 1) Settings related to output in init(), next() and stat(). +Init:showChangedSettings = on ! list changed settings +!Init:showChangedParticleData = off ! list changed particle data +!Next:numberCount = 0 ! print message every n events +!Next:numberShowInfo = 0 ! print event information n times +!Next:numberShowProcess = 1 ! print process record n times +!Next:numberShowEvent = 1 ! print event record n times + +! 2) Seed settings +! Seed is set inside the generator +! If run on the grid, seed is set to job id +! If run locally, seed is set to 0 +Random:setSeed = on ! Random seed on + +! 3) Tunes settings +! Tune:preferLHAPDF = 2 ! Using LHAPDF 6 +StringPT:sigma = 0.335 +StringZ:aLund = 0.36 +StringZ:bLund = 0.56 +StringFlav:probQQtoQ = 0.078 +StringFlav:ProbStoUD = 0.2 +StringFlav:probQQ1toQQ0join = 0.0275,0.0275,0.0275,0.0275 + +MultiPartonInteractions:pT0Ref = 2.12 + +BeamRemnants:remnantMode = 1 +BeamRemnants:saturation = 5 + +ColourReconnection:mode = 1 +ColourReconnection:allowDoubleJunRem = off +ColourReconnection:m0 = 2.9 +ColourReconnection:allowJunctions = on +ColourReconnection:junctionCorrection = 1.43 +ColourReconnection:timeDilationMode = 0 + +! 5) Settings for the softQCD-process generation. NSD scenario. +SoftQCD:nonDiffractive = on ! Non Diffractive on +SoftQCD:singleDiffractive = on ! Single Diffractive on +SoftQCD:doubleDiffractive = on ! Double Diffractive on +SoftQCD:centralDiffractive = on ! Central Diffractive on +SoftQCD:elastic = off ! Elastic off + +! 6) Decay Supression +ParticleDecays:limitTau0 = on ! Only particles with tau0 < tau0Max are decayed. +ParticleDecays:tau0Max = 0.001 ! Tau0Max = 3. mm/c + +! 7) Beam parameter settings. Values below agree with default ones. +!this is default do not need to be specified +Beams:idA = 2212 ! first beam, p = 2212, pbar = -2212 +Beams:idB = 2212 ! second beam, p = 2212, pbar = -2212 +Beams:eCM = 14000.0 //energy diff --git a/MC/config/ALICE3/pythia8/generator/pythia8_pp_136tev.cfg b/MC/config/ALICE3/pythia8/generator/pythia8_pp_136tev.cfg new file mode 100644 index 000000000..c3265b276 --- /dev/null +++ b/MC/config/ALICE3/pythia8/generator/pythia8_pp_136tev.cfg @@ -0,0 +1,16 @@ +### Specify beams +Beams:idA = 2212 +Beams:idB = 2212 +Beams:eCM = 13600. ### energy + +Beams:frameType = 1 +ParticleDecays:limitTau0 = on +ParticleDecays:tau0Max = 10. ### match alice: 1cm/c = 10.0mm/c + +### processes +SoftQCD:inelastic = on # all inelastic processes + +# default: do nothing, Monash 2013 will do its thing +Tune:pp = 14 + +Random:setSeed = on diff --git a/MC/config/ALICE3/pythia8/generator/pythia8_pp_13tev.cfg b/MC/config/ALICE3/pythia8/generator/pythia8_pp_13tev.cfg new file mode 100644 index 000000000..18c10c6dd --- /dev/null +++ b/MC/config/ALICE3/pythia8/generator/pythia8_pp_13tev.cfg @@ -0,0 +1,16 @@ +### Specify beams +Beams:idA = 2212 +Beams:idB = 2212 +Beams:eCM = 13000. ### energy + +Beams:frameType = 1 +ParticleDecays:limitTau0 = on +ParticleDecays:tau0Max = 10. ### match alice: 1cm/c = 10.0mm/c + +### processes +SoftQCD:inelastic = on # all inelastic processes + +# default: do nothing, Monash 2013 will do its thing +Tune:pp = 14 + +Random:setSeed = on diff --git a/MC/config/ALICE3/pythia8/generator/pythia8_pp_502tev.cfg b/MC/config/ALICE3/pythia8/generator/pythia8_pp_502tev.cfg new file mode 100644 index 000000000..68e3ef034 --- /dev/null +++ b/MC/config/ALICE3/pythia8/generator/pythia8_pp_502tev.cfg @@ -0,0 +1,16 @@ +### Specify beams +Beams:idA = 2212 +Beams:idB = 2212 +Beams:eCM = 5020. ### energy + +Beams:frameType = 1 +ParticleDecays:limitTau0 = on +ParticleDecays:tau0Max = 10. ### match alice: 1cm/c = 10.0mm/c + +### processes +SoftQCD:inelastic = on # all inelastic processes + +# default: do nothing, Monash 2013 will do its thing +Tune:pp = 14 + +Random:setSeed = on diff --git a/MC/config/ALICE3/pythia8/generator/pythia8_pp_536tev.cfg b/MC/config/ALICE3/pythia8/generator/pythia8_pp_536tev.cfg new file mode 100644 index 000000000..1f270e206 --- /dev/null +++ b/MC/config/ALICE3/pythia8/generator/pythia8_pp_536tev.cfg @@ -0,0 +1,16 @@ +### Specify beams +Beams:idA = 2212 +Beams:idB = 2212 +Beams:eCM = 5360. ### energy + +Beams:frameType = 1 +ParticleDecays:limitTau0 = on +ParticleDecays:tau0Max = 10. ### match alice: 1cm/c = 10.0mm/c + +### processes +SoftQCD:inelastic = on # all inelastic processes + +# default: do nothing, Monash 2013 will do its thing +Tune:pp = 14 + +Random:setSeed = on diff --git a/MC/config/ALICE3/pythia8/generator/pythia8_pp_7tev.cfg b/MC/config/ALICE3/pythia8/generator/pythia8_pp_7tev.cfg new file mode 100644 index 000000000..c1bc3b3c0 --- /dev/null +++ b/MC/config/ALICE3/pythia8/generator/pythia8_pp_7tev.cfg @@ -0,0 +1,16 @@ +### Specify beams +Beams:idA = 2212 +Beams:idB = 2212 +Beams:eCM = 7000. ### energy + +Beams:frameType = 1 +ParticleDecays:limitTau0 = on +ParticleDecays:tau0Max = 10. ### match alice: 1cm/c = 10.0mm/c + +### processes +SoftQCD:inelastic = on # all inelastic processes + +# default: do nothing, Monash 2013 will do its thing +Tune:pp = 14 + +Random:setSeed = on diff --git a/MC/config/ALICE3/pythia8/generator/pythia8_pp_clr-blc0_13tev.cfg b/MC/config/ALICE3/pythia8/generator/pythia8_pp_clr-blc0_13tev.cfg new file mode 100644 index 000000000..a5fc8e17f --- /dev/null +++ b/MC/config/ALICE3/pythia8/generator/pythia8_pp_clr-blc0_13tev.cfg @@ -0,0 +1,44 @@ +### ============================================ +### PYTHIA 8 — CLR-BLC Mode 0 configuration +### Based on table: Monash vs CLR-BLC (Mode 0) https://link.springer.com/article/10.1007/JHEP08(2015)003 in Appendix A +### ============================================ + +### Beams +Beams:idA = 2212 +Beams:idB = 2212 +Beams:eCM = 13000. +Beams:frameType = 1 + +### Decays (match ALICE) +ParticleDecays:limitTau0 = on +ParticleDecays:tau0Max = 10.0 + +### Processes and base tune +SoftQCD:inelastic = on # all inelastic processes +Tune:pp = 14 # Monash 2013 as baseline + +### String fragmentation +StringPT:sigma = 0.335 +StringZ:aLund = 0.36 +StringZ:bLund = 0.56 +StringFlav:probQQtoQ = 0.078 # probQQtoQ → Q (Mode 0,2,3) +StringFlav:probStoUD = 0.20 # s/u,d suppression (Mode 0,2,3) + +# Diquark–diquark join probabilities (vector of 4 numbers) +StringFlav:probQQ1toQQ0join = 0.0275,0.0275,0.0275,0.0275 # (Mode 0,2,3) +### Multi-parton interactions +MultiPartonInteractions:pT0Ref = 2.12 # Mode 0 pT0Ref + +### Beam remnants +BeamRemnants:remnantMode = 1 # required for CR mode 1 +BeamRemnants:saturation = 5 + +### Colour reconnection: CLR-BLC Mode 0 +ColourReconnection:mode = 1 # new CR scheme +ColourReconnection:allowDoubleJunRem = off # as in table +ColourReconnection:m0 = 2.9 # Mode 0 mass scale +ColourReconnection:allowJunctions = on +ColourReconnection:junctionCorrection = 1.43 # Mode 0 +ColourReconnection:timeDilationMode = 0 # Mode 0 + +Random:setSeed = on diff --git a/MC/config/ALICE3/pythia8/generator/pythia8_pp_clr-blc2_13tev.cfg b/MC/config/ALICE3/pythia8/generator/pythia8_pp_clr-blc2_13tev.cfg new file mode 100644 index 000000000..405de08bb --- /dev/null +++ b/MC/config/ALICE3/pythia8/generator/pythia8_pp_clr-blc2_13tev.cfg @@ -0,0 +1,45 @@ +### ============================================ +### PYTHIA 8 — CLR-BLC Mode 2 configuration +### Based on table: Monash vs CLR-BLC (Mode 2) https://link.springer.com/article/10.1007/JHEP08(2015)003 in Appendix A +### ============================================ + +### Beams +Beams:idA = 2212 +Beams:idB = 2212 +Beams:eCM = 13000. +Beams:frameType = 1 + +### Decays (match ALICE) +ParticleDecays:limitTau0 = on +ParticleDecays:tau0Max = 10.0 + +### Processes and base tune +SoftQCD:inelastic = on # all inelastic processes +Tune:pp = 14 # Monash 2013 as baseline + +### String fragmentation +StringPT:sigma = 0.335 +StringZ:aLund = 0.36 +StringZ:bLund = 0.56 +StringFlav:probQQtoQ = 0.078 # probQQtoQ → Q (Mode 0,2,3) +StringFlav:probStoUD = 0.20 # s/u,d suppression (Mode 0,2,3) + +# Diquark–diquark join probabilities (vector of 4 numbers) +StringFlav:probQQ1toQQ0join = 0.0275,0.0275,0.0275,0.0275 # (Mode 0,2,3) +### Multi-parton interactions +MultiPartonInteractions:pT0Ref = 2.15 # Mode 2 pT0Ref + +### Beam remnants +BeamRemnants:remnantMode = 1 # required for CR mode 1 +BeamRemnants:saturation = 5 + +### Colour reconnection: CLR-BLC Mode 2 +ColourReconnection:mode = 1 # new CR scheme +ColourReconnection:allowDoubleJunRem = off # as in table +ColourReconnection:m0 = 0.3 # Mode 2 mass scale +ColourReconnection:allowJunctions = on +ColourReconnection:junctionCorrection = 1.2 # Mode 2 +ColourReconnection:timeDilationMode = 2 # Mode 2 +ColourReconnection:timeDilationPar = 0.18 # Mode 2 + +Random:setSeed = on diff --git a/MC/config/ALICE3/pythia8/generator/pythia8_pp_clr-blc3_13tev.cfg b/MC/config/ALICE3/pythia8/generator/pythia8_pp_clr-blc3_13tev.cfg new file mode 100644 index 000000000..34c006469 --- /dev/null +++ b/MC/config/ALICE3/pythia8/generator/pythia8_pp_clr-blc3_13tev.cfg @@ -0,0 +1,45 @@ +### ============================================ +### PYTHIA 8 — CLR-BLC Mode 3 configuration +### Based on table: Monash vs CLR-BLC (Mode 3) https://link.springer.com/article/10.1007/JHEP08(2015)003 in Appendix A +### ============================================ + +### Beams +Beams:idA = 2212 +Beams:idB = 2212 +Beams:eCM = 13000. +Beams:frameType = 1 + +### Decays (match ALICE) +ParticleDecays:limitTau0 = on +ParticleDecays:tau0Max = 10.0 + +### Processes and base tune +SoftQCD:inelastic = on # all inelastic processes +Tune:pp = 14 # Monash 2013 as baseline + +### String fragmentation +StringPT:sigma = 0.335 +StringZ:aLund = 0.36 +StringZ:bLund = 0.56 +StringFlav:probQQtoQ = 0.078 # probQQtoQ → Q (Mode 0,2,3) +StringFlav:probStoUD = 0.20 # s/u,d suppression (Mode 0,2,3) + +# Diquark–diquark join probabilities (vector of 4 numbers) +StringFlav:probQQ1toQQ0join = 0.0275,0.0275,0.0275,0.0275 # (Mode 0,2,3) +### Multi-parton interactions +MultiPartonInteractions:pT0Ref = 2.05 # Mode 3 pT0Ref + +### Beam remnants +BeamRemnants:remnantMode = 1 # required for CR mode 1 +BeamRemnants:saturation = 5 + +### Colour reconnection: CLR-BLC Mode 3 +ColourReconnection:mode = 1 # new CR scheme +ColourReconnection:allowDoubleJunRem = off # as in table +ColourReconnection:m0 = 0.3 # Mode 3 mass scale +ColourReconnection:allowJunctions = on +ColourReconnection:junctionCorrection = 1.15 # Mode 3 +ColourReconnection:timeDilationMode = 3 # Mode 3 +ColourReconnection:timeDilationPar = 0.073 # Mode 3 + +Random:setSeed = on diff --git a/MC/config/ALICE3/pythia8/generator/pythia8_pp_cr_136tev.cfg b/MC/config/ALICE3/pythia8/generator/pythia8_pp_cr_136tev.cfg new file mode 100644 index 000000000..c3303726e --- /dev/null +++ b/MC/config/ALICE3/pythia8/generator/pythia8_pp_cr_136tev.cfg @@ -0,0 +1,21 @@ +### Specify beams +Beams:idA = 2212 +Beams:idB = 2212 +Beams:eCM = 13600. ### energy + +Beams:frameType = 1 +ParticleDecays:limitTau0 = on +ParticleDecays:tau0Max = 10. ### match alice: 1cm/c = 10.0mm/c + +### processes +SoftQCD:inelastic = on # all inelastic processes +# default: Monash 2013 +Tune:pp = 14 + +### CR configurations +ColourReconnection:mode = 1 # new colour reconnection scheme +ColourReconnection:allowJunctions = on + +BeamRemnants:remnantMode = 1 # work with CR mode = 1 + + diff --git a/MC/config/ALICE3/pythia8/generator/pythia8_pp_hardQCD_136tev.cfg b/MC/config/ALICE3/pythia8/generator/pythia8_pp_hardQCD_136tev.cfg new file mode 100644 index 000000000..bdac88560 --- /dev/null +++ b/MC/config/ALICE3/pythia8/generator/pythia8_pp_hardQCD_136tev.cfg @@ -0,0 +1,21 @@ +### Specify beams +Beams:idA = 2212 +Beams:idB = 2212 +Beams:eCM = 13600. ### energy + +Beams:frameType = 1 +ParticleDecays:limitTau0 = on +ParticleDecays:tau0Max = 10. ### match alice: 1cm/c = 10.0mm/c + + +### processes: c-cbar and b-bbar processes +HardQCD:all on + +# default: do nothing, Monash 2013 will do its thing +Tune:pp = 14 + +Random:setSeed = on + +### pT-hard bins +PhaseSpace:pTHatMin = 35 +PhaseSpace:pTHatMax = 200 diff --git a/MC/config/ALICE3/pythia8/generator/pythia8_pp_hf_hardQCD_136tev.cfg b/MC/config/ALICE3/pythia8/generator/pythia8_pp_hf_hardQCD_136tev.cfg new file mode 100644 index 000000000..13d1a4689 --- /dev/null +++ b/MC/config/ALICE3/pythia8/generator/pythia8_pp_hf_hardQCD_136tev.cfg @@ -0,0 +1,22 @@ +### Specify beams +Beams:idA = 2212 +Beams:idB = 2212 +Beams:eCM = 13600. ### energy + +Beams:frameType = 1 +ParticleDecays:limitTau0 = on +ParticleDecays:tau0Max = 10. ### match alice: 1cm/c = 10.0mm/c + + +### processes: c-cbar and b-bbar processes +HardQCD:hardccbar on +HardQCD:hardbbbar on + +# default: do nothing, Monash 2013 will do its thing +Tune:pp = 14 + +Random:setSeed = on + +### pT-hard bins +PhaseSpace:pTHatMin = 35 +PhaseSpace:pTHatMax = 200 diff --git a/MC/config/ALICE3/pythia8/generator/pythia8_pp_rescattering_136tev.cfg b/MC/config/ALICE3/pythia8/generator/pythia8_pp_rescattering_136tev.cfg new file mode 100644 index 000000000..9d0adf1fe --- /dev/null +++ b/MC/config/ALICE3/pythia8/generator/pythia8_pp_rescattering_136tev.cfg @@ -0,0 +1,23 @@ +### Specify beams +Beams:idA = 2212 +Beams:idB = 2212 +Beams:eCM = 13600. ### energy + +Beams:frameType = 1 +ParticleDecays:limitTau0 = on +ParticleDecays:tau0Max = 10. ### match alice: 1cm/c = 10.0mm/c + +### processes +SoftQCD:inelastic = on # all inelastic processes + +# default: do nothing, Monash 2013 will do its thing +Tune:pp = 14 + +### enable hadronic rescattering +HadronLevel:Rescatter = on # default = off +Fragmentation:setVertices = on # default = off +PartonVertex:setVertex = on # default = off +Rescattering:nearestNeighbours = off # default = on (but "require a larger retuning effort") +Rescattering:inelastic = on # default = on + +Random:setSeed = on diff --git a/MC/config/ALICE3/pythia8/generator/pythia8_pp_ropes.cfg b/MC/config/ALICE3/pythia8/generator/pythia8_pp_ropes.cfg new file mode 100644 index 000000000..5f44f9d09 --- /dev/null +++ b/MC/config/ALICE3/pythia8/generator/pythia8_pp_ropes.cfg @@ -0,0 +1,43 @@ +### Specify beams +Beams:idA = 2212 +Beams:idB = 2212 +Beams:eCM = 14000. ### energy + +Beams:frameType = 1 +ParticleDecays:limitTau0 = on +ParticleDecays:tau0Max = 10. ### match alice: 1cm/c = 10.0mm/c + +### processes +SoftQCD:inelastic = on # all inelastic processes + +### Ropes settings +MultiPartonInteractions:pT0Ref = 2.15 + +BeamRemnants:remnantMode = 1 +BeamRemnants:saturation = 5 + +ColourReconnection:mode = 1 +ColourReconnection:allowDoubleJunRem = off +ColourReconnection:m0 = 0.3 +ColourReconnection:allowJunctions = on +ColourReconnection:junctionCorrection = 1.2 +ColourReconnection:timeDilationMode = 2 +ColourReconnection:timeDilationPar = 0.18 + +Ropewalk:RopeHadronization = on +Ropewalk:doShoving = on +Ropewalk:tInit = 1.5 # Propagation time +Ropewalk:deltat = 0.05 +Ropewalk:tShove 0.1 +Ropewalk:gAmplitude = 0. # Set shoving strength to 0 explicitly +Ropewalk:doFlavour = on +Ropewalk:r0 = 0.5 +Ropewalk:m0 = 0.2 +Ropewalk:beta = 0.1 + +### Enabling setting of vertex information. +PartonVertex:setVertex = on +PartonVertex:protonRadius = 0.7 +PartonVertex:emissionWidth = 0.1 + +Random:setSeed = on diff --git a/MC/config/ALICE3/pythia8/generator/pythia8_pp_ropes_136tev.cfg b/MC/config/ALICE3/pythia8/generator/pythia8_pp_ropes_136tev.cfg new file mode 100644 index 000000000..30ff0becf --- /dev/null +++ b/MC/config/ALICE3/pythia8/generator/pythia8_pp_ropes_136tev.cfg @@ -0,0 +1,43 @@ +### Specify beams +Beams:idA = 2212 +Beams:idB = 2212 +Beams:eCM = 13600. ### energy + +Beams:frameType = 1 +ParticleDecays:limitTau0 = on +ParticleDecays:tau0Max = 10. ### match alice: 1cm/c = 10.0mm/c + +### processes +SoftQCD:inelastic = on # all inelastic processes + +### Ropes settings +MultiPartonInteractions:pT0Ref = 2.15 + +BeamRemnants:remnantMode = 1 +BeamRemnants:saturation = 5 + +ColourReconnection:mode = 1 +ColourReconnection:allowDoubleJunRem = off +ColourReconnection:m0 = 0.3 +ColourReconnection:allowJunctions = on +ColourReconnection:junctionCorrection = 1.2 +ColourReconnection:timeDilationMode = 2 +ColourReconnection:timeDilationPar = 0.18 + +Ropewalk:RopeHadronization = on +Ropewalk:doShoving = on +Ropewalk:tInit = 1.5 # Propagation time +Ropewalk:deltat = 0.05 +Ropewalk:tShove 0.1 +Ropewalk:gAmplitude = 0. # Set shoving strength to 0 explicitly +Ropewalk:doFlavour = on +Ropewalk:r0 = 0.5 +Ropewalk:m0 = 0.2 +Ropewalk:beta = 0.1 + +### Enabling setting of vertex information. +PartonVertex:setVertex = on +PartonVertex:protonRadius = 0.7 +PartonVertex:emissionWidth = 0.1 + +Random:setSeed = on diff --git a/MC/config/ALICE3/pythia8/generator/pythia8_pp_ropes_13tev.cfg b/MC/config/ALICE3/pythia8/generator/pythia8_pp_ropes_13tev.cfg new file mode 100644 index 000000000..a8bd9dc17 --- /dev/null +++ b/MC/config/ALICE3/pythia8/generator/pythia8_pp_ropes_13tev.cfg @@ -0,0 +1,43 @@ +### Specify beams +Beams:idA = 2212 +Beams:idB = 2212 +Beams:eCM = 13000. ### energy + +Beams:frameType = 1 +ParticleDecays:limitTau0 = on +ParticleDecays:tau0Max = 10. ### match alice: 1cm/c = 10.0mm/c + +### processes +SoftQCD:inelastic = on # all inelastic processes + +### Ropes settings +MultiPartonInteractions:pT0Ref = 2.15 + +BeamRemnants:remnantMode = 1 +BeamRemnants:saturation = 5 + +ColourReconnection:mode = 1 +ColourReconnection:allowDoubleJunRem = off +ColourReconnection:m0 = 0.3 +ColourReconnection:allowJunctions = on +ColourReconnection:junctionCorrection = 1.2 +ColourReconnection:timeDilationMode = 2 +ColourReconnection:timeDilationPar = 0.18 + +Ropewalk:RopeHadronization = on +Ropewalk:doShoving = on +Ropewalk:tInit = 1.5 # Propagation time +Ropewalk:deltat = 0.05 +Ropewalk:tShove 0.1 +Ropewalk:gAmplitude = 0. # Set shoving strength to 0 explicitly +Ropewalk:doFlavour = on +Ropewalk:r0 = 0.5 +Ropewalk:m0 = 0.2 +Ropewalk:beta = 0.1 + +### Enabling setting of vertex information. +PartonVertex:setVertex = on +PartonVertex:protonRadius = 0.7 +PartonVertex:emissionWidth = 0.1 + +Random:setSeed = on diff --git a/MC/config/ALICE3/pythia8/generator/pythia8_pp_shoving.cfg b/MC/config/ALICE3/pythia8/generator/pythia8_pp_shoving.cfg new file mode 100644 index 000000000..57d44de31 --- /dev/null +++ b/MC/config/ALICE3/pythia8/generator/pythia8_pp_shoving.cfg @@ -0,0 +1,34 @@ +### Specify beams +Beams:idA = 2212 +Beams:idB = 2212 +Beams:eCM = 14000. ### energy + +Beams:frameType = 1 +ParticleDecays:limitTau0 = on +ParticleDecays:tau0Max = 10. ### match alice: 1cm/c = 10.0mm/c + +### processes +SoftQCD:inelastic = on # all inelastic processes + +### Shoving settings +Ropewalk:RopeHadronization = on +Ropewalk:doShoving = on +Ropewalk:doFlavour = off +Ropewalk:rCutOff = 10.0 +Ropewalk:limitMom = on +Ropewalk:pTcut = 2.0 +Ropewalk:r0 = 0.41 +Ropewalk:m0 = 0.2 +Ropewalk:gAmplitude = 10.0 +Ropewalk:gExponent = 1.0 +Ropewalk:deltat = 0.1 +Ropewalk:tShove = 1. +Ropewalk:deltay = 0.1 +Ropewalk:tInit = 1.5 + +### Enabling setting of vertex information. +PartonVertex:setVertex = on +PartonVertex:protonRadius = 0.7 +PartonVertex:emissionWidth = 0.1 + +Random:setSeed = on diff --git a/MC/config/ALICE3/pythia8/generator/pythia8_pp_shoving_136tev.cfg b/MC/config/ALICE3/pythia8/generator/pythia8_pp_shoving_136tev.cfg new file mode 100644 index 000000000..7a586c2eb --- /dev/null +++ b/MC/config/ALICE3/pythia8/generator/pythia8_pp_shoving_136tev.cfg @@ -0,0 +1,34 @@ +### Specify beams +Beams:idA = 2212 +Beams:idB = 2212 +Beams:eCM = 13600. ### energy + +Beams:frameType = 1 +ParticleDecays:limitTau0 = on +ParticleDecays:tau0Max = 10. ### match alice: 1cm/c = 10.0mm/c + +### processes +SoftQCD:inelastic = on # all inelastic processes + +### Shoving settings +Ropewalk:RopeHadronization = on +Ropewalk:doShoving = on +Ropewalk:doFlavour = off +Ropewalk:rCutOff = 10.0 +Ropewalk:limitMom = on +Ropewalk:pTcut = 2.0 +Ropewalk:r0 = 0.41 +Ropewalk:m0 = 0.2 +Ropewalk:gAmplitude = 10.0 +Ropewalk:gExponent = 1.0 +Ropewalk:deltat = 0.1 +Ropewalk:tShove = 1. +Ropewalk:deltay = 0.1 +Ropewalk:tInit = 1.5 + +### Enabling setting of vertex information. +PartonVertex:setVertex = on +PartonVertex:protonRadius = 0.7 +PartonVertex:emissionWidth = 0.1 + +Random:setSeed = on diff --git a/MC/config/ALICE3/pythia8/generator/pythia8_pp_shoving_13tev.cfg b/MC/config/ALICE3/pythia8/generator/pythia8_pp_shoving_13tev.cfg new file mode 100644 index 000000000..221e08752 --- /dev/null +++ b/MC/config/ALICE3/pythia8/generator/pythia8_pp_shoving_13tev.cfg @@ -0,0 +1,34 @@ +### Specify beams +Beams:idA = 2212 +Beams:idB = 2212 +Beams:eCM = 13000. ### energy + +Beams:frameType = 1 +ParticleDecays:limitTau0 = on +ParticleDecays:tau0Max = 10. ### match alice: 1cm/c = 10.0mm/c + +### processes +SoftQCD:inelastic = on # all inelastic processes + +### Shoving settings +Ropewalk:RopeHadronization = on +Ropewalk:doShoving = on +Ropewalk:doFlavour = off +Ropewalk:rCutOff = 10.0 +Ropewalk:limitMom = on +Ropewalk:pTcut = 2.0 +Ropewalk:r0 = 0.41 +Ropewalk:m0 = 0.2 +Ropewalk:gAmplitude = 10.0 +Ropewalk:gExponent = 1.0 +Ropewalk:deltat = 0.1 +Ropewalk:tShove = 1. +Ropewalk:deltay = 0.1 +Ropewalk:tInit = 1.5 + +### Enabling setting of vertex information. +PartonVertex:setVertex = on +PartonVertex:protonRadius = 0.7 +PartonVertex:emissionWidth = 0.1 + +Random:setSeed = on diff --git a/MC/config/ALICE3/pythia8/generator_DittoMC.C b/MC/config/ALICE3/pythia8/generator_DittoMC.C new file mode 100644 index 000000000..62da98509 --- /dev/null +++ b/MC/config/ALICE3/pythia8/generator_DittoMC.C @@ -0,0 +1,69 @@ +#include +#include + +#include + +#include +#include +#include +#include + +// Ditto event generator +class GeneratorDittoMC : public o2::eventgen::GeneratorPythia8 +{ +public: + /// Constructor + explicit GeneratorDittoMC(const TString &tuneFile, const TString &signalFile) : mGenerator(makeConfig(tuneFile, signalFile)) + { + // We use the Pythia8 event only as the container passed to the O2 + // GeneratorPythia8 machinery. Particle production is handled by Ditto. + mPythia.readString("ProcessLevel:all = off"); + } + + /// Generate one Ditto event and export it to the Pythia8 event record. + bool generateEvent() override + { + mGenerator.generate(); + mGenerator.loadParticles(mPythia.event, true); + return true; + } + + /// Destructor + ~GeneratorDittoMC() override = default; + +private: + /// Build the Ditto runtime configuration. + static Ditto::Config makeConfig(const TString &tuneFile, const TString &signalFile) + { + if (tuneFile.IsNull()) + { + throw std::runtime_error("Ditto tune file must be specified"); + } + + Ditto::Config config; + config.mTuneFile = tuneFile.Data(); + config.mSignalFile = signalFile.Data(); + + // Use the Grid process ID as the Ditto seed when available. + // Outside the Grid, seed 0 is used. + if (const char *alienProcId = std::getenv("ALIEN_PROC_ID")) + { + config.mSeed = static_cast(std::strtoull(alienProcId, nullptr, 10)); + } + else + { + config.mSeed = static_cast(std::time(nullptr)); + } + + Printf("Using Ditto tune: %s and seed: %llu", config.mTuneFile.c_str(), static_cast(config.mSeed)); + + return config; + } + + Ditto::Generator mGenerator; +}; + +FairGenerator *generator_DittoMC(const TString &tuneFile, const TString &signalFile = "") +{ + return new GeneratorDittoMC(tuneFile, signalFile); +} diff --git a/MC/config/ALICE3/pythia8/generator_pythia8_ALICE3.C b/MC/config/ALICE3/pythia8/generator_pythia8_ALICE3.C new file mode 100644 index 000000000..bd562c082 --- /dev/null +++ b/MC/config/ALICE3/pythia8/generator_pythia8_ALICE3.C @@ -0,0 +1,45 @@ +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include "Pythia8/Pythia.h" +#include "FairGenerator.h" +#include "FairPrimaryGenerator.h" +#include "Generators/GeneratorPythia8.h" +#include + +using namespace Pythia8; +#endif + +// Default pythia8 minimum bias generator + +class GeneratorPythia8ALICE3 : public o2::eventgen::GeneratorPythia8 +{ +public: + + /// Constructor + GeneratorPythia8ALICE3() { + + char* alien_proc_id = getenv("ALIEN_PROC_ID"); + uint64_t seedFull; + uint64_t seed = 0; + + if (alien_proc_id != NULL) { + seedFull = static_cast(atol(alien_proc_id)); + for(int ii=0; ii<29; ii++) // there might be a cleaner way but this will work + seed |= ((seedFull) & (static_cast(1) << static_cast(ii))); + LOG(info) << "Value of ALIEN_PROC_ID: " << seedFull <<" truncated to 0-28 bits: "< +#include + +using namespace Pythia8; +#endif +class GeneratorPythia8Gun : public o2::eventgen::GeneratorPythia8{ +public: + /// default constructor + GeneratorPythia8Gun() = default; + + /// constructor + GeneratorPythia8Gun(int input_pdg){ + genMinPt=0.0; + genMaxPt=20.0; + genminY=-1.5; + genmaxY=1.5; + genminEta=-1.5; + genmaxEta=1.5; + + UEOverSampling = 20; + genEventCountUse = 2000; //start at large number: regen + + pdg = input_pdg; + E=0; + px=0; + py=0; + pz=0; + p=0; + y=0; + eta=0; + xProd=0; + yProd=0; + zProd=0; + xProd=0.; yProd=0.; zProd=0.; + //addFurtherPion=false; + + randomizePDGsign=false; + + //fSpectra = new TF1("fPtDist",myLevyPt,0.0,10,3); + fSpectra = STAR_BlastWave("fSpectra", m, 20); + fSpectra ->SetNpx( 1000 ); + fSpectraXi = STAR_BlastWave("fSpectraXi", 1.32171, 20); + fSpectraXi ->SetNpx( 1000 ); + fSpectraOm = STAR_BlastWave("fSpectraOm", 1.67245, 20); + fSpectraOm ->SetNpx( 1000 ); + + fSpectra->SetParameter(0, m); //mass: automatic! + fSpectra->SetParameter(1,0.6615); //beta-max + fSpectra->SetParameter(2,0.0905); //T + fSpectra->SetParameter(3,0.7355); //n + fSpectra->SetParameter(4,1000); //norm (not relevant) + + fSpectraXi->SetParameter(0,1.32171); //beta-max + fSpectraXi->SetParameter(1,0.6615); //beta-max + fSpectraXi->SetParameter(2,0.0905); //T + fSpectraXi->SetParameter(3,0.7355); //n + fSpectraXi->SetParameter(4,1000); //norm (not relevant) + + fSpectraOm->SetParameter(0,1.67245); //beta-max + fSpectraOm->SetParameter(1,0.6615); //beta-max + fSpectraOm->SetParameter(2,0.0905); //T + fSpectraOm->SetParameter(3,0.7355); //n + fSpectraOm->SetParameter(4,1000); //norm (not relevant) + + fLVHelper = new TLorentzVector(); + + if( input_pdg!=0 ) m = getMass(input_pdg); + if( input_pdg==4444 ) m = 4.797; + if( input_pdg==0 ) m = 1.0; + furtherPrim={}; + keys_furtherPrim={}; + cout<<"Initalizing extra PYTHIA object"<SetParameters(mt, pt, beta_max, temp, n); + + integral = fIntegrandXi->Integral(0., 1.); + } + if(TMath::Abs(mass-1.67245)<0.002){ + if (!fIntegrandOm) + fIntegrandOm = new TF1("fIntegrandOm", this, &GeneratorPythia8Gun::STAR_BlastWave_Integrand_Improved, 0., 1., 5, "GeneratorPythia8Gun", "STAR_BlastWave_Integrand_Improved"); + fIntegrandOm->SetParameters(mt, pt, beta_max, temp, n); + + integral = fIntegrandOm->Integral(0., 1.); + } + if(TMath::Abs(mass-1.67245)>0.002&&TMath::Abs(mass-1.32171)>0.002){ + if (!fIntegrand) + fIntegrand = new TF1("fIntegrand", this, &GeneratorPythia8Gun::STAR_BlastWave_Integrand_Improved, 0., 1., 5, "GeneratorPythia8Gun", "STAR_BlastWave_Integrand_Improved"); + fIntegrand->SetParameters(mt, pt, beta_max, temp, n); + + integral = fIntegrand->Integral(0., 1.); + } + return norm * pt * integral; + } + + //___________________________________________________________________ + + Double_t STAR_BlastWave_Integrand_Improved(const Double_t *x, const Double_t *p) { + + /* + x[0] -> r (radius) + p[0] -> mT (transverse mass) + p[1] -> pT (transverse momentum) + p[2] -> beta_max (surface velocity) + p[3] -> T (freezout temperature) + p[4] -> n (velocity profile) + */ + + Double_t r = x[0]; + Double_t mt = p[0]; + Double_t pt = p[1]; + Double_t beta_max = p[2]; + Double_t temp_1 = 1. / p[3]; + Double_t n = p[4]; + + Double_t beta = beta_max * TMath::Power(r, n); + Double_t rho = TMath::ATanH(beta); + Double_t argI0 = pt * TMath::SinH(rho) * temp_1; + Double_t argK1 = mt * TMath::CosH(rho) * temp_1; + // if (argI0 > 100 || argI0 < -100) + // printf("r=%f, pt=%f, beta_max=%f, temp=%f, n=%f, mt=%f, beta=%f, rho=%f, argI0=%f, argK1=%f\n", r, pt, beta_max, 1. / temp_1, n, mt, beta, rho, argI0, argK1); + return r * mt * TMath::BesselI0(argI0) * TMath::BesselK1(argK1); + + } + + //___________________________________________________________________ + + TF1 *STAR_BlastWave(const Char_t *name, Double_t mass,Float_t upperlim, Double_t beta_max = 0.9, Double_t temp = 0.1, Double_t n = 1., Double_t norm = 1.e6) { + + //new TF1("fSpectra",this ,&GeneratorPythia8GunPbPb::myLevyPt, 0.0,20,3, "GeneratorPythia8GunPbPb","myLevyPt"); + TF1 *fBlastWave = new TF1(name, this, &GeneratorPythia8Gun::STAR_BlastWave_Func, 0., upperlim, 5, "GeneratorPythia8Gun", "STAR_BlastWave_Func"); + fBlastWave->SetParameters(mass, beta_max, temp, n, norm); + fBlastWave->SetParNames("mass", "beta_max", "T", "n", "norm"); + fBlastWave->FixParameter(0, mass); + fBlastWave->SetParLimits(1, 0.1, 0.9); // don't touch :) adding some 99 youu get floating point exception + fBlastWave->SetParLimits(2, 0.03,1.);//0.05, 1.); // no negative values!! for the following as well + fBlastWave->SetParLimits(3, 0.25,4.5); // was 2.5 // omega-->at limit even moving it to 4.5 but yield same + return fBlastWave; + } + + Double_t y2eta(Double_t pt, Double_t mass, Double_t y){ + Double_t mt = TMath::Sqrt(mass * mass + pt * pt); + return TMath::ASinH(mt / pt * TMath::SinH(y)); + } + + /// set mass + void setMass(int input_m){m=input_m;} + + /// set 4-momentum + void set4momentum(double input_px, double input_py, double input_pz){ + px = input_px; + py = input_py; + pz = input_pz; + E = sqrt( m*m+px*px+py*py+pz*pz ); + fourMomentum.px(px); + fourMomentum.py(py); + fourMomentum.pz(pz); + fourMomentum.e(E); + p = sqrt( px*px+py*py+pz*pz ); + y = 0.5*log( (E+pz)/(E-pz) ); + eta = 0.5*log( (p+pz)/(p-pz) ); + + ////std::cout << "##### Particle #####" << std::endl; + ////std::cout << " - PDG code: " << pdg << std::endl; + ////std::cout << " - mass: " << m << std::endl; + ////std::cout << " - (px,py,pz): (" << px << "," << py << "," << pz << ")" << std::endl; + ////std::cout << " - momentum: " << p << std::endl; + ////std::cout << " - energy: " << E << std::endl; + ////std::cout << " - rapidity: " << y << std::endl; + ////std::cout << " - pseudorapidity: " << eta << std::endl; + ////std::cout << " - production vertex: (" << xProd << "," << yProd << "," << zProd << ")" << std::endl; + } + + /// set 3-momentum + void setMomentum(double input_p){p=input_p;} + + /// set x,y,z of production vertex + void setProdVtx(double input_xProd, double input_yProd, double input_zProd){xProd=input_xProd; yProd=input_xProd; zProd=input_zProd;} + + /// setter to add further primary particles to the event + void setAddFurtherPrimaries(const int pdgCode, const int howMany){ + /// check if this species has been already added + const int map_counts = furtherPrim.count(pdgCode); + if(map_counts==1){ // species already present + const int howMany_already = furtherPrim[pdgCode]; + std::cout << "BEWARE: " << howMany_already << " particles of species " << pdgCode << " already required."; + std::cout << " Ignoring the command setAddFurtherPrimaries(" << pdgCode << "," << howMany << ")" << std::endl; + return; + } + /// add particles, if not yet present + furtherPrim[pdgCode] = howMany; + keys_furtherPrim.insert(pdgCode); + } + + /// set add a further primary pion + //void setAddFurtherPion(){addFurtherPion=true;} + + /// get mass from TParticlePDG + double getMass(int input_pdg){ + double mass = 0; + if(TDatabasePDG::Instance()){ + TParticlePDG* particle = TDatabasePDG::Instance()->GetParticle(input_pdg); + if(particle) mass = particle->Mass(); + else std::cout << "===> particle mass equal to 0" << std::endl; + } + return mass; + } + + //_________________________________________________________________________________ + /// generate uniform eta and uniform momentum + void genUniformMomentumEta(double minP, double maxP, double minY, double maxY){ + // random generator + std::unique_ptr ranGenerator { new TRandom3() }; + ranGenerator->SetSeed(0); + + // momentum + const double gen_p = ranGenerator->Uniform(minP,maxP); + // eta + const double gen_eta = ranGenerator->Uniform(minY,maxY); + // z-component momentum from eta + const double cosTheta = ( exp(2*gen_eta)-1 ) / ( exp(2*gen_eta)+1 ); // starting from eta = -ln(tan(theta/2)) = 1/2*ln( (1+cos(theta))/(1-cos(theta)) ) ---> NB: valid for cos(theta)!=1 + const double gen_pz = gen_p*cosTheta; + // y-component: random uniform + const double maxVal = sqrt( gen_p*gen_p-gen_pz*gen_pz ); + double sign_py = ranGenerator->Uniform(0,1); + sign_py = (sign_py>0.5)?1.:-1.; + const double gen_py = ranGenerator->Uniform(0.,maxVal)*sign_py; + // x-component momentum + double sign_px = ranGenerator->Uniform(0,1); + sign_px = (sign_px>0.5)?1.:-1.; + const double gen_px = sqrt( gen_p*gen_p-gen_pz*gen_pz-gen_py*gen_py )*sign_px; + + set4momentum(gen_px,gen_py,gen_pz); + } + + //_________________________________________________________________________________ + /// generate uniform eta and uniform momentum + void genSpectraMomentumEta(double minP, double maxP, double minY, double maxY){ + // random generator + std::unique_ptr ranGenerator { new TRandom3() }; + ranGenerator->SetSeed(0); + + // generate transverse momentum + const double gen_pT = fSpectra->GetRandom(minP,maxP); + + //Actually could be something else without loss of generality but okay + const double gen_phi = ranGenerator->Uniform(0,2*TMath::Pi()); + + // sample flat in rapidity, calculate eta + Double_t gen_Y=10, gen_eta=10; + + while( gen_eta>genmaxEta || gen_etaUniform(minY,maxY); + //(Double_t pt, Double_t mass, Double_t y) + gen_eta = y2eta(gen_pT, m, gen_Y); + } + + fLVHelper->SetPtEtaPhiM(gen_pT, gen_eta, gen_phi, m); + set4momentum(fLVHelper->Px(),fLVHelper->Py(),fLVHelper->Pz()); + } + + //_________________________________________________________________________________ + /// generate uniform eta and uniform momentum + void genSpectraMomentumEtaXi(double minP, double maxP, double minY, double maxY){ + // random generator + std::unique_ptr ranGenerator { new TRandom3() }; + ranGenerator->SetSeed(0); + + // generate transverse momentum + const double gen_pT = fSpectraXi->GetRandom(minP,maxP); + + //Actually could be something else without loss of generality but okay + const double gen_phi = ranGenerator->Uniform(0,2*TMath::Pi()); + + // sample flat in rapidity, calculate eta + Double_t gen_Y=10, gen_eta=10; + + while( gen_eta>genmaxEta || gen_etaUniform(minY,maxY); + //(Double_t pt, Double_t mass, Double_t y) + gen_eta = y2eta(gen_pT, m, gen_Y); + } + + fLVHelper->SetPtEtaPhiM(gen_pT, gen_eta, gen_phi, m); + set4momentum(fLVHelper->Px(),fLVHelper->Py(),fLVHelper->Pz()); + } + + //_________________________________________________________________________________ + /// generate uniform eta and uniform momentum + void genSpectraMomentumEtaOm(double minP, double maxP, double minY, double maxY){ + // random generator + std::unique_ptr ranGenerator { new TRandom3() }; + ranGenerator->SetSeed(0); + + // generate transverse momentum + const double gen_pT = fSpectraOm->GetRandom(minP,maxP); + + //Actually could be something else without loss of generality but okay + const double gen_phi = ranGenerator->Uniform(0,2*TMath::Pi()); + + // sample flat in rapidity, calculate eta + Double_t gen_Y=10, gen_eta=10; + + while( gen_eta>genmaxEta || gen_etaUniform(minY,maxY); + //(Double_t pt, Double_t mass, Double_t y) + gen_eta = y2eta(gen_pT, m, gen_Y); + } + + fLVHelper->SetPtEtaPhiM(gen_pT, gen_eta, gen_phi, m); + set4momentum(fLVHelper->Px(),fLVHelper->Py(),fLVHelper->Pz()); + } + +protected: + + //__________________________________________________________________ + Pythia8::Particle createParticle(){ + //std::cout << "createParticle() mass " << m << " pdgCode " << pdg << std::endl; + Pythia8::Particle myparticle; + myparticle.id(pdg); + myparticle.status(11); + myparticle.px(px); + myparticle.py(py); + myparticle.pz(pz); + myparticle.e(E); + myparticle.m(m); + myparticle.xProd(xProd); + myparticle.yProd(yProd); + myparticle.zProd(zProd); + + return myparticle; + } + + //__________________________________________________________________ + int randomizeSign(){ + + std::unique_ptr gen_random {new TRandom3(0)}; + const float n = gen_random->Uniform(-1,1); + + return n/abs(n); + } + + //__________________________________________________________________ + Bool_t generateEvent() override { + + double original_m = m; + int original_pdg = pdg; + + /// reset event + mPythia.event.reset(); + + if(original_pdg!=211){ + for(Int_t ii=0; ii<15; ii++){ + xProd=0.0; + yProd=0.0; + zProd=0.0; + genSpectraMomentumEta(genMinPt,genMaxPt,genminY,genmaxY); + Pythia8::Particle lAddedParticle = createParticle(); + mPythia.event.append(lAddedParticle); + lAddedParticles++; + } + } + + //+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + Bool_t lPythiaOK = kFALSE; + while (!lPythiaOK){ + lPythiaOK = pythiaObject.next(); + //Select rough central events, please, disregard + //if( pythiaObject.info.hiInfo->b() > 6) lPythiaOK = kFALSE; //regenerate, please + } + //+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // use event + Long_t nParticles = pythiaObject.event.size(); + Long_t nChargedParticlesAtMidRap = 0; + Long_t nPionsAtMidRap = 0; + lAddedParticles = 0; + for ( Long_t j=0; j < nParticles; j++ ) { + Int_t pypid = pythiaObject.event[j].id(); + Float_t pypx = pythiaObject.event[j].px(); + Float_t pypy = pythiaObject.event[j].py(); + Float_t pypz = pythiaObject.event[j].pz(); + Float_t pypt = pythiaObject.event[j].pT(); + Float_t pyrap = pythiaObject.event[j].y(); + Float_t pyeta = pythiaObject.event[j].eta(); + Int_t pystate = pythiaObject.event[j].status(); + //if(TMath::Abs(state > 89)) {continue;} + Float_t pyenergy = pythiaObject.event[j].e(); + Int_t pycharge = pythiaObject.event[j].charge(); + + //Per-species loop: skip outside of mid-rapidity, please + if ( TMath::Abs(pyeta) > 4.0 ) continue; //only within ALICE 3 acceptance + + //final only + if (!pythiaObject.event[j].isFinal()) continue; + + if ( TMath::Abs(pyeta) < 0.5 ){ + if ( TMath::Abs(pythiaObject.event[j].charge())>1e-5 ) nChargedParticlesAtMidRap++; + if ( TMath::Abs(pypid)==211 ) nPionsAtMidRap++; + } + + pdg = pypid; + px = pypx; + py = pypy; + pz = pypz; + E = pyenergy; + m = pythiaObject.event[j].m(); + xProd = pythiaObject.event[j].xProd(); + yProd = pythiaObject.event[j].yProd(); + zProd = pythiaObject.event[j].zProd(); + + Pythia8::Particle lAddedParticle = createParticle(); + mPythia.event.append(lAddedParticle); + lAddedParticles++; + } + //+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + //+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // XI ABUNDANCE FIX +// FCN=0.35879 FROM MINOS STATUS=SUCCESSFUL 126 CALLS 634 TOTAL +// EDM=3.7456e-09 STRATEGY= 1 ERROR MATRIX ACCURATE +// EXT PARAMETER STEP FIRST +// NO. NAME VALUE ERROR SIZE DERIVATIVE +// 1 p0 4.74929e-03 3.29248e-04 -3.35914e-06 5.38225e+00 +// 2 p1 -4.08255e-03 8.62587e-04 -2.02577e-05 2.45132e+00 +// 3 p2 4.76660e+00 1.93593e+00 1.93593e+00 2.70369e-04 +// Info in : created default TCanvas with name c1 + //Adjust relative abundance of multi-strange particles by injecting some + Double_t lExpectedXiToPion = TMath::Max(4.74929e-03 - 4.08255e-03*TMath::Exp(-nChargedParticlesAtMidRap/4.76660e+00) - 0.00211334,0.); + Double_t lExpectedXi = nPionsAtMidRap*lExpectedXiToPion; + Int_t lXiYield = gRandom->Poisson(3*lExpectedXi); //factor 3: fix the rapidity acceptance + m = 1.32171; + pdg = 3312; + cout<<"Adding extra xi: "<Uniform()>0.5?+1:-1; + xProd=0.0; + yProd=0.0; + zProd=0.0; + genSpectraMomentumEtaXi(genMinPt,genMaxPt,genminY,genmaxY); + Pythia8::Particle lAddedParticle = createParticle(); + mPythia.event.append(lAddedParticle); + lAddedParticles++; + } + //+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + //+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // OMEGA ABUNDANCE FIX + //Adjust relative abundance of multi-strange particles by injecting some + Double_t lExpectedOmegaToPion = TMath::Max(8.55057e-04 - 7.38732e-04*TMath::Exp(-nChargedParticlesAtMidRap/2.40545e+01) - 6.56785e-05,0.); + Double_t lExpectedOmega = nPionsAtMidRap*lExpectedOmegaToPion; + Int_t lOmegaYield = gRandom->Poisson(3*lExpectedOmega); //factor 3: fix the rapidity acceptance + m = 1.67245; + pdg = 3334; + cout<<"Adding extra omegas: "<Uniform()>0.5?+1:-1; + xProd=0.0; + yProd=0.0; + zProd=0.0; + genSpectraMomentumEtaOm(genMinPt,genMaxPt,genminY,genmaxY); + Pythia8::Particle lAddedParticle = createParticle(); + mPythia.event.append(lAddedParticle); + lAddedParticles++; + } + //+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + //Revert back or else there'll be trouble next time! + m = original_m; + pdg = original_pdg; + + /// go to next Pythia event + mPythia.next(); + + return true; + } + +private: + + double genMinPt; /// minimum 3-momentum for generated particles + double genMaxPt; /// maximum 3-momentum for generated particles + double genminY; /// minimum pseudorapidity for generated particles + double genmaxY; /// maximum pseudorapidity for generated particles + double genminEta; + double genmaxEta; + int UEOverSampling; //number of times to repeat underlying event + int genEventCountUse; + + Pythia8::Vec4 fourMomentum; /// four-momentum (px,py,pz,E) + double E; /// energy: sqrt( m*m+px*px+py*py+pz*pz ) [GeV/c] + double m; /// particle mass [GeV/c^2] + int pdg; /// particle pdg code + double px; /// x-component momentum [GeV/c] + double py; /// y-component momentum [GeV/c] + double pz; /// z-component momentum [GeV/c] + double p; /// momentum + double y; /// rapidity + double eta; /// pseudorapidity + double xProd; /// x-coordinate position production vertex [cm] + double yProd; /// y-coordinate position production vertex [cm] + double zProd; /// z-coordinate position production vertex [cm] + + //Max number: max number of particles to be added + long lAddedParticles; + float ue_E[5000]; + float ue_m[5000]; + float ue_px[5000]; + float ue_py[5000]; + float ue_pz[5000]; + float ue_xProd[5000]; + float ue_yProd[5000]; + float ue_zProd[5000]; + int ue_pdg[5000]; + + bool randomizePDGsign; /// bool to randomize the PDG code of the core particle + + TF1 *fSpectra; /// TF1 to store more realistic shape of spectrum + TF1 *fSpectraXi; /// TF1 to store more realistic shape of spectrum + TF1 *fSpectraOm; /// TF1 to store more realistic shape of spectrum + + //BW integrand + TF1 *fIntegrand = NULL; + TF1 *fIntegrandXi = NULL; + TF1 *fIntegrandOm = NULL; + + TLorentzVector *fLVHelper; + + Pythia8::Pythia pythiaObject; ///Generate a full event if requested to do so + + //bool addFurtherPion; /// bool to attach an additional primary pion + std::map furtherPrim; /// key: PDG code; value: how many further primaries of this species to be added + std::unordered_set keys_furtherPrim; /// keys of the above map (NB: only unique elements allowed!) +}; + +FairGenerator* generateNativeOmegaCCC(){ + return reinterpret_cast(new GeneratorPythia8Gun(4444)); +} + +FairGenerator* generateNativeOmegaCC(){ + return reinterpret_cast(new GeneratorPythia8Gun(4432)); +} + +FairGenerator* generateNativeOmegaC(){ + return reinterpret_cast(new GeneratorPythia8Gun(4332)); +} + +FairGenerator* generateNativeOmega(){ + return reinterpret_cast(new GeneratorPythia8Gun(3334)); +} + +FairGenerator* generatePYTHIA(){ + return reinterpret_cast(new GeneratorPythia8Gun(211)); +} diff --git a/MC/config/ALICE3/pythia8/generator_pythia8_gun_PbPb.C b/MC/config/ALICE3/pythia8/generator_pythia8_gun_PbPb.C new file mode 100755 index 000000000..4ebf92b9c --- /dev/null +++ b/MC/config/ALICE3/pythia8/generator_pythia8_gun_PbPb.C @@ -0,0 +1,470 @@ +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include "Pythia8/Pythia.h" +#include "FairGenerator.h" +#include "FairPrimaryGenerator.h" +#include "Generators/GeneratorPythia8.h" +#include "TRandom3.h" +#include "TParticlePDG.h" +#include "TDatabasePDG.h" + +#include +#include +//#include // for std::pair + +using namespace Pythia8; +#endif + +class GeneratorPythia8GunPbPb : public o2::eventgen::GeneratorPythia8{ +public: + /// default constructor + GeneratorPythia8GunPbPb() = default; + + /// constructor + GeneratorPythia8GunPbPb(int input_pdg){ + genMinPt=0.0; + genMaxPt=20.0; + genminY=-1.5; + genmaxY=1.5; + genminEta=-1.5; + genmaxEta=1.5; + + UEOverSampling = 20; + genEventCountUse = 2000; //start at large number: regen + + pdg = input_pdg; + E=0; + px=0; + py=0; + pz=0; + p=0; + y=0; + eta=0; + xProd=0; + yProd=0; + zProd=0; + xProd=0.; yProd=0.; zProd=0.; + //addFurtherPion=false; + + randomizePDGsign=false; + + fLVHelper = new TLorentzVector(); + + if( input_pdg!=0 ) m = getMass(input_pdg); + if( input_pdg==0 ) m = 1.0; + //fSpectra = new TF1("fPtDist",myLevyPt,0.0,10,3); + fSpectra = new TF1("fSpectra",this ,&GeneratorPythia8GunPbPb::myLevyPt, 0.0,20,4, "GeneratorPythia8GunPbPb","myLevyPt"); + fSpectra ->SetNpx( 1000 ); + + fSpectra->SetParameter(3, m); +// FCN=15.8379 FROM MINOS STATUS=SUCCESSFUL 178 CALLS 585 TOTAL +// EDM=9.01697e-09 STRATEGY= 1 ERROR MATRIX ACCURATE +// EXT PARAMETER STEP FIRST +// NO. NAME VALUE ERROR SIZE DERIVATIVE +// 1 p0 1.26147e+02 8.41042e+00 -8.50770e-02 -1.07709e-04 +// 2 p1 1.09727e+00 1.90660e-01 4.06859e-03 -1.92045e-02 +// 3 p2 7.63330e+00 4.45207e+00 4.45207e+00 6.13882e-05 + + //Default: OmegaCCC parameters, cause we're bold + fSpectra->SetParameter(0, 1.26147e+02); + fSpectra->SetParameter(1, 1.09727e+00); + fSpectra->SetParameter(2, 7.63330e+00); + + if(input_pdg == 4432){ +// FCN=42.0607 FROM MINOS STATUS=SUCCESSFUL 46 CALLS 320 TOTAL +// EDM=7.15202e-08 STRATEGY= 1 ERROR MATRIX ACCURATE +// EXT PARAMETER STEP FIRST +// NO. NAME VALUE ERROR SIZE DERIVATIVE +// 1 p0 1.33563e+04 8.17396e+01 -2.03998e-02 -4.89388e-06 +// 2 p1 1.03858e+00 1.31862e-02 7.73307e-04 -8.47151e-02 +// 3 p2 1.21413e+01 6.20997e-01 6.20997e-01 -9.73367e-04 + fSpectra->SetParameter(0, 1.33563e+04); + fSpectra->SetParameter(1, 1.03858e+00); + fSpectra->SetParameter(2, 1.21413e+01); + } + if(input_pdg == 4422){ +// FCN=171.16 FROM MINOS STATUS=SUCCESSFUL 40 CALLS 273 TOTAL +// EDM=1.03965e-07 STRATEGY= 1 ERROR MATRIX ACCURATE +// EXT PARAMETER STEP FIRST +// NO. NAME VALUE ERROR SIZE DERIVATIVE +// 1 p0 7.13200e+04 1.88918e+02 -3.46221e-02 -1.08268e-07 +// 2 p1 1.02063e+00 5.68532e-03 1.16706e-04 -8.96155e-03 +// 3 p2 1.04043e+01 1.95790e-01 1.95790e-01 4.39759e-04 + fSpectra->SetParameter(0, 7.13200e+04); + fSpectra->SetParameter(1, 1.02063e+00); + fSpectra->SetParameter(2, 1.04043e+01); + } + + if(input_pdg == 4232){ +// FCN=2950.22 FROM MINOS STATUS=SUCCESSFUL 40 CALLS 290 TOTAL +// EDM=1.43216e-07 STRATEGY= 1 ERROR MATRIX ACCURATE +// EXT PARAMETER STEP FIRST +// NO. NAME VALUE ERROR SIZE DERIVATIVE +// 1 p0 6.95382e+04 8.34455e+01 -6.76782e-03 -7.12877e-09 +// 2 p1 5.21608e-01 1.34221e-03 6.80074e-06 -8.00314e-04 +// 3 p2 6.54815e+00 3.09274e-02 3.09274e-02 4.20673e-02 + fSpectra->SetParameter(0, 6.95382e+04); + fSpectra->SetParameter(1, 5.21608e-01); + fSpectra->SetParameter(2, 6.54815e+00); + } + + furtherPrim={}; + keys_furtherPrim={}; + + cout << "Initalizing extra PYTHIA object " << endl; + + // Read settings from external file. + std::string O2DPG_ROOT(getenv("O2DPG_MC_CONFIG_ROOT")); + std::string infile = O2DPG_ROOT + "/MC/config/ALICE3/pythia8/generator/pythia8_hi.cmnd"; + pythiaObject.readFile(infile); + + // Set seed to job id + char* alien_proc_id = getenv("ALIEN_PROC_ID"); + uint64_t seedFull; + uint64_t seed = 0; + if (alien_proc_id != NULL) { + seedFull = static_cast(atol(alien_proc_id)); + for(int ii=0; ii<29; ii++) // there might be a cleaner way but this will work + seed |= ((seedFull) & (static_cast(1) << static_cast(ii))); + LOG(info) << "Value of ALIEN_PROC_ID: " << seedFull <<" truncated to 0-28 bits: "<GetParticle(input_pdg); + if(particle) mass = particle->Mass(); + else std::cout << "===> particle mass equal to 0" << std::endl; + } + //Override database if needed, please + if( input_pdg==4444 ) mass = 4.797; //OmegaCCC + if( input_pdg==4432 ) mass = 3.746; //OmegaCC + if( input_pdg==4422) mass = 3.621; //XiCC + if( input_pdg==4232) mass = 2.46793; //XiC+ + return mass; + } + + //_________________________________________________________________________________ + /// generate uniform eta and uniform momentum + void genUniformMomentumEta(double minP, double maxP, double minY, double maxY){ + // Warning: this generator samples randomly in p and not in pT. Care is advised + + // random generator + std::unique_ptr ranGenerator { new TRandom3() }; + ranGenerator->SetSeed(0); + + // momentum + const double gen_p = ranGenerator->Uniform(minP,maxP); + // eta + const double gen_eta = ranGenerator->Uniform(minY,maxY); + // z-component momentum from eta + const double cosTheta = ( exp(2*gen_eta)-1 ) / ( exp(2*gen_eta)+1 ); // starting from eta = -ln(tan(theta/2)) = 1/2*ln( (1+cos(theta))/(1-cos(theta)) ) ---> NB: valid for cos(theta)!=1 + const double gen_pz = gen_p*cosTheta; + // phi: random uniform, X, Y conform + const double pT = sqrt(gen_p * gen_p - gen_pz * gen_pz); + double phi = ranGenerator->Uniform(0., 2.0f*TMath::Pi()); + const double gen_px = pT*TMath::Cos(phi); + const double gen_py = pT*TMath::Sin(phi); + + set4momentum(gen_px,gen_py,gen_pz); + } + + //_________________________________________________________________________________ + /// generate uniform eta and uniform momentum + void genSpectraMomentumEta(double minP, double maxP, double minY, double maxY){ + // random generator + std::unique_ptr ranGenerator { new TRandom3() }; + ranGenerator->SetSeed(0); + + // generate transverse momentum + const double gen_pT = fSpectra->GetRandom(minP,maxP); + + //Actually could be something else without loss of generality but okay + const double gen_phi = ranGenerator->Uniform(0,2*TMath::Pi()); + + // sample flat in rapidity, calculate eta + Double_t gen_Y=10, gen_eta=10; + + while( gen_eta>genmaxEta || gen_etaUniform(minY,maxY); + //(Double_t pt, Double_t mass, Double_t y) + gen_eta = y2eta(gen_pT, m, gen_Y); + } + + fLVHelper->SetPtEtaPhiM(gen_pT, gen_eta, gen_phi, m); + set4momentum(fLVHelper->Px(),fLVHelper->Py(),fLVHelper->Pz()); + } + +protected: + + //__________________________________________________________________ + Pythia8::Particle createParticle(){ + //std::cout << "createParticle() mass " << m << " pdgCode " << pdg << std::endl; + Pythia8::Particle myparticle; + myparticle.id(pdg); + myparticle.status(11); + myparticle.px(px); + myparticle.py(py); + myparticle.pz(pz); + myparticle.e(E); + myparticle.m(m); + myparticle.xProd(xProd); + myparticle.yProd(yProd); + myparticle.zProd(zProd); + + return myparticle; + } + + //__________________________________________________________________ + int randomizeSign(){ + + std::unique_ptr gen_random {new TRandom3(0)}; + const float n = gen_random->Uniform(-1,1); + + return n/abs(n); + } + + //__________________________________________________________________ + Bool_t generateEvent() override { + + double original_m = m; + int original_pdg = pdg; + + /// reset event + mPythia.event.reset(); + + if(original_pdg!=211){ + for(Int_t ii=0; ii<15; ii++){ + xProd=0.0; + yProd=0.0; + zProd=0.0; + genSpectraMomentumEta(genMinPt,genMaxPt,genminY,genmaxY); + Pythia8::Particle lAddedParticle = createParticle(); + mPythia.event.append(lAddedParticle); + lAddedParticles++; + } + } + + //+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + Bool_t lPythiaOK = kFALSE; + while (!lPythiaOK){ + lPythiaOK = pythiaObject.next(); + //Select rough central events, please, disregard + //if( pythiaObject.info.hiInfo->b() > 6) lPythiaOK = kFALSE; //regenerate, please + } + //+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // use event + Long_t nParticles = pythiaObject.event.size(); + lAddedParticles = 0; + for ( Long_t j=0; j < nParticles; j++ ) { + Int_t pypid = pythiaObject.event[j].id(); + Float_t pypx = pythiaObject.event[j].px(); + Float_t pypy = pythiaObject.event[j].py(); + Float_t pypz = pythiaObject.event[j].pz(); + Float_t pypt = pythiaObject.event[j].pT(); + Float_t pyrap = pythiaObject.event[j].y(); + Float_t pyeta = pythiaObject.event[j].eta(); + Int_t pystate = pythiaObject.event[j].status(); + //if(TMath::Abs(state > 89)) {continue;} + Float_t pyenergy = pythiaObject.event[j].e(); + Int_t pycharge = pythiaObject.event[j].charge(); + + //Per-species loop: skip outside of mid-rapidity, please + if ( TMath::Abs(pyeta) > 2.0 ) continue; + + //final only + if (!pythiaObject.event[j].isFinal()) continue; + + pdg = pypid; + px = pypx; + py = pypy; + pz = pypz; + E = pyenergy; + m = pythiaObject.event[j].m(); + xProd = pythiaObject.event[j].xProd(); + yProd = pythiaObject.event[j].yProd(); + zProd = pythiaObject.event[j].zProd(); + + Pythia8::Particle lAddedParticle = createParticle(); + mPythia.event.append(lAddedParticle); + lAddedParticles++; + } + //+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + //Revert back or else there'll be trouble next time! + m = original_m; + pdg = original_pdg; + + /// go to next Pythia event + mPythia.next(); + + return true; + } + +private: + + double genMinPt; /// minimum 3-momentum for generated particles + double genMaxPt; /// maximum 3-momentum for generated particles + double genminY; /// minimum pseudorapidity for generated particles + double genmaxY; /// maximum pseudorapidity for generated particles + double genminEta; + double genmaxEta; + int UEOverSampling; //number of times to repeat underlying event + int genEventCountUse; + + Pythia8::Vec4 fourMomentum; /// four-momentum (px,py,pz,E) + double E; /// energy: sqrt( m*m+px*px+py*py+pz*pz ) [GeV/c] + double m; /// particle mass [GeV/c^2] + int pdg; /// particle pdg code + double px; /// x-component momentum [GeV/c] + double py; /// y-component momentum [GeV/c] + double pz; /// z-component momentum [GeV/c] + double p; /// momentum + double y; /// rapidity + double eta; /// pseudorapidity + double xProd; /// x-coordinate position production vertex [cm] + double yProd; /// y-coordinate position production vertex [cm] + double zProd; /// z-coordinate position production vertex [cm] + + //Max number: max number of particles to be added + long lAddedParticles; + + bool randomizePDGsign; /// bool to randomize the PDG code of the core particle + + TF1 *fSpectra; /// TF1 to store more realistic shape of spectrum + + TLorentzVector *fLVHelper; + + Pythia8::Pythia pythiaObject; ///Generate a full event if requested to do so + + //bool addFurtherPion; /// bool to attach an additional primary pion + std::map furtherPrim; /// key: PDG code; value: how many further primaries of this species to be added + std::unordered_set keys_furtherPrim; /// keys of the above map (NB: only unique elements allowed!) +}; + +//The Omega Family +FairGenerator* generateNativeOmegaCCC(){ + return reinterpret_cast(new GeneratorPythia8GunPbPb(4444)); +} + +FairGenerator* generateNativeOmegaCC(){ + return reinterpret_cast(new GeneratorPythia8GunPbPb(4432)); +} + +FairGenerator* generateNativeOmegaC(){ + return reinterpret_cast(new GeneratorPythia8GunPbPb(4332)); +} + +FairGenerator* generateNativeOmega(){ + return reinterpret_cast(new GeneratorPythia8GunPbPb(3334)); +} + +//The Xi Family +FairGenerator* generateNativeXiCC(){ + return reinterpret_cast(new GeneratorPythia8GunPbPb(4422)); +} + +FairGenerator* generateNativeXiC(){ + return reinterpret_cast(new GeneratorPythia8GunPbPb(4232)); +} + +FairGenerator* generateNativeXi(){ + return reinterpret_cast(new GeneratorPythia8GunPbPb(3312)); +} + +//Use this for minimum-bias +FairGenerator* generatePYTHIA(){ + return reinterpret_cast(new GeneratorPythia8GunPbPb(211)); +} \ No newline at end of file diff --git a/MC/config/ALICE3/pythia8/generator_pythia8_gun_pp.C b/MC/config/ALICE3/pythia8/generator_pythia8_gun_pp.C new file mode 100755 index 000000000..f5292d32d --- /dev/null +++ b/MC/config/ALICE3/pythia8/generator_pythia8_gun_pp.C @@ -0,0 +1,470 @@ +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include "Pythia8/Pythia.h" +#include "FairGenerator.h" +#include "FairPrimaryGenerator.h" +#include "Generators/GeneratorPythia8.h" +#include "TRandom3.h" +#include "TParticlePDG.h" +#include "TDatabasePDG.h" + +#include +#include +#include +//#include // for std::pair + +using namespace Pythia8; +#endif + +class GeneratorPythia8Gunpp : public o2::eventgen::GeneratorPythia8{ +public: + /// default constructor + GeneratorPythia8Gunpp() = default; + + /// constructor + GeneratorPythia8Gunpp(int input_pdg){ + genMinPt=0.0; + genMaxPt=20.0; + genminY=-1.5; + genmaxY=1.5; + genminEta=-1.5; + genmaxEta=1.5; + + UEOverSampling = 20; + genEventCountUse = 2000; //start at large number: regen + + pdg = input_pdg; + E=0; + px=0; + py=0; + pz=0; + p=0; + y=0; + eta=0; + xProd=0; + yProd=0; + zProd=0; + xProd=0.; yProd=0.; zProd=0.; + //addFurtherPion=false; + + randomizePDGsign=false; + + fLVHelper = new TLorentzVector(); + + if( input_pdg!=0 ) m = getMass(input_pdg); + if( input_pdg==0 ) m = 1.0; + + //fSpectra = new TF1("fPtDist",myLevyPt,0.0,10,3); + fSpectra = new TF1("fSpectra",this ,&GeneratorPythia8Gunpp::myLevyPt, 0.0,20,4, "GeneratorPythia8Gunpp","myLevyPt"); + fSpectra ->SetNpx( 1000 ); + + fSpectra->SetParameter(3, m); +// FCN=15.8379 FROM MINOS STATUS=SUCCESSFUL 178 CALLS 585 TOTAL +// EDM=9.01697e-09 STRATEGY= 1 ERROR MATRIX ACCURATE +// EXT PARAMETER STEP FIRST +// NO. NAME VALUE ERROR SIZE DERIVATIVE +// 1 p0 1.26147e+02 8.41042e+00 -8.50770e-02 -1.07709e-04 +// 2 p1 1.09727e+00 1.90660e-01 4.06859e-03 -1.92045e-02 +// 3 p2 7.63330e+00 4.45207e+00 4.45207e+00 6.13882e-05 + + //Default: OmegaCCC parameters, cause we're bold + fSpectra->SetParameter(0, 1.26147e+02); + fSpectra->SetParameter(1, 1.09727e+00); + fSpectra->SetParameter(2, 7.63330e+00); + + if(input_pdg == 4432){ +// FCN=42.0607 FROM MINOS STATUS=SUCCESSFUL 46 CALLS 320 TOTAL +// EDM=7.15202e-08 STRATEGY= 1 ERROR MATRIX ACCURATE +// EXT PARAMETER STEP FIRST +// NO. NAME VALUE ERROR SIZE DERIVATIVE +// 1 p0 1.33563e+04 8.17396e+01 -2.03998e-02 -4.89388e-06 +// 2 p1 1.03858e+00 1.31862e-02 7.73307e-04 -8.47151e-02 +// 3 p2 1.21413e+01 6.20997e-01 6.20997e-01 -9.73367e-04 + fSpectra->SetParameter(0, 1.33563e+04); + fSpectra->SetParameter(1, 1.03858e+00); + fSpectra->SetParameter(2, 1.21413e+01); + } + if(input_pdg == 4422){ +// FCN=171.16 FROM MINOS STATUS=SUCCESSFUL 40 CALLS 273 TOTAL +// EDM=1.03965e-07 STRATEGY= 1 ERROR MATRIX ACCURATE +// EXT PARAMETER STEP FIRST +// NO. NAME VALUE ERROR SIZE DERIVATIVE +// 1 p0 7.13200e+04 1.88918e+02 -3.46221e-02 -1.08268e-07 +// 2 p1 1.02063e+00 5.68532e-03 1.16706e-04 -8.96155e-03 +// 3 p2 1.04043e+01 1.95790e-01 1.95790e-01 4.39759e-04 + fSpectra->SetParameter(0, 7.13200e+04); + fSpectra->SetParameter(1, 1.02063e+00); + fSpectra->SetParameter(2, 1.04043e+01); + } + + if(input_pdg == 4232){ +// FCN=2950.22 FROM MINOS STATUS=SUCCESSFUL 40 CALLS 290 TOTAL +// EDM=1.43216e-07 STRATEGY= 1 ERROR MATRIX ACCURATE +// EXT PARAMETER STEP FIRST +// NO. NAME VALUE ERROR SIZE DERIVATIVE +// 1 p0 6.95382e+04 8.34455e+01 -6.76782e-03 -7.12877e-09 +// 2 p1 5.21608e-01 1.34221e-03 6.80074e-06 -8.00314e-04 +// 3 p2 6.54815e+00 3.09274e-02 3.09274e-02 4.20673e-02 + fSpectra->SetParameter(0, 6.95382e+04); + fSpectra->SetParameter(1, 5.21608e-01); + fSpectra->SetParameter(2, 6.54815e+00); + } + + furtherPrim={}; + keys_furtherPrim={}; + + cout << "Initalizing extra PYTHIA object " << endl; + + // Read settings from external file. + std::string O2DPG_ROOT(getenv("O2DPG_MC_CONFIG_ROOT")); + std::string infile = O2DPG_ROOT + "/MC/config/ALICE3/pythia8/generator/pythia8_pp.cmnd"; + pythiaObject.readFile(infile); + + // Set seed to job id + char* alien_proc_id = getenv("ALIEN_PROC_ID"); + uint64_t seedFull; + uint64_t seed = 0; + if (alien_proc_id != NULL) { + seedFull = static_cast(atol(alien_proc_id)); + for(int ii=0; ii<29; ii++) // there might be a cleaner way but this will work + seed |= ((seedFull) & (static_cast(1) << static_cast(ii))); + LOG(info) << "Value of ALIEN_PROC_ID: " << seedFull <<" truncated to 0-28 bits: "<GetParticle(input_pdg); + if(particle) mass = particle->Mass(); + else std::cout << "===> particle mass equal to 0" << std::endl; + } + //Override database if needed, please + if( input_pdg==4444 ) mass = 4.797; //OmegaCCC + if( input_pdg==4432 ) mass = 3.746; //OmegaCC + if( input_pdg==4422) mass = 3.621; //XiCC + if( input_pdg==4232) mass = 2.46793; //XiC+ + return mass; + } + + //_________________________________________________________________________________ + /// generate uniform eta and uniform momentum + void genUniformMomentumEta(double minP, double maxP, double minY, double maxY){ + // Warning: this generator samples randomly in p and not in pT. Care is advised + + // random generator + std::unique_ptr ranGenerator { new TRandom3() }; + ranGenerator->SetSeed(0); + + // momentum + const double gen_p = ranGenerator->Uniform(minP,maxP); + // eta + const double gen_eta = ranGenerator->Uniform(minY,maxY); + // z-component momentum from eta + const double cosTheta = ( exp(2*gen_eta)-1 ) / ( exp(2*gen_eta)+1 ); // starting from eta = -ln(tan(theta/2)) = 1/2*ln( (1+cos(theta))/(1-cos(theta)) ) ---> NB: valid for cos(theta)!=1 + const double gen_pz = gen_p*cosTheta; + // phi: random uniform, X, Y conform + const double pT = sqrt(gen_p * gen_p - gen_pz * gen_pz); + double phi = ranGenerator->Uniform(0., 2.0f*TMath::Pi()); + const double gen_px = pT*TMath::Cos(phi); + const double gen_py = pT*TMath::Sin(phi); + + set4momentum(gen_px,gen_py,gen_pz); + } + + //_________________________________________________________________________________ + /// generate uniform eta and uniform momentum + void genSpectraMomentumEta(double minP, double maxP, double minY, double maxY){ + // random generator + std::unique_ptr ranGenerator { new TRandom3() }; + ranGenerator->SetSeed(0); + + // generate transverse momentum + const double gen_pT = fSpectra->GetRandom(minP,maxP); + + //Actually could be something else without loss of generality but okay + const double gen_phi = ranGenerator->Uniform(0,2*TMath::Pi()); + + // sample flat in rapidity, calculate eta + Double_t gen_Y=10, gen_eta=10; + + while( gen_eta>genmaxEta || gen_etaUniform(minY,maxY); + //(Double_t pt, Double_t mass, Double_t y) + gen_eta = y2eta(gen_pT, m, gen_Y); + } + + fLVHelper->SetPtEtaPhiM(gen_pT, gen_eta, gen_phi, m); + set4momentum(fLVHelper->Px(),fLVHelper->Py(),fLVHelper->Pz()); + } + +protected: + + //__________________________________________________________________ + Pythia8::Particle createParticle(){ + //std::cout << "createParticle() mass " << m << " pdgCode " << pdg << std::endl; + Pythia8::Particle myparticle; + myparticle.id(pdg); + myparticle.status(11); + myparticle.px(px); + myparticle.py(py); + myparticle.pz(pz); + myparticle.e(E); + myparticle.m(m); + myparticle.xProd(xProd); + myparticle.yProd(yProd); + myparticle.zProd(zProd); + + return myparticle; + } + + //__________________________________________________________________ + int randomizeSign(){ + + std::unique_ptr gen_random {new TRandom3(0)}; + const float n = gen_random->Uniform(-1,1); + + return n/abs(n); + } + + //__________________________________________________________________ + Bool_t generateEvent() override { + + double original_m = m; + int original_pdg = pdg; + + /// reset event + mPythia.event.reset(); + + if(original_pdg!=211){ + xProd=0.0; + yProd=0.0; + zProd=0.0; + genSpectraMomentumEta(genMinPt,genMaxPt,genminY,genmaxY); + Pythia8::Particle lAddedParticle = createParticle(); + mPythia.event.append(lAddedParticle); + lAddedParticles++; + } + + //+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + Bool_t lPythiaOK = kFALSE; + while (!lPythiaOK){ + lPythiaOK = pythiaObject.next(); + //Select rough central events, please, disregard + //if( pythiaObject.info.hiInfo->b() > 6) lPythiaOK = kFALSE; //regenerate, please + } + //+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // use event + Long_t nParticles = pythiaObject.event.size(); + lAddedParticles = 0; + for ( Long_t j=0; j < nParticles; j++ ) { + Int_t pypid = pythiaObject.event[j].id(); + Float_t pypx = pythiaObject.event[j].px(); + Float_t pypy = pythiaObject.event[j].py(); + Float_t pypz = pythiaObject.event[j].pz(); + Float_t pypt = pythiaObject.event[j].pT(); + Float_t pyrap = pythiaObject.event[j].y(); + Float_t pyeta = pythiaObject.event[j].eta(); + Int_t pystate = pythiaObject.event[j].status(); + //if(TMath::Abs(state > 89)) {continue;} + Float_t pyenergy = pythiaObject.event[j].e(); + Int_t pycharge = pythiaObject.event[j].charge(); + + //Per-species loop: skip outside of mid-rapidity, please + if ( TMath::Abs(pyeta) > 2.0 ) continue; + + //final only + if (!pythiaObject.event[j].isFinal()) continue; + + pdg = pypid; + px = pypx; + py = pypy; + pz = pypz; + E = pyenergy; + m = pythiaObject.event[j].m(); + xProd = pythiaObject.event[j].xProd(); + yProd = pythiaObject.event[j].yProd(); + zProd = pythiaObject.event[j].zProd(); + + Pythia8::Particle lAddedParticle = createParticle(); + mPythia.event.append(lAddedParticle); + lAddedParticles++; + } + //+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + //Revert back or else there'll be trouble next time! + m = original_m; + pdg = original_pdg; + + /// go to next Pythia event + mPythia.next(); + + return true; + } + +private: + + double genMinPt; /// minimum 3-momentum for generated particles + double genMaxPt; /// maximum 3-momentum for generated particles + double genminY; /// minimum pseudorapidity for generated particles + double genmaxY; /// maximum pseudorapidity for generated particles + double genminEta; + double genmaxEta; + int UEOverSampling; //number of times to repeat underlying event + int genEventCountUse; + + Pythia8::Vec4 fourMomentum; /// four-momentum (px,py,pz,E) + double E; /// energy: sqrt( m*m+px*px+py*py+pz*pz ) [GeV/c] + double m; /// particle mass [GeV/c^2] + int pdg; /// particle pdg code + double px; /// x-component momentum [GeV/c] + double py; /// y-component momentum [GeV/c] + double pz; /// z-component momentum [GeV/c] + double p; /// momentum + double y; /// rapidity + double eta; /// pseudorapidity + double xProd; /// x-coordinate position production vertex [cm] + double yProd; /// y-coordinate position production vertex [cm] + double zProd; /// z-coordinate position production vertex [cm] + + //Max number: max number of particles to be added + long lAddedParticles; + + bool randomizePDGsign; /// bool to randomize the PDG code of the core particle + + TF1 *fSpectra; /// TF1 to store more realistic shape of spectrum + + TLorentzVector *fLVHelper; + + Pythia8::Pythia pythiaObject; ///Generate a full event if requested to do so + + //bool addFurtherPion; /// bool to attach an additional primary pion + std::map furtherPrim; /// key: PDG code; value: how many further primaries of this species to be added + std::unordered_set keys_furtherPrim; /// keys of the above map (NB: only unique elements allowed!) +}; + +//The Omega Family +FairGenerator* generateNativeOmegaCCC(){ + return reinterpret_cast(new GeneratorPythia8Gunpp(4444)); +} + +FairGenerator* generateNativeOmegaCC(){ + return reinterpret_cast(new GeneratorPythia8Gunpp(4432)); +} + +FairGenerator* generateNativeOmegaC(){ + return reinterpret_cast(new GeneratorPythia8Gunpp(4332)); +} + +FairGenerator* generateNativeOmega(){ + return reinterpret_cast(new GeneratorPythia8Gunpp(3334)); +} + +//The Xi Family +FairGenerator* generateNativeXiCC(){ + return reinterpret_cast(new GeneratorPythia8Gunpp(4422)); +} + +FairGenerator* generateNativeXiC(){ + return reinterpret_cast(new GeneratorPythia8Gunpp(4232)); +} + +FairGenerator* generateNativeXi(){ + return reinterpret_cast(new GeneratorPythia8Gunpp(3312)); +} + +//Use this for minimum-bias +FairGenerator* generatePYTHIA(){ + return reinterpret_cast(new GeneratorPythia8Gunpp(211)); +} diff --git a/MC/config/ALICE3/pythia8/generator_pythia8_strangeness_gun_PbPb.C b/MC/config/ALICE3/pythia8/generator_pythia8_strangeness_gun_PbPb.C new file mode 100755 index 000000000..a810d7ef4 --- /dev/null +++ b/MC/config/ALICE3/pythia8/generator_pythia8_strangeness_gun_PbPb.C @@ -0,0 +1,612 @@ +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include "Pythia8/Pythia.h" +#include "Pythia8/HeavyIons.h" +#include "FairGenerator.h" +#include "FairPrimaryGenerator.h" +#include "Generators/GeneratorPythia8.h" +#include "TRandom3.h" +#include "TParticlePDG.h" +#include "TDatabasePDG.h" + +#include +#include +//#include // for std::pair + +using namespace Pythia8; +#endif +class GeneratorPythia8Gun : public o2::eventgen::GeneratorPythia8{ +public: + /// default constructor + GeneratorPythia8Gun() = default; + + /// constructor + GeneratorPythia8Gun(int input_pdg){ + genMinPt=0.0; + genMaxPt=20.0; + genminY=-1.5; + genmaxY=1.5; + genminEta=-1.5; + genmaxEta=1.5; + + UEOverSampling = 20; + genEventCountUse = 2000; //start at large number: regen + + pdg = input_pdg; + E=0; + px=0; + py=0; + pz=0; + p=0; + y=0; + eta=0; + xProd=0; + yProd=0; + zProd=0; + xProd=0.; yProd=0.; zProd=0.; + //addFurtherPion=false; + + randomizePDGsign=false; + + //fSpectra = new TF1("fPtDist",myLevyPt,0.0,10,3); + fSpectra = STAR_BlastWave("fSpectra", m, 20); + fSpectra ->SetNpx( 1000 ); + fSpectraXi = STAR_BlastWave("fSpectraXi", 1.32171, 20); + fSpectraXi ->SetNpx( 1000 ); + fSpectraOm = STAR_BlastWave("fSpectraOm", 1.67245, 20); + fSpectraOm ->SetNpx( 1000 ); + + fSpectra->SetParameter(0, m); //mass: automatic! + fSpectra->SetParameter(1,0.6615); //beta-max + fSpectra->SetParameter(2,0.0905); //T + fSpectra->SetParameter(3,0.7355); //n + fSpectra->SetParameter(4,1000); //norm (not relevant) + + fSpectraXi->SetParameter(0,1.32171); //beta-max + fSpectraXi->SetParameter(1,0.6615); //beta-max + fSpectraXi->SetParameter(2,0.0905); //T + fSpectraXi->SetParameter(3,0.7355); //n + fSpectraXi->SetParameter(4,1000); //norm (not relevant) + + fSpectraOm->SetParameter(0,1.67245); //beta-max + fSpectraOm->SetParameter(1,0.6615); //beta-max + fSpectraOm->SetParameter(2,0.0905); //T + fSpectraOm->SetParameter(3,0.7355); //n + fSpectraOm->SetParameter(4,1000); //norm (not relevant) + + fLVHelper = new TLorentzVector(); + + if( input_pdg!=0 ) m = getMass(input_pdg); + if( input_pdg==4444 ) m = 4.797; + if( input_pdg==0 ) m = 1.0; + furtherPrim={}; + keys_furtherPrim={}; + cout<<"Initalizing extra PYTHIA object"<SetParameters(mt, pt, beta_max, temp, n); + + integral = fIntegrandXi->Integral(0., 1.); + } + if(TMath::Abs(mass-1.67245)<0.002){ + if (!fIntegrandOm) + fIntegrandOm = new TF1("fIntegrandOm", this, &GeneratorPythia8Gun::STAR_BlastWave_Integrand_Improved, 0., 1., 5, "GeneratorPythia8Gun", "STAR_BlastWave_Integrand_Improved"); + fIntegrandOm->SetParameters(mt, pt, beta_max, temp, n); + + integral = fIntegrandOm->Integral(0., 1.); + } + if(TMath::Abs(mass-1.67245)>0.002&&TMath::Abs(mass-1.32171)>0.002){ + if (!fIntegrand) + fIntegrand = new TF1("fIntegrand", this, &GeneratorPythia8Gun::STAR_BlastWave_Integrand_Improved, 0., 1., 5, "GeneratorPythia8Gun", "STAR_BlastWave_Integrand_Improved"); + fIntegrand->SetParameters(mt, pt, beta_max, temp, n); + + integral = fIntegrand->Integral(0., 1.); + } + return norm * pt * integral; + } + + //___________________________________________________________________ + + Double_t STAR_BlastWave_Integrand_Improved(const Double_t *x, const Double_t *p) { + + /* + x[0] -> r (radius) + p[0] -> mT (transverse mass) + p[1] -> pT (transverse momentum) + p[2] -> beta_max (surface velocity) + p[3] -> T (freezout temperature) + p[4] -> n (velocity profile) + */ + + Double_t r = x[0]; + Double_t mt = p[0]; + Double_t pt = p[1]; + Double_t beta_max = p[2]; + Double_t temp_1 = 1. / p[3]; + Double_t n = p[4]; + + Double_t beta = beta_max * TMath::Power(r, n); + Double_t rho = TMath::ATanH(beta); + Double_t argI0 = pt * TMath::SinH(rho) * temp_1; + Double_t argK1 = mt * TMath::CosH(rho) * temp_1; + // if (argI0 > 100 || argI0 < -100) + // printf("r=%f, pt=%f, beta_max=%f, temp=%f, n=%f, mt=%f, beta=%f, rho=%f, argI0=%f, argK1=%f\n", r, pt, beta_max, 1. / temp_1, n, mt, beta, rho, argI0, argK1); + return r * mt * TMath::BesselI0(argI0) * TMath::BesselK1(argK1); + + } + + //___________________________________________________________________ + + TF1 *STAR_BlastWave(const Char_t *name, Double_t mass,Float_t upperlim, Double_t beta_max = 0.9, Double_t temp = 0.1, Double_t n = 1., Double_t norm = 1.e6) { + + //new TF1("fSpectra",this ,&GeneratorPythia8GunPbPb::myLevyPt, 0.0,20,3, "GeneratorPythia8GunPbPb","myLevyPt"); + TF1 *fBlastWave = new TF1(name, this, &GeneratorPythia8Gun::STAR_BlastWave_Func, 0., upperlim, 5, "GeneratorPythia8Gun", "STAR_BlastWave_Func"); + fBlastWave->SetParameters(mass, beta_max, temp, n, norm); + fBlastWave->SetParNames("mass", "beta_max", "T", "n", "norm"); + fBlastWave->FixParameter(0, mass); + fBlastWave->SetParLimits(1, 0.1, 0.9); // don't touch :) adding some 99 youu get floating point exception + fBlastWave->SetParLimits(2, 0.03,1.);//0.05, 1.); // no negative values!! for the following as well + fBlastWave->SetParLimits(3, 0.25,4.5); // was 2.5 // omega-->at limit even moving it to 4.5 but yield same + return fBlastWave; + } + + Double_t y2eta(Double_t pt, Double_t mass, Double_t y){ + Double_t mt = TMath::Sqrt(mass * mass + pt * pt); + return TMath::ASinH(mt / pt * TMath::SinH(y)); + } + + /// set mass + void setMass(int input_m){m=input_m;} + + /// set 4-momentum + void set4momentum(double input_px, double input_py, double input_pz){ + px = input_px; + py = input_py; + pz = input_pz; + E = sqrt( m*m+px*px+py*py+pz*pz ); + fourMomentum.px(px); + fourMomentum.py(py); + fourMomentum.pz(pz); + fourMomentum.e(E); + p = sqrt( px*px+py*py+pz*pz ); + y = 0.5*log( (E+pz)/(E-pz) ); + eta = 0.5*log( (p+pz)/(p-pz) ); + + ////std::cout << "##### Particle #####" << std::endl; + ////std::cout << " - PDG code: " << pdg << std::endl; + ////std::cout << " - mass: " << m << std::endl; + ////std::cout << " - (px,py,pz): (" << px << "," << py << "," << pz << ")" << std::endl; + ////std::cout << " - momentum: " << p << std::endl; + ////std::cout << " - energy: " << E << std::endl; + ////std::cout << " - rapidity: " << y << std::endl; + ////std::cout << " - pseudorapidity: " << eta << std::endl; + ////std::cout << " - production vertex: (" << xProd << "," << yProd << "," << zProd << ")" << std::endl; + } + + /// set 3-momentum + void setMomentum(double input_p){p=input_p;} + + /// set x,y,z of production vertex + void setProdVtx(double input_xProd, double input_yProd, double input_zProd){xProd=input_xProd; yProd=input_xProd; zProd=input_zProd;} + + /// setter to add further primary particles to the event + void setAddFurtherPrimaries(const int pdgCode, const int howMany){ + /// check if this species has been already added + const int map_counts = furtherPrim.count(pdgCode); + if(map_counts==1){ // species already present + const int howMany_already = furtherPrim[pdgCode]; + std::cout << "BEWARE: " << howMany_already << " particles of species " << pdgCode << " already required."; + std::cout << " Ignoring the command setAddFurtherPrimaries(" << pdgCode << "," << howMany << ")" << std::endl; + return; + } + /// add particles, if not yet present + furtherPrim[pdgCode] = howMany; + keys_furtherPrim.insert(pdgCode); + } + + /// set add a further primary pion + //void setAddFurtherPion(){addFurtherPion=true;} + + /// get mass from TParticlePDG + double getMass(int input_pdg){ + double mass = 0; + if(TDatabasePDG::Instance()){ + TParticlePDG* particle = TDatabasePDG::Instance()->GetParticle(input_pdg); + if(particle) mass = particle->Mass(); + else std::cout << "===> particle mass equal to 0" << std::endl; + } + return mass; + } + + //_________________________________________________________________________________ + /// generate uniform eta and uniform momentum + void genUniformMomentumEta(double minP, double maxP, double minY, double maxY){ + // random generator + std::unique_ptr ranGenerator { new TRandom3() }; + ranGenerator->SetSeed(0); + + // momentum + const double gen_p = ranGenerator->Uniform(minP,maxP); + // eta + const double gen_eta = ranGenerator->Uniform(minY,maxY); + // z-component momentum from eta + const double cosTheta = ( exp(2*gen_eta)-1 ) / ( exp(2*gen_eta)+1 ); // starting from eta = -ln(tan(theta/2)) = 1/2*ln( (1+cos(theta))/(1-cos(theta)) ) ---> NB: valid for cos(theta)!=1 + const double gen_pz = gen_p*cosTheta; + // y-component: random uniform + const double maxVal = sqrt( gen_p*gen_p-gen_pz*gen_pz ); + double sign_py = ranGenerator->Uniform(0,1); + sign_py = (sign_py>0.5)?1.:-1.; + const double gen_py = ranGenerator->Uniform(0.,maxVal)*sign_py; + // x-component momentum + double sign_px = ranGenerator->Uniform(0,1); + sign_px = (sign_px>0.5)?1.:-1.; + const double gen_px = sqrt( gen_p*gen_p-gen_pz*gen_pz-gen_py*gen_py )*sign_px; + + set4momentum(gen_px,gen_py,gen_pz); + } + + //_________________________________________________________________________________ + /// generate uniform eta and uniform momentum + void genSpectraMomentumEta(double minP, double maxP, double minY, double maxY){ + // random generator + std::unique_ptr ranGenerator { new TRandom3() }; + ranGenerator->SetSeed(0); + + // generate transverse momentum + const double gen_pT = fSpectra->GetRandom(minP,maxP); + + //Actually could be something else without loss of generality but okay + const double gen_phi = ranGenerator->Uniform(0,2*TMath::Pi()); + + // sample flat in rapidity, calculate eta + Double_t gen_Y=10, gen_eta=10; + + while( gen_eta>genmaxEta || gen_etaUniform(minY,maxY); + //(Double_t pt, Double_t mass, Double_t y) + gen_eta = y2eta(gen_pT, m, gen_Y); + } + + fLVHelper->SetPtEtaPhiM(gen_pT, gen_eta, gen_phi, m); + set4momentum(fLVHelper->Px(),fLVHelper->Py(),fLVHelper->Pz()); + } + + //_________________________________________________________________________________ + /// generate uniform eta and uniform momentum + void genSpectraMomentumEtaXi(double minP, double maxP, double minY, double maxY){ + // random generator + std::unique_ptr ranGenerator { new TRandom3() }; + ranGenerator->SetSeed(0); + + // generate transverse momentum + const double gen_pT = fSpectraXi->GetRandom(minP,maxP); + + //Actually could be something else without loss of generality but okay + const double gen_phi = ranGenerator->Uniform(0,2*TMath::Pi()); + + // sample flat in rapidity, calculate eta + Double_t gen_Y=10, gen_eta=10; + + while( gen_eta>genmaxEta || gen_etaUniform(minY,maxY); + //(Double_t pt, Double_t mass, Double_t y) + gen_eta = y2eta(gen_pT, m, gen_Y); + } + + fLVHelper->SetPtEtaPhiM(gen_pT, gen_eta, gen_phi, m); + set4momentum(fLVHelper->Px(),fLVHelper->Py(),fLVHelper->Pz()); + } + + //_________________________________________________________________________________ + /// generate uniform eta and uniform momentum + void genSpectraMomentumEtaOm(double minP, double maxP, double minY, double maxY){ + // random generator + std::unique_ptr ranGenerator { new TRandom3() }; + ranGenerator->SetSeed(0); + + // generate transverse momentum + const double gen_pT = fSpectraOm->GetRandom(minP,maxP); + + //Actually could be something else without loss of generality but okay + const double gen_phi = ranGenerator->Uniform(0,2*TMath::Pi()); + + // sample flat in rapidity, calculate eta + Double_t gen_Y=10, gen_eta=10; + + while( gen_eta>genmaxEta || gen_etaUniform(minY,maxY); + //(Double_t pt, Double_t mass, Double_t y) + gen_eta = y2eta(gen_pT, m, gen_Y); + } + + fLVHelper->SetPtEtaPhiM(gen_pT, gen_eta, gen_phi, m); + set4momentum(fLVHelper->Px(),fLVHelper->Py(),fLVHelper->Pz()); + } + +protected: + + //__________________________________________________________________ + Pythia8::Particle createParticle(){ + //std::cout << "createParticle() mass " << m << " pdgCode " << pdg << std::endl; + Pythia8::Particle myparticle; + myparticle.id(pdg); + myparticle.status(11); + myparticle.px(px); + myparticle.py(py); + myparticle.pz(pz); + myparticle.e(E); + myparticle.m(m); + myparticle.xProd(xProd); + myparticle.yProd(yProd); + myparticle.zProd(zProd); + + return myparticle; + } + + //__________________________________________________________________ + int randomizeSign(){ + + std::unique_ptr gen_random {new TRandom3(0)}; + const float n = gen_random->Uniform(-1,1); + + return n/abs(n); + } + + //__________________________________________________________________ + Bool_t generateEvent() override { + + double original_m = m; + int original_pdg = pdg; + + /// reset event + mPythia.event.reset(); + + if(original_pdg!=211){ + for(Int_t ii=0; ii<15; ii++){ + xProd=0.0; + yProd=0.0; + zProd=0.0; + genSpectraMomentumEta(genMinPt,genMaxPt,genminY,genmaxY); + Pythia8::Particle lAddedParticle = createParticle(); + mPythia.event.append(lAddedParticle); + lAddedParticles++; + } + } + + //+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + Bool_t lPythiaOK = kFALSE; + while (!lPythiaOK){ + lPythiaOK = pythiaObject.next(); + //Select rough central events, please, disregard + //if( pythiaObject.info.hiInfo->b() > 6) lPythiaOK = kFALSE; //regenerate, please + } + //+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // use event + Long_t nParticles = pythiaObject.event.size(); + Long_t nChargedParticlesAtMidRap = 0; + Long_t nPionsAtMidRap = 0; + lAddedParticles = 0; + for ( Long_t j=0; j < nParticles; j++ ) { + Int_t pypid = pythiaObject.event[j].id(); + Float_t pypx = pythiaObject.event[j].px(); + Float_t pypy = pythiaObject.event[j].py(); + Float_t pypz = pythiaObject.event[j].pz(); + Float_t pypt = pythiaObject.event[j].pT(); + Float_t pyrap = pythiaObject.event[j].y(); + Float_t pyeta = pythiaObject.event[j].eta(); + Int_t pystate = pythiaObject.event[j].status(); + //if(TMath::Abs(state > 89)) {continue;} + Float_t pyenergy = pythiaObject.event[j].e(); + Int_t pycharge = pythiaObject.event[j].charge(); + + //Per-species loop: skip outside of mid-rapidity, please + if ( TMath::Abs(pyeta) > 4.0 ) continue; //only within ALICE 3 acceptance + + //final only + if (!pythiaObject.event[j].isFinal()) continue; + + if ( TMath::Abs(pyeta) < 0.5 ){ + if ( TMath::Abs(pythiaObject.event[j].charge())>1e-5 ) nChargedParticlesAtMidRap++; + if ( TMath::Abs(pypid)==211 ) nPionsAtMidRap++; + } + + pdg = pypid; + px = pypx; + py = pypy; + pz = pypz; + E = pyenergy; + m = pythiaObject.event[j].m(); + xProd = pythiaObject.event[j].xProd(); + yProd = pythiaObject.event[j].yProd(); + zProd = pythiaObject.event[j].zProd(); + + Pythia8::Particle lAddedParticle = createParticle(); + mPythia.event.append(lAddedParticle); + lAddedParticles++; + } + //+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + //+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // XI ABUNDANCE FIX +// FCN=0.35879 FROM MINOS STATUS=SUCCESSFUL 126 CALLS 634 TOTAL +// EDM=3.7456e-09 STRATEGY= 1 ERROR MATRIX ACCURATE +// EXT PARAMETER STEP FIRST +// NO. NAME VALUE ERROR SIZE DERIVATIVE +// 1 p0 4.74929e-03 3.29248e-04 -3.35914e-06 5.38225e+00 +// 2 p1 -4.08255e-03 8.62587e-04 -2.02577e-05 2.45132e+00 +// 3 p2 4.76660e+00 1.93593e+00 1.93593e+00 2.70369e-04 +// Info in : created default TCanvas with name c1 + //Adjust relative abundance of multi-strange particles by injecting some + Double_t lExpectedXiToPion = TMath::Max(4.74929e-03 - 4.08255e-03*TMath::Exp(-nChargedParticlesAtMidRap/4.76660e+00) - 0.00211334,0.); + Double_t lExpectedXi = nPionsAtMidRap*lExpectedXiToPion; + Int_t lXiYield = gRandom->Poisson(3*lExpectedXi); //factor 3: fix the rapidity acceptance + m = 1.32171; + pdg = 3312; + cout<<"Adding extra xi: "<Uniform()>0.5?+1:-1; + xProd=0.0; + yProd=0.0; + zProd=0.0; + genSpectraMomentumEtaXi(genMinPt,genMaxPt,genminY,genmaxY); + Pythia8::Particle lAddedParticle = createParticle(); + mPythia.event.append(lAddedParticle); + lAddedParticles++; + } + //+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + //+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // OMEGA ABUNDANCE FIX + //Adjust relative abundance of multi-strange particles by injecting some + Double_t lExpectedOmegaToPion = TMath::Max(8.55057e-04 - 7.38732e-04*TMath::Exp(-nChargedParticlesAtMidRap/2.40545e+01) - 6.56785e-05,0.); + Double_t lExpectedOmega = nPionsAtMidRap*lExpectedOmegaToPion; + Int_t lOmegaYield = gRandom->Poisson(3*lExpectedOmega); //factor 3: fix the rapidity acceptance + m = 1.67245; + pdg = 3334; + cout<<"Adding extra omegas: "<Uniform()>0.5?+1:-1; + xProd=0.0; + yProd=0.0; + zProd=0.0; + genSpectraMomentumEtaOm(genMinPt,genMaxPt,genminY,genmaxY); + Pythia8::Particle lAddedParticle = createParticle(); + mPythia.event.append(lAddedParticle); + lAddedParticles++; + } + //+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + //Revert back or else there'll be trouble next time! + m = original_m; + pdg = original_pdg; + + /// go to next Pythia event + mPythia.next(); + + return true; + } + +private: + + double genMinPt; /// minimum 3-momentum for generated particles + double genMaxPt; /// maximum 3-momentum for generated particles + double genminY; /// minimum pseudorapidity for generated particles + double genmaxY; /// maximum pseudorapidity for generated particles + double genminEta; + double genmaxEta; + int UEOverSampling; //number of times to repeat underlying event + int genEventCountUse; + + Pythia8::Vec4 fourMomentum; /// four-momentum (px,py,pz,E) + double E; /// energy: sqrt( m*m+px*px+py*py+pz*pz ) [GeV/c] + double m; /// particle mass [GeV/c^2] + int pdg; /// particle pdg code + double px; /// x-component momentum [GeV/c] + double py; /// y-component momentum [GeV/c] + double pz; /// z-component momentum [GeV/c] + double p; /// momentum + double y; /// rapidity + double eta; /// pseudorapidity + double xProd; /// x-coordinate position production vertex [cm] + double yProd; /// y-coordinate position production vertex [cm] + double zProd; /// z-coordinate position production vertex [cm] + + //Max number: max number of particles to be added + long lAddedParticles; + float ue_E[5000]; + float ue_m[5000]; + float ue_px[5000]; + float ue_py[5000]; + float ue_pz[5000]; + float ue_xProd[5000]; + float ue_yProd[5000]; + float ue_zProd[5000]; + int ue_pdg[5000]; + + bool randomizePDGsign; /// bool to randomize the PDG code of the core particle + + TF1 *fSpectra; /// TF1 to store more realistic shape of spectrum + TF1 *fSpectraXi; /// TF1 to store more realistic shape of spectrum + TF1 *fSpectraOm; /// TF1 to store more realistic shape of spectrum + + //BW integrand + TF1 *fIntegrand = NULL; + TF1 *fIntegrandXi = NULL; + TF1 *fIntegrandOm = NULL; + + TLorentzVector *fLVHelper; + + Pythia8::Pythia pythiaObject; ///Generate a full event if requested to do so + + //bool addFurtherPion; /// bool to attach an additional primary pion + std::map furtherPrim; /// key: PDG code; value: how many further primaries of this species to be added + std::unordered_set keys_furtherPrim; /// keys of the above map (NB: only unique elements allowed!) +}; + +FairGenerator* generateNativeOmegaCCC(){ + return reinterpret_cast(new GeneratorPythia8Gun(4444)); +} + +FairGenerator* generateNativeOmegaCC(){ + return reinterpret_cast(new GeneratorPythia8Gun(4432)); +} + +FairGenerator* generateNativeOmegaC(){ + return reinterpret_cast(new GeneratorPythia8Gun(4332)); +} + +FairGenerator* generateNativeOmega(){ + return reinterpret_cast(new GeneratorPythia8Gun(3334)); +} + +FairGenerator* generatePYTHIA(){ + return reinterpret_cast(new GeneratorPythia8Gun(211)); +} diff --git a/MC/config/PWGDQ/EvtGen/DecayTablesEvtgen/BTOPSIJPSITODIELECTRON.DEC b/MC/config/PWGDQ/EvtGen/DecayTablesEvtgen/BTOPSIJPSITODIELECTRON.DEC index 64a835910..06b609242 100644 --- a/MC/config/PWGDQ/EvtGen/DecayTablesEvtgen/BTOPSIJPSITODIELECTRON.DEC +++ b/MC/config/PWGDQ/EvtGen/DecayTablesEvtgen/BTOPSIJPSITODIELECTRON.DEC @@ -1,151 +1,224 @@ ### Decay B0 ### -0.000310000 psi(2S) K_S0 SVS; #[Reconstructed PDG2011] -0.000310000 psi(2S) K_L0 SVS; #[Reconstructed PDG2011] # -# -0.000610000 psi(2S) K*0 SVV_HELAMP PKHplus PKphHplus PKHzero PKphHzero PKHminus PKphHminus; #[Reconstructed PDG2011] +0.000590000 psi(2S) K*0 SVV_HELAMP PKHplus PKphHplus PKHzero PKphHzero PKHminus PKphHminus; #[Reconstructed PDG2011] 0.0004 psi(2S) K+ pi- PHSP; 0.0002 psi(2S) K0 pi0 PHSP; -0.0002 psi(2S) K0 pi- pi+ PHSP; +0.000281 psi(2S) K0 pi- pi+ PHSP; 0.0001 psi(2S) K0 pi0 pi0 PHSP; 0.0001 psi(2S) K+ pi- pi0 PHSP; 0.0004 psi(2S) K_10 PHSP; ### -0.000620000 psi(2S) K0 PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000580000 psi(2S) K0 SVS; #[New mode added] #[Reconstructed PDG2011] -0.000435500 J/psi K_S0 SVS; #[Reconstructed PDG2011] -0.000435500 J/psi K_L0 SVS; #[Reconstructed PDG2011] +0.000891000 J/psi K0 SVS; #[Reconstructed PDG2011] # # -0.001330000 J/psi K*0 SVV_HELAMP PKHplus PKphHplus PKHzero PKphHzero PKHminus PKphHminus; #[Reconstructed PDG2011] -0.000017600 J/psi pi0 SVS; #[Reconstructed PDG2011] -0.000027000 J/psi rho0 SVV_HELAMP PKHplus PKphHplus PKHzero PKphHzero PKHminus PKphHminus; #[Reconstructed PDG2011] -0.00003 J/psi omega SVV_HELAMP PKHplus PKphHplus PKHzero PKphHzero PKHminus PKphHminus; +0.001270000 J/psi K*0 SVV_HELAMP PKHplus PKphHplus PKHzero PKphHzero PKHminus PKphHminus; #[Reconstructed PDG2011] +0.000017300 J/psi pi0 SVS; #[Reconstructed PDG2011] +0.000025500 J/psi rho0 SVV_HELAMP PKHplus PKphHplus PKHzero PKphHzero PKHminus PKphHminus; #[Reconstructed PDG2011] +0.0000208 J/psi omega SVV_HELAMP PKHplus PKphHplus PKHzero PKphHzero PKHminus PKphHminus; 0.000000000 J/psi K+ pi- PHSP; #[Reconstructed PDG2011] 0.0001 J/psi K0 pi0 PHSP; 0.001300000 J/psi K_10 SVV_HELAMP 0.5 0.0 1.0 0.0 0.5 0.0; #[Reconstructed PDG2011] 0.0001 J/psi K'_10 SVV_HELAMP 0.5 0.0 1.0 0.0 0.5 0.0; 0.0005 J/psi K_2*0 PHSP; -0.000094000 J/psi phi K0 PHSP; #[Reconstructed PDG2011] +0.000049000 J/psi phi K0 PHSP; #[Reconstructed PDG2011] #### -0.000871000 J/psi K0 PHSP; #[New mode added] #[Reconstructed PDG2011] -0.000310000 J/psi omega K0 PHSP; #[New mode added] #[Reconstructed PDG2011] -0.000009500 J/psi eta PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000230000 J/psi omega K0 PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000012300 J/psi eta PHSP; #[New mode added] #[Reconstructed PDG2011] 0.000019000 J/psi pi+ pi- PHSP; #[New mode added] #[Reconstructed PDG2011] -0.000460000 J/psi K0 pi+ pi- PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000450000 J/psi K0 pi+ pi- PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000025000 J/psi K0 K+ K- PHSP; #[New mode added] #[Reconstructed PDG2011] 0.000540000 J/psi K0 rho0 PHSP; #[New mode added] #[Reconstructed PDG2011] 0.000800000 J/psi K*+ pi- PHSP; #[New mode added] #[Reconstructed PDG2011] 0.000660000 J/psi K*0 pi+ pi- PHSP; #[New mode added] #[Reconstructed PDG2011] + +# --- chi_c1 --- +0.000011200 chi_c1 pi0 PHSP; +0.000395000 chi_c1 K0 SVS; +0.000497000 chi_c1 K+ pi- PHSP; +0.000320000 chi_c1 K0 pi+ pi- PHSP; +0.000350000 chi_c1 K+ pi0 pi- PHSP; + +# --- chi_c2 --- +0.000015000 chi_c2 K0 STS; +0.000049000 chi_c2 K*0 PHSP; +0.000072000 chi_c2 K+ pi- PHSP; +0.000174000 chi_c2 pi+ pi- K0 PHSP; +0.000074000 chi_c2 pi- pi0 K+ PHSP; + +# --- chi_c1(3872) --- +0.000110000 X_1(3872) K0 SVS; +0.000180000 X_1(3872) K+ pi- PHSP; Enddecay Decay anti-B0 ### -0.000310000 psi(2S) K_S0 SVS; #[Reconstructed PDG2011] -0.000310000 psi(2S) K_L0 SVS; #[Reconstructed PDG2011] # -0.000610000 psi(2S) anti-K*0 SVV_HELAMP PKHminus PKphHminus PKHzero PKphHzero PKHplus PKphHplus; #[Reconstructed PDG2011] +0.000590000 psi(2S) anti-K*0 SVV_HELAMP PKHminus PKphHminus PKHzero PKphHzero PKHplus PKphHplus; #[Reconstructed PDG2011] 0.0004 psi(2S) K- pi+ PHSP; 0.0002 psi(2S) anti-K0 pi0 PHSP; -0.0002 psi(2S) anti-K0 pi+ pi- PHSP; +0.000281 psi(2S) anti-K0 pi+ pi- PHSP; 0.0001 psi(2S) anti-K0 pi0 pi0 PHSP; 0.0001 psi(2S) K- pi+ pi0 PHSP; 0.0004 psi(2S) anti-K_10 PHSP; ### -0.000620000 psi(2S) anti-K0 PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000580000 psi(2S) anti-K0 SVS; #[New mode added] #[Reconstructed PDG2011] -0.000435500 J/psi K_S0 SVS; #[Reconstructed PDG2011] -0.000435500 J/psi K_L0 SVS; #[Reconstructed PDG2011] +0.000891000 J/psi anti-K0 SVS; #[Reconstructed PDG2011] # updated BR and corrected for double-couting (https://repo.hepforge.org/rEVTGEN16d483d4962aeeb93e857939666a9ba3383bb71f) # # -0.001330000 J/psi anti-K*0 SVV_HELAMP PKHminus PKphHminus PKHzero PKphHzero PKHplus PKphHplus; #[Reconstructed PDG2011] -0.000017600 J/psi pi0 SVS; #[Reconstructed PDG2011] -0.000027000 J/psi rho0 SVV_HELAMP PKHminus PKphHminus PKHzero PKphHzero PKHplus PKphHplus; #[Reconstructed PDG2011] -0.000030 J/psi omega SVV_HELAMP PKHminus PKphHminus PKHzero PKphHzero PKHplus PKphHplus; +0.001270000 J/psi anti-K*0 SVV_HELAMP PKHminus PKphHminus PKHzero PKphHzero PKHplus PKphHplus; #[Reconstructed PDG2011] +0.000017300 J/psi pi0 SVS; #[Reconstructed PDG2011] +0.000025500 J/psi rho0 SVV_HELAMP PKHminus PKphHminus PKHzero PKphHzero PKHplus PKphHplus; #[Reconstructed PDG2011] +0.0000208 J/psi omega SVV_HELAMP PKHminus PKphHminus PKHzero PKphHzero PKHplus PKphHplus; 0.000000000 J/psi K- pi+ PHSP; #[Reconstructed PDG2011] 0.0001 J/psi anti-K0 pi0 PHSP; 0.001300000 J/psi anti-K_10 SVV_HELAMP 0.5 0.0 1.0 0.0 0.5 0.0; #[Reconstructed PDG2011] 0.0001 J/psi anti-K'_10 SVV_HELAMP 0.5 0.0 1.0 0.0 0.5 0.0; 0.0005 J/psi anti-K_2*0 PHSP; -0.000094000 J/psi phi anti-K0 PHSP; #[Reconstructed PDG2011] +0.000049000 J/psi phi anti-K0 PHSP; #[Reconstructed PDG2011] ### -0.000871000 J/psi anti-K0 PHSP; #[New mode added] #[Reconstructed PDG2011] -0.000310000 J/psi omega anti-K0 PHSP; #[New mode added] #[Reconstructed PDG2011] -0.000009500 J/psi eta PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000230000 J/psi omega anti-K0 PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000012300 J/psi eta PHSP; #[New mode added] #[Reconstructed PDG2011] 0.000019000 J/psi pi- pi+ PHSP; #[New mode added] #[Reconstructed PDG2011] -0.000460000 J/psi anti-K0 pi- pi+ PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000450000 J/psi anti-K0 pi- pi+ PHSP; #[New mode added] #[Reconstructed PDG2011] +0.0000250000 J/psi anti-K0 K- K+ PHSP; #[New mode added] #[Reconstructed PDG2011] 0.000540000 J/psi anti-K0 rho0 PHSP; #[New mode added] #[Reconstructed PDG2011] 0.000800000 J/psi K*- pi+ PHSP; #[New mode added] #[Reconstructed PDG2011] 0.000660000 J/psi anti-K*0 pi- pi+ PHSP; #[New mode added] #[Reconstructed PDG2011] + +# --- chi_c1 --- +0.000011200 chi_c1 pi0 PHSP; +0.000395000 chi_c1 anti-K0 SVS; +0.000497000 chi_c1 K- pi+ PHSP; +0.000320000 chi_c1 anti-K0 pi- pi+ PHSP; +0.000350000 chi_c1 K- pi0 pi+ PHSP; + +# --- chi_c2 --- +0.000015000 chi_c2 anti-K0 STS; +0.000049000 chi_c2 anti-K*0 PHSP; +0.000072000 chi_c2 K- pi+ PHSP; +0.000174000 chi_c2 pi- pi+ anti-K0 PHSP; +0.000074000 chi_c2 pi+ pi0 K- PHSP; + +# --- chi_c1(3872) --- +0.000110000 X_1(3872) anti-K0 SVS; +0.000180000 X_1(3872) K- pi+ PHSP; + Enddecay Decay B+ ### -0.000646000 psi(2S) K+ SVS; #[Reconstructed PDG2011] -0.000620000 psi(2S) K*+ SVV_HELAMP PKHplus PKphHplus PKHzero PKphHzero PKHminus PKphHminus; #[Reconstructed PDG2011] +0.000625000 psi(2S) K+ SVS; #[Reconstructed PDG2011] +0.000670000 psi(2S) K*+ SVV_HELAMP PKHplus PKphHplus PKHzero PKphHzero PKHminus PKphHminus; #[Reconstructed PDG2011] 0.0004 psi(2S) K0 pi+ PHSP; 0.0002 psi(2S) K+ pi0 PHSP; -0.001900000 psi(2S) K+ pi- pi+ PHSP; #[Reconstructed PDG2011] +0.000430 psi(2S) K+ pi- pi+ PHSP; #[Reconstructed PDG2011] 0.0001 psi(2S) K+ pi0 pi0 PHSP; 0.0001 psi(2S) K0 pi+ pi0 PHSP; 0.0004 psi(2S) K_1+ PHSP; ### -0.000025800 psi(2S) pi+ PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000024400 psi(2S) pi+ PHSP; #[New mode added] #[Reconstructed PDG2011] -0.001014000 J/psi K+ SVS; #[Reconstructed PDG2011] +0.001019000 J/psi K+ SVS; #[Reconstructed PDG2011] 0.001430000 J/psi K*+ SVV_HELAMP PKHplus PKphHplus PKHzero PKphHzero PKHminus PKphHminus; #[Reconstructed PDG2011] -0.000049000 J/psi pi+ SVS; #[Reconstructed PDG2011] -0.000050000 J/psi rho+ SVV_HELAMP PKHplus PKphHplus PKHzero PKphHzero PKHminus PKphHminus; #[Reconstructed PDG2011] -0.0002 J/psi K0 pi+ PHSP; -0.0001 J/psi K+ pi0 PHSP; +0.000041000 J/psi pi+ SVS; #[Reconstructed PDG2011] +0.000041000 J/psi rho+ SVV_HELAMP PKHplus PKphHplus PKHzero PKphHzero PKHminus PKphHminus; #[Reconstructed PDG2011] +0.00113 J/psi K0 pi+ PHSP; +0.0001 J/psi K+ pi0 PHSP; 0.0001 J/psi K'_1+ SVV_HELAMP 0.5 0.0 1.0 0.0 0.5 0.0; 0.0005 J/psi K_2*+ PHSP; 0.001800000 J/psi K_1+ SVV_HELAMP 0.5 0.0 1.0 0.0 0.5 0.0; #[Reconstructed PDG2011] 0.000052000 J/psi phi K+ PHSP; #[Reconstructed PDG2011] # #### -0.001070000 J/psi K+ pi+ pi- PHSP; #[New mode added] #[Reconstructed PDG2011] -0.000108000 J/psi eta K+ PHSP; #[New mode added] #[Reconstructed PDG2011] -0.000350000 J/psi omega K+ PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000810000 J/psi K+ pi+ pi- PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000033700 J/psi K+ K- K+ PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000124000 J/psi eta K+ PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000031000 J/psi eta' K+ PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000320000 J/psi omega K+ PHSP; #[New mode added] #[Reconstructed PDG2011] 0.000011800 J/psi p+ anti-Lambda0 PHSP; #[New mode added] #[Reconstructed PDG2011] + +# --- chi_c1 --- +0.000022000 chi_c1 pi+ PHSP; +0.000474000 chi_c1 K+ SVS; +0.000300000 chi_c1 K*+ SVV_HELAMP PKHplus PKphHplus PKHzero PKphHzero PKHminus PKphHminus; +0.000329000 chi_c1 K+ pi0 PHSP; +0.000580000 chi_c1 K0 pi+ PHSP; +0.000374000 chi_c1 K+ pi+ pi- PHSP; + +# --- chi_c2 --- +0.000110000 chi_c2 K+ STS; +0.000120000 chi_c2 K*+ PHSP; +0.000062000 chi_c2 K+ pi0 PHSP; +0.000124000 chi_c2 K0 pi+ PHSP; +0.000134000 chi_c2 K+ pi+ pi- PHSP; + +# --- chi_c1(3872) --- +0.000190000 X_1(3872) K+ SVS; +0.000240000 X_1(3872) K0 pi+ PHSP; Enddecay Decay B- ### -0.000646000 psi(2S) K- SVS; #[Reconstructed PDG2011] -0.000620000 psi(2S) K*- SVV_HELAMP PKHminus PKphHminus PKHzero PKphHzero PKHplus PKphHplus; #[Reconstructed PDG2011] +0.000625000 psi(2S) K- SVS; #[Reconstructed PDG2011] +0.000670000 psi(2S) K*- SVV_HELAMP PKHminus PKphHminus PKHzero PKphHzero PKHplus PKphHplus; #[Reconstructed PDG2011] 0.0004 psi(2S) anti-K0 pi- PHSP; 0.0002 psi(2S) K- pi0 PHSP; -0.001900000 psi(2S) K- pi+ pi- PHSP; #[Reconstructed PDG2011] +0.000430 psi(2S) K- pi+ pi- PHSP; #[Reconstructed PDG2011] 0.0001 psi(2S) K- pi0 pi0 PHSP; 0.0001 psi(2S) anti-K0 pi- pi0 PHSP; 0.0004 psi(2S) K_1- PHSP; ### -0.000025800 psi(2S) pi- PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000024400 psi(2S) pi- PHSP; #[New mode added] #[Reconstructed PDG2011] -0.001014000 J/psi K- SVS; #[Reconstructed PDG2011] +0.001019000 J/psi K- SVS; #[Reconstructed PDG2011] 0.001430000 J/psi K*- SVV_HELAMP PKHminus PKphHminus PKHzero PKphHzero PKHplus PKphHplus; #[Reconstructed PDG2011] -0.000049000 J/psi pi- SVS; #[Reconstructed PDG2011] -0.000050000 J/psi rho- SVV_HELAMP PKHminus PKphHminus PKHzero PKphHzero PKHplus PKphHplus; #[Reconstructed PDG2011] -0.0002 J/psi anti-K0 pi- PHSP; +0.000041000 J/psi pi- SVS; #[Reconstructed PDG2011] +0.000041000 J/psi rho- SVV_HELAMP PKHminus PKphHminus PKHzero PKphHzero PKHplus PKphHplus; #[Reconstructed PDG2011] +0.00113 J/psi anti-K0 pi- PHSP; 0.0001 J/psi K- pi0 PHSP; 0.0001 J/psi K'_1- SVV_HELAMP 0.5 0.0 1.0 0.0 0.5 0.0; 0.0005 J/psi K_2*- PHSP; 0.001800000 J/psi K_1- SVV_HELAMP 0.5 0.0 1.0 0.0 0.5 0.0; #[Reconstructed PDG2011] 0.000052000 J/psi phi K- PHSP; #[Reconstructed PDG2011] # -0.001070000 J/psi K- pi- pi+ PHSP; #[New mode added] #[Reconstructed PDG2011] -0.000108000 J/psi eta K- PHSP; #[New mode added] #[Reconstructed PDG2011] -0.000350000 J/psi omega K- PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000810000 J/psi K- pi- pi+ PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000033700 J/psi K- K+ K- PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000124000 J/psi eta K- PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000031000 J/psi eta' K- PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000320000 J/psi omega K- PHSP; #[New mode added] #[Reconstructed PDG2011] 0.000011800 J/psi anti-p- Lambda0 PHSP; #[New mode added] #[Reconstructed PDG2011] + +# --- chi_c1 --- +0.000022000 chi_c1 pi- PHSP; +0.000474000 chi_c1 K- SVS; +0.000300000 chi_c1 K*- SVV_HELAMP PKHminus PKphHminus PKHzero PKphHzero PKHplus PKphHplus; +0.000329000 chi_c1 K- pi0 PHSP; +0.000580000 chi_c1 anti-K0 pi- PHSP; +0.000374000 chi_c1 K- pi- pi+ PHSP; + +# --- chi_c2 --- +0.000110000 chi_c2 K- STS; +0.000120000 chi_c2 K*- PHSP; +0.000062000 chi_c2 K- pi0 PHSP; +0.000124000 chi_c2 anti-K0 pi- PHSP; +0.000134000 chi_c2 K- pi- pi+ PHSP; + +# --- chi_c1(3872) --- +0.000190000 X_1(3872) K- SVS; +0.000240000 X_1(3872) K0 pi- PHSP; + Enddecay Decay B_s0 ### psi' = 0.34% CLNS 94/1315 -0.000465 psi(2S) eta' SVS; -0.000235 psi(2S) eta SVS; -0.000680000 psi(2S) phi SVV_HELAMP 1.0 0.0 1.0 0.0 1.0 0.0; #[Reconstructed PDG2011] +0.000137 psi(2S) eta' SVS; +0.00037 psi(2S) eta SVS; +0.000510000 psi(2S) phi SVV_HELAMP 1.0 0.0 1.0 0.0 1.0 0.0; #[Reconstructed PDG2011] 0.0003 psi(2S) K- K+ PHSP; 0.0003 psi(2S) anti-K0 K0 PHSP; 0.0003 psi(2S) K0 K- pi+ PHSP; @@ -157,19 +230,22 @@ Decay B_s0 0.0002 psi(2S) eta pi0 pi0 PHSP; 0.0004 psi(2S) eta' pi+ pi- PHSP; 0.0004 psi(2S) eta' pi0 pi0 PHSP; -0.0002 psi(2S) pi+ pi- PHSP; +0.000068 psi(2S) pi+ pi- PHSP; 0.0002 psi(2S) pi0 pi0 PHSP; #### -0.00064 J/psi eta' SVS; -0.00032 J/psi eta SVS; -0.001300000 J/psi phi SVV_HELAMP 1.0 0.0 1.0 0.0 1.0 0.0; #[Reconstructed PDG2011] -0.00008 J/psi K0 SVS; -0.00070 J/psi K- K+ PHSP; +0.000353 J/psi eta' SVS; +0.000445 J/psi eta SVS; +0.00026 J/psi f'_2 PHSP; +0.001010000 J/psi phi SVV_HELAMP 1.0 0.0 1.0 0.0 1.0 0.0; #[Reconstructed PDG2011] +0.0000384 J/psi K0 SVS; +0.00079 J/psi K- K+ PHSP; 0.00070 J/psi anti-K0 K0 PHSP; -0.00070 J/psi K0 K- pi+ PHSP; +0.000475 J/psi K0 K- pi+ PHSP; +0.000475 J/psi K0 K+ pi- PHSP; 0.00070 J/psi anti-K0 K0 pi0 PHSP; 0.00070 J/psi K- K+ pi0 PHSP; +0.000107 J/psi K*0 anti-K*0 PHSP; # LHCb PR 04/02/04 Add (cc) phi n pi(+/0) 0.00039 J/psi phi pi+ pi- PHSP; 0.00039 J/psi phi pi0 pi0 PHSP; @@ -182,13 +258,22 @@ Decay B_s0 0.0002 J/psi pi0 pi0 PHSP; # PR LHCb 04/08/2004 : add Bs -> phi mu mu, phi e e 0.0000023 phi e+ e- BTOSLLALI; + +# --- chi_c1 --- +0.0001920 chi_c1 phi SVV_HELAMP 1.0 0.0 1.0 0.0 1.0 0.0; + +# --- chi_c1(3872) --- +0.000096000 X_1(3872) phi SVV_HELAMP 1.0 0.0 1.0 0.0 1.0 0.0; +0.000076000 X_1(3872) K+ K- PHSP; +0.000037000 X_1(3872) pi+ pi- PHSP; + Enddecay Decay anti-B_s0 ### -0.000465 psi(2S) eta' SVS; -0.000235 psi(2S) eta SVS; -0.000680000 psi(2S) phi SVV_HELAMP 1.0 0.0 1.0 0.0 1.0 0.0; #[Reconstructed PDG2011] +0.000137 psi(2S) eta' SVS; +0.00037 psi(2S) eta SVS; +0.000510000 psi(2S) phi SVV_HELAMP 1.0 0.0 1.0 0.0 1.0 0.0; #[Reconstructed PDG2011] 0.0003 psi(2S) K- K+ PHSP; 0.0003 psi(2S) anti-K0 K0 PHSP; 0.0003 psi(2S) anti-K0 K+ pi- PHSP; @@ -200,18 +285,21 @@ Decay anti-B_s0 0.0002 psi(2S) eta pi0 pi0 PHSP; 0.0004 psi(2S) eta' pi+ pi- PHSP; 0.0004 psi(2S) eta' pi0 pi0 PHSP; -0.0002 psi(2S) pi+ pi- PHSP; +0.000068 psi(2S) pi+ pi- PHSP; 0.0002 psi(2S) pi0 pi0 PHSP; ### -0.00064 J/psi eta' SVS; -0.00032 J/psi eta SVS; -0.001300000 J/psi phi SVV_HELAMP 1.0 0.0 1.0 0.0 1.0 0.0; #[Reconstructed PDG2011] -0.00008 J/psi K0 SVS; -0.00070 J/psi K- K+ PHSP; +0.000353 J/psi eta' SVS; +0.000445 J/psi eta SVS; +0.00026 J/psi f'_2 PHSP; +0.001010000 J/psi phi SVV_HELAMP 1.0 0.0 1.0 0.0 1.0 0.0; #[Reconstructed PDG2011] +0.0000384 J/psi K0 SVS; +0.00079 J/psi K- K+ PHSP; 0.00070 J/psi anti-K0 K0 PHSP; -0.00070 J/psi anti-K0 K+ pi- PHSP; +0.000475 J/psi anti-K0 K+ pi- PHSP; +0.000475 J/psi anti-K0 K- pi+ PHSP; 0.00070 J/psi anti-K0 K0 pi0 PHSP; 0.00070 J/psi K- K+ pi0 PHSP; +0.000107 J/psi K*0 anti-K*0 PHSP; # LHCb PR 04/02/04 Add (cc) phi n pi(+/0) 0.00039 J/psi phi pi+ pi- PHSP; 0.00039 J/psi phi pi0 pi0 PHSP; @@ -224,27 +312,65 @@ Decay anti-B_s0 0.0002 J/psi pi0 pi0 PHSP; # PR LHCb 04/08/2004 : add Bs -> phi mu mu, phi e e 0.0000023 phi e- e+ BTOSLLALI; + +# --- chi_c1 --- +0.0001970 chi_c1 phi SVV_HELAMP 1.0 0.0 1.0 0.0 1.0 0.0; + +# --- chi_c1(3872) --- +0.000096000 X_1(3872) phi SVV_HELAMP 1.0 0.0 1.0 0.0 1.0 0.0; +0.000076000 X_1(3872) K+ K- PHSP; +0.000037000 X_1(3872) pi+ pi- PHSP; Enddecay Decay Lambda_b0 ### -0.00038 Lambda0 psi(2S) PHSP; -0.00047 Lambda0 J/psi PHSP; +0.00015 Lambda0 psi(2S) PHSP; +0.0003 Lambda0 J/psi PHSP; +0.000012 Lambda0 J/psi phi PHSP; +0.000026 p+ J/psi pi- PHSP; +0.00032 p+ J/psi K- PHSP; +0.000066 p+ J/psi pi- pi+ K- PHSP; +0.000066 p+ psi(2S) K- PHSP; +0.0000760 chi_c1 p+ K- PHSP; +0.0000050 chi_c1 p+ pi- PHSP; +0.0000770 chi_c2 p+ K- PHSP; +0.0000048 chi_c2 p+ pi- PHSP; + +# --- chi_c1(3872) --- +0.0000280 X_1(3872) p+ K- PHSP; +0.0000160 X_1(3872) Lambda(1520)0 PHSP; Enddecay Decay anti-Lambda_b0 ### -0.00038 anti-Lambda0 psi(2S) PHSP; -0.00047 anti-Lambda0 J/psi PHSP; +0.00015 anti-Lambda0 psi(2S) PHSP; +0.0003 anti-Lambda0 J/psi PHSP; +0.000012 Lambda0 J/psi phi PHSP; +0.000026 anti-p- J/psi pi+ PHSP; +0.00032 anti-p- J/psi K+ PHSP; +0.000066 anti-p- J/psi pi- pi+ K+ PHSP; +0.000066 anti-p- psi(2S) K+ PHSP; +0.0000760 chi_c1 anti-p- K+ PHSP; +0.0000050 chi_c1 anti-p- pi+ PHSP; +0.0000770 chi_c2 anti-p- K+ PHSP; +0.0000048 chi_c2 anti-p- pi+ PHSP; + +# --- chi_c1(3872) --- +0.0000280 X_1(3872) anti-p- K+ PHSP; +0.0000160 X_1(3872) anti-Lambda(1520)0 PHSP; Enddecay Decay Xi_b- 0.00047 Xi- J/psi PHSP; + 0.000108 J/psi Lambda0 K- PHSP; + 0.000394 Xi- psi(2S) PHSP; Enddecay Decay anti-Xi_b+ 0.00047 anti-Xi+ J/psi PHSP; + 0.000108 J/psi anti-Lambda0 K+ PHSP; + 0.000394 anti-Xi+ psi(2S) PHSP; Enddecay Decay Xi_b0 @@ -263,15 +389,68 @@ Decay anti-Omega_b+ 0.00047 anti-Omega+ J/psi PHSP; Enddecay +Decay B_c- +# SemiLeptonic Decays +0.01900 J/psi e- anti-nu_e PHOTOS BC_VMN 1; +0.00094 psi(2S) e- anti-nu_e PHOTOS BC_VMN 1; +Enddecay + +Decay B_c+ +# SemiLeptonic Decays +0.01900 J/psi e+ nu_e PHOTOS BC_VMN 1; +0.00094 psi(2S) e+ nu_e PHOTOS BC_VMN 1; +Enddecay + + +# ============================================================================================================================= +# JPsi, Psi(2S), ChiC, Upsilon Decays +# ============================================================================================================================= +### +Decay chi_c0 +0.0135 gamma J/psi PHSP; +0.000129 J/psi e+ e- PHOTOS PHSP; +Enddecay + +### +Decay chi_c1 +0.339 J/psi gamma VVP 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; +0.00342 J/psi e+ e- PHOTOS PHSP; +Enddecay + +### +Decay chi_c2 +0.190 gamma J/psi PHSP; +0.00214 J/psi e+ e- PHOTOS PHSP; +Enddecay Decay psi(2S) -### from DECAY.DEC -1.000 e+ e- PHOTOS VLL; +0.00795 e+ e- PHOTOS VLL; +0.0208 J/psi pi+ pi- VVPIPI; # multiplied by J/psi->ee BR to keep relative statistics, since J/psi->ee itself is also enhanced +0.0000819 gamma chi_c0 PHSP; # * BR(J/psi->ee) * BR(chi_c0->J/psi) +0.00201 gamma chi_c1 PHSP; # * BR(J/psi->ee) * BR(chi_c1->J/psi) +0.00110 gamma chi_c2 PHSP; # * BR(J/psi->ee) * BR(chi_c2->J/psi) Enddecay Decay J/psi 1.000 e+ e- PHOTOS VLL; Enddecay +Decay Upsilon +1.000 e+ e- PHOTOS VLL; +Enddecay + +Decay Upsilon(2S) +1.000 e+ e- PHOTOS VLL; +Enddecay + +Decay Upsilon(3S) +1.000 e+ e- PHOTOS VLL; +Enddecay + +Decay X_1(3872) +#### Breit-Wigner function for the pion distribution (S-Wave approximation) +1.000 J/psi pi+ pi- XJPSIRHO0PIPI_S; +Enddecay + End diff --git a/MC/config/PWGDQ/EvtGen/DecayTablesEvtgen/BTOPSITOJPSIPIPI.DEC b/MC/config/PWGDQ/EvtGen/DecayTablesEvtgen/BTOPSITOJPSIPIPI.DEC new file mode 100644 index 000000000..82102bf60 --- /dev/null +++ b/MC/config/PWGDQ/EvtGen/DecayTablesEvtgen/BTOPSITOJPSIPIPI.DEC @@ -0,0 +1,167 @@ +#### +Decay B0 +# B -> cc= s +0.000310000 psi(2S) K_S0 SVS; #[Reconstructed PDG2011] +0.000310000 psi(2S) K_L0 SVS; #[Reconstructed PDG2011] +# +# +0.000610000 psi(2S) K*0 SVV_HELAMP PKHplus PKphHplus PKHzero PKphHzero PKHminus PKphHminus; #[Reconstructed PDG2011] + +0.0004 psi(2S) K+ pi- PHSP; +0.0002 psi(2S) K0 pi0 PHSP; +0.0002 psi(2S) K0 pi- pi+ PHSP; +0.0001 psi(2S) K0 pi0 pi0 PHSP; +0.0001 psi(2S) K+ pi- pi0 PHSP; +0.0004 psi(2S) K_10 PHSP; +# +#### +0.000620000 psi(2S) K0 PHSP; #[New mode added] #[Reconstructed PDG2011] + +Enddecay + +Decay anti-B0 +# +0.000310000 psi(2S) K_S0 SVS; #[Reconstructed PDG2011] +0.000310000 psi(2S) K_L0 SVS; #[Reconstructed PDG2011] +# +# +0.000610000 psi(2S) anti-K*0 SVV_HELAMP PKHminus PKphHminus PKHzero PKphHzero PKHplus PKphHplus; #[Reconstructed PDG2011] + +0.0004 psi(2S) K- pi+ PHSP; +0.0002 psi(2S) anti-K0 pi0 PHSP; +0.0002 psi(2S) anti-K0 pi+ pi- PHSP; +0.0001 psi(2S) anti-K0 pi0 pi0 PHSP; +0.0001 psi(2S) K- pi+ pi0 PHSP; +0.0004 psi(2S) anti-K_10 PHSP; +# +0.000620000 psi(2S) anti-K0 PHSP; #[New mode added] #[Reconstructed PDG2011] +#### +Enddecay + +Decay B- +# +# B -> cc= s sum = 1.92% +# +0.000646000 psi(2S) K- SVS; #[Reconstructed PDG2011] +0.000620000 psi(2S) K*- SVV_HELAMP PKHminus PKphHminus PKHzero PKphHzero PKHplus PKphHplus; #[Reconstructed PDG2011] +0.0004 psi(2S) anti-K0 pi- PHSP; +0.0002 psi(2S) K- pi0 PHSP; +0.001900000 psi(2S) K- pi+ pi- PHSP; #[Reconstructed PDG2011] +0.0001 psi(2S) K- pi0 pi0 PHSP; +0.0001 psi(2S) anti-K0 pi- pi0 PHSP; +0.0004 psi(2S) K_1- PHSP; +# +0.000025800 psi(2S) pi- PHSP; #[New mode added] #[Reconstructed PDG2011] + +Enddecay + +Decay B+ +# +# B -> cc= s sum = 1.92% +# +0.000646000 psi(2S) K+ SVS; #[Reconstructed PDG2011] +0.000620000 psi(2S) K*+ SVV_HELAMP PKHplus PKphHplus PKHzero PKphHzero PKHminus PKphHminus; #[Reconstructed PDG2011] +0.0004 psi(2S) K0 pi+ PHSP; +0.0002 psi(2S) K+ pi0 PHSP; +0.001900000 psi(2S) K+ pi- pi+ PHSP; #[Reconstructed PDG2011] +0.0001 psi(2S) K+ pi0 pi0 PHSP; +0.0001 psi(2S) K0 pi+ pi0 PHSP; +0.0004 psi(2S) K_1+ PHSP; +# +#### +0.000025800 psi(2S) pi+ PHSP; #[New mode added] #[Reconstructed PDG2011] + +Enddecay + +Decay B_s0 +# psi' = 0.34% CLNS 94/1315 +0.000465 psi(2S) eta' SVS; +0.000235 psi(2S) eta SVS; +0.000680000 psi(2S) phi SVV_HELAMP 1.0 0.0 1.0 0.0 1.0 0.0; #[Reconstructed PDG2011] +0.0003 psi(2S) K- K+ PHSP; +0.0003 psi(2S) anti-K0 K0 PHSP; +0.0003 psi(2S) K0 K- pi+ PHSP; +0.0003 psi(2S) anti-K0 K0 pi0 PHSP; +0.0003 psi(2S) K- K+ pi0 PHSP; +0.00034 psi(2S) phi pi+ pi- PHSP; +0.00034 psi(2S) phi pi0 pi0 PHSP; +0.0002 psi(2S) eta pi+ pi- PHSP; +0.0002 psi(2S) eta pi0 pi0 PHSP; +0.0004 psi(2S) eta' pi+ pi- PHSP; +0.0004 psi(2S) eta' pi0 pi0 PHSP; +0.0002 psi(2S) pi+ pi- PHSP; +0.0002 psi(2S) pi0 pi0 PHSP; +# +# PR LHCb 04/08/2004 : add Bs -> phi mu mu, phi e e +0.0000023 phi e+ e- BTOSLLALI; +Enddecay + +Decay anti-B_s0 +# psi' = 0.34% CLNS 94/1315 +# +0.000465 psi(2S) eta' SVS; +0.000235 psi(2S) eta SVS; +0.000680000 psi(2S) phi SVV_HELAMP 1.0 0.0 1.0 0.0 1.0 0.0; #[Reconstructed PDG2011] +0.0003 psi(2S) K- K+ PHSP; +0.0003 psi(2S) anti-K0 K0 PHSP; +0.0003 psi(2S) anti-K0 K+ pi- PHSP; +0.0003 psi(2S) anti-K0 K0 pi0 PHSP; +0.0003 psi(2S) K- K+ pi0 PHSP; +0.00034 psi(2S) phi pi+ pi- PHSP; +0.00034 psi(2S) phi pi0 pi0 PHSP; +0.0002 psi(2S) eta pi+ pi- PHSP; +0.0002 psi(2S) eta pi0 pi0 PHSP; +0.0004 psi(2S) eta' pi+ pi- PHSP; +0.0004 psi(2S) eta' pi0 pi0 PHSP; +0.0002 psi(2S) pi+ pi- PHSP; +0.0002 psi(2S) pi0 pi0 PHSP; +# +# PR LHCb 04/08/2004 : add Bs -> phi mu mu, phi e e +0.0000023 phi e- e+ BTOSLLALI; + +Enddecay + +Decay Lambda_b0 + 0.00038 Lambda0 psi(2S) PHSP; +Enddecay + +Decay anti-Lambda_b0 + 0.00038 anti-Lambda0 psi(2S) PHSP; +Enddecay + +Decay Xi_b- + 0.00038 Xi- psi(2S) PHSP; +Enddecay + +Decay anti-Xi_b+ + 0.00038 anti-Xi+ psi(2S) PHSP; +Enddecay + +Decay Omega_b- + 0.00038 Omega- psi(2S) PHSP; +Enddecay + +Decay anti-Omega_b+ + 0.00038 anti-Omega+ psi(2S) PHSP; +Enddecay + +Decay B_c- +# SemiLeptonic Decays +0.00094 psi(2S) e- anti-nu_e PHOTOS PHSP; +Enddecay + +Decay B_c+ +# SemiLeptonic Decays +0.00094 psi(2S) e+ nu_e PHOTOS PHSP; +Enddecay + +Decay psi(2S) +1.000 J/psi pi+ pi- VVPIPI; +Enddecay + +Decay J/psi +### from DECAY.DEC +1.000 e+ e- PHOTOS VLL; +Enddecay + +End \ No newline at end of file diff --git a/MC/config/PWGDQ/EvtGen/DecayTablesEvtgen/CHARMONIATOJPSIANDDIELECTRON.DEC b/MC/config/PWGDQ/EvtGen/DecayTablesEvtgen/CHARMONIATOJPSIANDDIELECTRON.DEC new file mode 100644 index 000000000..094ce961f --- /dev/null +++ b/MC/config/PWGDQ/EvtGen/DecayTablesEvtgen/CHARMONIATOJPSIANDDIELECTRON.DEC @@ -0,0 +1,47 @@ +Decay chi_c0 +0.0135 gamma J/psi PHSP; +0.000129 J/psi e+ e- PHOTOS PHSP; +Enddecay + +### +Decay chi_c1 +0.339 J/psi gamma VVP 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; +0.00342 J/psi e+ e- PHOTOS PHSP; +Enddecay + +### +Decay chi_c2 +0.190 gamma J/psi PHSP; +0.00214 J/psi e+ e- PHOTOS PHSP; +Enddecay + +Decay psi(2S) +0.00795 e+ e- PHOTOS VLL; +0.0208 J/psi pi+ pi- VVPIPI; # multiplied by J/psi->ee BR to keep relative statistics, since J/psi->ee itself is also enhanced +0.0000819 gamma chi_c0 PHSP; # * BR(J/psi->ee) * BR(chi_c0->J/psi) +0.00201 gamma chi_c1 PHSP; # * BR(J/psi->ee) * BR(chi_c1->J/psi) +0.00110 gamma chi_c2 PHSP; # * BR(J/psi->ee) * BR(chi_c2->J/psi) +Enddecay + +Decay J/psi +1.000 e+ e- PHOTOS VLL; +Enddecay + +Decay Upsilon +1.000 e+ e- PHOTOS VLL; +Enddecay + +Decay Upsilon(2S) +1.000 e+ e- PHOTOS VLL; +Enddecay + +Decay Upsilon(3S) +1.000 e+ e- PHOTOS VLL; +Enddecay + +Decay X_1(3872) +#### Breit-Wigner function for the pion distribution (S-Wave approximation) +1.000 J/psi pi+ pi- XJPSIRHO0PIPI_S; +Enddecay + +End diff --git a/MC/config/PWGDQ/EvtGen/DecayTablesEvtgen/PSITOJPSIPIPI.DEC b/MC/config/PWGDQ/EvtGen/DecayTablesEvtgen/PSITOJPSIPIPI.DEC new file mode 100644 index 000000000..70bc72517 --- /dev/null +++ b/MC/config/PWGDQ/EvtGen/DecayTablesEvtgen/PSITOJPSIPIPI.DEC @@ -0,0 +1,8 @@ +Decay J/psi +### from DECAY.DEC +1.000 e+ e- PHOTOS VLL; +Enddecay +Decay psi(2S) +1.000 J/psi pi+ pi- VVPIPI; +Enddecay +End diff --git a/MC/config/PWGDQ/EvtGen/DecayTablesEvtgen/PSITOJPSITOMU.DEC b/MC/config/PWGDQ/EvtGen/DecayTablesEvtgen/PSITOJPSITOMU.DEC new file mode 100644 index 000000000..764f43237 --- /dev/null +++ b/MC/config/PWGDQ/EvtGen/DecayTablesEvtgen/PSITOJPSITOMU.DEC @@ -0,0 +1,9 @@ +Decay psi(2S) +1.000 J/psi pi+ pi- VVPIPI; +Enddecay + +Decay J/psi +1.000 mu+ mu- PHOTOS VLL; +Enddecay + +End diff --git a/MC/config/PWGDQ/EvtGen/DecayTablesEvtgen/X3872ANDPSI2STOJPSIPIPI.DEC b/MC/config/PWGDQ/EvtGen/DecayTablesEvtgen/X3872ANDPSI2STOJPSIPIPI.DEC new file mode 100644 index 000000000..53f19edc6 --- /dev/null +++ b/MC/config/PWGDQ/EvtGen/DecayTablesEvtgen/X3872ANDPSI2STOJPSIPIPI.DEC @@ -0,0 +1,15 @@ +Decay X_1(3872) +#### Breit-Wigner function for the pion distribution (S-Wave approximation) +1.000 J/psi pi+ pi- XJPSIRHO0PIPI_S; +Enddecay + +Decay psi(2S) +1.000 J/psi pi+ pi- VVPIPI; +Enddecay + +Decay J/psi +### from DECAY.DEC +1.000 e+ e- PHOTOS VLL; +Enddecay + +End diff --git a/MC/config/PWGDQ/EvtGen/DecayTablesEvtgen/X3872TOJPSIPIPI.DEC b/MC/config/PWGDQ/EvtGen/DecayTablesEvtgen/X3872TOJPSIPIPI.DEC new file mode 100644 index 000000000..8451b0777 --- /dev/null +++ b/MC/config/PWGDQ/EvtGen/DecayTablesEvtgen/X3872TOJPSIPIPI.DEC @@ -0,0 +1,10 @@ +Decay X_1(3872) +#### Breit-Wigner function for the pion distribution (S-Wave approximation) +1.000 J/psi pi+ pi- XJPSIRHO0PIPI_S; +Enddecay +Decay J/psi +### from DECAY.DEC +1.000 e+ e- PHOTOS VLL; +Enddecay + +End diff --git a/MC/config/PWGDQ/EvtGen/GeneratorEvtGen.C b/MC/config/PWGDQ/EvtGen/GeneratorEvtGen.C index 7c0f4650e..9c073b5ea 100644 --- a/MC/config/PWGDQ/EvtGen/GeneratorEvtGen.C +++ b/MC/config/PWGDQ/EvtGen/GeneratorEvtGen.C @@ -42,7 +42,9 @@ enum DecayModeEvt { kEvtAll = 0, kEvtElectronEM, kEvtDiElectronEM, kEvtGammaEM, - kEvtBeautyUpgrade }; + kEvtBtoPsi2SToJpsiPiPi, + kEvtBeautyUpgrade, + kCharmoniaToJpsiAndDielectron}; namespace o2 { @@ -105,10 +107,10 @@ class GeneratorEvtGen : public T { auto nparticles = T::mParticles.size(); for (Int_t iparticle = 0; iparticle < nparticles; ++iparticle) { - auto particle = (TParticle)T::mParticles.at(iparticle); + const auto& particle = T::mParticles.at(iparticle); if (checkPdg(particle.GetPdgCode())) { if (mDebug) - std::cout << "particles in the array (before decay): PDG " << particle.GetPdgCode() << " STATUS " << particle.GetStatusCode() << " position in the array" << iparticle << " First daughter" << particle.GetFirstDaughter() << " Last daughter " << particle.GetLastDaughter() << std::endl; + std::cout << "particles in the array (before decay): PDG " << particle.GetPdgCode() << " STATUS " << particle.GetStatusCode() << " position in the array " << iparticle << " First daughter " << particle.GetFirstDaughter() << " Last daughter " << particle.GetLastDaughter() << std::endl; TLorentzVector* momentum = new TLorentzVector(); momentum->SetPxPyPzE(particle.Px(), particle.Py(), particle.Pz(), particle.Energy()); DecayEvtGen(particle.GetPdgCode(), momentum, mPolarization); @@ -117,7 +119,7 @@ class GeneratorEvtGen : public T return kFALSE; } if (mDebug) - std::cout << "particles in the array (after decay): PDG " << particle.GetPdgCode() << " STATUS " << particle.GetStatusCode() << " position in the array" << iparticle << " First daughter" << particle.GetFirstDaughter() << " Last daughter " << particle.GetLastDaughter() << std::endl; + std::cout << "particles in the array (after decay): PDG " << particle.GetPdgCode() << " STATUS " << particle.GetStatusCode() << " position in the array " << iparticle << " First daughter " << particle.GetFirstDaughter() << " Last daughter " << particle.GetLastDaughter() << std::endl; } } return kTRUE; @@ -264,7 +266,7 @@ class GeneratorEvtGen : public T // different decay table. case kAll read the default decay table only // DecayModeEvt decay = mDecayMode; - TString pathO2 = gSystem->ExpandPathName("$O2DPG_ROOT/MC/config/PWGDQ/EvtGen/DecayTablesEvtgen"); + TString pathO2 = gSystem->ExpandPathName("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/EvtGen/DecayTablesEvtgen"); switch (decay) { case kEvtAll: // particles decayed "naturally" according to $ALICE_ROOT/TEvtGen/EvtGen/DECAY.DEC break; @@ -355,6 +357,12 @@ class GeneratorEvtGen : public T case kEvtBeautyUpgrade: SetDecayTable(Form("%s/BEAUTYUPGRADE.DEC", pathO2.Data())); break; + case kEvtBtoPsi2SToJpsiPiPi: + SetDecayTable(Form("%s/BTOPSITOJPSIPIPI.DEC", pathO2.Data())); + break; + case kCharmoniaToJpsiAndDielectron: + SetDecayTable(Form("%s/CHARMONIATOJPSIANDDIELECTRON.DEC", pathO2.Data())); + break; } return; }; diff --git a/MC/config/PWGDQ/external/generator/GeneratorBeautyToJpsi_EvtGen.C b/MC/config/PWGDQ/external/generator/GeneratorBeautyToJpsi_EvtGen.C deleted file mode 100644 index bf8b1ea69..000000000 --- a/MC/config/PWGDQ/external/generator/GeneratorBeautyToJpsi_EvtGen.C +++ /dev/null @@ -1,84 +0,0 @@ -// usage (fwdy) : -// o2-sim -j 4 -n 10 -g external -t external -m "PIPE ITS TPC" -o sgn --configFile GeneratorHF_bbbar_fwdy.ini -// usage (midy) : -// o2-sim -j 4 -n 10 -g external -t external -m "PIPE ITS TPC" -o sgn --configFile GeneratorHF_bbbar_midy.ini -// -// -R__ADD_INCLUDE_PATH($O2DPG_ROOT/MC/config/PWGDQ/EvtGen) -R__ADD_INCLUDE_PATH($O2DPG_ROOT/MC/config/PWGHF/external/generator) -#include "GeneratorEvtGen.C" -#include "GeneratorHF.C" - -FairGenerator* - GeneratorBeautyToJpsi_EvtGenMidY(double rapidityMin = -1.5, double rapidityMax = 1.5, bool ispp = true, bool verbose = false, TString pdgs = "511;521;531;541;5112;5122;5232;5132;5332") -{ - auto gen = new o2::eventgen::GeneratorEvtGen(); - gen->setRapidity(rapidityMin, rapidityMax); - gen->setPDG(5); - TString pathO2table = gSystem->ExpandPathName("$O2DPG_ROOT/MC/config/PWGDQ/pythia8/decayer/switchOffBhadrons.cfg"); - gen->readFile(pathO2table.Data()); - - gen->setVerbose(verbose); - if (ispp) - gen->setFormula("1"); - else - gen->setFormula("max(1.,120.*(x<5.)+80.*(1.-x/20.)*(x>5.)*(x<11.)+240.*(1.-x/13.)*(x>11.))"); - std::string spdg; - TObjArray* obj = pdgs.Tokenize(";"); - gen->SetSizePdg(obj->GetEntriesFast()); - for (int i = 0; i < obj->GetEntriesFast(); i++) { - spdg = obj->At(i)->GetName(); - gen->AddPdg(std::stoi(spdg), i); - printf("PDG %d \n", std::stoi(spdg)); - } - gen->SetForceDecay(kEvtBJpsiDiElectron); - - // set random seed - gen->readString("Random:setSeed on"); - uint random_seed; - unsigned long long int random_value = 0; - ifstream urandom("/dev/urandom", ios::in|ios::binary); - urandom.read(reinterpret_cast(&random_value), sizeof(random_seed)); - gen->readString(Form("Random:seed = %d", random_value % 900000001)); - - // print debug - // gen->PrintDebug(); - - return gen; -} - -FairGenerator* - GeneratorBeautyToJpsi_EvtGenFwdY(double rapidityMin = -4.3, double rapidityMax = -2.2, bool ispp = true, bool verbose = false, TString pdgs = "511;521;531;541;5112;5122;5232;5132;5332") -{ - auto gen = new o2::eventgen::GeneratorEvtGen(); - gen->setRapidity(rapidityMin, rapidityMax); - gen->setPDG(5); - TString pathO2table = gSystem->ExpandPathName("$O2DPG_ROOT/MC/config/PWGDQ/pythia8/decayer/switchOffBhadrons.cfg"); - gen->readFile(pathO2table.Data()); - - gen->setVerbose(verbose); - if (ispp) - gen->setFormula("1"); - else - gen->setFormula("max(1.,120.*(x<5.)+80.*(1.-x/20.)*(x>5.)*(x<11.)+240.*(1.-x/13.)*(x>11.))"); - std::string spdg; - TObjArray* obj = pdgs.Tokenize(";"); - gen->SetSizePdg(obj->GetEntriesFast()); - for (int i = 0; i < obj->GetEntriesFast(); i++) { - spdg = obj->At(i)->GetName(); - gen->AddPdg(std::stoi(spdg), i); - printf("PDG %d \n", std::stoi(spdg)); - } - gen->SetForceDecay(kEvtBJpsiDiMuon); - // set random seed - gen->readString("Random:setSeed on"); - uint random_seed; - unsigned long long int random_value = 0; - ifstream urandom("/dev/urandom", ios::in|ios::binary); - urandom.read(reinterpret_cast(&random_value), sizeof(random_seed)); - gen->readString(Form("Random:seed = %d", random_value % 900000001)); - // print debug - // gen->PrintDebug(); - - return gen; -} diff --git a/MC/config/PWGDQ/external/generator/GeneratorBeautyToMu_EvtGen.C b/MC/config/PWGDQ/external/generator/GeneratorBeautyToMu_EvtGen.C deleted file mode 100644 index 98b71a00b..000000000 --- a/MC/config/PWGDQ/external/generator/GeneratorBeautyToMu_EvtGen.C +++ /dev/null @@ -1,46 +0,0 @@ -// usage (fwdy) : -//o2-sim -j 4 -n 10 -g external -t external -m "PIPE ITS TPC" -o sgn --configFile GeneratorHF_bbbar_fwdy.ini -// usage (midy) : -//o2-sim -j 4 -n 10 -g external -t external -m "PIPE ITS TPC" -o sgn --configFile GeneratorHF_bbbar_midy.ini -// -// -R__ADD_INCLUDE_PATH($O2DPG_ROOT/MC/config/PWGDQ/EvtGen) -R__ADD_INCLUDE_PATH($O2DPG_ROOT/MC/config/PWGHF/external/generator) -#include "GeneratorEvtGen.C" -#include "GeneratorHF.C" - - -FairGenerator* -GeneratorBeautyToMu_EvtGenFwdY(double rapidityMin = -4.3, double rapidityMax = -2.2, bool ispp = true, bool verbose = false, TString pdgs = "511;521;531;541;5112;5122;5232;5132;5332") -{ - auto gen = new o2::eventgen::GeneratorEvtGen(); - gen->setRapidity(rapidityMin,rapidityMax); - gen->setPDG(5); - TString pathO2table = gSystem->ExpandPathName("$O2DPG_ROOT/MC/config/PWGDQ/pythia8/decayer/switchOffBhadrons.cfg"); - gen->readFile(pathO2table.Data()); - - gen->setVerbose(verbose); - if(ispp) gen->setFormula("1"); - else gen->setFormula("max(1.,120.*(x<5.)+80.*(1.-x/20.)*(x>5.)*(x<11.)+240.*(1.-x/13.)*(x>11.))"); - std::string spdg; - TObjArray *obj = pdgs.Tokenize(";"); - gen->SetSizePdg(obj->GetEntriesFast()); - for(int i=0; iGetEntriesFast(); i++) { - spdg = obj->At(i)->GetName(); - gen->AddPdg(std::stoi(spdg),i); - printf("PDG %d \n",std::stoi(spdg)); - } - gen->SetForceDecay(kEvtSemiMuonic); - // set random seed - gen->readString("Random:setSeed on"); - uint random_seed; - unsigned long long int random_value = 0; - ifstream urandom("/dev/urandom", ios::in|ios::binary); - urandom.read(reinterpret_cast(&random_value), sizeof(random_seed)); - gen->readString(Form("Random:seed = %d", random_value % 900000001)); - // print debug - // gen->PrintDebug(); - - return gen; -} - diff --git a/MC/config/PWGDQ/external/generator/GeneratorBeautyToPsiAndJpsi_EvtGen.C b/MC/config/PWGDQ/external/generator/GeneratorBeautyToPsiAndJpsi_EvtGen.C deleted file mode 100644 index 98a4763c6..000000000 --- a/MC/config/PWGDQ/external/generator/GeneratorBeautyToPsiAndJpsi_EvtGen.C +++ /dev/null @@ -1,84 +0,0 @@ -// usage (fwdy) : -// o2-sim -j 4 -n 10 -g external -t external -m "PIPE ITS TPC" -o sgn --configFile GeneratorHF_bbbar_PsiAndJpsi_midy.ini -// usage (midy) : -// o2-sim -j 4 -n 10 -g external -t external -m "PIPE ITS TPC" -o sgn --configFile GeneratorHF_bbbar_PsiAndJpsi_fwdy.ini -// -// -R__ADD_INCLUDE_PATH($O2DPG_ROOT/MC/config/PWGDQ/EvtGen) -R__ADD_INCLUDE_PATH($O2DPG_ROOT/MC/config/PWGHF/external/generator) -#include "GeneratorEvtGen.C" -#include "GeneratorHF.C" - -FairGenerator* - GeneratorBeautyToPsiAndJpsi_EvtGenMidY(double rapidityMin = -1.5, double rapidityMax = 1.5, bool ispp = true, bool verbose = false, TString pdgs = "511;521;531;541;5112;5122;5232;5132;5332") -{ - auto gen = new o2::eventgen::GeneratorEvtGen(); - gen->setRapidity(rapidityMin, rapidityMax); - gen->setPDG(5); - TString pathO2table = gSystem->ExpandPathName("$O2DPG_ROOT/MC/config/PWGDQ/pythia8/decayer/switchOffBhadrons.cfg"); - gen->readFile(pathO2table.Data()); - - gen->setVerbose(verbose); - if (ispp) - gen->setFormula("1"); - else - gen->setFormula("max(1.,120.*(x<5.)+80.*(1.-x/20.)*(x>5.)*(x<11.)+240.*(1.-x/13.)*(x>11.))"); - std::string spdg; - TObjArray* obj = pdgs.Tokenize(";"); - gen->SetSizePdg(obj->GetEntriesFast()); - for (int i = 0; i < obj->GetEntriesFast(); i++) { - spdg = obj->At(i)->GetName(); - gen->AddPdg(std::stoi(spdg), i); - printf("PDG %d \n", std::stoi(spdg)); - } - gen->SetForceDecay(kEvtBPsiAndJpsiDiElectron); - - // set random seed - gen->readString("Random:setSeed on"); - uint random_seed; - unsigned long long int random_value = 0; - ifstream urandom("/dev/urandom", ios::in|ios::binary); - urandom.read(reinterpret_cast(&random_value), sizeof(random_seed)); - gen->readString(Form("Random:seed = %d", random_value % 900000001)); - - // print debug - // gen->PrintDebug(); - - return gen; -} - -FairGenerator* - GeneratorBeautyToPsiAndJpsi_EvtGenFwdY(double rapidityMin = -4.3, double rapidityMax = -2.2, bool ispp = true, bool verbose = false, TString pdgs = "511;521;531;541;5112;5122;5232;5132;5332") -{ - auto gen = new o2::eventgen::GeneratorEvtGen(); - gen->setRapidity(rapidityMin, rapidityMax); - gen->setPDG(5); - TString pathO2table = gSystem->ExpandPathName("$O2DPG_ROOT/MC/config/PWGDQ/pythia8/decayer/switchOffBhadrons.cfg"); - gen->readFile(pathO2table.Data()); - - gen->setVerbose(verbose); - if (ispp) - gen->setFormula("1"); - else - gen->setFormula("max(1.,120.*(x<5.)+80.*(1.-x/20.)*(x>5.)*(x<11.)+240.*(1.-x/13.)*(x>11.))"); - std::string spdg; - TObjArray* obj = pdgs.Tokenize(";"); - gen->SetSizePdg(obj->GetEntriesFast()); - for (int i = 0; i < obj->GetEntriesFast(); i++) { - spdg = obj->At(i)->GetName(); - gen->AddPdg(std::stoi(spdg), i); - printf("PDG %d \n", std::stoi(spdg)); - } - gen->SetForceDecay(kEvtBPsiAndJpsiDiMuon); - // set random seed - gen->readString("Random:setSeed on"); - uint random_seed; - unsigned long long int random_value = 0; - ifstream urandom("/dev/urandom", ios::in|ios::binary); - urandom.read(reinterpret_cast(&random_value), sizeof(random_seed)); - gen->readString(Form("Random:seed = %d", random_value % 900000001)); - // print debug - // gen->PrintDebug(); - - return gen; -} diff --git a/MC/config/PWGDQ/external/generator/GeneratorBeautyToPsi_EvtGen.C b/MC/config/PWGDQ/external/generator/GeneratorBeautyToPsi_EvtGen.C deleted file mode 100644 index f68cd3525..000000000 --- a/MC/config/PWGDQ/external/generator/GeneratorBeautyToPsi_EvtGen.C +++ /dev/null @@ -1,82 +0,0 @@ -// usage (fwdy) : -// o2-sim -j 4 -n 10 -g external -t external -m "PIPE ITS TPC" -o sgn --configFile GeneratorHF_bbbar_Psi2S_fwdy.ini -// usage (midy) : -// o2-sim -j 4 -n 10 -g external -t external -m "PIPE ITS TPC" -o sgn --configFile GeneratorHF_bbbar_Psi2S_midy.ini -// -// -R__ADD_INCLUDE_PATH($O2DPG_ROOT/MC/config/PWGDQ/EvtGen) -R__ADD_INCLUDE_PATH($O2DPG_ROOT/MC/config/PWGHF/external/generator) -#include "GeneratorEvtGen.C" -#include "GeneratorHF.C" - -FairGenerator* - GeneratorBeautyToPsi_EvtGenMidY(double rapidityMin = -1.5, double rapidityMax = 1.5, bool ispp = true, bool verbose = false, TString pdgs = "511;521;531;541;5112;5122;5132;5332") -{ - auto gen = new o2::eventgen::GeneratorEvtGen(); - gen->setRapidity(rapidityMin, rapidityMax); - gen->setPDG(5); - TString pathO2table = gSystem->ExpandPathName("$O2DPG_ROOT/MC/config/PWGDQ/pythia8/decayer/switchOffBhadrons.cfg"); - gen->readFile(pathO2table.Data()); - - gen->setVerbose(verbose); - if (ispp) - gen->setFormula("1"); - else - gen->setFormula("max(1.,120.*(x<5.)+80.*(1.-x/20.)*(x>5.)*(x<11.)+240.*(1.-x/13.)*(x>11.))"); - std::string spdg; - TObjArray* obj = pdgs.Tokenize(";"); - gen->SetSizePdg(obj->GetEntriesFast()); - for (int i = 0; i < obj->GetEntriesFast(); i++) { - spdg = obj->At(i)->GetName(); - gen->AddPdg(std::stoi(spdg), i); - printf("PDG %d \n", std::stoi(spdg)); - } - gen->SetForceDecay(kEvtBPsiDiElectron); - // set random seed - gen->readString("Random:setSeed on"); - uint random_seed; - unsigned long long int random_value = 0; - ifstream urandom("/dev/urandom", ios::in|ios::binary); - urandom.read(reinterpret_cast(&random_value), sizeof(random_seed)); - gen->readString(Form("Random:seed = %d", random_value % 900000001)); - // print debug - // gen->PrintDebug(); - - return gen; -} - -FairGenerator* - GeneratorBeautyToPsi_EvtGenFwdY(double rapidityMin = -4.3, double rapidityMax = -2.2, bool ispp = true, bool verbose = false, TString pdgs = "511;521;531;541;5112;5122;5132;5332") -{ - auto gen = new o2::eventgen::GeneratorEvtGen(); - gen->setRapidity(rapidityMin, rapidityMax); - gen->setPDG(5); - TString pathO2table = gSystem->ExpandPathName("$O2DPG_ROOT/MC/config/PWGDQ/pythia8/decayer/switchOffBhadrons.cfg"); - gen->readFile(pathO2table.Data()); - - gen->setVerbose(verbose); - if (ispp) - gen->setFormula("1"); - else - gen->setFormula("max(1.,120.*(x<5.)+80.*(1.-x/20.)*(x>5.)*(x<11.)+240.*(1.-x/13.)*(x>11.))"); - std::string spdg; - TObjArray* obj = pdgs.Tokenize(";"); - gen->SetSizePdg(obj->GetEntriesFast()); - for (int i = 0; i < obj->GetEntriesFast(); i++) { - spdg = obj->At(i)->GetName(); - gen->AddPdg(std::stoi(spdg), i); - printf("PDG %d \n", std::stoi(spdg)); - } - gen->SetForceDecay(kEvtBPsiDiMuon); - // set random seed - gen->readString("Random:setSeed on"); - uint random_seed; - unsigned long long int random_value = 0; - ifstream urandom("/dev/urandom", ios::in|ios::binary); - urandom.read(reinterpret_cast(&random_value), sizeof(random_seed)); - gen->readString(Form("Random:seed = %d", random_value % 900000001)); - // print debug - // gen->PrintDebug(); - - return gen; -} diff --git a/MC/config/PWGDQ/external/generator/GeneratorBottomonia.C b/MC/config/PWGDQ/external/generator/GeneratorBottomonia.C new file mode 100644 index 000000000..2da2edf9b --- /dev/null +++ b/MC/config/PWGDQ/external/generator/GeneratorBottomonia.C @@ -0,0 +1,537 @@ +// +// generators for bottomonia considering at midrapidity and forward rapidity +// + +R__ADD_INCLUDE_PATH($O2DPG_MC_CONFIG_ROOT/MC/config/PWGDQ/EvtGen) +R__LOAD_LIBRARY(libpythia6) +#include "GeneratorCocktail.C" +#include "GeneratorEvtGen.C" +// ========================== +// === pp 13 TeV classes ==== +// ========================== +namespace o2 +{ +namespace eventgen +{ + +///////////////////////////////////////////////////////////////////////////// +class O2_GeneratorParamUpsilon1SFwdY : public GeneratorTGenerator +{ + + public: + O2_GeneratorParamUpsilon1SFwdY() : GeneratorTGenerator("ParamUpsilon1S") + { + paramUpsilon1S = new GeneratorParam(1, -1, PtUpsilon1Spp13TeV, YUpsilon1Spp13TeV, V2Upsilon1Spp13TeV, IpUpsilon1Spp13TeV); + paramUpsilon1S->SetMomentumRange(0., 1.e6); + paramUpsilon1S->SetPtRange(0, 999.); + paramUpsilon1S->SetYRange(-4.2, -2.3); + paramUpsilon1S->SetPhiRange(0., 360.); + paramUpsilon1S->SetDecayer(new TPythia6Decayer()); + paramUpsilon1S->SetForceDecay(kNoDecay); // particle left undecayed + // - - paramUpsilon1S->SetTrackingFlag(1); // (from AliGenParam) -> check this + setTGenerator(paramUpsilon1S); + }; + + ~O2_GeneratorParamUpsilon1SFwdY() + { + delete paramUpsilon1S; + }; + + Bool_t Init() override + { + GeneratorTGenerator::Init(); + paramUpsilon1S->Init(); + return true; + } + + void SetNSignalPerEvent(Int_t nsig) { paramUpsilon1S->SetNumberParticles(nsig); } + + //-------------------------------------------------------------------------// + static Double_t PtUpsilon1Spp13TeV(const Double_t* px, const Double_t* /*dummy*/) + { + // Upsilon1S pt shape from LHCb pp@13TeV arXiv:1804.09214 + Double_t x = *px; + Float_t p0, p1, p2, p3; + + p0 = 4.11195e+02; + p1 = 1.03097e+01; + p2 = 1.62309e+00; + p3 = 4.84709e+00; + + return (p0 * x / TMath::Power(1. + TMath::Power(x / p1, p2), p3)); + } + + //-------------------------------------------------------------------------// + static Double_t YUpsilon1Spp13TeV(const Double_t* py, const Double_t* /*dummy*/) + { + // Upsilon1S y shape from LHCb pp@13TeV arXiv:1804.09214 + Double_t x = *py; + Float_t p0, p1; + + p0 = 3.07931e+03; + p1 = -3.53102e-02; + + return (p0 * (1. + p1 * x * x)); + } + + //-------------------------------------------------------------------------// + static Double_t V2Upsilon1Spp13TeV(const Double_t* /*dummy*/, const Double_t* /*dummy*/) + { + // Upsilon(1S) v2 + return 0.; + } + + //-------------------------------------------------------------------------// + static Int_t IpUpsilon1Spp13TeV(TRandom*) + { + return 553; + } + + private: + GeneratorParam* paramUpsilon1S = nullptr; +}; + +///////////////////////////////////////////////////////////////////////////// +class O2_GeneratorParamUpsilon2SFwdY : public GeneratorTGenerator +{ + + public: + O2_GeneratorParamUpsilon2SFwdY() : GeneratorTGenerator("ParamUpsilon2S") + { + paramUpsilon2S = new GeneratorParam(1, -1, PtUpsilon2Spp13TeV, YUpsilon2Spp13TeV, V2Upsilon2Spp13TeV, IpUpsilon2Spp13TeV); + paramUpsilon2S->SetMomentumRange(0., 1.e6); + paramUpsilon2S->SetPtRange(0, 999.); + paramUpsilon2S->SetYRange(-4.2, -2.3); + paramUpsilon2S->SetPhiRange(0., 360.); + paramUpsilon2S->SetDecayer(new TPythia6Decayer()); + paramUpsilon2S->SetForceDecay(kNoDecay); // particle left undecayed + // - - paramUpsilon2S->SetTrackingFlag(1); // (from AliGenParam) -> check this + setTGenerator(paramUpsilon2S); + }; + + ~O2_GeneratorParamUpsilon2SFwdY() + { + delete paramUpsilon2S; + }; + + Bool_t Init() override + { + GeneratorTGenerator::Init(); + paramUpsilon2S->Init(); + return true; + } + + void SetNSignalPerEvent(Int_t nsig) { paramUpsilon2S->SetNumberParticles(nsig); } + + //-------------------------------------------------------------------------// + static Double_t PtUpsilon2Spp13TeV(const Double_t* px, const Double_t* /*dummy*/) + { + // Upsilon2S pt shape from LHCb pp@13TeV arXiv:1804.09214 + Double_t x = *px; + Float_t p0, p1, p2, p3; + + p0 = 8.15699e+01; + p1 = 1.48060e+01; + p2 = 1.50018e+00; + p3 = 6.34208e+00; + + return (p0 * x / TMath::Power(1. + TMath::Power(x / p1, p2), p3)); + } + + //-------------------------------------------------------------------------// + static Double_t YUpsilon2Spp13TeV(const Double_t* py, const Double_t* /*dummy*/) + { + // Upsilon2s y shape from LHCb pp@13TeV arXiv:1804.09214 + Double_t x = *py; + Float_t p0, p1; + + p0 = 7.50409e+02; + p1 = -3.57039e-02; + + return (p0 * (1. + p1 * x * x)); + } + + //-------------------------------------------------------------------------// + static Double_t V2Upsilon2Spp13TeV(const Double_t* /*dummy*/, const Double_t* /*dummy*/) + { + // Upsilon(2S) v2 + return 0.; + } + + //-------------------------------------------------------------------------// + static Int_t IpUpsilon2Spp13TeV(TRandom*) + { + return 100553; + } + + private: + GeneratorParam* paramUpsilon2S = nullptr; +}; + +///////////////////////////////////////////////////////////////////////////// +class O2_GeneratorParamUpsilon3SFwdY : public GeneratorTGenerator +{ + + public: + O2_GeneratorParamUpsilon3SFwdY() : GeneratorTGenerator("ParamUpsilon3S") + { + paramUpsilon3S = new GeneratorParam(1, -1, PtUpsilon3Spp13TeV, YUpsilon3Spp13TeV, V2Upsilon3Spp13TeV, IpUpsilon3Spp13TeV); + paramUpsilon3S->SetMomentumRange(0., 1.e6); + paramUpsilon3S->SetPtRange(0, 999.); + paramUpsilon3S->SetYRange(-4.2, -2.3); + paramUpsilon3S->SetPhiRange(0., 360.); + paramUpsilon3S->SetDecayer(new TPythia6Decayer()); + paramUpsilon3S->SetForceDecay(kNoDecay); // particle left undecayed + // - - paramUpsilon3S->SetTrackingFlag(1); // (from AliGenParam) -> check this + setTGenerator(paramUpsilon3S); + }; + + ~O2_GeneratorParamUpsilon3SFwdY() + { + delete paramUpsilon3S; + }; + + Bool_t Init() override + { + GeneratorTGenerator::Init(); + paramUpsilon3S->Init(); + return true; + } + + void SetNSignalPerEvent(Int_t nsig) { paramUpsilon3S->SetNumberParticles(nsig); } + + //-------------------------------------------------------------------------// + static Double_t PtUpsilon3Spp13TeV(const Double_t* px, const Double_t* /*dummy*/) + { + // Upsilon3S pt shape from LHCb pp@13TeV arXiv:1804.09214 + Double_t x = *px; + Float_t p0, p1, p2, p3; + + p0 = 3.51590e+01; + p1 = 2.30813e+01; + p2 = 1.40822e+00; + p3 = 9.38026e+00; + + return (p0 * x / TMath::Power(1. + TMath::Power(x / p1, p2), p3)); + } + + //-------------------------------------------------------------------------// + static Double_t YUpsilon3Spp13TeV(const Double_t* py, const Double_t* /*dummy*/) + { + // Upsilon3s y shape from LHCb pp@13TeV arXiv:1804.09214 + Double_t x = *py; + Float_t p0, p1; + + p0 = 3.69961e+02; + p1 = -3.54650e-02; + + return (p0 * (1. + p1 * x * x)); + } + + //-------------------------------------------------------------------------// + static Double_t V2Upsilon3Spp13TeV(const Double_t* /*dummy*/, const Double_t* /*dummy*/) + { + // Upsilon(3S) v2 + return 0.; + } + + //-------------------------------------------------------------------------// + static Int_t IpUpsilon3Spp13TeV(TRandom*) + { + return 200553; + } + + private: + GeneratorParam* paramUpsilon3S = nullptr; +}; + + +} // namespace eventgen +} // namespace o2 + +FairGenerator* GeneratorCocktailBottomoniaToMuonEvtGen_pp13TeV() +{ + + auto genCocktailEvtGen = new o2::eventgen::GeneratorEvtGen(); + + auto genUpsilon1S = new o2::eventgen::O2_GeneratorParamUpsilon1SFwdY; + genUpsilon1S->SetNSignalPerEvent(1); // 1 Upsilon(1S) generated per event by GeneratorParam + + auto genUpsilon2S = new o2::eventgen::O2_GeneratorParamUpsilon2SFwdY; + genUpsilon2S->SetNSignalPerEvent(1); // 1 Upsilon(2S) generated per event by GeneratorParam + + auto genUpsilon3S = new o2::eventgen::O2_GeneratorParamUpsilon3SFwdY; + genUpsilon3S->SetNSignalPerEvent(1); // 1 Upsilon(3S) generated per event by GeneratorParam + + genCocktailEvtGen->AddGenerator(genUpsilon1S, 1); // add Upsilon(1S) generator + genCocktailEvtGen->AddGenerator(genUpsilon2S, 1); // add Upsilon(2S) generator + genCocktailEvtGen->AddGenerator(genUpsilon3S, 1); // add Upsilon(3S) generator + + TString pdgs = "553;100553;200553"; + std::string spdg; + TObjArray* obj = pdgs.Tokenize(";"); + genCocktailEvtGen->SetSizePdg(obj->GetEntriesFast()); + for (int i = 0; i < obj->GetEntriesFast(); i++) { + spdg = obj->At(i)->GetName(); + genCocktailEvtGen->AddPdg(std::stoi(spdg), i); + printf("PDG %d \n", std::stoi(spdg)); + } + genCocktailEvtGen->SetForceDecay(kEvtDiMuon); + + return genCocktailEvtGen; +} +// === END OF pp13tev CLASSES ===== + +// ================================ +// ==== PbPb 5.36 TeV classes ===== +// ================================ +namespace o2 +{ +namespace eventgen +{ +///////////////////////////////////////////////////////////////////////////// +class O2_GeneratorParamUpsilon1SFwdY_PbPb5TeV : public GeneratorTGenerator +{ + public: + O2_GeneratorParamUpsilon1SFwdY_PbPb5TeV() : GeneratorTGenerator("ParamUpsilon1S_PbPb5TeV") + { + paramUpsilon1S = new GeneratorParam(1, -1, PtUpsilon1SPbPb5TeV, YUpsilon1SPbPb5TeV, V2Upsilon1SPbPb5TeV, IpUpsilon1SPbPb5TeV); + paramUpsilon1S->SetMomentumRange(0., 1.e6); + paramUpsilon1S->SetPtRange(0, 999.); + paramUpsilon1S->SetYRange(-4.2, -2.3); // ALICE muon spectrometer + paramUpsilon1S->SetPhiRange(0., 360.); + paramUpsilon1S->SetDecayer(new TPythia6Decayer()); + paramUpsilon1S->SetForceDecay(kNoDecay); // left undecayed for EvtGen + setTGenerator(paramUpsilon1S); + }; + + ~O2_GeneratorParamUpsilon1SFwdY_PbPb5TeV() { delete paramUpsilon1S; }; + + Bool_t Init() override + { + GeneratorTGenerator::Init(); + paramUpsilon1S->Init(); + return true; + } + void SetNSignalPerEvent(Int_t nsig) { paramUpsilon1S->SetNumberParticles(nsig); } + + static Double_t PtUpsilon1SPbPb5TeV(const Double_t* px, const Double_t* /*dummy*/) + { + // Upsilon(1S) pT in PbPb at 5.36 TeV + // Parameterized from pp LHCb 13 TeV (arXiv:1804.09214) scaled to PbPb 5.36 TeV + // NOTE: should be updated once dedicated ALICE PbPb 5.36 TeV tuning is available + Double_t x = *px; + Float_t p0, p1, p2, p3; + p0 = 3.68558e+02; + p1 = 9.15000e+00; // softer than pp 13TeV (p1=10.31) reflecting PbPb medium + p2 = 1.62309e+00; + p3 = 4.84709e+00; + return (p0 * x / TMath::Power(1. + TMath::Power(x / p1, p2), p3)); + } + + + //-------------------------------------------------------------------------// + static Double_t YUpsilon1SPbPb5TeV(const Double_t* py, const Double_t* /*dummy*/) + { + // Upsilon(1S) y in PbPb at 5.36 TeV + // Based on LHCb pp 13 TeV shape (arXiv:1804.09214), reasonable for PbPb forward y + Double_t x = *py; + Float_t p0, p1; + p0 = 3.07931e+03; + p1 = -3.53102e-02; + return (p0 * (1. + p1 * x * x)); + } + + //-------------------------------------------------------------------------// + static Double_t V2Upsilon1SPbPb5TeV(const Double_t* /*dummy*/, const Double_t* /*dummy*/) + { + // Upsilon(1S) v2 - set to 0, can be updated with measurement + return 0.; + } + //-------------------------------------------------------------------------// + static Int_t IpUpsilon1SPbPb5TeV(TRandom*) + { + return 553; // PDG code Upsilon(1S) + } + + private: + GeneratorParam* paramUpsilon1S = nullptr; +}; +///////////////////////////////////////////////////////////////////////////// +class O2_GeneratorParamUpsilon2SFwdY_PbPb5TeV : public GeneratorTGenerator +{ + + public: + O2_GeneratorParamUpsilon2SFwdY_PbPb5TeV() + : GeneratorTGenerator("ParamUpsilon2S_PbPb5TeV") + { + paramUpsilon2S = new GeneratorParam(1, -1, PtUpsilon2SPbPb5TeV, YUpsilon2SPbPb5TeV, V2Upsilon2SPbPb5TeV, IpUpsilon2SPbPb5TeV); + paramUpsilon2S->SetMomentumRange(0., 1.e6); + paramUpsilon2S->SetPtRange(0, 999.); + paramUpsilon2S->SetYRange(-4.2, -2.3); + paramUpsilon2S->SetPhiRange(0., 360.); + paramUpsilon2S->SetDecayer(new TPythia6Decayer()); + paramUpsilon2S->SetForceDecay(kNoDecay); + setTGenerator(paramUpsilon2S); + }; + + ~O2_GeneratorParamUpsilon2SFwdY_PbPb5TeV() { delete paramUpsilon2S; }; + + Bool_t Init() override + { + GeneratorTGenerator::Init(); + paramUpsilon2S->Init(); + return true; + } + + void SetNSignalPerEvent(Int_t nsig) { paramUpsilon2S->SetNumberParticles(nsig); } + + //-------------------------------------------------------------------------// + static Double_t PtUpsilon2SPbPb5TeV(const Double_t* px, const Double_t* /*dummy*/) + { + // Upsilon(2S) pT in PbPb at 5.36 TeV + // Scaled from LHCb pp 13 TeV (arXiv:1804.09214) + // NOTE: should be updated once dedicated ALICE PbPb 5.36 TeV tuning is available + Double_t x = *px; + Float_t p0, p1, p2, p3; + p0 = 7.29541e+01; + p1 = 1.30000e+01; // scaled down from pp 13TeV (p1=14.81) for PbPb + p2 = 1.50018e+00; + p3 = 6.34208e+00; + return (p0 * x / TMath::Power(1. + TMath::Power(x / p1, p2), p3)); + } + + //-------------------------------------------------------------------------// + static Double_t YUpsilon2SPbPb5TeV(const Double_t* py, const Double_t* /*dummy*/) + { + // Upsilon(2S) y in PbPb at 5.36 TeV + Double_t x = *py; + Float_t p0, p1; + p0 = 7.50409e+02; + p1 = -3.57039e-02; + return (p0 * (1. + p1 * x * x)); + } + + //-------------------------------------------------------------------------// + static Double_t V2Upsilon2SPbPb5TeV(const Double_t* /*dummy*/, const Double_t* /*dummy*/) + { + return 0.; + } + + //-------------------------------------------------------------------------// + static Int_t IpUpsilon2SPbPb5TeV(TRandom*) + { + return 100553; // PDG code Upsilon(2S) + } + + private: + GeneratorParam* paramUpsilon2S = nullptr; +}; + +///////////////////////////////////////////////////////////////////////////// +class O2_GeneratorParamUpsilon3SFwdY_PbPb5TeV : public GeneratorTGenerator +{ + public: + O2_GeneratorParamUpsilon3SFwdY_PbPb5TeV() : GeneratorTGenerator("ParamUpsilon3S_PbPb5TeV") + { + paramUpsilon3S = new GeneratorParam(1, -1, PtUpsilon3SPbPb5TeV, YUpsilon3SPbPb5TeV, V2Upsilon3SPbPb5TeV, IpUpsilon3SPbPb5TeV); + paramUpsilon3S->SetMomentumRange(0., 1.e6); + paramUpsilon3S->SetPtRange(0, 999.); + paramUpsilon3S->SetYRange(-4.2, -2.3); + paramUpsilon3S->SetPhiRange(0., 360.); + paramUpsilon3S->SetDecayer(new TPythia6Decayer()); + paramUpsilon3S->SetForceDecay(kNoDecay); + setTGenerator(paramUpsilon3S); + }; + + ~O2_GeneratorParamUpsilon3SFwdY_PbPb5TeV() { delete paramUpsilon3S; }; + + Bool_t Init() override + { + GeneratorTGenerator::Init(); + paramUpsilon3S->Init(); + return true; + } + + void SetNSignalPerEvent(Int_t nsig) { paramUpsilon3S->SetNumberParticles(nsig); } + + //-------------------------------------------------------------------------// + static Double_t PtUpsilon3SPbPb5TeV(const Double_t* px, const Double_t* /*dummy*/) + { + // Upsilon(3S) pT in PbPb at 5.36 TeV + // Scaled from LHCb pp 13 TeV (arXiv:1804.09214) + // NOTE: should be updated once dedicated ALICE PbPb 5.36 TeV tuning is available + Double_t x = *px; + Float_t p0, p1, p2, p3; + p0 = 3.14590e+01; + p1 = 2.05000e+01; // scaled from pp 13TeV (p1=23.08) for PbPb + p2 = 1.40822e+00; + p3 = 9.38026e+00; + return (p0 * x / TMath::Power(1. + TMath::Power(x / p1, p2), p3)); + } + + //-------------------------------------------------------------------------// + static Double_t YUpsilon3SPbPb5TeV(const Double_t* py, const Double_t* /*dummy*/) + { + // Upsilon(3S) y in PbPb at 5.36 TeV + Double_t x = *py; + Float_t p0, p1; + p0 = 3.69961e+02; + p1 = -3.54650e-02; + return (p0 * (1. + p1 * x * x)); + } + + //-------------------------------------------------------------------------// + static Double_t V2Upsilon3SPbPb5TeV(const Double_t* /*dummy*/, const Double_t* /*dummy*/) + { + return 0.; + } + + //-------------------------------------------------------------------------// + static Int_t IpUpsilon3SPbPb5TeV(TRandom*) + { + return 200553; // PDG code Upsilon(3S) + } + + private: + GeneratorParam* paramUpsilon3S = nullptr; +}; + +} // namespace eventgen +} // namespace o2 + +// ================================== +// NEW COCKTAIL FUCNTION FOR PbPb === +// ================================== + +FairGenerator* GeneratorCocktailBottomoniaToMuonEvtGen_PbPb5TeV() +{ + auto genCocktailEvtGen = new o2::eventgen::GeneratorEvtGen(); + + auto genUpsilon1S = new o2::eventgen::O2_GeneratorParamUpsilon1SFwdY_PbPb5TeV; + genUpsilon1S->SetNSignalPerEvent(1); + + auto genUpsilon2S = new o2::eventgen::O2_GeneratorParamUpsilon2SFwdY_PbPb5TeV; + genUpsilon2S->SetNSignalPerEvent(1); + + auto genUpsilon3S = new o2::eventgen::O2_GeneratorParamUpsilon3SFwdY_PbPb5TeV; + genUpsilon3S->SetNSignalPerEvent(1); + + genCocktailEvtGen->AddGenerator(genUpsilon1S, 1); + genCocktailEvtGen->AddGenerator(genUpsilon2S, 1); + genCocktailEvtGen->AddGenerator(genUpsilon3S, 1); + + TString pdgs = "553;100553;200553"; + std::string spdg; + TObjArray* obj = pdgs.Tokenize(";"); + genCocktailEvtGen->SetSizePdg(obj->GetEntriesFast()); + for (int i = 0; i < obj->GetEntriesFast(); i++) { + spdg = obj->At(i)->GetName(); + genCocktailEvtGen->AddPdg(std::stoi(spdg), i); + printf("PDG %d \n", std::stoi(spdg)); + } + genCocktailEvtGen->SetForceDecay(kEvtDiMuon); // Υ → μ⁺μ⁻ via EvtGen + + return genCocktailEvtGen; +} +// ==== END of cocktail function ====== diff --git a/MC/config/PWGDQ/external/generator/GeneratorBplusToJpsiKaon_EvtGen.C b/MC/config/PWGDQ/external/generator/GeneratorBplusToJpsiKaon_EvtGen.C deleted file mode 100644 index 01d389f55..000000000 --- a/MC/config/PWGDQ/external/generator/GeneratorBplusToJpsiKaon_EvtGen.C +++ /dev/null @@ -1,45 +0,0 @@ -// o2-sim -j 1 -n 10 -g external -t external -m "PIPE ITS TPC" -o sgn --configKeyValues "GeneratorExternal.fileName=GeneratorBplusToJpsiKaon_EvtGen.C;GeneratorExternal.funcName=GeneratorBplusToJpsiKaon_EvtGen()" --configFile GeneratorHF_bbbarToBplus_midy.ini -// -// -R__ADD_INCLUDE_PATH($O2DPG_ROOT/MC/config/PWGDQ/EvtGen) -R__ADD_INCLUDE_PATH($O2DPG_ROOT/MC/config/PWGHF/external/generator) -#include "GeneratorEvtGen.C" -#include "GeneratorHF.C" - -FairGenerator* - GeneratorBplusToJpsiKaon_EvtGen(double rapidityMin = -1.5, double rapidityMax = 1.5, bool ispp = true, bool verbose = false, TString pdgs = "521") -{ - auto gen = new o2::eventgen::GeneratorEvtGen(); - gen->setRapidity(rapidityMin, rapidityMax); - gen->setPDG(5); - TString pathO2table = gSystem->ExpandPathName("$O2DPG_ROOT/MC/config/PWGDQ/pythia8/decayer/switchOffBplus.cfg"); - gen->readFile(pathO2table.Data()); - - gen->setVerbose(verbose); - if (ispp) - gen->setFormula("1"); - else - gen->setFormula("max(1.,120.*(x<5.)+80.*(1.-x/20.)*(x>5.)*(x<11.)+240.*(1.-x/13.)*(x>11.))"); - std::string spdg; - TObjArray* obj = pdgs.Tokenize(";"); - gen->SetSizePdg(obj->GetEntriesFast()); - for (int i = 0; i < obj->GetEntriesFast(); i++) { - spdg = obj->At(i)->GetName(); - gen->AddPdg(std::stoi(spdg), i); - printf("PDG %d \n", std::stoi(spdg)); - } - - TString pathO2 = gSystem->ExpandPathName("$O2DPG_ROOT/MC/config/PWGDQ/EvtGen/DecayTablesEvtgen"); - gen->SetDecayTable(Form("%s/BPLUSTOKAONJPSITOELE.DEC", pathO2.Data())); - // print debug - // gen->PrintDebug(); - // set random seed - gen->readString("Random:setSeed on"); - uint random_seed; - unsigned long long int random_value = 0; - ifstream urandom("/dev/urandom", ios::in|ios::binary); - urandom.read(reinterpret_cast(&random_value), sizeof(random_seed)); - gen->readString(Form("Random:seed = %d", random_value % 900000001)); - - return gen; -} diff --git a/MC/config/PWGDQ/external/generator/GeneratorCharmToMu_EvtGen.C b/MC/config/PWGDQ/external/generator/GeneratorCharmToMu_EvtGen.C deleted file mode 100644 index 0f5f45d20..000000000 --- a/MC/config/PWGDQ/external/generator/GeneratorCharmToMu_EvtGen.C +++ /dev/null @@ -1,46 +0,0 @@ -// usage (fwdy) : -//o2-sim -j 4 -n 10 -g external -t external -m "PIPE ITS TPC" -o sgn --configFile GeneratorHF_bbbar_fwdy.ini -// usage (midy) : -//o2-sim -j 4 -n 10 -g external -t external -m "PIPE ITS TPC" -o sgn --configFile GeneratorHF_bbbar_midy.ini -// -// -R__ADD_INCLUDE_PATH($O2DPG_ROOT/MC/config/PWGDQ/EvtGen) -R__ADD_INCLUDE_PATH($O2DPG_ROOT/MC/config/PWGHF/external/generator) -#include "GeneratorEvtGen.C" -#include "GeneratorHF.C" - - -FairGenerator* -GeneratorCharmToMu_EvtGenFwdY(double rapidityMin = -4.3, double rapidityMax = -2.2, bool ispp = true, bool verbose = false, TString pdgs = "411;421;431;4122;4232;4332") -{ - auto gen = new o2::eventgen::GeneratorEvtGen(); - gen->setRapidity(rapidityMin,rapidityMax); - gen->setPDG(4); - TString pathO2table = gSystem->ExpandPathName("$O2DPG_ROOT/MC/config/PWGDQ/pythia8/decayer/switchOffChadrons.cfg"); - gen->readFile(pathO2table.Data()); - - gen->setVerbose(verbose); - if(ispp) gen->setFormula("1"); - else gen->setFormula("max(1.,120.*(x<5.)+80.*(1.-x/20.)*(x>5.)*(x<11.)+240.*(1.-x/13.)*(x>11.))"); - std::string spdg; - TObjArray *obj = pdgs.Tokenize(";"); - gen->SetSizePdg(obj->GetEntriesFast()); - for(int i=0; iGetEntriesFast(); i++) { - spdg = obj->At(i)->GetName(); - gen->AddPdg(std::stoi(spdg),i); - printf("PDG %d \n",std::stoi(spdg)); - } - gen->SetForceDecay(kEvtSemiMuonic); - // set random seed - gen->readString("Random:setSeed on"); - uint random_seed; - unsigned long long int random_value = 0; - ifstream urandom("/dev/urandom", ios::in|ios::binary); - urandom.read(reinterpret_cast(&random_value), sizeof(random_seed)); - gen->readString(Form("Random:seed = %d", random_value % 900000001)); - // print debug - // gen->PrintDebug(); - - return gen; -} - diff --git a/MC/config/PWGDQ/external/generator/GeneratorCocktailPromptCharmoniaToElectronEvtGen_pp13TeV.C b/MC/config/PWGDQ/external/generator/GeneratorCocktailPromptCharmoniaToElectronEvtGen_pp13TeV.C index 4af9a0c51..2bfcdd5a3 100644 --- a/MC/config/PWGDQ/external/generator/GeneratorCocktailPromptCharmoniaToElectronEvtGen_pp13TeV.C +++ b/MC/config/PWGDQ/external/generator/GeneratorCocktailPromptCharmoniaToElectronEvtGen_pp13TeV.C @@ -2,8 +2,9 @@ // o2-sim -j 4 -n 10 -g external -o sgn --configKeyValues "GeneratorExternal.fileName=GeneratorCocktailPromptCharmoniaToElectronEvtGen_pp13TeV.C;GeneratorExternal.funcName=GeneratorCocktailPromptCharmoniaToElectronEvtGen_pp13TeV()" // -R__ADD_INCLUDE_PATH($O2DPG_ROOT/MC/config/PWGDQ/EvtGen) -R__ADD_INCLUDE_PATH($O2DPG_ROOT/MC/config/PWGDQ/PromptQuarkonia) +R__ADD_INCLUDE_PATH($O2DPG_MC_CONFIG_ROOT/MC/config/PWGDQ/EvtGen) +R__ADD_INCLUDE_PATH($O2DPG_MC_CONFIG_ROOT/MC/config/PWGDQ/PromptQuarkonia) +R__LOAD_LIBRARY(libpythia6) #include "GeneratorCocktail.C" #include "GeneratorEvtGen.C" diff --git a/MC/config/PWGDQ/external/generator/GeneratorCocktailPromptCharmoniaToMuonEvtGen_PbPb5TeV.C b/MC/config/PWGDQ/external/generator/GeneratorCocktailPromptCharmoniaToMuonEvtGen_PbPb5TeV.C index 4ae78262a..cd7e5c000 100644 --- a/MC/config/PWGDQ/external/generator/GeneratorCocktailPromptCharmoniaToMuonEvtGen_PbPb5TeV.C +++ b/MC/config/PWGDQ/external/generator/GeneratorCocktailPromptCharmoniaToMuonEvtGen_PbPb5TeV.C @@ -1,8 +1,9 @@ // usage // o2-sim -j 4 -n 10 -g external -o sgn --configKeyValues "GeneratorExternal.fileName=GeneratorCocktailPromptCharmoniaToMuonEvtGen_PbPb5TeV.C;GeneratorExternal.funcName=GeneratorCocktailPromptCharmoniaToMuonEvtGen_PbPb5TeV()" // -R__ADD_INCLUDE_PATH($O2DPG_ROOT/MC/config/PWGDQ/EvtGen) -R__ADD_INCLUDE_PATH($O2DPG_ROOT/MC/config/PWGDQ/PromptQuarkonia) +R__ADD_INCLUDE_PATH($O2DPG_MC_CONFIG_ROOT/MC/config/PWGDQ/EvtGen) +R__ADD_INCLUDE_PATH($O2DPG_MC_CONFIG_ROOT/MC/config/PWGDQ/PromptQuarkonia) +R__LOAD_LIBRARY(libpythia6) #include "GeneratorCocktail.C" #include "GeneratorEvtGen.C" diff --git a/MC/config/PWGDQ/external/generator/GeneratorCocktailPromptCharmoniaToMuonEvtGen_pp13TeV.C b/MC/config/PWGDQ/external/generator/GeneratorCocktailPromptCharmoniaToMuonEvtGen_pp13TeV.C index 8c267f703..237b9c475 100644 --- a/MC/config/PWGDQ/external/generator/GeneratorCocktailPromptCharmoniaToMuonEvtGen_pp13TeV.C +++ b/MC/config/PWGDQ/external/generator/GeneratorCocktailPromptCharmoniaToMuonEvtGen_pp13TeV.C @@ -1,8 +1,9 @@ // usage // o2-sim -j 4 -n 10 -g external -o sgn --configKeyValues "GeneratorExternal.fileName=GeneratorCocktailPromptCharmoniaToMuonEvtGen_pp13TeV.C;GeneratorExternal.funcName=GeneratorCocktailPromptCharmoniaToMuonEvtGen_pp13TeV()" // -R__ADD_INCLUDE_PATH($O2DPG_ROOT/MC/config/PWGDQ/EvtGen) -R__ADD_INCLUDE_PATH($O2DPG_ROOT/MC/config/PWGDQ/PromptQuarkonia) +R__ADD_INCLUDE_PATH($O2DPG_MC_CONFIG_ROOT/MC/config/PWGDQ/EvtGen) +R__ADD_INCLUDE_PATH($O2DPG_MC_CONFIG_ROOT/MC/config/PWGDQ/PromptQuarkonia) +R__LOAD_LIBRARY(libpythia6) #include "GeneratorCocktail.C" #include "GeneratorEvtGen.C" diff --git a/MC/config/PWGDQ/external/generator/GeneratorCocktailPromptCharmoniaToMuonEvtGen_pp5TeV.C b/MC/config/PWGDQ/external/generator/GeneratorCocktailPromptCharmoniaToMuonEvtGen_pp5TeV.C new file mode 100644 index 000000000..63174d4b4 --- /dev/null +++ b/MC/config/PWGDQ/external/generator/GeneratorCocktailPromptCharmoniaToMuonEvtGen_pp5TeV.C @@ -0,0 +1,194 @@ +// usage +// o2-sim -j 4 -n 10 -g external -o sgn --configKeyValues "GeneratorExternal.fileName=GeneratorCocktailPromptCharmoniaToMuonEvtGen_pp5TeV.C;GeneratorExternal.funcName=GeneratorCocktailPromptCharmoniaToMuonEvtGen_pp5TeV()" +// +R__ADD_INCLUDE_PATH($O2DPG_MC_CONFIG_ROOT/MC/config/PWGDQ/EvtGen) +R__ADD_INCLUDE_PATH($O2DPG_MC_CONFIG_ROOT/MC/config/PWGDQ/PromptQuarkonia) +R__LOAD_LIBRARY(libpythia6) +#include "GeneratorCocktail.C" +#include "GeneratorEvtGen.C" + +namespace o2 +{ +namespace eventgen +{ + +class O2_GeneratorParamJpsi : public GeneratorTGenerator +{ + + public: + O2_GeneratorParamJpsi() : GeneratorTGenerator("ParamJpsi") + { + paramJpsi = new GeneratorParam(1, -1, PtJPsipp5TeV, YJPsipp5TeV, V2JPsipp5TeV, IpJPsipp5TeV); + paramJpsi->SetMomentumRange(0., 1.e6); + paramJpsi->SetPtRange(0, 999.); + paramJpsi->SetYRange(-4.2, -2.3); + paramJpsi->SetPhiRange(0., 360.); + paramJpsi->SetDecayer(new TPythia6Decayer()); + paramJpsi->SetForceDecay(kNoDecay); // particle left undecayed + // - - paramJpsi->SetTrackingFlag(1); // (from AliGenParam) -> check this + setTGenerator(paramJpsi); + }; + + ~O2_GeneratorParamJpsi() + { + delete paramJpsi; + }; + + Bool_t Init() override + { + GeneratorTGenerator::Init(); + paramJpsi->Init(); + return true; + } + + void SetNSignalPerEvent(Int_t nsig) { paramJpsi->SetNumberParticles(nsig); } + + //-------------------------------------------------------------------------// + static Double_t PtJPsipp5TeV(const Double_t* px, const Double_t* /*dummy*/) + { + // jpsi pT in pp at 5.02 TeV, tuned on https://www.hepdata.net/record/ins1935680 + Double_t x = *px; + Float_t p0, p1, p2, p3; + p0 = 1; + p1 = 4.30923; + p2 = 1.82061; + p3 = 4.37563; + return p0 * x / TMath::Power(1. + TMath::Power(x / p1, p2), p3); + } + + //-------------------------------------------------------------------------// + static Double_t YJPsipp5TeV(const Double_t* py, const Double_t* /*dummy*/) + { + // jpsi y in pp at 5.02 TeV, tuned on https://www.hepdata.net/record/ins1935680 + Double_t y = *py; + Float_t p0, p1, p2; + p0 = 1; + p1 = 0.0338222; + p2 = 2.96748; + return p0 * TMath::Exp(-(1. / 2.) * TMath::Power(((y - p1) / p2), 2)); + } + + //-------------------------------------------------------------------------// + static Double_t V2JPsipp5TeV(const Double_t* /*dummy*/, const Double_t* /*dummy*/) + { + // jpsi v2 + return 0.; + } + + //-------------------------------------------------------------------------// + static Int_t IpJPsipp5TeV(TRandom*) + { + return 443; + } + + private: + GeneratorParam* paramJpsi = nullptr; +}; + +class O2_GeneratorParamPsi : public GeneratorTGenerator +{ + + public: + O2_GeneratorParamPsi() : GeneratorTGenerator("ParamPsi") + { + paramPsi = new GeneratorParam(1, -1, PtPsipp5TeV, YPsipp5TeV, V2Psipp5TeV, IpPsipp5TeV); + paramPsi->SetMomentumRange(0., 1.e6); + paramPsi->SetPtRange(0, 999.); + paramPsi->SetYRange(-4.2, -2.3); + paramPsi->SetPhiRange(0., 360.); + paramPsi->SetDecayer(new TPythia6Decayer()); + paramPsi->SetForceDecay(kNoDecay); // particle left undecayed + // - - paramJpsi->SetTrackingFlag(1); // check this + setTGenerator(paramPsi); + }; + + ~O2_GeneratorParamPsi() + { + delete paramPsi; + }; + + Bool_t Init() override + { + GeneratorTGenerator::Init(); + paramPsi->Init(); + return true; + } + + void SetNSignalPerEvent(Int_t nsig) { paramPsi->SetNumberParticles(nsig); } + + //-------------------------------------------------------------------------// + static Double_t PtPsipp5TeV(const Double_t* px, const Double_t* /*dummy*/) + { + // psi2s pT in pp at 5.02 TeV, tuned on https://www.hepdata.net/record/ins1935680 + Double_t x = *px; + Float_t p0, p1, p2, p3; + p0 = 1; + p1 = 2.6444; + p2 = 6.17572; + p3 = 0.701753; + return p0 * x / TMath::Power(1. + TMath::Power(x / p1, p2), p3); + } + + //-------------------------------------------------------------------------// + static Double_t YPsipp5TeV(const Double_t* py, const Double_t* /*dummy*/) + { + // psi2s y in pp at 5.02 TeV, tuned on https://www.hepdata.net/record/ins1935680 + // WARNING! The shape extracted from data provide wired rapidity shape (low stat.), the J/psi one is used + Double_t y = *py; + Float_t p0, p1, p2; + // Extracted from Psi(2S) Run 2 data + //p0 = 1; + //p1 = -17.4857; + //p2 = 2.98887; + // Same parametrization as J/psi + p0 = 1; + p1 = 0.0338222; + p2 = 2.96748; + return p0 * TMath::Exp(-(1. / 2.) * TMath::Power(((y - p1) / p2), 2)); + } + + //-------------------------------------------------------------------------// + static Double_t V2Psipp5TeV(const Double_t* /*dummy*/, const Double_t* /*dummy*/) + { + // jpsi v2 + return 0.; + } + + //-------------------------------------------------------------------------// + static Int_t IpPsipp5TeV(TRandom*) + { + return 100443; + } + + private: + GeneratorParam* paramPsi = nullptr; +}; + +} // namespace eventgen +} // namespace o2 + +FairGenerator* GeneratorCocktailPromptCharmoniaToMuonEvtGen_pp5TeV() +{ + + auto genCocktailEvtGen = new o2::eventgen::GeneratorEvtGen(); + + auto genJpsi = new o2::eventgen::O2_GeneratorParamJpsi; + genJpsi->SetNSignalPerEvent(4); // 4 J/psi generated per event by GeneratorParam + auto genPsi = new o2::eventgen::O2_GeneratorParamPsi; + genPsi->SetNSignalPerEvent(2); // 2 Psi(2S) generated per event by GeneratorParam + genCocktailEvtGen->AddGenerator(genJpsi, 1); // 2/3 J/psi + genCocktailEvtGen->AddGenerator(genPsi, 1); // 1/3 Psi(2S) + + TString pdgs = "443;100443"; + std::string spdg; + TObjArray* obj = pdgs.Tokenize(";"); + genCocktailEvtGen->SetSizePdg(obj->GetEntriesFast()); + for (int i = 0; i < obj->GetEntriesFast(); i++) { + spdg = obj->At(i)->GetName(); + genCocktailEvtGen->AddPdg(std::stoi(spdg), i); + printf("PDG %d \n", std::stoi(spdg)); + } + genCocktailEvtGen->SetForceDecay(kEvtDiMuon); + + return genCocktailEvtGen; +} diff --git a/MC/config/PWGDQ/external/generator/GeneratorCocktailStarlight_PbPb5TeV.C b/MC/config/PWGDQ/external/generator/GeneratorCocktailStarlight_PbPb5TeV.C new file mode 100644 index 000000000..a67aeb7f2 --- /dev/null +++ b/MC/config/PWGDQ/external/generator/GeneratorCocktailStarlight_PbPb5TeV.C @@ -0,0 +1,121 @@ +R__ADD_INCLUDE_PATH($O2DPG_MC_CONFIG_ROOT/MC/config/PWGUD/external/generator) +R__ADD_INCLUDE_PATH($O2DPG_MC_CONFIG_ROOT/MC/config/PWGDQ/EvtGen) +#include "GeneratorCocktail.C" +#include "GeneratorStarlight.C" +#include "GeneratorEvtGen.C" +#include + +namespace +{ +o2::eventgen::GeneratorStarlight_class* makeStarlightGenerator(std::string configuration, + float energyCM, + int beam1Z, + int beam1A, + int beam2Z, + int beam2A, + std::string extraParams = "", + std::string dpmjetconf = "") +{ + auto generator = new o2::eventgen::GeneratorStarlight_class(); + generator->selectConfiguration(configuration); + generator->setCollisionSystem(energyCM, beam1Z, beam1A, beam2Z, beam2A); + generator->setExtraParams(extraParams); + // generator->setupDpmjet(dpmjetconf); + return generator; +} +void configureEvtGen(o2::eventgen::GeneratorEvtGen* generator, bool midrapidity) +{ + generator->SetPolarization(1); // Transverse + generator->SetSizePdg(2); + generator->AddPdg(443, 0); + generator->AddPdg(100443, 1); + TString pathO2 = gSystem->ExpandPathName("$O2DPG_MC_CONFIG_ROOT/MC/config/PWGDQ/EvtGen/DecayTablesEvtgen"); + if (midrapidity) { + // generator->SetSizePdg(2); + // generator->AddPdg(443, 0); + // generator->AddPdg(100443, 1); + // TString pathO2 = gSystem->ExpandPathName("$O2DPG_MC_CONFIG_ROOT/MC/config/PWGDQ/EvtGen/DecayTablesEvtgen"); + generator->SetDecayTable(Form("%s/PSITOJPSIPIPI.DEC", pathO2.Data())); + } else { + // generator->SetSizePdg(1); + // generator->AddPdg(100443, 0); + // generator->SetSizePdg(2); + // generator->AddPdg(443, 0); + // generator->AddPdg(100443, 1); + // TString pathO2 = gSystem->ExpandPathName("$O2DPG_MC_CONFIG_ROOT/MC/config/PWGDQ/EvtGen/DecayTablesEvtgen"); + generator->SetDecayTable(Form("%s/PSITOJPSITOMU.DEC", pathO2.Data())); + // TString pathO2 = gSystem->ExpandPathName("$O2DPG_MC_CONFIG_ROOT/MC/config/PWGUD/external/generator/DecayTablesEvtGen"); + // generator->SetDecayTable(Form("%s/PSI2S.MUMUPIPI.DEC", pathO2.Data())); + } +} +} // namespace + +FairGenerator* GeneratorCocktailStarlightMidy_PbPb5TeV(float energyCM = 5360, int beam1Z = 82, int beam1A = 208, int beam2Z = 82, int beam2A = 208, std::string extrapars = "",std::string dpmjetconf = "") +{ + auto genCocktailEvtGen = new o2::eventgen::GeneratorEvtGen(); + configureEvtGen(genCocktailEvtGen, true); + auto genCohJpsi = makeStarlightGenerator("kCohJpsiToElRad", energyCM, beam1Z, beam1A, beam2Z, beam2A, extrapars, dpmjetconf); + genCocktailEvtGen->AddGenerator(genCohJpsi, 1); + auto genIncohJpsi = makeStarlightGenerator("kIncohJpsiToElRad", energyCM, beam1Z, beam1A, beam2Z, beam2A, extrapars, dpmjetconf); + genCocktailEvtGen->AddGenerator(genIncohJpsi, 1); + auto genTwoGammaToEl = makeStarlightGenerator("kTwoGammaToElLow", energyCM, beam1Z, beam1A, beam2Z, beam2A, extrapars, dpmjetconf); + genCocktailEvtGen->AddGenerator(genTwoGammaToEl, 1); + auto genCohPsi2S = makeStarlightGenerator("kCohPsi2sToElPi", energyCM, beam1Z, beam1A, beam2Z, beam2A, extrapars, dpmjetconf); + genCocktailEvtGen->AddGenerator(genCohPsi2S, 1); + auto genIncohPsi2S = makeStarlightGenerator("kIncohPsi2sToElPi", energyCM, beam1Z, beam1A, beam2Z, beam2A, extrapars, dpmjetconf); + genCocktailEvtGen->AddGenerator(genIncohPsi2S, 1); + + return genCocktailEvtGen; +} + +FairGenerator* GeneratorCocktailStarlightCoherentMidy_PbPb5TeV(float energyCM = 5360, int beam1Z = 82, int beam1A = 208, int beam2Z = 82, int beam2A = 208, std::string extrapars = "",std::string dpmjetconf = "") +{ + auto genCocktailEvtGen = new o2::eventgen::GeneratorEvtGen(); + configureEvtGen(genCocktailEvtGen, true); + auto genCohJpsi = makeStarlightGenerator("kCohJpsiToElRad", energyCM, beam1Z, beam1A, beam2Z, beam2A, extrapars, dpmjetconf); + genCocktailEvtGen->AddGenerator(genCohJpsi, 1); + auto genTwoGammaToEl = makeStarlightGenerator("kTwoGammaToElLow", energyCM, beam1Z, beam1A, beam2Z, beam2A, extrapars, dpmjetconf); + genCocktailEvtGen->AddGenerator(genTwoGammaToEl, 1); + auto genCohPsi2S = makeStarlightGenerator("kCohPsi2sToElPi", energyCM, beam1Z, beam1A, beam2Z, beam2A, extrapars, dpmjetconf); + genCocktailEvtGen->AddGenerator(genCohPsi2S, 1); + + return genCocktailEvtGen; +} + +FairGenerator* GeneratorCocktailStarlightIncoherentMidy_PbPb5TeV(float energyCM = 5360, int beam1Z = 82, int beam1A = 208, int beam2Z = 82, int beam2A = 208, std::string extrapars = "",std::string dpmjetconf = "") +{ + auto genCocktailEvtGen = new o2::eventgen::GeneratorEvtGen(); + configureEvtGen(genCocktailEvtGen, true); + auto genIncohJpsi = makeStarlightGenerator("kIncohJpsiToElRad", energyCM, beam1Z, beam1A, beam2Z, beam2A, extrapars, dpmjetconf); + genCocktailEvtGen->AddGenerator(genIncohJpsi, 1); + auto genIncohPsi2S = makeStarlightGenerator("kIncohPsi2sToElPi", energyCM, beam1Z, beam1A, beam2Z, beam2A, extrapars, dpmjetconf); + genCocktailEvtGen->AddGenerator(genIncohPsi2S, 1); + + return genCocktailEvtGen; +} + +FairGenerator* GeneratorCocktailStarlightCoherentFwdy_PbPb5TeV(float energyCM = 5360, int beam1Z = 82, int beam1A = 208, int beam2Z = 82, int beam2A = 208, std::string extrapars = "",std::string dpmjetconf = "") +{ + auto genCocktailEvtGen = new o2::eventgen::GeneratorEvtGen(); + configureEvtGen(genCocktailEvtGen, false); + auto genCohJpsi = makeStarlightGenerator("kCohJpsiToElRad", energyCM, beam1Z, beam1A, beam2Z, beam2A, extrapars, dpmjetconf); + genCocktailEvtGen->AddGenerator(genCohJpsi, 1); + auto genTwoGammaToMu = makeStarlightGenerator("kTwoGammaToMuLow", energyCM, beam1Z, beam1A, beam2Z, beam2A, extrapars, dpmjetconf); + genCocktailEvtGen->AddGenerator(genTwoGammaToMu, 1); + auto genCohPsi2S = makeStarlightGenerator("kCohPsi2sToMuPi", energyCM, beam1Z, beam1A, beam2Z, beam2A, extrapars, dpmjetconf); + genCocktailEvtGen->AddGenerator(genCohPsi2S, 1); + + return genCocktailEvtGen; +} + +FairGenerator* GeneratorCocktailStarlightIncoherentFwdy_PbPb5TeV(float energyCM = 5360, int beam1Z = 82, int beam1A = 208, int beam2Z = 82, int beam2A = 208, std::string extrapars = "",std::string dpmjetconf = "") +{ + auto genCocktailEvtGen = new o2::eventgen::GeneratorEvtGen(); + configureEvtGen(genCocktailEvtGen, false); + auto genIncohJpsi = makeStarlightGenerator("kIncohJpsiToElRad", energyCM, beam1Z, beam1A, beam2Z, beam2A, extrapars, dpmjetconf); + genCocktailEvtGen->AddGenerator(genIncohJpsi, 1); + auto genIncohPsi2S = makeStarlightGenerator("kIncohPsi2sToMuPi", energyCM, beam1Z, beam1A, beam2Z, beam2A, extrapars, dpmjetconf); + genCocktailEvtGen->AddGenerator(genIncohPsi2S, 1); + + return genCocktailEvtGen; +} diff --git a/MC/config/PWGDQ/external/generator/GeneratorHFToMu_EvtGenFwdY_gaptriggered_dq.C b/MC/config/PWGDQ/external/generator/GeneratorHFToMu_EvtGenFwdY_gaptriggered_dq.C new file mode 100644 index 000000000..830c17c5f --- /dev/null +++ b/MC/config/PWGDQ/external/generator/GeneratorHFToMu_EvtGenFwdY_gaptriggered_dq.C @@ -0,0 +1,203 @@ +#include "FairGenerator.h" +#include "Generators/GeneratorPythia8.h" +#include "Pythia8/Pythia.h" +#include "TRandom.h" + +R__ADD_INCLUDE_PATH($O2DPG_MC_CONFIG_ROOT/MC/config/PWGDQ/EvtGen) +#include "GeneratorEvtGen.C" + +#include + +using namespace o2::eventgen; + +namespace o2 +{ +namespace eventgen +{ + +class GeneratorHFToMu_EvtGenFwdY_gaptriggered : public o2::eventgen::GeneratorPythia8 { +public: + + /// constructor + GeneratorHFToMu_EvtGenFwdY_gaptriggered(int inputTriggerRatio = 4) { + + mGeneratedEvents = 0; + mInverseTriggerRatio = inputTriggerRatio; + // define minimum bias event generator + auto seed = (gRandom->TRandom::GetSeed() % 900000000); + TString pathconfigMB = gSystem->ExpandPathName("$O2DPG_MC_CONFIG_ROOT/MC/config/PWGDQ/pythia8/generator/pythia8_inel_triggerGap.cfg"); + pythiaMBgen.readFile(pathconfigMB.Data()); + pythiaMBgen.readString("Random:setSeed on"); + pythiaMBgen.readString("Random:seed " + std::to_string(seed)); + mConfigMBdecays = ""; + mPDG = 4; + mRapidityMin = -1.; + mRapidityMax = 1.; + mHadronMultiplicity = -1; + mHadronRapidityMin = -1.; + mHadronRapidityMax = 1.; + mVerbose = false; + } + + /// Destructor + ~GeneratorHFToMu_EvtGenFwdY_gaptriggered() = default; + + void setPDG(int val) { mPDG = val; }; + void addHadronPDGs(int pdg) { mHadronsPDGs.push_back(pdg); }; + void setHadronMultiplicity(int val) { mHadronMultiplicity = val; }; + void setRapidity(double valMin, double valMax) + { + mRapidityMin = valMin; + mRapidityMax = valMax; + }; + + void setRapidityHadron(double valMin, double valMax) + { + mHadronRapidityMin = valMin; + mHadronRapidityMax = valMax; + }; + + void setConfigMBdecays(TString val){mConfigMBdecays = val;} + + void setVerbose(bool val) { mVerbose = val; }; + +protected: + +Bool_t generateEvent() override + { + // reset event + bool genOk = false; + if (mGeneratedEvents % mInverseTriggerRatio == 0){ + bool ancestor = false; + while (! (genOk && ancestor) ){ + /// reset event + mPythia.event.reset(); + genOk = GeneratorPythia8::generateEvent(); + // find the q-qbar ancestor + ancestor = findHeavyQuarkPair(mPythia.event); + } + } else { + /// reset event + pythiaMBgen.event.reset(); + while (!genOk) { + genOk = pythiaMBgen.next(); + } + mPythia.event = pythiaMBgen.event; + } + mGeneratedEvents++; + if (mVerbose) mOutputEvent.list(); + return true; + } + +Bool_t Init() override + { + if(mConfigMBdecays.Contains("cfg")) pythiaMBgen.readFile(mConfigMBdecays.Data()); + GeneratorPythia8::Init(); + pythiaMBgen.init(); + return true; + } + + // search for q-qbar mother with at least one q in a selected rapidity window + bool findHeavyQuarkPair(Pythia8::Event& event) + { + int countH[mHadronsPDGs.size()]; for(int ipdg=0; ipdg < mHadronsPDGs.size(); ipdg++) countH[ipdg]=0; + bool hasq = false, hasqbar = false, atSelectedY = false, isOkAtPartonicLevel = false; + for (int ipa = 0; ipa < event.size(); ++ipa) { + + if(!isOkAtPartonicLevel){ + auto daughterList = event[ipa].daughterList(); + hasq = false; hasqbar = false; atSelectedY = false; + for (auto ida : daughterList) { + if (event[ida].id() == mPDG) + hasq = true; + if (event[ida].id() == -mPDG) + hasqbar = true; + if ((event[ida].y() > mRapidityMin) && (event[ida].y() < mRapidityMax)) + atSelectedY = true; + } + if (hasq && hasqbar && atSelectedY) isOkAtPartonicLevel = true; + } + + if( (mHadronMultiplicity <= 0) && isOkAtPartonicLevel) return true; // no selection at hadron level + + /// check at hadron level if needed + int ipdg=0; + for (auto& pdgVal : mHadronsPDGs){ + if ( (TMath::Abs(event[ipa].id()) == pdgVal) && (event[ipa].y() > mHadronRapidityMin) && (event[ipa].y() < mHadronRapidityMax) ) countH[ipdg]++; + if(isOkAtPartonicLevel && countH[ipdg] >= mHadronMultiplicity) return true; + ipdg++; + } + } + return false; + }; + + +private: +// Interface to override import particles +Pythia8::Event mOutputEvent; + + // Control gap-triggering + unsigned long long mGeneratedEvents; + int mInverseTriggerRatio; + Pythia8::Pythia pythiaMBgen; // minimum bias event + TString mConfigMBdecays; + int mPDG; + std::vector mHadronsPDGs; + int mHadronMultiplicity; + double mRapidityMin; + double mRapidityMax; + double mHadronRapidityMin; + double mHadronRapidityMax; + bool mVerbose; + }; + +} + +} + +FairGenerator* + GeneratorHFToMu_EvtGenFwdY_gaptriggered_dq(double rapidityMin = -4.3, double rapidityMax = -2.3, bool verbose = false, bool isbb = false, bool forceSemimuonicDecay = false) +{ + TString pdgs; + auto gen = new o2::eventgen::GeneratorEvtGen(); + if (isbb == false) { + gen->setPDG(4); + pdgs = "411;421;431;4122;4132;4232;4332"; + } else { + gen->setPDG(5); + pdgs = "511;521;531;541;5112;5122;5232;5132;5332"; + } + gen->setRapidity(rapidityMin, rapidityMax); + + gen->setRapidityHadron(rapidityMin,rapidityMax); + gen->setHadronMultiplicity(1); + TString pathO2table = gSystem->ExpandPathName("$O2DPG_MC_CONFIG_ROOT/MC/config/PWGDQ/pythia8/decayer/switchOffCBhadrons.cfg"); + gen->readFile(pathO2table.Data()); + gen->setConfigMBdecays(pathO2table); + gen->setVerbose(verbose); + + std::string spdg; + TObjArray* obj = pdgs.Tokenize(";"); + gen->SetSizePdg(obj->GetEntriesFast()); + for (int i = 0; i < obj->GetEntriesFast(); i++) { + spdg = obj->At(i)->GetName(); + gen->AddPdg(std::stoi(spdg), i); + printf("PDG %d \n", std::stoi(spdg)); + gen->addHadronPDGs(std::stoi(spdg)); + } + if (forceSemimuonicDecay == 1) gen->SetForceDecay(kEvtSemiMuonic); + else gen->SetForceDecay(kEvtAll); + + // set random seed + gen->readString("Random:setSeed on"); + uint random_seed; + unsigned long long int random_value = 0; + ifstream urandom("/dev/urandom", ios::in|ios::binary); + urandom.read(reinterpret_cast(&random_value), sizeof(random_seed)); + gen->readString(Form("Random:seed = %d", random_value % 900000001)); + + // print debug + //gengen->PrintDebug(); + + return gen; +} diff --git a/MC/config/PWGDQ/external/generator/GeneratorParam.C b/MC/config/PWGDQ/external/generator/GeneratorParam.C index 2f7a25a7f..8218cc2d4 100644 --- a/MC/config/PWGDQ/external/generator/GeneratorParam.C +++ b/MC/config/PWGDQ/external/generator/GeneratorParam.C @@ -1,3 +1,5 @@ +R__LOAD_LIBRARY(libpythia6) + namespace o2 { namespace eventgen diff --git a/MC/config/PWGDQ/external/generator/GeneratorParamPromptJpsiToElectronEvtGen_pp13TeV.C b/MC/config/PWGDQ/external/generator/GeneratorParamPromptJpsiToElectronEvtGen_pp13TeV.C index 6875edfa3..13d9ac545 100644 --- a/MC/config/PWGDQ/external/generator/GeneratorParamPromptJpsiToElectronEvtGen_pp13TeV.C +++ b/MC/config/PWGDQ/external/generator/GeneratorParamPromptJpsiToElectronEvtGen_pp13TeV.C @@ -1,7 +1,8 @@ // usage // o2-sim -j 4 -n 10 -g external -o sgn --configKeyValues "GeneratorExternal.fileName=GeneratorParamPromptJpsiToElectronEvtGen_pp13TeV.C;GeneratorExternal.funcName=GeneratorParamPromptJpsiToElectronEvtGen_pp13TeV()" // -R__ADD_INCLUDE_PATH($O2DPG_ROOT/MC/config/PWGDQ/EvtGen) +R__ADD_INCLUDE_PATH($O2DPG_MC_CONFIG_ROOT/MC/config/PWGDQ/EvtGen) +R__LOAD_LIBRARY(libpythia6) #include "GeneratorEvtGen.C" namespace o2 diff --git a/MC/config/PWGDQ/external/generator/GeneratorParamPromptJpsiToMuonEvtGen_pp13TeV.C b/MC/config/PWGDQ/external/generator/GeneratorParamPromptJpsiToMuonEvtGen_pp13TeV.C index cfacf86b3..abc9dd310 100644 --- a/MC/config/PWGDQ/external/generator/GeneratorParamPromptJpsiToMuonEvtGen_pp13TeV.C +++ b/MC/config/PWGDQ/external/generator/GeneratorParamPromptJpsiToMuonEvtGen_pp13TeV.C @@ -1,7 +1,8 @@ // usage // o2-sim -j 4 -n 10 -g external -o sgn --configKeyValues "GeneratorExternal.fileName=GeneratorParamPromptJpsiToMuonEvtGen_pp13TeV.C;GeneratorExternal.funcName=GeneratorParamPromptJpsiToMuonEvtGen_pp13TeV()" // -R__ADD_INCLUDE_PATH($O2DPG_ROOT/MC/config/PWGDQ/EvtGen) +R__ADD_INCLUDE_PATH($O2DPG_MC_CONFIG_ROOT/MC/config/PWGDQ/EvtGen) +R__LOAD_LIBRARY(libpythia6) #include "GeneratorEvtGen.C" namespace o2 diff --git a/MC/config/PWGDQ/external/generator/GeneratorParamSingleMuon_PbPb_5TeV.C b/MC/config/PWGDQ/external/generator/GeneratorParamSingleMuon_PbPb_5TeV.C new file mode 100644 index 000000000..91f20acd4 --- /dev/null +++ b/MC/config/PWGDQ/external/generator/GeneratorParamSingleMuon_PbPb_5TeV.C @@ -0,0 +1,161 @@ +// Parameterized generator for muons +// Adaptation of the Run 2 generator by P. Pillot: +// https://github.com/alisw/AliDPG/blob/master/MC/CustomGenerators/PWGDQ/Muon_GenParamSingle_PbPb5TeV_1.C + +#include "FairGenerator.h" +#include "TF1.h" +#include "TRandom.h" +#include "TDatabasePDG.h" +#include "TParticlePDG.h" + +class O2_GeneratorParamMuon : public FairGenerator +{ + public: + O2_GeneratorParamMuon(int npart = 2, int pdg = 13, double ymin = -4.3, double ymax = -2.3, double ptmin = 0.1, double ptmax = 999.) + : FairGenerator("ParaMuon"), fNParticles(npart), fPDGCode(pdg), fYMin(ymin), fYMax(ymax), fPtMin(ptmin), fPtMax(ptmax) + { + TParticlePDG* particle = TDatabasePDG::Instance()->GetParticle(fPDGCode); + fMass = particle->Mass(); + fMass2 = fMass * fMass; + } + + ~O2_GeneratorParamMuon() = default; + + void SetRandomCharge(bool flag) { fRandomizeCharge = flag; } + + void SetPtPars(double p0, double p1, double p2, double p3, double p4, double p5) + { + fPtP0 = p0; + fPtP1 = p1; + fPtP2 = p2; + fPtP3 = p3; + fPtP4 = p4; + fPtP5 = p5; + } + + void SetYPars(double p0, double p1, double p2, double p3, double p4) + { + fYP0 = p0; + fYP1 = p1; + fYP2 = p2; + fYP3 = p3; + fYP4 = p4; + } + + void InitParaFuncs() + { + fPtPara = new TF1("pt-para", PtMuon, fPtMin, fPtMax, 6); + fPtPara->SetParameter(0, fPtP0); + fPtPara->SetParameter(1, fPtP1); + fPtPara->SetParameter(2, fPtP2); + fPtPara->SetParameter(3, fPtP3); + fPtPara->SetParameter(4, fPtP4); + fPtPara->SetParameter(5, fPtP5); + fYPara = new TF1("y-para", YMuon, fYMin, fYMax, 5); + fYPara->SetParameter(0, fYP0); + fYPara->SetParameter(1, fYP1); + fYPara->SetParameter(2, fYP2); + fYPara->SetParameter(3, fYP3); + fYPara->SetParameter(4, fYP4); + } + + static double PtMuon(const double* xx, const double* par) + { + double x = xx[0]; + double p0 = par[0]; + double p1 = par[1]; + double p2 = par[2]; + double p3 = par[3]; + double p4 = par[4]; + double p5 = par[5]; + return p0 * (1. / std::pow(p1 + std::pow(x, p2), p3) + p4 * std::exp(p5 * x)); + } + + static double YMuon(const double* xx, const double* par) + { + double x = xx[0]; + double p0 = par[0]; + double p1 = par[1]; + double p2 = par[2]; + double p3 = par[3]; + double p4 = par[4]; + double x2 = x * x; + return p0 * (1. + p1 * x + p2 * x2 + p3 * x2 * x + p4 * x2 * x2); + } + + bool ReadEvent(FairPrimaryGenerator* primGen) override + { + // no kinematic cuts -> accepting all + for (int i = 0; i < fNParticles; ++i) { + double pt = fPtPara->GetRandom(); + double ty = std::tanh(fYPara->GetRandom()); + double xmt = std::sqrt(pt * pt + fMass2); + double pl = xmt * ty / std::sqrt((1. - ty * ty)); + double phi = gRandom->Uniform(0., 2. * M_PI); + double px = pt * std::cos(phi); + double py = pt * std::sin(phi); + int pdg = fPDGCode; + if (fRandomizeCharge) { + int charge; + if (gRandom->Rndm() < 0.5) { + charge = 1; + } else { + charge = -1; + } + pdg = std::abs(pdg) * charge; + } + primGen->AddTrack(pdg, px, py, pl, 0, 0, 0); + printf("Add track %d %.2f %.2f %.2f \n", pdg, px, py, pl); + } + return kTRUE; + } + + private: + TF1* fPtPara{nullptr}; + TF1* fYPara{nullptr}; + TF1* fdNdPhi{nullptr}; + + // parameters tuned to Pb-Pb @ 5.02 TeV + double fPtP0{797.446}; + double fPtP1{0.830278}; + double fPtP2{0.632177}; + double fPtP3{10.2202}; + double fPtP4{-0.000614809}; + double fPtP5{-1.70993}; + + double fYP0{1.87732}; + double fYP1{0.00658212}; + double fYP2{-0.0988071}; + double fYP3{-0.000452746}; + double fYP4{0.00269782}; + + // configuration + int fPDGCode{13}; + int fNParticles{2}; + double fYMin{-4.3}; + double fYMax{-2.3}; + double fPtMin{0.1}; + double fPtMax{999.}; + bool fRandomizeCharge{true}; + double fMass{0.10566}; + double fMass2{0}; +}; + +FairGenerator* paramMuGen(double ptP0 = 797.446, double ptP1 = 0.830278, + double ptP2 = 0.632177, double ptP3 = 10.2202, + double ptP4 = -0.000614809, double ptP5 = -1.70993, + double yP0 = 1.87732, double yP1 = 0.00658212, + double yP2 = -0.0988071, double yP3 = -0.000452746, + double yP4 = 0.00269782, + int nPart = 2, int pdg = 13, + double ymin = -4.3, double ymax = -2.3, + double ptmin = 0.1, float ptmax = 999., + int randCharge = 1) +{ + auto* gen = new O2_GeneratorParamMuon(nPart, pdg, ymin, ymax, ptmin, ptmax); + gen->SetPtPars(ptP0, ptP1, ptP2, ptP3, ptP4, ptP5); + gen->SetYPars(yP0, yP1, yP2, yP3, yP4); + gen->InitParaFuncs(); + gen->SetRandomCharge(randCharge); + return gen; +} \ No newline at end of file diff --git a/MC/config/PWGDQ/external/generator/GeneratorPromptCharmonia.C b/MC/config/PWGDQ/external/generator/GeneratorPromptCharmonia.C index 17f3e75af..ad603c1e8 100644 --- a/MC/config/PWGDQ/external/generator/GeneratorPromptCharmonia.C +++ b/MC/config/PWGDQ/external/generator/GeneratorPromptCharmonia.C @@ -2,15 +2,19 @@ // generators for prompt charmonia considering different cases (prompt jpsi, prompt psi2S, prompt jpsi+psi2S) at midrapidity and forward rapidity // // usage: -// Jpsi+Psi2S midy: o2-sim -j 4 -n 10 -g external -o sgn --configKeyValues "GeneratorExternal.fileName=$O2DPG_ROOT/MC/config/PWGDQ/external/generator/GeneratorPromptCharmonia.C;GeneratorExternal.funcName=GeneratorCocktailPromptCharmoniaToElectronEvtGen_pp13TeV()" -// Jpsi midy: o2-sim -j 4 -n 10 -g external -o sgn --configKeyValues "GeneratorExternal.fileName=$O2DPG_ROOT/MC/config/PWGDQ/external/generator/GeneratorPromptCharmonia.C;GeneratorExternal.funcName=GeneratorParamPromptJpsiToElectronEvtGen_pp13TeV()" -// Psi2S midy: o2-sim -j 4 -n 10 -g external -o sgn --configKeyValues "GeneratorExternal.fileName=$O2DPG_ROOT/MC/config/PWGDQ/external/generator/GeneratorPromptCharmonia.C;GeneratorExternal.funcName=GeneratorParamPromptPSiToElectronEvtGen_pp13TeV()" -// Jpsi+Psi2S fwdy: o2-sim -j 4 -n 10 -g external -o sgn --configKeyValues "GeneratorExternal.fileName=$O2DPG_ROOT/MC/config/PWGDQ/external/generator/GeneratorPromptCharmonia.C;GeneratorExternal.funcName=GeneratorCocktailPromptCharmoniaToMuonEvtGen_pp13TeV()" -// Jpsi fwdy: o2-sim -j 4 -n 10 -g external -o sgn --configKeyValues "GeneratorExternal.fileName=$O2DPG_ROOT/MC/config/PWGDQ/external/generator/GeneratorPromptCharmonia.C;GeneratorExternal.funcName=GeneratorParamPromptJpsiToMuonEvtGen_pp13TeV()" -// Psi2S fwdy: o2-sim -j 4 -n 10 -g external -o sgn --configKeyValues "GeneratorExternal.fileName=$O2DPG_ROOT/MC/config/PWGDQ/external/generator/GeneratorPromptCharmonia.C;GeneratorExternal.funcName=GeneratorParamPromptPSiToMuonEvtGen_pp13TeV()" +// Jpsi+Psi2S midy: o2-sim -j 4 -n 10 -g external -o sgn --configKeyValues "GeneratorExternal.fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/external/generator/GeneratorPromptCharmonia.C;GeneratorExternal.funcName=GeneratorCocktailPromptCharmoniaToElectronEvtGen_pp13TeV()" +// Jpsi midy: o2-sim -j 4 -n 10 -g external -o sgn --configKeyValues "GeneratorExternal.fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/external/generator/GeneratorPromptCharmonia.C;GeneratorExternal.funcName=GeneratorParamPromptJpsiToElectronEvtGen_pp13TeV()" +// Psi2S midy: o2-sim -j 4 -n 10 -g external -o sgn --configKeyValues "GeneratorExternal.fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/external/generator/GeneratorPromptCharmonia.C;GeneratorExternal.funcName=GeneratorParamPromptPSiToElectronEvtGen_pp13TeV()" +// Jpsi+Psi2S fwdy: o2-sim -j 4 -n 10 -g external -o sgn --configKeyValues "GeneratorExternal.fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/external/generator/GeneratorPromptCharmonia.C;GeneratorExternal.funcName=GeneratorCocktailPromptCharmoniaToMuonEvtGen_pp13TeV()" +// Jpsi fwdy: o2-sim -j 4 -n 10 -g external -o sgn --configKeyValues "GeneratorExternal.fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/external/generator/GeneratorPromptCharmonia.C;GeneratorExternal.funcName=GeneratorParamPromptJpsiToMuonEvtGen_pp13TeV()" +// Psi2S fwdy: o2-sim -j 4 -n 10 -g external -o sgn --configKeyValues "GeneratorExternal.fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/external/generator/GeneratorPromptCharmonia.C;GeneratorExternal.funcName=GeneratorParamPromptPSiToMuonEvtGen_pp13TeV()" +// ChiC1+ChiC2 midy:o2-sim -j 4 -n 10 -g external -o sgn --configKeyValues "GeneratorExternal.fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/external/generator/GeneratorPromptCharmonia.C;GeneratorExternal.funcName=GeneratorCocktailChiCToElectronEvtGen_pp13TeV()" // -R__ADD_INCLUDE_PATH($O2DPG_ROOT/MC/config/PWGDQ/EvtGen) +// + +R__ADD_INCLUDE_PATH($O2DPG_MC_CONFIG_ROOT/MC/config/PWGDQ/EvtGen) +R__LOAD_LIBRARY(libpythia6) #include "GeneratorCocktail.C" #include "GeneratorEvtGen.C" @@ -164,6 +168,294 @@ class O2_GeneratorParamPsiMidY : public GeneratorTGenerator GeneratorParam* paramPsi = nullptr; }; +class O2_GeneratorParamJpsiMidY_5TeV : public GeneratorTGenerator +{ + + public: + O2_GeneratorParamJpsiMidY_5TeV() : GeneratorTGenerator("paramJpsi") + { + paramJpsi = new GeneratorParam(1, -1, PtJPsipp5TeV, YJPsipp5TeV, V2JPsipp5TeV, IpJPsipp5TeV); + paramJpsi->SetMomentumRange(0., 1.e6); + paramJpsi->SetPtRange(0., 1000.); + paramJpsi->SetYRange(-1.0, 1.0); + paramJpsi->SetPhiRange(0., 360.); + paramJpsi->SetDecayer(new TPythia6Decayer()); // Pythia + paramJpsi->SetForceDecay(kNoDecay); // particle left undecayed + setTGenerator(paramJpsi); + }; + + ~O2_GeneratorParamJpsiMidY_5TeV() + { + delete paramJpsi; + }; + + Bool_t Init() override + { + GeneratorTGenerator::Init(); + paramJpsi->Init(); + return true; + } + + void SetNSignalPerEvent(Int_t nsig) { paramJpsi->SetNumberParticles(nsig); } + + //-------------------------------------------------------------------------// + static Double_t PtJPsipp5TeV(const Double_t* px, const Double_t* /*dummy*/) + { + // JPSi pt at 5.02 TeV: https: // https://www.hepdata.net/record/ins1899703 (2108.02523) + // + const Double_t kC = 1.10642; + const Double_t kpt0 = 4.5504; + const Double_t kn = 3.58669; + Double_t pt = px[0]; + + return kC * pt / TMath::Power((1. + (pt / kpt0) * (pt / kpt0)), kn); + } + + //-------------------------------------------------------------------------// + static Double_t YJPsipp5TeV(const Double_t* py, const Double_t* /*dummy*/) + { + // Taken the same as: jpsi y in pp at 13 TeV, tuned on data, prompt jpsi ALICE+LHCb, 13 TeV + Double_t y = *py; + Float_t p0, p1, p2; + p0 = 7.79382e+00; + p1 = 2.87827e-06; + p2 = 4.41847e+00; + return p0 * TMath::Exp(-(1. / 2.) * TMath::Power(((y - p1) / p2), 2)); + } + + //-------------------------------------------------------------------------// + static Double_t V2JPsipp5TeV(const Double_t* /*dummy*/, const Double_t* /*dummy*/) + { + // jpsi v2 + return 0.; + } + + //-------------------------------------------------------------------------// + static Int_t IpJPsipp5TeV(TRandom*) + { + return 443; + } + + private: + GeneratorParam* paramJpsi = nullptr; +}; + +class O2_GeneratorParamPsiMidY_5TeV : public GeneratorTGenerator +{ + + public: + O2_GeneratorParamPsiMidY_5TeV() : GeneratorTGenerator("ParamPsi") + { + paramPsi = new GeneratorParam(1, -1, PtPsipp5TeV, YPsipp5TeV, V2Psipp5TeV, IpPsipp5TeV); + paramPsi->SetMomentumRange(0., 1.e6); // Momentum range added from me + paramPsi->SetPtRange(0., 1000.); // transverse of momentum range + paramPsi->SetYRange(-1.0, 1.0); // rapidity range + paramPsi->SetPhiRange(0., 360.); // phi range + paramPsi->SetDecayer(new TPythia6Decayer()); // Pythia decayer + paramPsi->SetForceDecay(kNoDecay); // particle left undecayed + setTGenerator(paramPsi); // Setting parameters to ParamPsi for Psi(2S) + }; + + ~O2_GeneratorParamPsiMidY_5TeV() + { + delete paramPsi; + }; + + Bool_t Init() override + { + GeneratorTGenerator::Init(); + paramPsi->Init(); + return true; + } + void SetNSignalPerEvent(Int_t nsig) { paramPsi->SetNumberParticles(nsig); } + + //-------------------------------------------------------------------------// + static Double_t PtPsipp5TeV(const Double_t* px, const Double_t* /*dummy*/) + { + // Same as JPsi at 5.02 TeV since ratio is almost flat in pT + // + const Double_t kC = 1.10642; + const Double_t kpt0 = 4.55041; + const Double_t kn = 3.58669; + Double_t pt = px[0]; + + return kC * pt / TMath::Power((1. + (pt / kpt0) * (pt / kpt0)), kn); + } + + //-------------------------------------------------------------------------// + static Double_t YPsipp5TeV(const Double_t* py, const Double_t* /*dummy*/) + { + // Taken same as jpsi y in pp at 13 TeV, tuned on data, prompt jpsi ALICE+LHCb, 13 TeV + Double_t y = *py; + Float_t p0, p1, p2; + p0 = 7.79382e+00; + p1 = 2.87827e-06; + p2 = 4.41847e+00; + return p0 * TMath::Exp(-(1. / 2.) * TMath::Power(((y - p1) / p2), 2)); + } + + //-------------------------------------------------------------------------// + static Double_t V2Psipp5TeV(const Double_t* /*dummy*/, const Double_t* /*dummy*/) + { + // jpsi v2 + return 0.; + } + + //-------------------------------------------------------------------------// + static Int_t IpPsipp5TeV(TRandom*) + { + return 100443; + } + + private: + GeneratorParam* paramPsi = nullptr; +}; + +//==================================================================================================== +class O2_GeneratorParamJpsiMidY_96TeV : public GeneratorTGenerator +{ + + public: + O2_GeneratorParamJpsiMidY_96TeV() : GeneratorTGenerator("paramJpsi") + { + paramJpsi = new GeneratorParam(1, -1, PtJPsipp96TeV, YJPsipp96TeV, V2JPsipp96TeV, IpJPsipp96TeV); + paramJpsi->SetMomentumRange(0., 1.e6); + paramJpsi->SetPtRange(0., 1000.); + paramJpsi->SetYRange(-1.0, 1.0); + paramJpsi->SetPhiRange(0., 360.); + paramJpsi->SetDecayer(new TPythia6Decayer()); // Pythia + paramJpsi->SetForceDecay(kNoDecay); // particle left undecayed + setTGenerator(paramJpsi); + }; + + ~O2_GeneratorParamJpsiMidY_96TeV() + { + delete paramJpsi; + }; + + Bool_t Init() override + { + GeneratorTGenerator::Init(); + paramJpsi->Init(); + return true; + } + + void SetNSignalPerEvent(Int_t nsig) { paramJpsi->SetNumberParticles(nsig); } + + //-------------------------------------------------------------------------// + static Double_t PtJPsipp96TeV(const Double_t* px, const Double_t* /*dummy*/) + { + // JPSi pt extrapolated at 9.6 TeV: https: // https://www.hepdata.net/record/ins1899703 (2108.02523) + // + const Double_t kC = 1.48862; + const Double_t kpt0 = 4.64005; + const Double_t kn = 3.57353; + Double_t pt = px[0]; + + return kC * pt / TMath::Power((1. + (pt / kpt0) * (pt / kpt0)), kn); + } + + //-------------------------------------------------------------------------// + static Double_t YJPsipp96TeV(const Double_t* py, const Double_t* /*dummy*/) + { + // Taken the same as: jpsi y in pp at 13 TeV, tuned on data, prompt jpsi ALICE+LHCb, 13 TeV + Double_t y = *py; + Float_t p0, p1, p2; + p0 = 7.79382e+00; + p1 = 2.87827e-06; + p2 = 4.41847e+00; + return p0 * TMath::Exp(-(1. / 2.) * TMath::Power(((y - p1) / p2), 2)); + } + + //-------------------------------------------------------------------------// + static Double_t V2JPsipp96TeV(const Double_t* /*dummy*/, const Double_t* /*dummy*/) + { + // jpsi v2 + return 0.; + } + + //-------------------------------------------------------------------------// + static Int_t IpJPsipp96TeV(TRandom*) + { + return 443; + } + + private: + GeneratorParam* paramJpsi = nullptr; +}; + +class O2_GeneratorParamPsiMidY_96TeV : public GeneratorTGenerator +{ + + public: + O2_GeneratorParamPsiMidY_96TeV() : GeneratorTGenerator("ParamPsi") + { + paramPsi = new GeneratorParam(1, -1, PtPsipp96TeV, YPsipp96TeV, V2Psipp96TeV, IpPsipp96TeV); + paramPsi->SetMomentumRange(0., 1.e6); // Momentum range added from me + paramPsi->SetPtRange(0., 1000.); // transverse of momentum range + paramPsi->SetYRange(-1.0, 1.0); // rapidity range + paramPsi->SetPhiRange(0., 360.); // phi range + paramPsi->SetDecayer(new TPythia6Decayer()); // Pythia decayer + paramPsi->SetForceDecay(kNoDecay); // particle left undecayed + setTGenerator(paramPsi); // Setting parameters to ParamPsi for Psi(2S) + }; + + ~O2_GeneratorParamPsiMidY_96TeV() + { + delete paramPsi; + }; + + Bool_t Init() override + { + GeneratorTGenerator::Init(); + paramPsi->Init(); + return true; + } + void SetNSignalPerEvent(Int_t nsig) { paramPsi->SetNumberParticles(nsig); } + + //-------------------------------------------------------------------------// + static Double_t PtPsipp96TeV(const Double_t* px, const Double_t* /*dummy*/) + { + // Same as JPsi at JPSi pt extrapolated at 9.6 TeV (since ratio is almost flat in pT) + // + // + const Double_t kC = 1.48862; + const Double_t kpt0 = 4.64005; + const Double_t kn = 3.57353; + Double_t pt = px[0]; + + return kC * pt / TMath::Power((1. + (pt / kpt0) * (pt / kpt0)), kn); + } + + //-------------------------------------------------------------------------// + static Double_t YPsipp96TeV(const Double_t* py, const Double_t* /*dummy*/) + { + // Taken same as jpsi y in pp at 13 TeV, tuned on data, prompt jpsi ALICE+LHCb, 13 TeV + Double_t y = *py; + Float_t p0, p1, p2; + p0 = 7.79382e+00; + p1 = 2.87827e-06; + p2 = 4.41847e+00; + return p0 * TMath::Exp(-(1. / 2.) * TMath::Power(((y - p1) / p2), 2)); + } + + //-------------------------------------------------------------------------// + static Double_t V2Psipp96TeV(const Double_t* /*dummy*/, const Double_t* /*dummy*/) + { + // jpsi v2 + return 0.; + } + + //-------------------------------------------------------------------------// + static Int_t IpPsipp96TeV(TRandom*) + { + return 100443; + } + + private: + GeneratorParam* paramPsi = nullptr; +}; +//==================================================================================================== class O2_GeneratorParamJpsiFwdY : public GeneratorTGenerator { @@ -311,26 +603,867 @@ class O2_GeneratorParamPsiFwdY : public GeneratorTGenerator }; -} // namespace eventgen -} // namespace o2 - -FairGenerator* - GeneratorCocktailPromptCharmoniaToElectronEvtGen_pp13TeV() +class O2_GeneratorParamChiC1 : public GeneratorTGenerator { - auto genCocktailEvtGen = new o2::eventgen::GeneratorEvtGen(); - auto genJpsi = new o2::eventgen::O2_GeneratorParamJpsiMidY; - genJpsi->SetNSignalPerEvent(1); // signal per event for J/Psi - auto genPsi = new o2::eventgen::O2_GeneratorParamPsiMidY; - genPsi->SetNSignalPerEvent(1); // signal per event for Psi(2s) - genCocktailEvtGen->AddGenerator(genJpsi, 1); // add cocktail --> J/Psi - genCocktailEvtGen->AddGenerator(genPsi, 1); // add cocktail --> Psi(2s) + public: + O2_GeneratorParamChiC1() : GeneratorTGenerator("ParamChiC1") + { + paramChiC1 = new GeneratorParam(1, -1, PtChiC1pp13TeV, YChiC1pp13TeV, V2ChiC1pp13TeV, IpChiC1pp13TeV); + paramChiC1->SetMomentumRange(0., 1.e6); + paramChiC1->SetPtRange(0., 1000.); + paramChiC1->SetYRange(-1.0, 1.0); + paramChiC1->SetPhiRange(0., 360.); + paramChiC1->SetDecayer(new TPythia6Decayer()); // Pythia + paramChiC1->SetForceDecay(kNoDecay); // particle left undecayed + setTGenerator(paramChiC1); + }; - TString pdgs = "443;100443"; - std::string spdg; - TObjArray* obj = pdgs.Tokenize(";"); - genCocktailEvtGen->SetSizePdg(obj->GetEntriesFast()); - for (int i = 0; i < obj->GetEntriesFast(); i++) { + ~O2_GeneratorParamChiC1() + { + delete paramChiC1; + }; + + Bool_t Init() override + { + GeneratorTGenerator::Init(); + paramChiC1->Init(); + return true; + } + + void SetNSignalPerEvent(Int_t nsig) { paramChiC1->SetNumberParticles(nsig); } + + //-------------------------------------------------------------------------// + static Double_t PtChiC1pp13TeV(const Double_t* px, const Double_t* /*dummy*/) + { + // prompt J/Psi pT + // pp, 13TeV (tuned on pp 13 TeV, 2016-2018) + // + // ChiC1 R/(1+R12) Jpsi R= Ra+Rb pT(Jpsi), R12 = R12a + R12b pT(Jpsi)+ R12c pT(Jpsi)^2 + // C. Rosenthal, Y. Pachmayer. LHCb chiC/Jpsi and chiC1/chiC2, + // PLB 718 (2012) 431 + // JHEP 10 (2013) 115, PLB 714 (2012) 215 + // Linear function for chiC to Jpsi and quadratic for ChiC2/ChiC1 + + const Double_t Ra = 0.121; + const Double_t Rb = 0.011; + + const Double_t R12a = 1.43953; + const Double_t R12b = -0.145874; + const Double_t R12c = 0.00638469; + + const Double_t kC = 2.28550e+00; + const Double_t kpt0 = 3.73619e+00; + const Double_t kn = 2.81708e+00; + Double_t pt = px[0]; + + Double_t scaleChiC1 = (Ra+ Rb*pt)/(1+R12a+R12b*pt+R12c*pt*pt); + + + return scaleChiC1 * kC * pt / TMath::Power((1. + (pt / kpt0) * (pt / kpt0)), kn); + } + + //-------------------------------------------------------------------------// + static Double_t YChiC1pp13TeV(const Double_t* py, const Double_t* /*dummy*/) + { + // jpsi y in pp at 13 TeV, tuned on data, prompt jpsi ALICE+LHCb, 13 TeV + Double_t y = *py; + Float_t p0, p1, p2; + p0 = 7.79382e+00; + p1 = 2.87827e-06; + p2 = 4.41847e+00; + return p0 * TMath::Exp(-(1. / 2.) * TMath::Power(((y - p1) / p2), 2)); + } + + + //-------------------------------------------------------------------------// + static Double_t V2ChiC1pp13TeV(const Double_t* /*dummy*/, const Double_t* /*dummy*/) + { + // jpsi v2 + return 0.; + } + + //-------------------------------------------------------------------------// + static Int_t IpChiC1pp13TeV(TRandom*) + { + return 20443; + } + + private: + GeneratorParam* paramChiC1 = nullptr; +}; + +class O2_GeneratorParamChiC2 : public GeneratorTGenerator +{ + + public: + O2_GeneratorParamChiC2() : GeneratorTGenerator("ParamChiC2") + { + paramChiC2 = new GeneratorParam(1, -1, PtChiC2pp13TeV, YChiC2pp13TeV, V2ChiC2pp13TeV, IpChiC2pp13TeV); + paramChiC2->SetMomentumRange(0., 1.e6); // Momentum range added from me + paramChiC2->SetPtRange(0., 1000.); // transverse of momentum range + paramChiC2->SetYRange(-1.0, 1.0); // rapidity range + paramChiC2->SetPhiRange(0., 360.); // phi range + paramChiC2->SetDecayer(new TPythia6Decayer()); // Pythia decayer + paramChiC2->SetForceDecay(kNoDecay); // particle left undecayed + setTGenerator(paramChiC2); // Setting parameters to ParamPsi for Psi(2S) + }; + + ~O2_GeneratorParamChiC2() + { + delete paramChiC2; + }; + + Bool_t Init() override + { + GeneratorTGenerator::Init(); + paramChiC2->Init(); + return true; + } + void SetNSignalPerEvent(Int_t nsig) { paramChiC2->SetNumberParticles(nsig); } + + //-------------------------------------------------------------------------// + static Double_t PtChiC2pp13TeV(const Double_t* px, const Double_t* /*dummy*/) + { + + // ChiC2 R/(1+1/R12) Jpsi R= Ra+Rb pT(Jpsi), R12 = R12a + R12b pT(Jpsi)+ R12c pT(Jpsi)^2 + // prompt J/Psi pT + // pp, 13TeV (tuned on pp 13 TeV, 2016-2018) + // + const Double_t Ra = 0.121; + const Double_t Rb = 0.011; + + const Double_t R12a = 1.43953; + const Double_t R12b = -0.145874; + const Double_t R12c = 0.00638469; + + + + + const Double_t kC = 2.28550e+00; + const Double_t kpt0 = 3.73619e+00; + const Double_t kn = 2.81708e+00; + Double_t pt = px[0]; + Double_t scaleChiC2 = (Ra+ Rb*pt)/(1.+1./(R12a+R12b*pt+R12c*pt*pt)); + + + return scaleChiC2 * kC * pt / TMath::Power((1. + (pt / kpt0) * (pt / kpt0)), kn); + } + + + //-------------------------------------------------------------------------// + static Double_t YChiC2pp13TeV(const Double_t* py, const Double_t* /*dummy*/) + { + // jpsi y in pp at 13 TeV, tuned on data, prompt jpsi ALICE+LHCb, 13 TeV + Double_t y = *py; + Float_t p0, p1, p2; + p0 = 7.79382e+00; + p1 = 2.87827e-06; + p2 = 4.41847e+00; + return p0 * TMath::Exp(-(1. / 2.) * TMath::Power(((y - p1) / p2), 2)); + } + + //-------------------------------------------------------------------------// + static Double_t V2ChiC2pp13TeV(const Double_t* /*dummy*/, const Double_t* /*dummy*/) + { + // jpsi v2 + return 0.; + } + + //-------------------------------------------------------------------------// + static Int_t IpChiC2pp13TeV(TRandom*) + { + return 445; + } + + private: + GeneratorParam* paramChiC2 = nullptr; +}; + +class O2_GeneratorParamJpsipp5TeV : public GeneratorTGenerator +{ + + public: + O2_GeneratorParamJpsipp5TeV() : GeneratorTGenerator("ParamJpsi") + { + paramJpsi = new GeneratorParam(1, -1, PtJPsipp5TeV, YJPsipp5TeV, V2JPsipp5TeV, IpJPsipp5TeV); + paramJpsi->SetMomentumRange(0., 1.e6); + paramJpsi->SetPtRange(0, 999.); + paramJpsi->SetYRange(-4.2, -2.3); + paramJpsi->SetPhiRange(0., 360.); + paramJpsi->SetDecayer(new TPythia6Decayer()); + paramJpsi->SetForceDecay(kNoDecay); // particle left undecayed + // - - paramJpsi->SetTrackingFlag(1); // (from AliGenParam) -> check this + setTGenerator(paramJpsi); + }; + + ~O2_GeneratorParamJpsipp5TeV() + { + delete paramJpsi; + }; + + Bool_t Init() override + { + GeneratorTGenerator::Init(); + paramJpsi->Init(); + return true; + } + + void SetNSignalPerEvent(Int_t nsig) { paramJpsi->SetNumberParticles(nsig); } + + //-------------------------------------------------------------------------// + static Double_t PtJPsipp5TeV(const Double_t* px, const Double_t* /*dummy*/) + { + // jpsi pT in pp at 5.02 TeV, tuned on https://www.hepdata.net/record/ins1935680 + Double_t x = *px; + Float_t p0, p1, p2, p3; + p0 = 1; + p1 = 4.30923; + p2 = 1.82061; + p3 = 4.37563; + return p0 * x / TMath::Power(1. + TMath::Power(x / p1, p2), p3); + } + + //-------------------------------------------------------------------------// + static Double_t YJPsipp5TeV(const Double_t* py, const Double_t* /*dummy*/) + { + // jpsi y in pp at 5.02 TeV, tuned on https://www.hepdata.net/record/ins1935680 + Double_t y = *py; + Float_t p0, p1, p2; + p0 = 1; + p1 = 0.0338222; + p2 = 2.96748; + return p0 * TMath::Exp(-(1. / 2.) * TMath::Power(((y - p1) / p2), 2)); + } + + //-------------------------------------------------------------------------// + static Double_t V2JPsipp5TeV(const Double_t* /*dummy*/, const Double_t* /*dummy*/) + { + // jpsi v2 + return 0.; + } + + //-------------------------------------------------------------------------// + static Int_t IpJPsipp5TeV(TRandom*) + { + return 443; + } + + private: + GeneratorParam* paramJpsi = nullptr; +}; + +class O2_GeneratorParamPsipp5TeV : public GeneratorTGenerator +{ + + public: + O2_GeneratorParamPsipp5TeV() : GeneratorTGenerator("ParamPsi") + { + paramPsi = new GeneratorParam(1, -1, PtPsipp5TeV, YPsipp5TeV, V2Psipp5TeV, IpPsipp5TeV); + paramPsi->SetMomentumRange(0., 1.e6); + paramPsi->SetPtRange(0, 999.); + paramPsi->SetYRange(-4.2, -2.3); + paramPsi->SetPhiRange(0., 360.); + paramPsi->SetDecayer(new TPythia6Decayer()); + paramPsi->SetForceDecay(kNoDecay); // particle left undecayed + // - - paramJpsi->SetTrackingFlag(1); // check this + setTGenerator(paramPsi); + }; + + ~O2_GeneratorParamPsipp5TeV() + { + delete paramPsi; + }; + + Bool_t Init() override + { + GeneratorTGenerator::Init(); + paramPsi->Init(); + return true; + } + + void SetNSignalPerEvent(Int_t nsig) { paramPsi->SetNumberParticles(nsig); } + + //-------------------------------------------------------------------------// + static Double_t PtPsipp5TeV(const Double_t* px, const Double_t* /*dummy*/) + { + // psi2s pT in pp at 5.02 TeV, tuned on https://www.hepdata.net/record/ins1935680 + Double_t x = *px; + Float_t p0, p1, p2, p3; + p0 = 1; + p1 = 2.6444; + p2 = 6.17572; + p3 = 0.701753; + return p0 * x / TMath::Power(1. + TMath::Power(x / p1, p2), p3); + } + + //-------------------------------------------------------------------------// + static Double_t YPsipp5TeV(const Double_t* py, const Double_t* /*dummy*/) + { + // psi2s y in pp at 5.02 TeV, tuned on https://www.hepdata.net/record/ins1935680 + // WARNING! The shape extracted from data provide wired rapidity shape (low stat.), the J/psi one is used + Double_t y = *py; + Float_t p0, p1, p2; + // Extracted from Psi(2S) Run 2 data + //p0 = 1; + //p1 = -17.4857; + //p2 = 2.98887; + // Same parametrization as J/psi + p0 = 1; + p1 = 0.0338222; + p2 = 2.96748; + return p0 * TMath::Exp(-(1. / 2.) * TMath::Power(((y - p1) / p2), 2)); + } + + //-------------------------------------------------------------------------// + static Double_t V2Psipp5TeV(const Double_t* /*dummy*/, const Double_t* /*dummy*/) + { + // jpsi v2 + return 0.; + } + + //-------------------------------------------------------------------------// + static Int_t IpPsipp5TeV(TRandom*) + { + return 100443; + } + + private: + GeneratorParam* paramPsi = nullptr; +}; + +class O2_GeneratorParamJpsiOO5TeV : public GeneratorTGenerator +{ + + public: + O2_GeneratorParamJpsiOO5TeV() : GeneratorTGenerator("ParamJpsi") + { + paramJpsi = new GeneratorParam(1, -1, PtJPsiOO5TeV, YJPsiOO5TeV, V2JPsiOO5TeV, IpJPsiOO5TeV); + paramJpsi->SetMomentumRange(0., 1.e6); + paramJpsi->SetPtRange(0, 999.); + paramJpsi->SetYRange(-4.2, -2.3); + paramJpsi->SetPhiRange(0., 360.); + paramJpsi->SetDecayer(new TPythia6Decayer()); + paramJpsi->SetForceDecay(kNoDecay); // particle left undecayed + // - - paramJpsi->SetTrackingFlag(1); // (from AliGenParam) -> check this + setTGenerator(paramJpsi); + }; + + ~O2_GeneratorParamJpsiOO5TeV() + { + delete paramJpsi; + }; + + Bool_t Init() override + { + GeneratorTGenerator::Init(); + paramJpsi->Init(); + return true; + } + + void SetNSignalPerEvent(Int_t nsig) { paramJpsi->SetNumberParticles(nsig); } + + //-------------------------------------------------------------------------// + static Double_t PtJPsiOO5TeV(const Double_t* px, const Double_t* /*dummy*/) + { + // jpsi pT in OO at 5.36 TeV, tuned on Data LHC25ae_pass2 + Double_t x = *px; + Float_t p0, p1, p2, p3; + p0 = 1; + p1 = 4.11443; + p2 = 1.92874; + p3 = 3.79588; + return p0 * x / TMath::Power(1. + TMath::Power(x / p1, p2), p3); + } + + //-------------------------------------------------------------------------// + static Double_t YJPsiOO5TeV(const Double_t* py, const Double_t* /*dummy*/) + { + // jpsi y in OO at 5.36 TeV, tuned on Data LHC25ae_pass2 + Double_t y = *py; + Float_t p0, p1, p2; + p0 = 1; + p1 = 2.12653e-05; + p2 = 3.28228; + return p0 * TMath::Exp(-(1. / 2.) * TMath::Power(((y - p1) / p2), 2)); + } + + //-------------------------------------------------------------------------// + static Double_t V2JPsiOO5TeV(const Double_t* /*dummy*/, const Double_t* /*dummy*/) + { + // jpsi v2 + return 0.; + } + + //-------------------------------------------------------------------------// + static Int_t IpJPsiOO5TeV(TRandom*) + { + return 443; + } + + private: + GeneratorParam* paramJpsi = nullptr; +}; + +class O2_GeneratorParamPsiOO5TeV : public GeneratorTGenerator +{ + + public: + O2_GeneratorParamPsiOO5TeV() : GeneratorTGenerator("ParamPsi") + { + paramPsi = new GeneratorParam(1, -1, PtPsiOO5TeV, YPsiOO5TeV, V2PsiOO5TeV, IpPsiOO5TeV); + paramPsi->SetMomentumRange(0., 1.e6); + paramPsi->SetPtRange(0, 999.); + paramPsi->SetYRange(-4.2, -2.3); + paramPsi->SetPhiRange(0., 360.); + paramPsi->SetDecayer(new TPythia6Decayer()); + paramPsi->SetForceDecay(kNoDecay); // particle left undecayed + // - - paramJpsi->SetTrackingFlag(1); // check this + setTGenerator(paramPsi); + }; + + ~O2_GeneratorParamPsiOO5TeV() + { + delete paramPsi; + }; + + Bool_t Init() override + { + GeneratorTGenerator::Init(); + paramPsi->Init(); + return true; + } + + void SetNSignalPerEvent(Int_t nsig) { paramPsi->SetNumberParticles(nsig); } + + //-------------------------------------------------------------------------// + static Double_t PtPsiOO5TeV(const Double_t* px, const Double_t* /*dummy*/) + { + // jpsi pT in OO at 5.36 TeV, tuned on Data LHC25ae_pass2 + Double_t x = *px; + Float_t p0, p1, p2, p3; + p0 = 1; + p1 = 4.11443; + p2 = 1.92874; + p3 = 3.79588; + return p0 * x / TMath::Power(1. + TMath::Power(x / p1, p2), p3); + } + + //-------------------------------------------------------------------------// + static Double_t YPsiOO5TeV(const Double_t* py, const Double_t* /*dummy*/) + { + // jpsi y in OO at 5.36 TeV, tuned on Data LHC25ae_pass2 + Double_t y = *py; // DA AGGIUNGERE + Float_t p0, p1, p2; + p0 = 1; + p1 = 2.12653e-05; + p2 = 3.28228; + return p0 * TMath::Exp(-(1. / 2.) * TMath::Power(((y - p1) / p2), 2)); + } + + //-------------------------------------------------------------------------// + static Double_t V2PsiOO5TeV(const Double_t* /*dummy*/, const Double_t* /*dummy*/) + { + // jpsi v2 + return 0.; + } + + //-------------------------------------------------------------------------// + static Int_t IpPsiOO5TeV(TRandom*) + { + return 100443; + } + + private: + GeneratorParam* paramPsi = nullptr; +}; + +class O2_GeneratorParamJpsipp96TeV : public GeneratorTGenerator +{ + + public: + O2_GeneratorParamJpsipp96TeV() : GeneratorTGenerator("ParamJpsi") + { + paramJpsi = new GeneratorParam(1, -1, PtJPsipp96TeV, YJPsipp96TeV, V2JPsipp96TeV, IpJPsipp96TeV); + paramJpsi->SetMomentumRange(0., 1.e6); + paramJpsi->SetPtRange(0, 999.); + paramJpsi->SetYRange(-4.2, -2.0); + paramJpsi->SetPhiRange(0., 360.); + paramJpsi->SetDecayer(new TPythia6Decayer()); + paramJpsi->SetForceDecay(kNoDecay); // particle left undecayed + // - - paramJpsi->SetTrackingFlag(1); // (from AliGenParam) -> check this + setTGenerator(paramJpsi); + }; + + ~O2_GeneratorParamJpsipp96TeV() + { + delete paramJpsi; + }; + + Bool_t Init() override + { + GeneratorTGenerator::Init(); + paramJpsi->Init(); + return true; + } + + void SetNSignalPerEvent(Int_t nsig) { paramJpsi->SetNumberParticles(nsig); } + + //-------------------------------------------------------------------------// + static Double_t PtJPsipp96TeV(const Double_t* px, const Double_t* /*dummy*/) + { + // Parameters extrapolated linearly between 5 TeV and 13 TeV as a function of log(sqrt(s)) + Double_t x = *px; + Float_t p0, p1, p2, p3; + p0 = 1; + p1 = 4.61097; + p2 = 1.7333; + p3 = 4.45508; + return p0 * x / TMath::Power(1. + TMath::Power(x / p1, p2), p3); + } + + //-------------------------------------------------------------------------// + static Double_t YJPsipp96TeV(const Double_t* py, const Double_t* /*dummy*/) + { + // Parameters extrapolated linearly between 5 TeV and 13 TeV as a function of log(sqrt(s)), shifted by a rapidity boost dy=0.35 + Double_t y = *py; + Double_t deltaY = 0.35; + Double_t yCM = y - deltaY; + Float_t p0, p1, p2; + p0 = 1; + p1 = 0.0107769; + p2 = 2.98205; + return p0 * TMath::Exp(-(1. / 2.) * TMath::Power(((yCM - p1) / p2), 2)); + } + + //-------------------------------------------------------------------------// + static Double_t V2JPsipp96TeV(const Double_t* /*dummy*/, const Double_t* /*dummy*/) + { + // jpsi v2 + return 0.; + } + + //-------------------------------------------------------------------------// + static Int_t IpJPsipp96TeV(TRandom*) + { + return 443; + } + + private: + GeneratorParam* paramJpsi = nullptr; +}; + +class O2_GeneratorParamPsipp96TeV : public GeneratorTGenerator +{ + + public: + O2_GeneratorParamPsipp96TeV() : GeneratorTGenerator("ParamPsi") + { + paramPsi = new GeneratorParam(1, -1, PtPsipp96TeV, YPsipp96TeV, V2Psipp96TeV, IpPsipp96TeV); + paramPsi->SetMomentumRange(0., 1.e6); + paramPsi->SetPtRange(0, 999.); + paramPsi->SetYRange(-4.2, -2.0); + paramPsi->SetPhiRange(0., 360.); + paramPsi->SetDecayer(new TPythia6Decayer()); + paramPsi->SetForceDecay(kNoDecay); // particle left undecayed + // - - paramJpsi->SetTrackingFlag(1); // check this + setTGenerator(paramPsi); + }; + + ~O2_GeneratorParamPsipp96TeV() + { + delete paramPsi; + }; + + Bool_t Init() override + { + GeneratorTGenerator::Init(); + paramPsi->Init(); + return true; + } + + void SetNSignalPerEvent(Int_t nsig) { paramPsi->SetNumberParticles(nsig); } + + //-------------------------------------------------------------------------// + static Double_t PtPsipp96TeV(const Double_t* px, const Double_t* /*dummy*/) + { + // Taking same parameters as Psi(2S) at 13 TeV + Double_t x = *px; + Float_t p0, p1, p2, p3; + p0 = 1; + p1 = 4.75208; + p2 = 1.69247; + p3 = 4.49224; + return p0 * x / TMath::Power(1. + TMath::Power(x / p1, p2), p3); + } + + //-------------------------------------------------------------------------// + static Double_t YPsipp96TeV(const Double_t* py, const Double_t* /*dummy*/) + { + // Taking same parameters as Psi(2S) at 13 TeV + Double_t y = *py; + Double_t deltaY = 0.35; + Double_t yCM = y - deltaY; + Float_t p0, p1, p2; + p0 = 1; + p1 = 0; + p2 = 2.98887; + return p0 * TMath::Exp(-(1. / 2.) * TMath::Power(((yCM - p1) / p2), 2)); + } + + //-------------------------------------------------------------------------// + static Double_t V2Psipp96TeV(const Double_t* /*dummy*/, const Double_t* /*dummy*/) + { + // jpsi v2 + return 0.; + } + + //-------------------------------------------------------------------------// + static Int_t IpPsipp96TeV(TRandom*) + { + return 100443; + } + + private: + GeneratorParam* paramPsi = nullptr; +}; + + +class O2_GeneratorParamJpsiPbPb5TeV : public GeneratorTGenerator +{ + + public: + O2_GeneratorParamJpsiPbPb5TeV() : GeneratorTGenerator("ParamJpsi") + { + paramJpsi = new GeneratorParam(1, -1, PtJPsiPbPb5TeV, YJPsiPbPb5TeV, V2JPsiPbPb5TeV, IpJPsiPbPb5TeV); + paramJpsi->SetMomentumRange(0., 1.e6); + paramJpsi->SetPtRange(0, 999.); + paramJpsi->SetYRange(-4.2, -2.3); + paramJpsi->SetPhiRange(0., 360.); + paramJpsi->SetDecayer(new TPythia6Decayer()); + paramJpsi->SetForceDecay(kNoDecay); // particle left undecayed + // - - paramJpsi->SetTrackingFlag(1); // (from AliGenParam) -> check this + setTGenerator(paramJpsi); + }; + + ~O2_GeneratorParamJpsiPbPb5TeV() + { + delete paramJpsi; + }; + + Bool_t Init() override + { + GeneratorTGenerator::Init(); + paramJpsi->Init(); + return true; + } + + void SetNSignalPerEvent(Int_t nsig) { paramJpsi->SetNumberParticles(nsig); } + + //-------------------------------------------------------------------------// + static Double_t PtJPsiPbPb5TeV(const Double_t* px, const Double_t* /*dummy*/) + { + // jpsi pT in PbPb, tuned on data (2015) -> Castillo embedding https://alice.its.cern.ch/jira/browse/ALIROOT-8174?jql=text%20~%20%22LHC19a2%22 + Double_t x = *px; + Float_t p0, p1, p2, p3; + p0 = 1.00715e6; + p1 = 3.50274; + p2 = 1.93403; + p3 = 3.96363; + return p0 * x / TMath::Power(1. + TMath::Power(x / p1, p2), p3); + } + + //-------------------------------------------------------------------------// + static Double_t YJPsiPbPb5TeV(const Double_t* py, const Double_t* /*dummy*/) + { + // jpsi y in PbPb, tuned on data (2015) -> Castillo embedding https://alice.its.cern.ch/jira/browse/ALIROOT-8174?jql=text%20~%20%22LHC19a2%22 + Double_t y = *py; + Float_t p0, p1, p2; + p0 = 1.09886e6; + p1 = 0; + p2 = 2.12568; + return p0 * TMath::Exp(-(1. / 2.) * TMath::Power(((y - p1) / p2), 2)); + } + + //-------------------------------------------------------------------------// + static Double_t V2JPsiPbPb5TeV(const Double_t* /*dummy*/, const Double_t* /*dummy*/) + { + // jpsi v2 + return 0.; + } + + //-------------------------------------------------------------------------// + static Int_t IpJPsiPbPb5TeV(TRandom*) + { + return 443; + } + + private: + GeneratorParam* paramJpsi = nullptr; +}; + +class O2_GeneratorParamPsiPbPb5TeV : public GeneratorTGenerator +{ + + public: + O2_GeneratorParamPsiPbPb5TeV() : GeneratorTGenerator("ParamPsi") + { + paramPsi = new GeneratorParam(1, -1, PtPsiPbPb5TeV, YPsiPbPb5TeV, V2PsiPbPb5TeV, IpPsiPbPb5TeV); + paramPsi->SetMomentumRange(0., 1.e6); + paramPsi->SetPtRange(0, 999.); + paramPsi->SetYRange(-4.2, -2.3); + paramPsi->SetPhiRange(0., 360.); + paramPsi->SetDecayer(new TPythia6Decayer()); + paramPsi->SetForceDecay(kNoDecay); // particle left undecayed + // - - paramJpsi->SetTrackingFlag(1); // check this + setTGenerator(paramPsi); + }; + + ~O2_GeneratorParamPsiPbPb5TeV() + { + delete paramPsi; + }; + + Bool_t Init() override + { + GeneratorTGenerator::Init(); + paramPsi->Init(); + return true; + } + + void SetNSignalPerEvent(Int_t nsig) { paramPsi->SetNumberParticles(nsig); } + + //-------------------------------------------------------------------------// + static Double_t PtPsiPbPb5TeV(const Double_t* px, const Double_t* /*dummy*/) + { + // jpsi pT in PbPb, tuned on data (2015) -> Castillo embedding https://alice.its.cern.ch/jira/browse/ALIROOT-8174?jql=text%20~%20%22LHC19a2%22 + Double_t x = *px; + Float_t p0, p1, p2, p3; + p0 = 1.00715e6; + p1 = 3.50274; + p2 = 1.93403; + p3 = 3.96363; + return p0 * x / TMath::Power(1. + TMath::Power(x / p1, p2), p3); + } + + //-------------------------------------------------------------------------// + static Double_t YPsiPbPb5TeV(const Double_t* py, const Double_t* /*dummy*/) + { + // jpsi y in PbPb, tuned on data (2015) -> Castillo embedding https://alice.its.cern.ch/jira/browse/ALIROOT-8174?jql=text%20~%20%22LHC19a2%22 + Double_t y = *py; + Float_t p0, p1, p2; + p0 = 1.09886e6; + p1 = 0; + p2 = 2.12568; + return p0 * TMath::Exp(-(1. / 2.) * TMath::Power(((y - p1) / p2), 2)); + } + + //-------------------------------------------------------------------------// + static Double_t V2PsiPbPb5TeV(const Double_t* /*dummy*/, const Double_t* /*dummy*/) + { + // jpsi v2 + return 0.; + } + + //-------------------------------------------------------------------------// + static Int_t IpPsiPbPb5TeV(TRandom*) + { + return 100443; + } + + private: + GeneratorParam* paramPsi = nullptr; +}; + + +class O2_GeneratorParamX3872MidY : public GeneratorTGenerator +{ + + public: + O2_GeneratorParamX3872MidY() : GeneratorTGenerator("ParamX3872MidY") + { + paramX3872 = new GeneratorParam(1, -1, PtX3872pp13TeV, YX3872pp13TeV, V2X3872pp13TeV, IpX3872pp13TeV); + paramX3872->SetMomentumRange(0., 1.e6); + paramX3872->SetPtRange(0., 1000.); + paramX3872->SetYRange(-1.0, 1.0); + paramX3872->SetPhiRange(0., 360.); + paramX3872->SetDecayer(new TPythia6Decayer()); // Pythia + paramX3872->SetForceDecay(kNoDecay); // particle left undecayed + setTGenerator(paramX3872); + }; + + ~O2_GeneratorParamX3872MidY() + { + delete paramX3872; + }; + + Bool_t Init() override + { + GeneratorTGenerator::Init(); + paramX3872->Init(); + return true; + } + + void SetNSignalPerEvent(Int_t nsig) { paramX3872->SetNumberParticles(nsig); } + + //-------------------------------------------------------------------------// + static Double_t PtX3872pp13TeV(const Double_t* px, const Double_t* /*dummy*/) + { + // prompt X3872 pT + // pp, 13TeV (tuned LHCb pp 13 TeV) + // + const Double_t kC = 7.64519e+00 ; + const Double_t kpt0 = 5.30628e+00; + const Double_t kn = 3.30887e+00; + Double_t pt = px[0]; + return kC * pt / TMath::Power((1. + (pt / kpt0) * (pt / kpt0)), kn); + } + + //-------------------------------------------------------------------------// + static Double_t YX3872pp13TeV(const Double_t* py, const Double_t* /*dummy*/) + { + // flat rapidity distribution assumed at midrapidity + return 1.; + } + + //-------------------------------------------------------------------------// + static Double_t V2X3872pp13TeV(const Double_t* /*dummy*/, const Double_t* /*dummy*/) + { + // X3872 v2 + return 0.; + } + + //-------------------------------------------------------------------------// + static Int_t IpX3872pp13TeV(TRandom*) + { + return 9920443; + } + + private: + GeneratorParam* paramX3872 = nullptr; +}; + + +} // namespace eventgen +} // namespace o2 + +FairGenerator* + GeneratorCocktailPromptCharmoniaToElectronEvtGen_pp13TeV() +{ + auto genCocktailEvtGen = new o2::eventgen::GeneratorEvtGen(); + + auto genJpsi = new o2::eventgen::O2_GeneratorParamJpsiMidY; + genJpsi->SetNSignalPerEvent(1); // signal per event for J/Psi + auto genPsi = new o2::eventgen::O2_GeneratorParamPsiMidY; + genPsi->SetNSignalPerEvent(1); // signal per event for Psi(2s) + genCocktailEvtGen->AddGenerator(genJpsi, 1); // add cocktail --> J/Psi + genCocktailEvtGen->AddGenerator(genPsi, 1); // add cocktail --> Psi(2s) + + TString pdgs = "443;100443"; + std::string spdg; + TObjArray* obj = pdgs.Tokenize(";"); + genCocktailEvtGen->SetSizePdg(obj->GetEntriesFast()); + for (int i = 0; i < obj->GetEntriesFast(); i++) { spdg = obj->At(i)->GetName(); genCocktailEvtGen->AddPdg(std::stoi(spdg), i); printf("PDG %d \n", std::stoi(spdg)); @@ -343,11 +1476,11 @@ FairGenerator* return genCocktailEvtGen; } -FairGenerator* - GeneratorParamPromptJpsiToElectronEvtGen_pp13TeV(TString pdgs = "443") +FairGenerator * +GeneratorParamPromptJpsiToElectronEvtGen_pp13TeV(TString pdgs = "443", int nSignalPerEvent = 1) { auto gen = new o2::eventgen::GeneratorEvtGen(); - gen->SetNSignalPerEvent(1); // number of jpsis per event + gen->SetNSignalPerEvent(nSignalPerEvent); // number of jpsis per event std::string spdg; TObjArray* obj = pdgs.Tokenize(";"); @@ -414,6 +1547,161 @@ FairGenerator* GeneratorCocktailPromptCharmoniaToMuonEvtGen_pp13TeV() return genCocktailEvtGen; } +FairGenerator* GeneratorCocktailPromptCharmoniaToMuonEvtGen_pp5TeV() +{ + + auto genCocktailEvtGen = new o2::eventgen::GeneratorEvtGen(); + + auto genJpsi = new o2::eventgen::O2_GeneratorParamJpsipp5TeV; + genJpsi->SetNSignalPerEvent(1); // 1 J/psi generated per event by GeneratorParam + auto genPsi = new o2::eventgen::O2_GeneratorParamPsipp5TeV; + genPsi->SetNSignalPerEvent(1); // 1 Psi(2S) generated per event by GeneratorParam + genCocktailEvtGen->AddGenerator(genJpsi, 1); // add J/psi generator + genCocktailEvtGen->AddGenerator(genPsi, 1); // add Psi(2S) generator + + TString pdgs = "443;100443"; + std::string spdg; + TObjArray* obj = pdgs.Tokenize(";"); + genCocktailEvtGen->SetSizePdg(obj->GetEntriesFast()); + for (int i = 0; i < obj->GetEntriesFast(); i++) { + spdg = obj->At(i)->GetName(); + genCocktailEvtGen->AddPdg(std::stoi(spdg), i); + printf("PDG %d \n", std::stoi(spdg)); + } + genCocktailEvtGen->SetForceDecay(kEvtDiMuon); + + return genCocktailEvtGen; +} + +FairGenerator* GeneratorCocktailPromptCharmoniaToMuonEvtGen_OO5TeV() +{ + + auto genCocktailEvtGen = new o2::eventgen::GeneratorEvtGen(); + + auto genJpsi = new o2::eventgen::O2_GeneratorParamJpsiOO5TeV; + genJpsi->SetNSignalPerEvent(1); // 1 J/psi generated per event by GeneratorParam + auto genPsi = new o2::eventgen::O2_GeneratorParamPsiOO5TeV; + genPsi->SetNSignalPerEvent(1); // 1 Psi(2S) generated per event by GeneratorParam + genCocktailEvtGen->AddGenerator(genJpsi, 1); // add J/psi generator + genCocktailEvtGen->AddGenerator(genPsi, 1); // add Psi(2S) generator + + TString pdgs = "443;100443"; + std::string spdg; + TObjArray* obj = pdgs.Tokenize(";"); + genCocktailEvtGen->SetSizePdg(obj->GetEntriesFast()); + for (int i = 0; i < obj->GetEntriesFast(); i++) { + spdg = obj->At(i)->GetName(); + genCocktailEvtGen->AddPdg(std::stoi(spdg), i); + printf("PDG %d \n", std::stoi(spdg)); + } + genCocktailEvtGen->SetForceDecay(kEvtDiMuon); + + return genCocktailEvtGen; +} + +FairGenerator* GeneratorCocktailPromptCharmoniaToMuonEvtGen_pp96TeV() +{ + + auto genCocktailEvtGen = new o2::eventgen::GeneratorEvtGen(); + + auto genJpsi = new o2::eventgen::O2_GeneratorParamJpsipp96TeV; + genJpsi->SetNSignalPerEvent(1); // 1 J/psi generated per event by GeneratorParam + auto genPsi = new o2::eventgen::O2_GeneratorParamPsipp96TeV; + genPsi->SetNSignalPerEvent(1); // 1 Psi(2S) generated per event by GeneratorParam + genCocktailEvtGen->AddGenerator(genJpsi, 1); // add J/psi generator + genCocktailEvtGen->AddGenerator(genPsi, 1); // add Psi(2S) generator + + TString pdgs = "443;100443"; + std::string spdg; + TObjArray* obj = pdgs.Tokenize(";"); + genCocktailEvtGen->SetSizePdg(obj->GetEntriesFast()); + for (int i = 0; i < obj->GetEntriesFast(); i++) { + spdg = obj->At(i)->GetName(); + genCocktailEvtGen->AddPdg(std::stoi(spdg), i); + printf("PDG %d \n", std::stoi(spdg)); + } + genCocktailEvtGen->SetForceDecay(kEvtDiMuon); + + return genCocktailEvtGen; +} + + +FairGenerator* GeneratorCocktailPromptCharmoniaToElectronEvtGen_pp5TeV() +{ + + auto genCocktailEvtGen = new o2::eventgen::GeneratorEvtGen(); + + auto genJpsi = new o2::eventgen::O2_GeneratorParamJpsiMidY_5TeV; + genJpsi->SetNSignalPerEvent(1); // 1 J/psi generated per event by GeneratorParam + auto genPsi = new o2::eventgen::O2_GeneratorParamPsiMidY_5TeV; + genPsi->SetNSignalPerEvent(1); // 1 Psi(2S) generated per event by GeneratorParam + genCocktailEvtGen->AddGenerator(genJpsi, 1); // add J/psi generator + genCocktailEvtGen->AddGenerator(genPsi, 1); // add Psi(2S) generator + + TString pdgs = "443;100443"; + std::string spdg; + TObjArray* obj = pdgs.Tokenize(";"); + genCocktailEvtGen->SetSizePdg(obj->GetEntriesFast()); + for (int i = 0; i < obj->GetEntriesFast(); i++) { + spdg = obj->At(i)->GetName(); + genCocktailEvtGen->AddPdg(std::stoi(spdg), i); + printf("PDG %d \n", std::stoi(spdg)); + } + genCocktailEvtGen->SetForceDecay(kEvtDiElectron); + + return genCocktailEvtGen; +} + +FairGenerator* GeneratorCocktailPromptCharmoniaToElectronEvtGen_pp96TeV() +{ + + auto genCocktailEvtGen = new o2::eventgen::GeneratorEvtGen(); + + auto genJpsi = new o2::eventgen::O2_GeneratorParamJpsiMidY_96TeV; + genJpsi->SetNSignalPerEvent(1); // 1 J/psi generated per event by GeneratorParam + auto genPsi = new o2::eventgen::O2_GeneratorParamPsiMidY_96TeV; + genPsi->SetNSignalPerEvent(1); // 1 Psi(2S) generated per event by GeneratorParam + genCocktailEvtGen->AddGenerator(genJpsi, 1); // add J/psi generator + genCocktailEvtGen->AddGenerator(genPsi, 1); // add Psi(2S) generator + + TString pdgs = "443;100443"; + std::string spdg; + TObjArray* obj = pdgs.Tokenize(";"); + genCocktailEvtGen->SetSizePdg(obj->GetEntriesFast()); + for (int i = 0; i < obj->GetEntriesFast(); i++) { + spdg = obj->At(i)->GetName(); + genCocktailEvtGen->AddPdg(std::stoi(spdg), i); + printf("PDG %d \n", std::stoi(spdg)); + } + genCocktailEvtGen->SetForceDecay(kEvtDiElectron); + + return genCocktailEvtGen; +} + +FairGenerator* + GeneratorParamPromptPsiToJpsiPiPiEvtGen_pp13TeV(TString pdgs = "100443") +{ + auto gen = new o2::eventgen::GeneratorEvtGen(); + gen->SetNSignalPerEvent(1); // number of jpsis per event + + std::string spdg; + TObjArray* obj = pdgs.Tokenize(";"); + gen->SetSizePdg(obj->GetEntriesFast()); + for (int i = 0; i < obj->GetEntriesFast(); i++) { + spdg = obj->At(i)->GetName(); + gen->AddPdg(std::stoi(spdg), i); + printf("PDG %d \n", std::stoi(spdg)); + } + TString pathO2 = gSystem->ExpandPathName("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/EvtGen/DecayTablesEvtgen"); + gen->SetDecayTable(Form("%s/PSITOJPSIPIPI.DEC", pathO2.Data())); + + // print debug + gen->PrintDebug(); + + return gen; +} + + FairGenerator* GeneratorParamPromptJpsiToMuonEvtGen_pp13TeV(TString pdgs = "443") { @@ -458,3 +1746,113 @@ FairGenerator* return gen; } + + +FairGenerator* + GeneratorCocktailChiCToElectronEvtGen_pp13TeV() +{ + auto genCocktailEvtGen = new o2::eventgen::GeneratorEvtGen(); + + auto genChiC1 = new o2::eventgen::O2_GeneratorParamChiC1; + genChiC1->SetNSignalPerEvent(1); // signal per event for ChiC1 + auto genChiC2 = new o2::eventgen::O2_GeneratorParamChiC2; + genChiC2->SetNSignalPerEvent(1); // signal per event for ChiC2 + genCocktailEvtGen->AddGenerator(genChiC1, 1); // add cocktail --> ChiC1 + genCocktailEvtGen->AddGenerator(genChiC2, 1); // add cocktail --> ChiC2 + + + TString pdgs = "20443;445"; + std::string spdg; + TObjArray* obj = pdgs.Tokenize(";"); + genCocktailEvtGen->SetSizePdg(obj->GetEntriesFast()); + for (int i = 0; i < obj->GetEntriesFast(); i++) { + spdg = obj->At(i)->GetName(); + genCocktailEvtGen->AddPdg(std::stoi(spdg), i); + printf("PDG %d \n", std::stoi(spdg)); + } +// genCocktailEvtGen->SetForceDecay(kEvtDiElectron); + genCocktailEvtGen->SetForceDecay(kEvtChiToJpsiGammaToElectronElectron); + // print debug + genCocktailEvtGen->PrintDebug(); + + return genCocktailEvtGen; +} + +FairGenerator* + GeneratorCocktailPromptCharmoniaToMuonEvtGen_PbPb5TeV() +{ + auto genCocktailEvtGen = new o2::eventgen::GeneratorEvtGen(); + + auto genJpsi = new o2::eventgen::O2_GeneratorParamJpsiPbPb5TeV; + genJpsi->SetNSignalPerEvent(4); // 4 J/psi generated per event by GeneratorParam + auto genPsi = new o2::eventgen::O2_GeneratorParamPsiPbPb5TeV; + genPsi->SetNSignalPerEvent(2); // 2 Psi(2S) generated per event by GeneratorParam + genCocktailEvtGen->AddGenerator(genJpsi, 1); // 2/3 J/psi + genCocktailEvtGen->AddGenerator(genPsi, 1); // 1/3 Psi(2S) + + TString pdgs = "443;100443"; + std::string spdg; + TObjArray* obj = pdgs.Tokenize(";"); + genCocktailEvtGen->SetSizePdg(obj->GetEntriesFast()); + for (int i = 0; i < obj->GetEntriesFast(); i++) { + spdg = obj->At(i)->GetName(); + genCocktailEvtGen->AddPdg(std::stoi(spdg), i); + printf("PDG %d \n", std::stoi(spdg)); + } + genCocktailEvtGen->SetForceDecay(kEvtDiMuon); + + return genCocktailEvtGen; +} + + +FairGenerator* + GeneratorParamX3872ToJpsiEvtGen_pp13TeV(TString pdgs = "9920443") +{ + auto gen = new o2::eventgen::GeneratorEvtGen(); + gen->SetNSignalPerEvent(1); // number of jpsis per event + + std::string spdg; + TObjArray* obj = pdgs.Tokenize(";"); + gen->SetSizePdg(obj->GetEntriesFast()); + for (int i = 0; i < obj->GetEntriesFast(); i++) { + spdg = obj->At(i)->GetName(); + gen->AddPdg(std::stoi(spdg), i); + printf("PDG %d \n", std::stoi(spdg)); + } + TString pathO2 = gSystem->ExpandPathName("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/EvtGen/DecayTablesEvtgen"); + gen->SetDecayTable(Form("%s/X3872TOJPSIPIPI.DEC", pathO2.Data())); + + // print debug + gen->PrintDebug(); + + return gen; +} + + +FairGenerator* GeneratorCocktailX3872AndPsi2StoJpsi_pp13TeV() +{ + auto genCocktailEvtGen = new o2::eventgen::GeneratorEvtGen(); + + auto genX3872 = new o2::eventgen::O2_GeneratorParamX3872MidY; + genX3872->SetNSignalPerEvent(1); // number of jpsis per event + auto genPsi2S = new o2::eventgen::O2_GeneratorParamPsiMidY; + genPsi2S->SetNSignalPerEvent(1); // number of jpsis per event + genCocktailEvtGen->AddGenerator(genX3872, 1); // add J/psi generator + genCocktailEvtGen->AddGenerator(genPsi2S, 1); // add Psi(2S) generator + + TString pdgs = "9920443;100443"; + std::string spdg; + TObjArray* obj = pdgs.Tokenize(";"); + genCocktailEvtGen->SetSizePdg(obj->GetEntriesFast()); + for (int i = 0; i < obj->GetEntriesFast(); i++) { + spdg = obj->At(i)->GetName(); + genCocktailEvtGen->AddPdg(std::stoi(spdg), i); + printf("PDG %d \n", std::stoi(spdg)); + } + TString pathO2 = gSystem->ExpandPathName("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/EvtGen/DecayTablesEvtgen"); + genCocktailEvtGen->SetDecayTable(Form("%s/X3872ANDPSI2STOJPSIPIPI.DEC", pathO2.Data())); + + return genCocktailEvtGen; +} + + diff --git a/MC/config/PWGDQ/external/generator/generator_pythia8Onia_PromptSignals_gaptriggered.C b/MC/config/PWGDQ/external/generator/generator_pythia8Onia_PromptSignals_gaptriggered.C new file mode 100644 index 000000000..9f0b695a1 --- /dev/null +++ b/MC/config/PWGDQ/external/generator/generator_pythia8Onia_PromptSignals_gaptriggered.C @@ -0,0 +1,238 @@ +#include "FairGenerator.h" +#include "Generators/GeneratorPythia8.h" +#include "Pythia8/Pythia.h" +#include "TRandom.h" + +R__ADD_INCLUDE_PATH($O2DPG_MC_CONFIG_ROOT/MC/config/PWGDQ/EvtGen) +#include "GeneratorEvtGen.C" + +#include + +using namespace o2::eventgen; + +namespace o2 +{ +namespace eventgen +{ + +class GeneratorPythia8OniaPromptSignalsGapTriggered : public o2::eventgen::GeneratorPythia8 { +public: + + /// constructor + GeneratorPythia8OniaPromptSignalsGapTriggered(int inputTriggerRatio = 5) { + + mGeneratedEvents = 0; + mInverseTriggerRatio = inputTriggerRatio; + // define minimum bias event generator + auto seed = (gRandom->TRandom::GetSeed() % 900000000); + TString pathconfigMB = gSystem->ExpandPathName("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/pythia8/generator/pythia8_inel_triggerGap.cfg"); + pythiaMBgen.readFile(pathconfigMB.Data()); + pythiaMBgen.readString("Random:setSeed on"); + pythiaMBgen.readString("Random:seed " + std::to_string(seed)); + mConfigMBdecays = ""; + mRapidityMin = -1.; + mRapidityMax = 1.; + mVerbose = false; + } + + /// Destructor + ~GeneratorPythia8OniaPromptSignalsGapTriggered() = default; + + void addHadronPDGs(int pdg) { mHadronsPDGs.push_back(pdg); }; + + void setRapidityRange(double valMin, double valMax) + { + mRapidityMin = valMin; + mRapidityMax = valMax; + }; + + void setTriggerGap(int triggerGap) {mInverseTriggerRatio = triggerGap;} + + void setConfigMBdecays(TString val){mConfigMBdecays = val;} + + void setVerbose(bool val) { mVerbose = val; }; + +protected: + +bool generateEvent() override { + // reset event + bool genOk = false; + if (mGeneratedEvents % mInverseTriggerRatio == 0) { + bool found = false; + while (! (genOk && found)) { + /// reset event + mPythia.event.reset(); + genOk = GeneratorPythia8::generateEvent(); + // find the q-qbar or single hadron ancestor + found = findHadrons(mPythia.event); + } + notifySubGenerator(1); + } else { + /// reset event + pythiaMBgen.event.reset(); + while (!genOk) { + genOk = pythiaMBgen.next(); + } + mPythia.event = pythiaMBgen.event; + notifySubGenerator(0); + } + mGeneratedEvents++; + if (mVerbose) { + mOutputEvent.list(); + } + return true; +} + +bool Init() override { + + if(mConfigMBdecays.Contains("cfg")) { + pythiaMBgen.readFile(mConfigMBdecays.Data()); + } + addSubGenerator(0, "Minimum bias"); + addSubGenerator(1, "Onia injected"); + GeneratorPythia8::Init(); + pythiaMBgen.init(); + return true; +} + +// search for the presence of at least one of the required hadrons in a selected rapidity window +bool findHadrons(Pythia8::Event& event) { + + for (int ipa = 0; ipa < event.size(); ++ipa) { + + auto daughterList = event[ipa].daughterList(); + + for (auto ida : daughterList) { + for (int pdg : mHadronsPDGs) { // check that at least one of the pdg code is found in the event + if (event[ida].id() == pdg) { + if ((event[ida].y() > mRapidityMin) && (event[ida].y() < mRapidityMax)) { + cout << "============= Found jpsi y,pt " << event[ida].y() << ", " << event[ida].pT() << endl; + std::vector daughters = event[ida].daughterList(); + for (int d : daughters) { + cout << "###### daughter " << d << ": code " << event[d].id() << ", pt " << event[d].pT() << endl; + } + return true; + } + } + } + } + } + + return false; +}; + + +private: + // Interface to override import particles + Pythia8::Event mOutputEvent; + + // Control gap-triggering + unsigned long long mGeneratedEvents; + int mInverseTriggerRatio; + Pythia8::Pythia pythiaMBgen; // minimum bias event + TString mConfigMBdecays; + std::vector mHadronsPDGs; + double mRapidityMin; + double mRapidityMax; + bool mVerbose; +}; + +} + +} + +// Predefined generators: +FairGenerator* + GeneratorPromptJpsi_EvtGenMidY(int triggerGap, double rapidityMin = -1.5, double rapidityMax = 1.5, bool verbose = false) +{ + auto gen = new o2::eventgen::GeneratorEvtGen(); + gen->setTriggerGap(triggerGap); + gen->setRapidityRange(rapidityMin, rapidityMax); + gen->addHadronPDGs(443); + gen->setVerbose(verbose); + + TString pathO2table = gSystem->ExpandPathName("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/pythia8/decayer/switchOffJpsi.cfg"); + gen->readFile(pathO2table.Data()); + gen->setConfigMBdecays(pathO2table); + gen->PrintDebug(true); + + gen->SetSizePdg(1); + gen->AddPdg(443, 0); + + gen->SetForceDecay(kEvtDiElectron); + + // set random seed + gen->readString("Random:setSeed on"); + uint random_seed; + unsigned long long int random_value = 0; + ifstream urandom("/dev/urandom", ios::in|ios::binary); + urandom.read(reinterpret_cast(&random_value), sizeof(random_seed)); + gen->readString(Form("Random:seed = %llu", random_value % 900000001)); + + // print debug + // gen->PrintDebug(); + + return gen; +} + +FairGenerator* + GeneratorPromptJpsi_EvtGenFwdy(int triggerGap, double rapidityMin = -4.3, double rapidityMax = -2.3, bool verbose = false) +{ + auto gen = new o2::eventgen::GeneratorEvtGen(); + gen->setTriggerGap(triggerGap); + gen->setRapidityRange(rapidityMin, rapidityMax); + gen->addHadronPDGs(443); + gen->setVerbose(verbose); + + TString pathO2table = gSystem->ExpandPathName("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/pythia8/decayer/switchOffJpsi.cfg"); + gen->readFile(pathO2table.Data()); + gen->setConfigMBdecays(pathO2table); + gen->PrintDebug(true); + + gen->SetSizePdg(1); + gen->AddPdg(443, 0); + + gen->SetForceDecay(kEvtDiMuon); + + // set random seed + gen->readString("Random:setSeed on"); + uint random_seed; + unsigned long long int random_value = 0; + ifstream urandom("/dev/urandom", ios::in|ios::binary); + urandom.read(reinterpret_cast(&random_value), sizeof(random_seed)); + gen->readString(Form("Random:seed = %llu", random_value % 900000001)); + + // print debug + // gen->PrintDebug(); + + return gen; +} + +FairGenerator* + GeneratorPromptJpsiPsi2S_EvtGenFwdY(int triggerGap, double rapidityMin = -4.3, double rapidityMax = -2.3, bool verbose = false) +{ + auto gen = new o2::eventgen::GeneratorEvtGen(); + gen->setTriggerGap(triggerGap); + gen->setRapidityRange(rapidityMin, rapidityMax); + gen->addHadronPDGs(443); + gen->addHadronPDGs(100443); + gen->setVerbose(verbose); + TString pathO2table = gSystem->ExpandPathName("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/pythia8/decayer/switchOffJpsi.cfg"); + gen->readFile(pathO2table.Data()); + gen->setConfigMBdecays(pathO2table); + gen->PrintDebug(true); + gen->SetSizePdg(2); + gen->AddPdg(443, 0); + gen->AddPdg(100443, 1); + gen->SetForceDecay(kEvtDiMuon); + // set random seed + gen->readString("Random:setSeed on"); + uint random_seed; + unsigned long long int random_value = 0; + ifstream urandom("/dev/urandom", ios::in|ios::binary); + urandom.read(reinterpret_cast(&random_value), sizeof(random_seed)); + gen->readString(Form("Random:seed = %llu", random_value % 900000001)); + // print debug + // gen->PrintDebug(); + return gen; +} diff --git a/MC/config/PWGDQ/external/generator/generator_pythia8_HadronTriggered_PbPb.C b/MC/config/PWGDQ/external/generator/generator_pythia8_HadronTriggered_PbPb.C new file mode 100644 index 000000000..3c3034f80 --- /dev/null +++ b/MC/config/PWGDQ/external/generator/generator_pythia8_HadronTriggered_PbPb.C @@ -0,0 +1,422 @@ +#include "FairGenerator.h" +#include "Generators/GeneratorPythia8.h" +#include "Pythia8/Pythia.h" +#include "TRandom.h" +#include "TParticle.h" + +R__ADD_INCLUDE_PATH($O2DPG_MC_CONFIG_ROOT/MC/config/PWGDQ/EvtGen) +#include "GeneratorEvtGen.C" + +#include + +using namespace o2::eventgen; + +namespace o2 +{ +namespace eventgen +{ + +class GeneratorPythia8HadronTriggeredPbPb : public o2::eventgen::GeneratorPythia8 { +public: + + /// constructor + GeneratorPythia8HadronTriggeredPbPb(int inputTriggerRatio = 5) { + + mGeneratedEvents = 0; + mInverseTriggerRatio = inputTriggerRatio; + // define minimum bias event generator + auto seed = (gRandom->TRandom::GetSeed() % 900000000); + TString pathconfigMB = gSystem->ExpandPathName("${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator/pythia8_PbPb_536tev.cfg"); + pythiaMBgen.readFile(pathconfigMB.Data()); + pythiaMBgen.readString("Random:setSeed on"); + pythiaMBgen.readString("Random:seed " + std::to_string(seed)); + mConfigMBdecays = ""; + mRapidityMin = -1.; + mRapidityMax = 1.; + mVerbose = false; + } + + /// Destructor + ~GeneratorPythia8HadronTriggeredPbPb() = default; + + void addHadronPDGs(int pdg) { mHadronsPDGs.push_back(pdg); }; + + void setRapidityRange(double valMin, double valMax) + { + mRapidityMin = valMin; + mRapidityMax = valMax; + }; + + void setTriggerGap(int triggerGap) {mInverseTriggerRatio = triggerGap;} + + void setConfigMBdecays(TString val){mConfigMBdecays = val;} + + void setVerbose(bool val) { mVerbose = val; }; + +protected: + + bool generateEvent() override { + return true; + } + + bool Init() override { + if(mConfigMBdecays.Contains("cfg")) { + pythiaMBgen.readFile(mConfigMBdecays.Data()); + } + GeneratorPythia8::Init(); + pythiaMBgen.init(); + return true; + } + + std::vector findAllCharmonia(const Pythia8::Event& event) { + std::vector out; out.reserve(4); + + for (int ipa = 0; ipa < event.size(); ++ipa) { + + auto daughterList = event[ipa].daughterList(); + + for (auto ida : daughterList) { + for (int pdg : mHadronsPDGs) { // check that at least one of the pdg code is found in the event + if (event[ida].id() == pdg) { + if ((event[ida].y() > mRapidityMin) && (event[ida].y() < mRapidityMax)) { + //cout << "============= Found jpsi y,pt " << event[ida].y() << ", " << event[ida].pT() << endl; + out.push_back(ida); + } + } + } + } + } + + return out; + } + + bool keepAncestor(int idabs){ + // charmonium + int mid2 = (idabs / 10) % 100; // 443,100443,20443,445... + if (mid2 == 44) return true; + + // open charm/beauty hadron (D, B, charmed/bottom baryon...) + int flav = (idabs / 100) % 10; + if (flav == 4 || flav == 5) return true; + + return false; +} + +void collectAncestors(const Pythia8::Event& event, int idx, std::vector& decayChains, std::vector& visited) { + if (idx < 0 || idx >= event.size()) return; + if (visited[idx]) return; + visited[idx] = 1; + + const int idabs = std::abs(event[idx].id()); + if (idabs == 4 || idabs == 5 || idabs == 21) return; + + int mother1 = event[idx].mother1(); + int mother2 = event[idx].mother2(); + if (mother1 < 0) return; + if (mother2 < mother1) mother2 = mother1; + for (int m = mother1; m <= mother2; ++m) { + if (m == idx) continue; + collectAncestors(event, m, decayChains, visited); + } + + if (keepAncestor(idabs)) decayChains.push_back(idx); +} + +void collectDaughters(const Pythia8::Event& event, int idx, std::vector& decayChains, std::vector& visited) { + if (idx < 0 || idx >= event.size()) return; + if (visited[idx] == 0) { + decayChains.push_back(idx); + } + + if (visited[idx] == 2) return; + visited[idx] = 2; + + int daughter1 = event[idx].daughter1(); + int daughter2 = event[idx].daughter2(); + if (daughter1 < 0) return; + if (daughter2 < daughter1) daughter2 = daughter1; + + for (int d = daughter1; d <= daughter2; ++d) { + if (d == idx) continue; + collectDaughters(event, d, decayChains, visited); + } +} + +TParticle makeTParticleTemp(const Pythia8::Event& event, int idx) { + const auto& q = event[idx]; + int status = q.daughter1() < 0? 1 : 2; + + int m1 = q.mother1(); + int m2 = q.mother2(); + int d1 = q.daughter1(); + int d2 = q.daughter2(); + TParticle tparticle(q.id(), status, m1, m2, d1, d2, + q.px(), q.py(), q.pz(), q.e(), + q.xProd(), q.yProd(), q.zProd(), q.tProd()); + + if (tparticle.GetStatusCode() == 1) { + tparticle.SetStatusCode( + o2::mcgenstatus::MCGenStatusEncoding(1, 91).fullEncoding); + tparticle.SetBit(ParticleStatus::kToBeDone, true); + } else { + tparticle.SetStatusCode( + o2::mcgenstatus::MCGenStatusEncoding(2, -91).fullEncoding); + tparticle.SetBit(ParticleStatus::kToBeDone, false); + } + + return tparticle; + +} + +Bool_t importParticles() override +{ + //LOG(info) << ""; + //LOG(info) << "*************************************************************"; + //LOG(info) << "************** New signal event considered **************"; + //LOG(info) << "*************************************************************"; + //LOG(info) << ""; + + const int nSig = std::max(1, (int)std::lround(mNumSigEvs)); + for (int isig=0; isig charmonia; + while (! (genOk && !charmonia.empty())) { + /// reset event + mPythia.event.reset(); + genOk = GeneratorPythia8::generateEvent(); + if (!genOk) continue; + charmonia = findAllCharmonia(mPythia.event); + } + + std::vector decayChains; + std::vector visited(mPythia.event.size(), 0); + decayChains.reserve(mPythia.event.size()); + + //int originalSize = mParticles.size(); + + // find all ancestors of the charmonia + for (size_t ic = 0; ic < charmonia.size(); ++ic) { + int cidx = charmonia[ic]; + collectAncestors(mPythia.event, cidx, decayChains, visited); + } + + // find all daughters of the charmonia + for (size_t ic = 0; ic < charmonia.size(); ++ic) { + int cidx = charmonia[ic]; + collectDaughters(mPythia.event, cidx, decayChains, visited); + } + + std::vector idxMap(mPythia.event.size(), -1); + mParticles.reserve(mParticles.size() + (int)decayChains.size()); + + for (int i = 0; i < (int)decayChains.size(); ++i) { + const int srcIdx = decayChains[i]; + if (srcIdx < 0 || srcIdx >= mPythia.event.size()) continue; + + TParticle part = makeTParticleTemp(mPythia.event, srcIdx); + + int newIdx = (int)mParticles.size(); + mParticles.push_back(part); + idxMap[srcIdx] = newIdx; + } + + for (int iLoc = 0; iLoc < (int) decayChains.size(); ++iLoc) { + const int srcIdx = decayChains[iLoc]; + if (srcIdx < 0 || srcIdx >= (int)idxMap.size()) continue; + const int outIdx = idxMap[srcIdx]; + if (outIdx < 0) continue; + + const auto& src = mPythia.event[srcIdx]; + + const int mother1 = (src.mother1() >= 0 ? idxMap[src.mother1()] : -1); + const int mother2 = (src.mother2() >= 0 ? idxMap[src.mother2()] : -1); + const int daughter1 = (src.daughter1()>= 0 ? idxMap[src.daughter1()] : -1); + const int daughter2 = (src.daughter2()>= 0 ? idxMap[src.daughter2()] : -1); + + // update TParticle + TParticle& particle = mParticles[outIdx]; + particle.SetFirstMother(mother1); + particle.SetLastMother(mother2); + particle.SetFirstDaughter(daughter1); + particle.SetLastDaughter(daughter2); + } + //LOG(info) << "-----------------------------------------------"; + //LOG(info) << "============ After event " << isig << " (size " << decayChains.size() << ")"; + //LOG(info) << "Full stack (size " << mParticles.size() << "):"; + //LOG(info) << "New particles from signal event " << isig; + //for (int id = originalSize; id < (int)mParticles.size(); ++id) { + // const auto& p = mParticles[id]; + // LOG(info) << " id = " << id + // << ", pdg = " << p.GetPdgCode() + // << " --> firstMother=" << p.GetFirstMother() + // << ", lastMother=" << p.GetSecondMother() + // << ", firstDaughter=" << p.GetFirstDaughter() + // << ", lastDaughter=" << p.GetLastDaughter(); + //} + //LOG(info) << "-----------------------------------------------"; + } + + if (mVerbose) mOutputEvent.list(); + + return kTRUE; +} + +void notifyEmbedding(const o2::dataformats::MCEventHeader* bkgHeader) override { + LOG(info) << "[notifyEmbedding] ----- Function called"; + + /// Impact parameter between the two nuclei + const float x = bkgHeader->GetB(); + LOG(info) << "[notifyEmbedding] ----- Collision impact parameter: " << x; + + /// number of events to be embedded in a background event + mNumSigEvs = 5 + 0.886202881*std::pow(std::max(0.0f, 17.5f - x),1.7); + LOG(info) << "[notifyEmbedding] ----- generating " << mNumSigEvs << " signal events " << std::endl; +}; + +private: + // Interface to override import particles + Pythia8::Event mOutputEvent; + + // Control gap-triggering + unsigned long long mGeneratedEvents; + int mInverseTriggerRatio; + Pythia8::Pythia pythiaMBgen; // minimum bias event + TString mConfigMBdecays; + std::vector mHadronsPDGs; + double mRapidityMin; + double mRapidityMax; + bool mVerbose; + + // number of signal events to be embedded in a background event + int mNumSigEvs{1}; +}; + +} + +} + +// Predefined generators: +FairGenerator* + GeneratorPromptJpsi_EvtGenMidY(int triggerGap, double rapidityMin = -1.5, double rapidityMax = 1.5, bool verbose = false, bool embedding = false) +{ + auto gen = new o2::eventgen::GeneratorEvtGen(); + gen->setTriggerGap(triggerGap); + gen->setRapidityRange(rapidityMin, rapidityMax); + gen->addHadronPDGs(443); + gen->setVerbose(verbose); + + TString pathO2table = gSystem->ExpandPathName("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/pythia8/decayer/switchOffJpsi.cfg"); + gen->readFile(pathO2table.Data()); + gen->setConfigMBdecays(pathO2table); + gen->PrintDebug(true); + + gen->SetSizePdg(1); + gen->AddPdg(443, 0); + + gen->SetForceDecay(kEvtDiElectron); + + // set random seed + gen->readString("Random:setSeed on"); + uint random_seed; + unsigned long long int random_value = 0; + ifstream urandom("/dev/urandom", ios::in|ios::binary); + urandom.read(reinterpret_cast(&random_value), sizeof(random_seed)); + gen->readString(Form("Random:seed = %llu", random_value % 900000001)); + + // print debug + // gen->PrintDebug(); + + return gen; +} + +FairGenerator* + GeneratorPromptJpsiPsi2S_EvtGenMidY(int triggerGap, double rapidityMin = -1.5, double rapidityMax = 1.5, bool verbose = false, bool embedding = false) +{ + auto gen = new o2::eventgen::GeneratorEvtGen(); + gen->setTriggerGap(triggerGap); + gen->setRapidityRange(rapidityMin, rapidityMax); + gen->addHadronPDGs(443); + gen->addHadronPDGs(100443); + gen->setVerbose(verbose); + TString pathO2table = gSystem->ExpandPathName("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/pythia8/decayer/switchOffJpsi.cfg"); + gen->readFile(pathO2table.Data()); + gen->setConfigMBdecays(pathO2table); + gen->PrintDebug(true); + gen->SetSizePdg(2); + gen->AddPdg(443, 0); + gen->AddPdg(100443, 1); + gen->SetForceDecay(kEvtDiElectron); + // set random seed + gen->readString("Random:setSeed on"); + uint random_seed; + unsigned long long int random_value = 0; + ifstream urandom("/dev/urandom", ios::in|ios::binary); + urandom.read(reinterpret_cast(&random_value), sizeof(random_seed)); + gen->readString(Form("Random:seed = %llu", random_value % 900000001)); + // print debug + // gen->PrintDebug(); + return gen; +} + +FairGenerator* + GeneratorPromptJpsi_EvtGenFwdy(int triggerGap, double rapidityMin = -4.3, double rapidityMax = -2.3, bool verbose = false, bool embedding = false) +{ + auto gen = new o2::eventgen::GeneratorEvtGen(); + gen->setTriggerGap(triggerGap); + gen->setRapidityRange(rapidityMin, rapidityMax); + gen->addHadronPDGs(443); + gen->setVerbose(verbose); + + TString pathO2table = gSystem->ExpandPathName("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/pythia8/decayer/switchOffJpsi.cfg"); + gen->readFile(pathO2table.Data()); + gen->setConfigMBdecays(pathO2table); + gen->PrintDebug(true); + + gen->SetSizePdg(1); + gen->AddPdg(443, 0); + + gen->SetForceDecay(kEvtDiMuon); + + // set random seed + gen->readString("Random:setSeed on"); + uint random_seed; + unsigned long long int random_value = 0; + ifstream urandom("/dev/urandom", ios::in|ios::binary); + urandom.read(reinterpret_cast(&random_value), sizeof(random_seed)); + gen->readString(Form("Random:seed = %llu", random_value % 900000001)); + + // print debug + // gen->PrintDebug(); + + return gen; +} + +FairGenerator* + GeneratorPromptJpsiPsi2S_EvtGenFwdY(int triggerGap, double rapidityMin = -4.3, double rapidityMax = -2.3, bool verbose = false, bool embedding = false) +{ + auto gen = new o2::eventgen::GeneratorEvtGen(); + gen->setTriggerGap(triggerGap); + gen->setRapidityRange(rapidityMin, rapidityMax); + gen->addHadronPDGs(443); + gen->addHadronPDGs(100443); + gen->setVerbose(verbose); + TString pathO2table = gSystem->ExpandPathName("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/pythia8/decayer/switchOffJpsi.cfg"); + gen->readFile(pathO2table.Data()); + gen->setConfigMBdecays(pathO2table); + gen->PrintDebug(true); + gen->SetSizePdg(2); + gen->AddPdg(443, 0); + gen->AddPdg(100443, 1); + gen->SetForceDecay(kEvtDiMuon); + // set random seed + gen->readString("Random:setSeed on"); + uint random_seed; + unsigned long long int random_value = 0; + ifstream urandom("/dev/urandom", ios::in|ios::binary); + urandom.read(reinterpret_cast(&random_value), sizeof(random_seed)); + gen->readString(Form("Random:seed = %llu", random_value % 900000001)); + // print debug + // gen->PrintDebug(); + return gen; +} diff --git a/MC/config/PWGDQ/external/generator/generator_pythia8_HadronTriggered_withGap.C b/MC/config/PWGDQ/external/generator/generator_pythia8_HadronTriggered_withGap.C new file mode 100644 index 000000000..20b5660dc --- /dev/null +++ b/MC/config/PWGDQ/external/generator/generator_pythia8_HadronTriggered_withGap.C @@ -0,0 +1,616 @@ +#include "FairGenerator.h" +#include "Generators/GeneratorPythia8.h" +#include "Pythia8/Pythia.h" +#include "TRandom.h" +#include "CommonConstants/PhysicsConstants.h" + +R__ADD_INCLUDE_PATH($O2DPG_MC_CONFIG_ROOT/MC/config/PWGDQ/EvtGen) +#include "GeneratorEvtGen.C" + +#include + +using namespace o2::eventgen; + +namespace o2 +{ +namespace eventgen +{ + +class GeneratorPythia8HadronTriggeredWithGap : public o2::eventgen::GeneratorPythia8 { +public: + + /// constructor + GeneratorPythia8HadronTriggeredWithGap(int inputTriggerRatio = 5) { + + mGeneratedEvents = 0; + mInverseTriggerRatio = inputTriggerRatio; + // define minimum bias event generator + auto seed = (gRandom->TRandom::GetSeed() % 900000000); + // main physics option for the min bias pythia events: SoftQCD:Inelastic + TString pathconfigMB = gSystem->ExpandPathName("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/pythia8/generator/pythia8_inel_triggerGap.cfg"); + pythiaMBgen.readFile(pathconfigMB.Data()); + pythiaMBgen.readString("Random:setSeed on"); + pythiaMBgen.readString("Random:seed " + std::to_string(seed)); + mConfigMBdecays = ""; + mRapidityMin = -1.; + mRapidityMax = 1.; + mVerbose = false; + } + + /// Destructor + ~GeneratorPythia8HadronTriggeredWithGap() = default; + + void addHadronPDGs(int pdg) { + mHadronsPDGs.push_back(pdg); + mRejFactorPrompt.push_back(1.0); + mRejFactorNonPrompt.push_back(1.0); + } + + void decrementGeneratedEvents() { mGeneratedEvents--; } + + void setRejFactorPrompt(int pdg, float rejFactor) { + for (size_t i = 0; i < mHadronsPDGs.size(); i++) { + if (pdg == mHadronsPDGs[i]) { + mRejFactorPrompt[i] = rejFactor; + } + } + } + + void setRejFactorNonPrompt(int pdg, float rejFactor) { + for (size_t i = 0; i < mHadronsPDGs.size(); i++) { + if (pdg == mHadronsPDGs[i]) { + mRejFactorNonPrompt[i] = rejFactor; + } + } + } + + void setRapidityRange(double valMin, double valMax) + { + mRapidityMin = valMin; + mRapidityMax = valMax; + }; + + void setTriggerGap(int triggerGap) {mInverseTriggerRatio = triggerGap;} + void setConfigMBdecays(TString val){mConfigMBdecays = val;} + void setVerbose(bool val) { mVerbose = val; }; + + int getNGeneratedEvents() { return mGeneratedEvents; } + int getTriggerGap() { return mInverseTriggerRatio; } + int getHadronPDG(int i) { return mHadronsPDGs[i]; } + float getRejFactorPrompt(int i) { return mRejFactorPrompt[i]; } + float getRejFactorNonPrompt(int i) { return mRejFactorNonPrompt[i]; } + +protected: + +bool generateEvent() override { + // reset event + bool genOk = false; + if (mGeneratedEvents % mInverseTriggerRatio == 0) { + bool found = false; + while (! (genOk && found)) { + /// reset event + mPythia.event.reset(); + genOk = GeneratorPythia8::generateEvent(); + // find the q-qbar or single hadron ancestor + found = findHadrons(mPythia.event); + } + notifySubGenerator(1); + } else { + /// reset event + pythiaMBgen.event.reset(); + while (!genOk) { + genOk = pythiaMBgen.next(); + } + mPythia.event = pythiaMBgen.event; + notifySubGenerator(0); + } + mGeneratedEvents++; + if (mVerbose) { + mOutputEvent.list(); + } + return true; +} + +bool importParticles() override + { + // Possibility to make more actions after particles are imported, such as modifying some PDG values (for chi_c1) + if (!GeneratorPythia8::importParticles()) { + return false; + } + + if (!(mInverseTriggerRatio == 1 || mGeneratedEvents % mInverseTriggerRatio == 1) || !mHasChic1){ + return true; // This is a min bias event or event without prompt chi_c1 + } + + // Since chi_c1(3872) is not produced in PYTHIA we replace prompt chi_c1(1P) with prompt chi_c1(3872) with a given probabiliyt (from the rejFactor) + int iChic3872 = -1; + for (int ihad = 0; ihad < mHadronsPDGs.size(); ihad++) { + if (mHadronsPDGs[ihad] == 9920443 && mRejFactorPrompt[ihad] > 1e-8) { + iChic3872 = ihad; + } + } + if (iChic3872 == -1) { + return true; // In this case we don't request any chi_c1(3872) + } + if (gRandom->Rndm() > mRejFactorPrompt[iChic3872]) { + return true; + } + + for (int idx = 0; idx < mParticles.size(); idx++) { + if (mParticles[idx].GetPdgCode() != 20443 || mParticles[idx].Y() < mRapidityMin || mParticles[idx].Y() > mRapidityMax) { + continue; + } + + LOGF(info, "replacing one chi_c1(1P) with pt=%.3f and y=%.3f by a chi_c1(3872)", mParticles[idx].Pt(), mParticles[idx].Y()); + if (mParticles[idx].GetFirstDaughter() >= 0) { + LOGF(info, "particle has already been decayed, we do not replace it"); + return true; + } + + // modify e and pz to conserve rapidity and modify the mass + float deltaMassSquared = o2::constants::physics::MassX3872 * o2::constants::physics::MassX3872 - o2::constants::physics::MassChiC1 * o2::constants::physics::MassChiC1; + float pz_old = mParticles[idx].Pz(); + float e_old = mParticles[idx].Energy(); + float pz_new = 0.; + float e_new = sqrt(deltaMassSquared + e_old*e_old); + if (std::fabs(pz_old) > 1e-8) { + if (e_old*e_old <= pz_old*pz_old) { + LOGF(error, "|E| <= |p_z|, this is not expected, so we don't replace the chi_c1"); + break; + } + pz_new = sqrt(deltaMassSquared + e_old*e_old - pz_old*pz_old) / sqrt(e_old*e_old/pz_old/pz_old - 1.); + if (pz_old < 0.) { + pz_new = -pz_new; + } + e_new = e_old / pz_old * pz_new; + } + mParticles[idx].SetMomentum(mParticles[idx].Px(), mParticles[idx].Py(), pz_new, e_new); + + mParticles[idx].SetPdgCode(9920443); + + break; + } + + return true; + } + +bool Init() override { + + if(mConfigMBdecays.Contains("cfg")) { + pythiaMBgen.readFile(mConfigMBdecays.Data()); + } + addSubGenerator(0, "Minimum bias"); + addSubGenerator(1, "Hadron triggered"); + GeneratorPythia8::Init(); + pythiaMBgen.init(); + + for (size_t i = 0; i < mHadronsPDGs.size(); i++) { + LOGF(info, + "triggering hadron %d with rejection factor (prompt/non-prompt) %f/%f", + mHadronsPDGs[i], mRejFactorPrompt[i], mRejFactorNonPrompt[i]); + } + + return true; +} + +bool isOpenBhadron(int pdg) { + // all open beauty hadrons, no upsilon + return ((abs(pdg) >= 500 && abs(pdg) < 599) || + (abs(pdg) >= 5000 && abs(pdg) < 5999)) && + pdg != 553; +} + +bool isCharmonium(int pdg) { + int firstQuark = (abs(pdg) % 100 - abs(pdg) % 10) / 10; + int secondQuark = (abs(pdg) % 1000 - abs(pdg) % 100) / 100; + int thirdQuark = (abs(pdg) % 10000 - abs(pdg) % 1000) / 1000; + return (firstQuark == 4 && secondQuark == 4 && thirdQuark == 0); +} + +// search for the presence of at least one of the required hadrons in a selected rapidity window +bool findHadrons(Pythia8::Event& event) { + mHasChic1 = false; + int ihad = 0; + for (int ida = 0; ida < event.size(); ++ida) { + //auto daughterList = event[ipa].daughterList(); + //for (auto ida : daughterList) { + ihad = 0; + for (int pdg : mHadronsPDGs) { // check that at least one of the pdg code is found in the event + if (abs(event[ida].id()) == pdg) { + if ((event[ida].y() > mRapidityMin) && (event[ida].y() < mRapidityMax)) { + cout << "============= Found jpsi y,pt,pdg " << event[ida].y() << ", " << event[ida].pT() << ", " << event[ida].id() << endl; + std::vector daughters = event[ida].daughterList(); + for (int d : daughters) { + cout << "###### daughter " << d << ": code " << event[d].id() << ", pt " << event[d].pT() << endl; + } + if (event[ida].daughter1() == event[ida].daughter2() && event[ida].daughter1() > 0) { + continue; // particle has a carbon-copy as daughter, its daughter will already be considered for triggering + } + + // check whether particle is prompt or non-prompt, since rejection + // factor can depend on it + bool isNonPrompt = false; + if (isOpenBhadron(pdg)) { + isNonPrompt = true; + + // if the B hadron decays to something else than a charmonia, we don't trigger it + bool hasCharmoniaDecay = false; + if (daughters.size() == 0) { + // in this case B is left undecayed and will be decayed to enhanced charmonia BR with EVTGEN + hasCharmoniaDecay = true; + } + else { + for (int d : daughters) { + if (isCharmonium(event[d].id())) { + hasCharmoniaDecay = true; + } + } + } + if (!hasCharmoniaDecay) { + continue; + } + } else { + // check history + int currentIdx = ida; + int currentPdg = pdg; + cout << "particle history: "; + while (isCharmonium(currentPdg) && currentIdx >= 0) { + currentIdx = event[currentIdx].mother1(); + if (currentIdx < 0) { + break; + } + currentPdg = abs(event[currentIdx].id()); + cout << currentPdg << " "; + if (isOpenBhadron(currentPdg)) { + isNonPrompt = true; + break; + } + } + cout << endl; + } + + if (event[ida].id() == 20443 && !isNonPrompt) mHasChic1 = true; + + // rejection factor given in the ini file + float randomNumber = gRandom->Rndm(); + if ((!isNonPrompt && (randomNumber <= mRejFactorPrompt[ihad])) || + (isNonPrompt && (randomNumber <= mRejFactorNonPrompt[ihad]))) { + return true; + } + } + } + ihad++; + } + //} + } + return false; +}; + + +private: + // Interface to override import particles + Pythia8::Event mOutputEvent; + + // Control gap-triggering + unsigned long long mGeneratedEvents; + int mInverseTriggerRatio; + Pythia8::Pythia pythiaMBgen; // minimum bias event + TString mConfigMBdecays; + std::vector mHadronsPDGs; + std::vector + mRejFactorPrompt; // rejection factors for possibility to trigger a given + // particle only a fraction of the time, 1 by default + std::vector mRejFactorNonPrompt; + double mRapidityMin; + double mRapidityMax; + bool mVerbose; + bool mHasChic1; // in case we want to replace chi_c1(1P) by chi_c1(3872), avoid to recheck the full stack of particles if unnecessary +}; + +} + +} + +o2::eventgen::Trigger triggerPDGRap(double rapMin, double rapMax, int pdg, GeneratorPythia8HadronTriggeredWithGap *gen) { + auto trigger = [rapMin, rapMax, pdg, gen](const std::vector &particles) -> bool { + if (gen->getTriggerGap() != 1 && gen->getNGeneratedEvents() % gen->getTriggerGap() != 1) { + // this is a MB event + return true; + } + for (const auto &p : particles) { + if (p.Y() > rapMin && p.Y() < rapMax && std::abs(p.GetPdgCode()) == pdg) { + return true; + } + } + return false; + }; + return trigger; +} + +// Predefined generators: +FairGenerator* + GeneratorInclusiveJpsi_EvtGenMidY(int triggerGap, double rapidityMin = -1.5, double rapidityMax = 1.5, bool verbose = false) +{ + auto gen = new o2::eventgen::GeneratorEvtGen(); + gen->setTriggerGap(triggerGap); + gen->setRapidityRange(rapidityMin, rapidityMax); + gen->addHadronPDGs(443); + gen->setVerbose(verbose); + + TString pathO2table = gSystem->ExpandPathName("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/pythia8/decayer/switchOffJpsi.cfg"); + gen->readFile(pathO2table.Data()); + gen->setConfigMBdecays(pathO2table); + gen->PrintDebug(true); + + gen->SetSizePdg(1); + gen->AddPdg(443, 0); + + gen->SetForceDecay(kEvtDiElectron); + + // set random seed + gen->readString("Random:setSeed on"); + uint random_seed; + unsigned long long int random_value = 0; + ifstream urandom("/dev/urandom", ios::in|ios::binary); + urandom.read(reinterpret_cast(&random_value), sizeof(random_seed)); + gen->readString(Form("Random:seed = %llu", random_value % 900000001)); + + // print debug + // gen->PrintDebug(); + + return gen; + +} + +// Predefined generators: +FairGenerator* + GeneratorInclusiveJpsiPsi2S_EvtGenMidY(int triggerGap, double rapidityMin = -1.5, double rapidityMax = 1.5, bool verbose = false) +{ + auto gen = new o2::eventgen::GeneratorEvtGen(); + gen->setTriggerGap(triggerGap); + gen->setRapidityRange(rapidityMin, rapidityMax); + gen->addHadronPDGs(443); + gen->addHadronPDGs(100443); + gen->setVerbose(verbose); + + TString pathO2table = gSystem->ExpandPathName("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/pythia8/decayer/switchOffJpsi.cfg"); + gen->readFile(pathO2table.Data()); + gen->setConfigMBdecays(pathO2table); + gen->PrintDebug(true); + + gen->SetSizePdg(2); + gen->AddPdg(443, 0); + gen->AddPdg(100443, 1); + + gen->SetForceDecay(kEvtDiElectron); + + // set random seed + gen->readString("Random:setSeed on"); + uint random_seed; + unsigned long long int random_value = 0; + ifstream urandom("/dev/urandom", ios::in|ios::binary); + urandom.read(reinterpret_cast(&random_value), sizeof(random_seed)); + gen->readString(Form("Random:seed = %llu", random_value % 900000001)); + + // print debug + // gen->PrintDebug(); + + return gen; +} +FairGenerator * +GeneratorInclusiveJpsiPsi2SChiC_EvtGenMidY(int triggerGap, double rapidityMin = -1.5, double rapidityMax = 1.5, bool verbose = false) +{ + auto gen = new o2::eventgen::GeneratorEvtGen(); + gen->setTriggerGap(triggerGap); + gen->setRapidityRange(rapidityMin, rapidityMax); + gen->addHadronPDGs(443); + gen->addHadronPDGs(100443); + gen->addHadronPDGs(445); + gen->addHadronPDGs(20443); + gen->setVerbose(verbose); + + TString pathO2table = gSystem->ExpandPathName("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/pythia8/decayer/switchOffJpsi.cfg"); + gen->readFile(pathO2table.Data()); + gen->setConfigMBdecays(pathO2table); + gen->PrintDebug(true); + + gen->SetSizePdg(2); + gen->AddPdg(443, 0); + gen->AddPdg(100443, 1); + //gen->AddPdg(445, 2); + //gen->AddPdg(20443, 3); + + gen->SetForceDecay(kEvtDiElectron); + + // set random seed + gen->readString("Random:setSeed on"); + uint random_seed; + unsigned long long int random_value = 0; + ifstream urandom("/dev/urandom", ios::in | ios::binary); + urandom.read(reinterpret_cast(&random_value), sizeof(random_seed)); + gen->readString(Form("Random:seed = %llu", random_value % 900000001)); + + // print debug + // gen->PrintDebug(); + + return gen; +} +FairGenerator *GeneratorInclusiveAllQuarkonia_EvtGenMidY(int triggerGap, double rapidityMin = -1.0, double rapidityMax = 1.0, TString rejFactors = "", int decayMode = 0, bool verbose = false) { + + // This generator allow to choose from the ini file which decay file is to be used (decayMode parameter) and implement rejection factors in order to enhance one particle with respect to others by randomly rejecting trigger (configured through a string) + // The rapidity cut triggers both on the B hadrons rapidities and on quarkonia rapidities + + // This generator should be able to satisfy any needs, although you might have to add some other decayModes and additional particles in the particleList to be triggered + + // decay mode: 0 = enhance B hadrons decays + charmonia to charmonia decays + dileptons decay + // decay mode: 1 = enhance charmonia to charmonia decays + dileptons decays + // decay mode: 2 = enhance only dilepton decays + + // rejFactors string: + // possibility to enhance a particle compared to another (or completely reject one particle) using rejection factors configured from a string + // The rejection factors can be kept in a comma separated list (e.g. "pdg1:rejFactor1,pdg2:rejFactor2") + // The keywords "prompt" and "non-prompt" can be used to modify all prompt and all non-prompt (e.g. "prompt:rejFactor1,non-prompt:rejFactor2") + // The keyword can be used for only one particle (e.g. "pdg1:rejFactor1:prompt,pdg1:rejFactor2:non-prompt") + // Example if you want only to trigger on a given particle p: "prompt:0,non-prompt:0,p:1" + + // For particle which are not produced by default in Pythia (chi_c1(3872)), the rejFactors string should be understood as a probabiliy to inject it (default being 0.) + + std::vector particleList = { + 443, // Jpsi + 100443, // psi(2S) + 553, // upsilon(1S) + 100553, // upsilon(2S) + 200553, // upsilon(3S) + 10441, // chic0 + 20443, // chic1 + 445, // chic2 + 9920443, // chic1(3872) + // we also add B hadrons to trigger correct rapidity range (e.g. B is + // within |y|<1 but non-prompt J/psi has |y|>1) + 511, // B0 + 521, // B+ + 531, // Bs + 541, // Bc + 5122, // Lambdab + 5132, // Xib+ + 5232, // Xib0 + 5332 // Omegab + }; + + int nCharmonia = 9; + int nCharmoniaWoChic = 5; + + auto gen = new o2::eventgen::GeneratorEvtGen(); + gen->setTriggerGap(triggerGap); + // this is a trigger before EvtGen decays. After EVTGEN decay the rapidities of the particles are modified. + // There is therefore a loose rapidity cut before EVTGEN, and the correct rapidity cut after EVTGEN decay + if (decayMode == 2) { + // In this case EvtGen will not affect charmonia rapidities, so no need to start with a looser rapidity cut + gen->setRapidityRange(rapidityMin, rapidityMax); + } + else { + gen->setRapidityRange(rapidityMin - 1., rapidityMax + 1.); + } + // specify particles to be triggered + for (int i = 0; i < particleList.size(); i++) { + gen->addHadronPDGs(particleList[i]); + if (particleList[i] == 9920443) { + // chi_c1(3872) is by default not produced + gen->setRejFactorPrompt(particleList[i], 0.); + } + } + gen->setVerbose(verbose); + + TObjArray *objArray = rejFactors.Tokenize(","); + for (int i = 0; i < objArray->GetEntries(); i++) { + TString rejStr = TString(objArray->At(i)->GetName()); + TObjArray *objArrayCurrent = rejStr.Tokenize(":"); + if (objArrayCurrent->GetEntries() != 2 && objArrayCurrent->GetEntries() != 3) { + LOGF(fatal, "Problem when configuring string for particle rejection factors: %s, incorrect length", + rejStr.Data()); + } + TString str0 = TString(objArrayCurrent->At(0)->GetName()); + TString str1 = TString(objArrayCurrent->At(1)->GetName()); + if (!str1.IsFloat()) { + LOGF(fatal, + "Problem when configuring string for particle rejection factors: %s, is not float", + rejStr.Data()); + } + if (str0.CompareTo("prompt") == 0) { + // Common switch for all prompt particles + for (int ihad = 0; ihad < particleList.size(); ihad++) { + gen->setRejFactorPrompt(particleList[ihad], str1.Atof()); + } + continue; + } + if (str0.CompareTo("non-prompt") == 0) { + // Common switch for all non-prompt particles + for (int ihad = 0; ihad < particleList.size(); ihad++) { + gen->setRejFactorNonPrompt(particleList[ihad], str1.Atof()); + } + continue; + } + if (str0.IsDigit()) { + // Setting the rejection factor for a specific particle + if (objArrayCurrent->GetEntries() == 2) { + gen->setRejFactorPrompt(str0.Atoi(), str1.Atof()); + gen->setRejFactorNonPrompt(str0.Atoi(), str1.Atof()); + continue; + } else { + TString str2 = TString(objArrayCurrent->At(2)->GetName()); + if (str2.CompareTo("prompt") == 0) { + gen->setRejFactorPrompt(str0.Atoi(), str1.Atof()); + continue; + } + if (str2.CompareTo("non-prompt") == 0) { + gen->setRejFactorNonPrompt(str0.Atoi(), str1.Atof()); + continue; + } + } + } + LOGF(fatal, "Problem when configuring string for particle rejection factors: %s, incorrect template", + rejStr.Data()); + } + + TString pathO2table; + if (decayMode == 0) { + pathO2table = gSystem->ExpandPathName("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/pythia8/decayer/switchOffAllQuarkoniaAndBhadrons.cfg"); + } + if (decayMode == 1) { + pathO2table = gSystem->ExpandPathName("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/pythia8/decayer/switchOffAllQuarkonia.cfg"); + } + if (decayMode == 2) { + pathO2table = gSystem->ExpandPathName("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/pythia8/decayer/switchOffJpsiUpsilon.cfg"); + } + gen->readFile(pathO2table.Data()); + gen->setConfigMBdecays(pathO2table); + gen->PrintDebug(true); + + // specify particles to be decayed with EvtGen + gen->SetSizePdg(particleList.size()); + for (int i = 0; i < particleList.size(); i++) { + // Decay only what was left undecayed by Pythia + if (decayMode == 0 || (i < nCharmonia && decayMode == 1) || (i < nCharmoniaWoChic && decayMode == 2)) { + gen->AddPdg(particleList[i], i); + } + } + + if (decayMode == 0) { + gen->SetForceDecay(kEvtBPsiAndJpsiDiElectron); + } + if (decayMode == 1) { + gen->SetForceDecay(kCharmoniaToJpsiAndDielectron); + } + if (decayMode == 2) { + gen->SetForceDecay(kEvtDiElectron); + } + + // set random seed + gen->readString("Random:setSeed on"); + uint random_seed; + unsigned long long int random_value = 0; + ifstream urandom("/dev/urandom", ios::in | ios::binary); + urandom.read(reinterpret_cast(&random_value), sizeof(random_seed)); + gen->readString(Form("Random:seed = %llu", random_value % 900000001)); + + // print debug + // gen->PrintDebug(); + + // add trigger on the correct rapidity range after EvtGen decays + if (decayMode != 2) { + // If decayMode == 2, EvtGen will not affect charmonia rapidities, so no need to start with a looser rapidity cut, first trigger already does the job + gen->setTriggerMode(Generator::kTriggerOR); + for (int i = 0; i < particleList.size(); i++) { + // we trigger either on B hadron rapidity or on charmonia rapidity + gen->addTrigger(triggerPDGRap(rapidityMin, rapidityMax, particleList[i], gen)); + } + + // what to do if the trigger was rejected + gen->setTriggerFalseHook( + [gen](std::vector const &p, int eventCount) { + gen->decrementGeneratedEvents(); + }); + } + + return gen; +} diff --git a/MC/config/PWGDQ/external/generator/generator_pythia8_NonPromptSignals_OO_gaptriggered_dq.C b/MC/config/PWGDQ/external/generator/generator_pythia8_NonPromptSignals_OO_gaptriggered_dq.C new file mode 100644 index 000000000..0673ca60f --- /dev/null +++ b/MC/config/PWGDQ/external/generator/generator_pythia8_NonPromptSignals_OO_gaptriggered_dq.C @@ -0,0 +1,336 @@ +#include "FairGenerator.h" +#include "Generators/GeneratorPythia8.h" +#include "Pythia8/Pythia.h" +#include "TRandom.h" + +R__ADD_INCLUDE_PATH($O2DPG_MC_CONFIG_ROOT/MC/config/PWGDQ/EvtGen) +#include "GeneratorEvtGen.C" + +#include + +using namespace o2::eventgen; + +namespace o2 +{ +namespace eventgen +{ + +class GeneratorPythia8NonPromptInjectedOOGapTriggeredDQ : public o2::eventgen::GeneratorPythia8 { +public: + + /// constructor + GeneratorPythia8NonPromptInjectedOOGapTriggeredDQ(int inputTriggerRatio = 2) { + + mGeneratedEvents = 0; + mInverseTriggerRatio = inputTriggerRatio; + + // Read INPUT_TRIGGER_RATIO from the shell environment. + // If it is not defined, keep the default constructor value. + const char* envTriggerRatio = gSystem->Getenv("INPUT_TRIGGER_RATIO"); + + if (envTriggerRatio && envTriggerRatio[0] != '\0') { + const int valueFromEnv = TString(envTriggerRatio).Atoi(); + + if (valueFromEnv > 0) { + inputTriggerRatio = valueFromEnv; + } else { + printf( + "WARNING: invalid INPUT_TRIGGER_RATIO=%s, using default value %d\n", + envTriggerRatio, + inputTriggerRatio); + } + } + + mGeneratedEvents = 0; + mInverseTriggerRatio = inputTriggerRatio; + + printf("Input trigger ratio: %d\n", mInverseTriggerRatio); + + // define minimum bias event generator + auto seed = (gRandom->TRandom::GetSeed() % 900000000); + + TString pathconfigMB = gSystem->ExpandPathName("${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/generator/pythia8_OO_536.cfg"); + pythiaMBgen.readFile(pathconfigMB.Data()); + pythiaMBgen.readString("Random:setSeed on"); + pythiaMBgen.readString("Random:seed " + std::to_string(seed)); + mConfigMBdecays = ""; + mPDG = 5; + mRapidityMin = -1.; + mRapidityMax = 1.; + mHadronMultiplicity = -1; + mHadronRapidityMin = -1.; + mHadronRapidityMax = 1.; + mVerbose = false; + } + + /// Destructor + ~GeneratorPythia8NonPromptInjectedOOGapTriggeredDQ() = default; + + void setPDG(int val) { mPDG = val; }; + void addHadronPDGs(int pdg) { mHadronsPDGs.push_back(pdg); }; + void setHadronMultiplicity(int val) { mHadronMultiplicity = val; }; + void setRapidity(double valMin, double valMax) + { + mRapidityMin = valMin; + mRapidityMax = valMax; + }; + + void setRapidityHadron(double valMin, double valMax) + { + mHadronRapidityMin = valMin; + mHadronRapidityMax = valMax; + }; + + void setConfigMBdecays(TString val){mConfigMBdecays = val;} + + void setVerbose(bool val) { mVerbose = val; }; + +protected: + +Bool_t generateEvent() override + { + // reset event + bool genOk = false; + if (mGeneratedEvents % mInverseTriggerRatio == 0){ + bool ancestor = false; + while (! (genOk && ancestor) ){ + /// reset event + mPythia.event.reset(); + genOk = GeneratorPythia8::generateEvent(); + // find the q-qbar ancestor + ancestor = findHeavyQuarkPair(mPythia.event); + } + } else { + /// reset event + pythiaMBgen.event.reset(); + while (!genOk) { + genOk = pythiaMBgen.next(); + } + mPythia.event = pythiaMBgen.event; + } + mGeneratedEvents++; + if (mVerbose) mOutputEvent.list(); + return true; + } + +Bool_t Init() override + { + if(mConfigMBdecays.Contains("cfg")) pythiaMBgen.readFile(mConfigMBdecays.Data()); + GeneratorPythia8::Init(); + pythiaMBgen.init(); + return true; + } + + // search for q-qbar mother with at least one q in a selected rapidity window + bool findHeavyQuarkPair(Pythia8::Event& event) + { + int countH[mHadronsPDGs.size()]; for(int ipdg=0; ipdg < mHadronsPDGs.size(); ipdg++) countH[ipdg]=0; + bool hasq = false, hasqbar = false, atSelectedY = false, isOkAtPartonicLevel = false; + for (int ipa = 0; ipa < event.size(); ++ipa) { + + if(!isOkAtPartonicLevel){ + auto daughterList = event[ipa].daughterList(); + hasq = false; hasqbar = false; atSelectedY = false; + for (auto ida : daughterList) { + if (event[ida].id() == mPDG) + hasq = true; + if (event[ida].id() == -mPDG) + hasqbar = true; + if ((event[ida].y() > mRapidityMin) && (event[ida].y() < mRapidityMax)) + atSelectedY = true; + } + if (hasq && hasqbar && atSelectedY) isOkAtPartonicLevel = true; + } + + if( (mHadronMultiplicity <= 0) && isOkAtPartonicLevel) return true; // no selection at hadron level + + /// check at hadron level if needed + int ipdg=0; + for (auto& pdgVal : mHadronsPDGs){ + if ( (TMath::Abs(event[ipa].id()) == pdgVal) && (event[ipa].y() > mHadronRapidityMin) && (event[ipa].y() < mHadronRapidityMax) ) countH[ipdg]++; + if(isOkAtPartonicLevel && countH[ipdg] >= mHadronMultiplicity) return true; + ipdg++; + } + } + return false; + }; + + +private: +// Interface to override import particles +Pythia8::Event mOutputEvent; + + // Control gap-triggering + unsigned long long mGeneratedEvents; + int mInverseTriggerRatio; + Pythia8::Pythia pythiaMBgen; // minimum bias event + TString mConfigMBdecays; + int mPDG; + std::vector mHadronsPDGs; + int mHadronMultiplicity; + double mRapidityMin; + double mRapidityMax; + double mHadronRapidityMin; + double mHadronRapidityMax; + bool mVerbose; + }; + +} + +} + +// Predefined generators: +FairGenerator* + GeneratorBeautyToJpsi_EvtGenMidY_OO(double rapidityMin = -1.5, double rapidityMax = 1.5, bool verbose = false, TString pdgs = "511;521;531;541;5112;5122;5232;5132;5332") +{ + auto gen = new o2::eventgen::GeneratorEvtGen(); + gen->setRapidity(rapidityMin, rapidityMax); + gen->setPDG(5); + gen->setRapidityHadron(-1.5,1.5); + gen->setHadronMultiplicity(1); + TString pathO2table = gSystem->ExpandPathName("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/pythia8/decayer/switchOffBhadrons.cfg"); + gen->readFile(pathO2table.Data()); + gen->setConfigMBdecays(pathO2table); + gen->setVerbose(verbose); + + std::string spdg; + TObjArray* obj = pdgs.Tokenize(";"); + gen->SetSizePdg(obj->GetEntriesFast()); + for (int i = 0; i < obj->GetEntriesFast(); i++) { + spdg = obj->At(i)->GetName(); + gen->AddPdg(std::stoi(spdg), i); + gen->addHadronPDGs(std::stoi(spdg)); + printf("PDG %d \n", std::stoi(spdg)); + } + gen->SetForceDecay(kEvtBJpsiDiElectron); + + // set random seed + gen->readString("Random:setSeed on"); + uint random_seed; + unsigned long long int random_value = 0; + ifstream urandom("/dev/urandom", ios::in|ios::binary); + urandom.read(reinterpret_cast(&random_value), sizeof(random_seed)); + gen->readString(Form("Random:seed = %d", random_value % 900000001)); + + // print debug + // gen->PrintDebug(); + + return gen; +} + +FairGenerator* + GeneratorBeautyToPsiAndJpsi_EvtGenMidY_OO(double rapidityMin = -1.5, double rapidityMax = 1.5, bool verbose = false, TString pdgs = "511;521;531;541;5112;5122;5232;5132;5332") +{ + auto gen = new o2::eventgen::GeneratorEvtGen(); + gen->setRapidity(rapidityMin, rapidityMax); + gen->setPDG(5); + gen->setRapidityHadron(rapidityMin,rapidityMax); + gen->setHadronMultiplicity(1); + TString pathO2table = gSystem->ExpandPathName("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/pythia8/decayer/switchOffBhadrons.cfg"); + gen->readFile(pathO2table.Data()); + gen->setConfigMBdecays(pathO2table); + gen->setVerbose(verbose); + + std::string spdg; + TObjArray* obj = pdgs.Tokenize(";"); + gen->SetSizePdg(obj->GetEntriesFast()); + for (int i = 0; i < obj->GetEntriesFast(); i++) { + spdg = obj->At(i)->GetName(); + gen->AddPdg(std::stoi(spdg), i); + printf("PDG %d \n", std::stoi(spdg)); + gen->addHadronPDGs(std::stoi(spdg)); + } + gen->SetForceDecay(kEvtBPsiAndJpsiDiElectron); + + // set random seed + gen->readString("Random:setSeed on"); + uint random_seed; + unsigned long long int random_value = 0; + ifstream urandom("/dev/urandom", ios::in|ios::binary); + urandom.read(reinterpret_cast(&random_value), sizeof(random_seed)); + gen->readString(Form("Random:seed = %d", random_value % 900000001)); + + // print debug + // gen->PrintDebug(); + + return gen; +} + +// Predefined generators: +FairGenerator* + GeneratorBeautyToJpsi_EvtGenFwdY_OO(double rapidityMin = -4.3, double rapidityMax = -2.3, bool verbose = false, TString pdgs = "511;521;531;541;5112;5122;5232;5132;5332") +{ + auto gen = new o2::eventgen::GeneratorEvtGen(); + gen->setRapidity(rapidityMin, rapidityMax); + gen->setPDG(5); + gen->setRapidityHadron(rapidityMin,rapidityMax); + gen->setHadronMultiplicity(1); + TString pathO2table = gSystem->ExpandPathName("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/pythia8/decayer/switchOffBhadrons.cfg"); + gen->readFile(pathO2table.Data()); + gen->setConfigMBdecays(pathO2table); + gen->setVerbose(verbose); + + std::string spdg; + TObjArray* obj = pdgs.Tokenize(";"); + gen->SetSizePdg(obj->GetEntriesFast()); + for (int i = 0; i < obj->GetEntriesFast(); i++) { + spdg = obj->At(i)->GetName(); + gen->AddPdg(std::stoi(spdg), i); + gen->addHadronPDGs(std::stoi(spdg)); + printf("PDG %d \n", std::stoi(spdg)); + } + gen->SetForceDecay(kEvtBJpsiDiMuon); + + // set random seed + gen->readString("Random:setSeed on"); + uint random_seed; + unsigned long long int random_value = 0; + ifstream urandom("/dev/urandom", ios::in|ios::binary); + urandom.read(reinterpret_cast(&random_value), sizeof(random_seed)); + gen->readString(Form("Random:seed = %d", random_value % 900000001)); + + // print debug + // gen->PrintDebug(); + + return gen; +} + + +FairGenerator* + GeneratorBeautyToPsiAndJpsi_EvtGenFwdY_OO(double rapidityMin = -4.3, double rapidityMax = -2.3, bool verbose = false, TString pdgs = "511;521;531;541;5112;5122;5232;5132;5332") +{ + auto gen = new o2::eventgen::GeneratorEvtGen(); + gen->setRapidity(rapidityMin, rapidityMax); + gen->setPDG(5); + gen->setRapidityHadron(rapidityMin,rapidityMax); + gen->setHadronMultiplicity(1); + TString pathO2table = gSystem->ExpandPathName("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/pythia8/decayer/switchOffBhadrons.cfg"); + gen->readFile(pathO2table.Data()); + gen->setConfigMBdecays(pathO2table); + gen->setVerbose(verbose); + + std::string spdg; + TObjArray* obj = pdgs.Tokenize(";"); + gen->SetSizePdg(obj->GetEntriesFast()); + for (int i = 0; i < obj->GetEntriesFast(); i++) { + spdg = obj->At(i)->GetName(); + gen->AddPdg(std::stoi(spdg), i); + printf("PDG %d \n", std::stoi(spdg)); + gen->addHadronPDGs(std::stoi(spdg)); + } + gen->SetForceDecay(kEvtBPsiAndJpsiDiMuon); + + // set random seed + gen->readString("Random:setSeed on"); + uint random_seed; + unsigned long long int random_value = 0; + ifstream urandom("/dev/urandom", ios::in|ios::binary); + urandom.read(reinterpret_cast(&random_value), sizeof(random_seed)); + gen->readString(Form("Random:seed = %d", random_value % 900000001)); + + // print debug + // gen->PrintDebug(); + + return gen; +} + diff --git a/MC/config/PWGDQ/external/generator/generator_pythia8_NonPromptSignals_gaptriggered_dq.C b/MC/config/PWGDQ/external/generator/generator_pythia8_NonPromptSignals_gaptriggered_dq.C index eac21670c..9f21d5b28 100644 --- a/MC/config/PWGDQ/external/generator/generator_pythia8_NonPromptSignals_gaptriggered_dq.C +++ b/MC/config/PWGDQ/external/generator/generator_pythia8_NonPromptSignals_gaptriggered_dq.C @@ -3,13 +3,12 @@ #include "Pythia8/Pythia.h" #include "TRandom.h" -R__ADD_INCLUDE_PATH($O2DPG_ROOT/MC/config/PWGDQ/EvtGen) +R__ADD_INCLUDE_PATH($O2DPG_MC_CONFIG_ROOT/MC/config/PWGDQ/EvtGen) #include "GeneratorEvtGen.C" #include using namespace o2::eventgen; -using namespace Pythia8; namespace o2 { @@ -26,7 +25,7 @@ public: mInverseTriggerRatio = inputTriggerRatio; // define minimum bias event generator auto seed = (gRandom->TRandom::GetSeed() % 900000000); - TString pathconfigMB = gSystem->ExpandPathName("$O2DPG_ROOT/MC/config/PWGDQ/pythia8/generator/pythia8_inel_triggerGap.cfg"); + TString pathconfigMB = gSystem->ExpandPathName("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/pythia8/generator/pythia8_inel_triggerGap.cfg"); pythiaMBgen.readFile(pathconfigMB.Data()); pythiaMBgen.readString("Random:setSeed on"); pythiaMBgen.readString("Random:seed " + std::to_string(seed)); @@ -140,7 +139,7 @@ Pythia8::Event mOutputEvent; // Control gap-triggering unsigned long long mGeneratedEvents; int mInverseTriggerRatio; - Pythia pythiaMBgen; // minimum bias event + Pythia8::Pythia pythiaMBgen; // minimum bias event TString mConfigMBdecays; int mPDG; std::vector mHadronsPDGs; @@ -165,7 +164,7 @@ FairGenerator* gen->setPDG(5); gen->setRapidityHadron(-1.5,1.5); gen->setHadronMultiplicity(1); - TString pathO2table = gSystem->ExpandPathName("$O2DPG_ROOT/MC/config/PWGDQ/pythia8/decayer/switchOffBhadrons.cfg"); + TString pathO2table = gSystem->ExpandPathName("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/pythia8/decayer/switchOffBhadrons.cfg"); gen->readFile(pathO2table.Data()); gen->setConfigMBdecays(pathO2table); gen->setVerbose(verbose); @@ -203,7 +202,7 @@ FairGenerator* gen->setPDG(5); gen->setRapidityHadron(rapidityMin,rapidityMax); gen->setHadronMultiplicity(1); - TString pathO2table = gSystem->ExpandPathName("$O2DPG_ROOT/MC/config/PWGDQ/pythia8/decayer/switchOffBhadrons.cfg"); + TString pathO2table = gSystem->ExpandPathName("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/pythia8/decayer/switchOffBhadrons.cfg"); gen->readFile(pathO2table.Data()); gen->setConfigMBdecays(pathO2table); gen->setVerbose(verbose); @@ -234,18 +233,18 @@ FairGenerator* } FairGenerator* - GeneratorBplusToJpsiKaon_EvtGen(double rapidityMin = -1.5, double rapidityMax = 1.5, bool verbose = false, TString pdgs = "521") + GeneratorBplusToJpsiKaon_EvtGen(double rapidityMin = -1.5, double rapidityMax = 1.5, bool verbose = false, TString pdgs = "511;521;531;541;5112;5122;5232;5132;5332") { auto gen = new o2::eventgen::GeneratorEvtGen(); gen->setRapidity(rapidityMin, rapidityMax); gen->setPDG(5); - gen->addHadronPDGs(521); + //gen->addHadronPDGs(521); gen->setRapidityHadron(rapidityMin,rapidityMax); gen->setHadronMultiplicity(2); - TString pathO2table = gSystem->ExpandPathName("$O2DPG_ROOT/MC/config/PWGDQ/pythia8/decayer/switchOffBplus.cfg"); + TString pathO2table = gSystem->ExpandPathName("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/pythia8/decayer/switchOffBhadrons.cfg"); gen->readFile(pathO2table.Data()); gen->setConfigMBdecays(pathO2table); - gen->setVerbose(verbose); + gen->setVerbose(true); std::string spdg; TObjArray* obj = pdgs.Tokenize(";"); @@ -257,9 +256,11 @@ FairGenerator* printf("PDG %d \n", std::stoi(spdg)); } - TString pathO2 = gSystem->ExpandPathName("$O2DPG_ROOT/MC/config/PWGDQ/EvtGen/DecayTablesEvtgen"); + TString pathO2 = gSystem->ExpandPathName("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/EvtGen/DecayTablesEvtgen"); //gen->SetDecayTable(Form("%s/BPLUSTOKAONJPSITOELE.DEC", pathO2.Data())); - gen->SetDecayTable(Form("%s/BPLUSTOKAONJPSITOELEALLMODES.DEC", pathO2.Data())); // decay table including decay modes for correlated background + //gen->SetDecayTable(Form("%s/BPLUSTOKAONJPSITOELEALLMODES.DEC", pathO2.Data())); // decay table including decay modes for correlated background + //gen->SetDecayTable(Form("%s/BTOJPSITOELE.DEC", pathO2.Data())); // decay table including decay modes for correlated background + gen->SetDecayTable(Form("%s/BTOPSIJPSITODIELECTRON.DEC", pathO2.Data())); // decay table including decay modes for correlated background and jpsi feeddown // print debug // gen->PrintDebug(); // set random seed @@ -282,7 +283,7 @@ FairGenerator* gen->setPDG(5); gen->setRapidityHadron(rapidityMin,rapidityMax); gen->setHadronMultiplicity(1); - TString pathO2table = gSystem->ExpandPathName("$O2DPG_ROOT/MC/config/PWGDQ/pythia8/decayer/switchOffBhadrons.cfg"); + TString pathO2table = gSystem->ExpandPathName("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/pythia8/decayer/switchOffBhadrons.cfg"); gen->readFile(pathO2table.Data()); gen->setConfigMBdecays(pathO2table); gen->setVerbose(verbose); @@ -321,7 +322,7 @@ FairGenerator* gen->setPDG(5); gen->setRapidityHadron(rapidityMin,rapidityMax); gen->setHadronMultiplicity(1); - TString pathO2table = gSystem->ExpandPathName("$O2DPG_ROOT/MC/config/PWGDQ/pythia8/decayer/switchOffBhadrons.cfg"); + TString pathO2table = gSystem->ExpandPathName("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/pythia8/decayer/switchOffBhadrons.cfg"); gen->readFile(pathO2table.Data()); gen->setConfigMBdecays(pathO2table); gen->setVerbose(verbose); @@ -351,3 +352,41 @@ FairGenerator* return gen; } +FairGenerator* + GeneratorBeautyToPsiToJpsi_EvtGenMidY(double rapidityMin = -1.5, double rapidityMax = 1.5, bool verbose = false, TString pdgs = "511;521;531;541;5112;5122;5232;5132;5332") +{ + auto gen = new o2::eventgen::GeneratorEvtGen(); + gen->setRapidity(rapidityMin, rapidityMax); + gen->setPDG(5); + gen->setRapidityHadron(rapidityMin,rapidityMax); + gen->setHadronMultiplicity(1); + TString pathO2table = gSystem->ExpandPathName("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/pythia8/decayer/switchOffBhadrons.cfg"); + gen->readFile(pathO2table.Data()); + gen->setConfigMBdecays(pathO2table); + gen->setVerbose(verbose); + + std::string spdg; + TObjArray* obj = pdgs.Tokenize(";"); + gen->SetSizePdg(obj->GetEntriesFast()); + for (int i = 0; i < obj->GetEntriesFast(); i++) { + spdg = obj->At(i)->GetName(); + gen->AddPdg(std::stoi(spdg), i); + printf("PDG %d \n", std::stoi(spdg)); + gen->addHadronPDGs(std::stoi(spdg)); + } + gen->SetForceDecay(kEvtBtoPsi2SToJpsiPiPi); + + // set random seed + gen->readString("Random:setSeed on"); + uint random_seed; + unsigned long long int random_value = 0; + ifstream urandom("/dev/urandom", ios::in|ios::binary); + urandom.read(reinterpret_cast(&random_value), sizeof(random_seed)); + gen->readString(Form("Random:seed = %d", random_value % 900000001)); + + // print debug + // gen->PrintDebug(); + + return gen; +} + diff --git a/MC/config/PWGDQ/external/generator/generator_pythia8_NonPromptSignals_pO_gaptriggered_dq.C b/MC/config/PWGDQ/external/generator/generator_pythia8_NonPromptSignals_pO_gaptriggered_dq.C new file mode 100644 index 000000000..ed028b373 --- /dev/null +++ b/MC/config/PWGDQ/external/generator/generator_pythia8_NonPromptSignals_pO_gaptriggered_dq.C @@ -0,0 +1,335 @@ +#include "FairGenerator.h" +#include "Generators/GeneratorPythia8.h" +#include "Pythia8/Pythia.h" +#include "TRandom.h" + +R__ADD_INCLUDE_PATH($O2DPG_MC_CONFIG_ROOT/MC/config/PWGDQ/EvtGen) +#include "GeneratorEvtGen.C" + +#include + +using namespace o2::eventgen; + +namespace o2 +{ +namespace eventgen +{ + +class GeneratorPythia8NonPromptInjectedpOGapTriggeredDQ : public o2::eventgen::GeneratorPythia8 { +public: + + /// constructor + GeneratorPythia8NonPromptInjectedpOGapTriggeredDQ(int inputTriggerRatio = 2) { + + mGeneratedEvents = 0; + mInverseTriggerRatio = inputTriggerRatio; + + // Read INPUT_TRIGGER_RATIO from the shell environment. + // If it is not defined, keep the default constructor value. + const char* envTriggerRatio = gSystem->Getenv("INPUT_TRIGGER_RATIO"); + + if (envTriggerRatio && envTriggerRatio[0] != '\0') { + const int valueFromEnv = TString(envTriggerRatio).Atoi(); + + if (valueFromEnv > 0) { + inputTriggerRatio = valueFromEnv; + } else { + printf( + "WARNING: invalid INPUT_TRIGGER_RATIO=%s, using default value %d\n", + envTriggerRatio, + inputTriggerRatio); + } + } + + mGeneratedEvents = 0; + mInverseTriggerRatio = inputTriggerRatio; + + printf("Input trigger ratio: %d\n", mInverseTriggerRatio); + + // define minimum bias event generator + auto seed = (gRandom->TRandom::GetSeed() % 900000000); + TString pathconfigMB = gSystem->ExpandPathName("${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/generator/pythia8_pO_961.cfg"); + pythiaMBgen.readFile(pathconfigMB.Data()); + pythiaMBgen.readString("Random:setSeed on"); + pythiaMBgen.readString("Random:seed " + std::to_string(seed)); + mConfigMBdecays = ""; + mPDG = 5; + mRapidityMin = -1.; + mRapidityMax = 1.; + mHadronMultiplicity = -1; + mHadronRapidityMin = -1.; + mHadronRapidityMax = 1.; + mVerbose = false; + } + + /// Destructor + ~GeneratorPythia8NonPromptInjectedpOGapTriggeredDQ() = default; + + void setPDG(int val) { mPDG = val; }; + void addHadronPDGs(int pdg) { mHadronsPDGs.push_back(pdg); }; + void setHadronMultiplicity(int val) { mHadronMultiplicity = val; }; + void setRapidity(double valMin, double valMax) + { + mRapidityMin = valMin; + mRapidityMax = valMax; + }; + + void setRapidityHadron(double valMin, double valMax) + { + mHadronRapidityMin = valMin; + mHadronRapidityMax = valMax; + }; + + void setConfigMBdecays(TString val){mConfigMBdecays = val;} + + void setVerbose(bool val) { mVerbose = val; }; + +protected: + +Bool_t generateEvent() override + { + // reset event + bool genOk = false; + if (mGeneratedEvents % mInverseTriggerRatio == 0){ + bool ancestor = false; + while (! (genOk && ancestor) ){ + /// reset event + mPythia.event.reset(); + genOk = GeneratorPythia8::generateEvent(); + // find the q-qbar ancestor + ancestor = findHeavyQuarkPair(mPythia.event); + } + } else { + /// reset event + pythiaMBgen.event.reset(); + while (!genOk) { + genOk = pythiaMBgen.next(); + } + mPythia.event = pythiaMBgen.event; + } + mGeneratedEvents++; + if (mVerbose) mOutputEvent.list(); + return true; + } + +Bool_t Init() override + { + if(mConfigMBdecays.Contains("cfg")) pythiaMBgen.readFile(mConfigMBdecays.Data()); + GeneratorPythia8::Init(); + pythiaMBgen.init(); + return true; + } + + // search for q-qbar mother with at least one q in a selected rapidity window + bool findHeavyQuarkPair(Pythia8::Event& event) + { + int countH[mHadronsPDGs.size()]; for(int ipdg=0; ipdg < mHadronsPDGs.size(); ipdg++) countH[ipdg]=0; + bool hasq = false, hasqbar = false, atSelectedY = false, isOkAtPartonicLevel = false; + for (int ipa = 0; ipa < event.size(); ++ipa) { + + if(!isOkAtPartonicLevel){ + auto daughterList = event[ipa].daughterList(); + hasq = false; hasqbar = false; atSelectedY = false; + for (auto ida : daughterList) { + if (event[ida].id() == mPDG) + hasq = true; + if (event[ida].id() == -mPDG) + hasqbar = true; + if ((event[ida].y() > mRapidityMin) && (event[ida].y() < mRapidityMax)) + atSelectedY = true; + } + if (hasq && hasqbar && atSelectedY) isOkAtPartonicLevel = true; + } + + if( (mHadronMultiplicity <= 0) && isOkAtPartonicLevel) return true; // no selection at hadron level + + /// check at hadron level if needed + int ipdg=0; + for (auto& pdgVal : mHadronsPDGs){ + if ( (TMath::Abs(event[ipa].id()) == pdgVal) && (event[ipa].y() > mHadronRapidityMin) && (event[ipa].y() < mHadronRapidityMax) ) countH[ipdg]++; + if(isOkAtPartonicLevel && countH[ipdg] >= mHadronMultiplicity) return true; + ipdg++; + } + } + return false; + }; + + +private: +// Interface to override import particles +Pythia8::Event mOutputEvent; + + // Control gap-triggering + unsigned long long mGeneratedEvents; + int mInverseTriggerRatio; + Pythia8::Pythia pythiaMBgen; // minimum bias event + TString mConfigMBdecays; + int mPDG; + std::vector mHadronsPDGs; + int mHadronMultiplicity; + double mRapidityMin; + double mRapidityMax; + double mHadronRapidityMin; + double mHadronRapidityMax; + bool mVerbose; + }; + +} + +} + +// Predefined generators: +FairGenerator* + GeneratorBeautyToJpsi_EvtGenMidY_pO(double rapidityMin = -1.5, double rapidityMax = 1.5, bool verbose = false, TString pdgs = "511;521;531;541;5112;5122;5232;5132;5332") +{ + auto gen = new o2::eventgen::GeneratorEvtGen(); + gen->setRapidity(rapidityMin, rapidityMax); + gen->setPDG(5); + gen->setRapidityHadron(-1.5,1.5); + gen->setHadronMultiplicity(1); + TString pathO2table = gSystem->ExpandPathName("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/pythia8/decayer/switchOffBhadrons.cfg"); + gen->readFile(pathO2table.Data()); + gen->setConfigMBdecays(pathO2table); + gen->setVerbose(verbose); + + std::string spdg; + TObjArray* obj = pdgs.Tokenize(";"); + gen->SetSizePdg(obj->GetEntriesFast()); + for (int i = 0; i < obj->GetEntriesFast(); i++) { + spdg = obj->At(i)->GetName(); + gen->AddPdg(std::stoi(spdg), i); + gen->addHadronPDGs(std::stoi(spdg)); + printf("PDG %d \n", std::stoi(spdg)); + } + gen->SetForceDecay(kEvtBJpsiDiElectron); + + // set random seed + gen->readString("Random:setSeed on"); + uint random_seed; + unsigned long long int random_value = 0; + ifstream urandom("/dev/urandom", ios::in|ios::binary); + urandom.read(reinterpret_cast(&random_value), sizeof(random_seed)); + gen->readString(Form("Random:seed = %d", random_value % 900000001)); + + // print debug + // gen->PrintDebug(); + + return gen; +} + +FairGenerator* + GeneratorBeautyToPsiAndJpsi_EvtGenMidY_pO(double rapidityMin = -1.5, double rapidityMax = 1.5, bool verbose = false, TString pdgs = "511;521;531;541;5112;5122;5232;5132;5332") +{ + auto gen = new o2::eventgen::GeneratorEvtGen(); + gen->setRapidity(rapidityMin, rapidityMax); + gen->setPDG(5); + gen->setRapidityHadron(rapidityMin,rapidityMax); + gen->setHadronMultiplicity(1); + TString pathO2table = gSystem->ExpandPathName("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/pythia8/decayer/switchOffBhadrons.cfg"); + gen->readFile(pathO2table.Data()); + gen->setConfigMBdecays(pathO2table); + gen->setVerbose(verbose); + + std::string spdg; + TObjArray* obj = pdgs.Tokenize(";"); + gen->SetSizePdg(obj->GetEntriesFast()); + for (int i = 0; i < obj->GetEntriesFast(); i++) { + spdg = obj->At(i)->GetName(); + gen->AddPdg(std::stoi(spdg), i); + printf("PDG %d \n", std::stoi(spdg)); + gen->addHadronPDGs(std::stoi(spdg)); + } + gen->SetForceDecay(kEvtBPsiAndJpsiDiElectron); + + // set random seed + gen->readString("Random:setSeed on"); + uint random_seed; + unsigned long long int random_value = 0; + ifstream urandom("/dev/urandom", ios::in|ios::binary); + urandom.read(reinterpret_cast(&random_value), sizeof(random_seed)); + gen->readString(Form("Random:seed = %d", random_value % 900000001)); + + // print debug + // gen->PrintDebug(); + + return gen; +} + +// Predefined generators: +FairGenerator* + GeneratorBeautyToJpsi_EvtGenFwdY_pO(double rapidityMin = -4.3, double rapidityMax = -2.3, bool verbose = false, TString pdgs = "511;521;531;541;5112;5122;5232;5132;5332") +{ + auto gen = new o2::eventgen::GeneratorEvtGen(); + gen->setRapidity(rapidityMin, rapidityMax); + gen->setPDG(5); + gen->setRapidityHadron(rapidityMin,rapidityMax); + gen->setHadronMultiplicity(1); + TString pathO2table = gSystem->ExpandPathName("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/pythia8/decayer/switchOffBhadrons.cfg"); + gen->readFile(pathO2table.Data()); + gen->setConfigMBdecays(pathO2table); + gen->setVerbose(verbose); + + std::string spdg; + TObjArray* obj = pdgs.Tokenize(";"); + gen->SetSizePdg(obj->GetEntriesFast()); + for (int i = 0; i < obj->GetEntriesFast(); i++) { + spdg = obj->At(i)->GetName(); + gen->AddPdg(std::stoi(spdg), i); + gen->addHadronPDGs(std::stoi(spdg)); + printf("PDG %d \n", std::stoi(spdg)); + } + gen->SetForceDecay(kEvtBJpsiDiMuon); + + // set random seed + gen->readString("Random:setSeed on"); + uint random_seed; + unsigned long long int random_value = 0; + ifstream urandom("/dev/urandom", ios::in|ios::binary); + urandom.read(reinterpret_cast(&random_value), sizeof(random_seed)); + gen->readString(Form("Random:seed = %d", random_value % 900000001)); + + // print debug + // gen->PrintDebug(); + + return gen; +} + + +FairGenerator* + GeneratorBeautyToPsiAndJpsi_EvtGenFwdY_pO(double rapidityMin = -4.3, double rapidityMax = -2.3, bool verbose = false, TString pdgs = "511;521;531;541;5112;5122;5232;5132;5332") +{ + auto gen = new o2::eventgen::GeneratorEvtGen(); + gen->setRapidity(rapidityMin, rapidityMax); + gen->setPDG(5); + gen->setRapidityHadron(rapidityMin,rapidityMax); + gen->setHadronMultiplicity(1); + TString pathO2table = gSystem->ExpandPathName("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/pythia8/decayer/switchOffBhadrons.cfg"); + gen->readFile(pathO2table.Data()); + gen->setConfigMBdecays(pathO2table); + gen->setVerbose(verbose); + + std::string spdg; + TObjArray* obj = pdgs.Tokenize(";"); + gen->SetSizePdg(obj->GetEntriesFast()); + for (int i = 0; i < obj->GetEntriesFast(); i++) { + spdg = obj->At(i)->GetName(); + gen->AddPdg(std::stoi(spdg), i); + printf("PDG %d \n", std::stoi(spdg)); + gen->addHadronPDGs(std::stoi(spdg)); + } + gen->SetForceDecay(kEvtBPsiAndJpsiDiMuon); + + // set random seed + gen->readString("Random:setSeed on"); + uint random_seed; + unsigned long long int random_value = 0; + ifstream urandom("/dev/urandom", ios::in|ios::binary); + urandom.read(reinterpret_cast(&random_value), sizeof(random_seed)); + gen->readString(Form("Random:seed = %d", random_value % 900000001)); + + // print debug + // gen->PrintDebug(); + + return gen; +} + diff --git a/MC/config/PWGDQ/external/generator/generator_pythia8_StarlightTriggered_withGap.C b/MC/config/PWGDQ/external/generator/generator_pythia8_StarlightTriggered_withGap.C new file mode 100644 index 000000000..d4ab1fedf --- /dev/null +++ b/MC/config/PWGDQ/external/generator/generator_pythia8_StarlightTriggered_withGap.C @@ -0,0 +1,212 @@ +#include "FairGenerator.h" +#include "Generators/GeneratorPythia8.h" +#include "Pythia8/HeavyIons.h" +#include "Pythia8/Pythia.h" +#include "TRandom.h" +#include "GeneratorCocktailStarlight_PbPb5TeV.C" +#include +#include +#include +#include + +using namespace o2::eventgen; +using namespace Pythia8; + +namespace o2 +{ +namespace eventgen +{ + + +class GeneratorPythia8StarlightTriggeredWithGap : public o2::eventgen::GeneratorPythia8 { +public: + + /// default constructor + GeneratorPythia8StarlightTriggeredWithGap() = default; + + /// constructor + GeneratorPythia8StarlightTriggeredWithGap(int inputTriggerRatio = 5, int gentype = 0) { + mGeneratedEvents = 0; + mInverseTriggerRatio = inputTriggerRatio; + mNumSignals = 0; + switch (gentype) { + case 0: // generate photoproduced charmonia cocktail at mid rapidity at 5TeV + mGeneratorParam = (Generator*)GeneratorCocktailStarlightMidy_PbPb5TeV(); + break; + case 1: // generate coherent charmonia cocktail at mid rapidity at 5TeV + mGeneratorParam = (Generator*)GeneratorCocktailStarlightCoherentMidy_PbPb5TeV(); + break; + case 2: // generate incoherent charmonia cocktail at mid rapidity at 5TeV + mGeneratorParam = (Generator*)GeneratorCocktailStarlightIncoherentMidy_PbPb5TeV(); + break; + case 3: // generate coherent charmonia cocktail at forward rapidity at 5TeV + mGeneratorParam = (Generator*)GeneratorCocktailStarlightCoherentFwdy_PbPb5TeV(); + break; + case 4: // generate incoherent charmonia cocktail at forward rapidity at 5TeV + mGeneratorParam = (Generator*)GeneratorCocktailStarlightIncoherentFwdy_PbPb5TeV(); + break; + } + mGeneratorParam->Init(); + + addSubGenerator(0, "Minimum bias"); + addSubGenerator(1, "event with injected signals"); + } + + /// Deconstructor + ~GeneratorPythia8StarlightTriggeredWithGap() = default; + + void addSignalPDGs(int pdg) { mSignalsPDGs.push_back(pdg); }; + + void setRapidityRange(double valMin, double valMax) + { + mHadronRapidityMin = valMin; + mHadronRapidityMax = valMax; + }; + +protected: + Bool_t generateEvent() override + { + // GeneratorPythia8::generateEvent(); + mNumSignals = 0; + bool genOk = false; + if (mGeneratedEvents % mInverseTriggerRatio == 0){ // add injected prompt signals to the stack + std::cout<<"generating event with injected signals"<b(); + mNumSignals = getNumSignalsForImpactParameter(impactParameter); + } else { + std::cout<<"no heavy-ion info available; embedding no signal events"< 0 ? 1 : 0); + } else { // gap event + while (!genOk) { + genOk = GeneratorPythia8::generateEvent(); + } + notifySubGenerator(0); + } + mGeneratedEvents++; + std::cout<<"generated events: "<generateEvent()) { + continue; + } + if (!mGeneratorParam->importParticles()) { + mGeneratorParam->clearParticles(); + continue; + } + found = findSignalInAcceptance(); + if (found) { + appendCurrentSignalParticles(isig); + } + mGeneratorParam->clearParticles(); + } + } + } + + return true; + } + + bool findSignalInAcceptance() { + // std::cout<<"loop over" << mGeneratorParam->getParticles().size()<<" particles"<getParticles().size(); ipart++) { + TParticle part = TParticle(mGeneratorParam->getParticles().at(ipart)); + // make sure all signals are in the acceptance + for (int pdg : mSignalsPDGs) { + if (part.GetPdgCode() == pdg) { + // std::cout<<"found signal with pdg: "< mHadronRapidityMax) { + return false; + } + } + } + } + } + std::cout<<"generated signal in acceptance"< 10% centrality + // if (impactParameter < photoproductionImpactParameterCut) { + // std::cout<<"impact parameter: "<(std::lround(5.0 + 0.886202881 * std::pow(std::max(0.0, 17.5 - impactParameter), 1.7)))); + } + + void appendCurrentSignalParticles(int signalIndex) + { + int originalSize = mParticles.size(); + // std::cout<<"adding "<getParticles().size()<<" particles from signal event "<getParticles().size(); ipart++){ + TParticle part = TParticle(mGeneratorParam->getParticles().at(ipart)); + if(part.GetFirstMother() >= 0) part.SetFirstMother(part.GetFirstMother() + originalSize); + if(part.GetSecondMother() >= 0) part.SetLastMother(part.GetSecondMother() + originalSize); + if(part.GetFirstDaughter() >= 0) part.SetFirstDaughter(part.GetFirstDaughter() + originalSize); + if(part.GetLastDaughter() >= 0) part.SetLastDaughter(part.GetLastDaughter() + originalSize); + mParticles.push_back(part); + // encodeParticleStatusAndTracking method already called in GeneratorEvtGen.C + } + } + +private: + Generator* mGeneratorParam = nullptr; + unsigned long long mGeneratedEvents = 0; + int mInverseTriggerRatio = 1; + int mNumSignals = 0; + // Pythia8::Pythia pythiaMBgen; // minimum bias event + std::vector mSignalsPDGs; + double mHadronRapidityMin; + double mHadronRapidityMax; +}; +} +} + +FairGenerator* + GeneratorPhotoproduction(int triggerGap, int gentype = 0, double rapidityMin = -1.5, double rapidityMax = 1.5) +{ + auto gen = new o2::eventgen::GeneratorPythia8StarlightTriggeredWithGap(triggerGap, gentype); + gen->setRapidityRange(rapidityMin, rapidityMax); + gen->addSignalPDGs(443); // J/Psi + gen->addSignalPDGs(100443); // Psi(2S) + gen->addSignalPDGs(11); // e +// auto seed = (gRandom->TRandom::GetSeed() % 900000000); +// gen->readString("Random:setSeed on"); +// gen->readString("Random:seed " + std::to_string(seed)); + return gen; +} + +FairGenerator* + GeneratorPhotoproductionFwd(int triggerGap, int gentype = 0, double rapidityMin = -1.5, double rapidityMax = 1.5) +{ + auto gen = new o2::eventgen::GeneratorPythia8StarlightTriggeredWithGap(triggerGap, gentype); + gen->setRapidityRange(rapidityMin, rapidityMax); + gen->addSignalPDGs(443); // J/Psi + gen->addSignalPDGs(100443); // Psi(2S) + gen->addSignalPDGs(13); // mu +// auto seed = (gRandom->TRandom::GetSeed() % 900000000); +// gen->readString("Random:setSeed on"); +// gen->readString("Random:seed " + std::to_string(seed)); + return gen; +} \ No newline at end of file diff --git a/MC/config/PWGDQ/external/generator/generator_pythia8_withInjectedBottomoniaSignals_gaptriggered_dq.C b/MC/config/PWGDQ/external/generator/generator_pythia8_withInjectedBottomoniaSignals_gaptriggered_dq.C new file mode 100644 index 000000000..7f2ced70e --- /dev/null +++ b/MC/config/PWGDQ/external/generator/generator_pythia8_withInjectedBottomoniaSignals_gaptriggered_dq.C @@ -0,0 +1,79 @@ +#include "FairGenerator.h" +#include "Generators/GeneratorPythia8.h" +#include "Pythia8/Pythia.h" +#include "TRandom.h" +#include "GeneratorBottomonia.C" +#include + +using namespace o2::eventgen; +using namespace Pythia8; + +class GeneratorPythia8BottomoniaInjectedGapTriggeredDQ : public o2::eventgen::GeneratorPythia8 { +public: + + /// default constructor + GeneratorPythia8BottomoniaInjectedGapTriggeredDQ() = default; + + /// constructor + GeneratorPythia8BottomoniaInjectedGapTriggeredDQ(int inputTriggerRatio = 5, int gentype = 0) { + + mGeneratedEvents = 0; + mGeneratorParam = 0x0; + mInverseTriggerRatio = inputTriggerRatio; + switch (gentype) { + case 0: // generate bottomonia cocktail at forward rapidity + mGeneratorParam = (Generator*)GeneratorCocktailBottomoniaToMuonEvtGen_pp13TeV(); + break; + case 1: // generate bottomonia cocktail at FwdY for PbPb 5p36TeV + mGeneratorParam = (Generator*)GeneratorCocktailBottomoniaToMuonEvtGen_PbPb5TeV(); + break; + } + mGeneratorParam->Init(); + } + + /// Destructor + ~GeneratorPythia8BottomoniaInjectedGapTriggeredDQ() = default; + +protected: + +Bool_t importParticles() override + { + GeneratorPythia8::importParticles(); + bool genOk = false; + if (mGeneratedEvents % mInverseTriggerRatio == 0) { // add injected prompt signals to the stack + bool genOk = false; + while (!genOk) { + genOk = (mGeneratorParam->generateEvent() && mGeneratorParam->importParticles()) ? true : false ; + } + int originalSize = mParticles.size(); + for (int ipart=0; ipart < mGeneratorParam->getParticles().size(); ipart++) { + TParticle part = TParticle(mGeneratorParam->getParticles().at(ipart)); + if(part.GetFirstMother() >= 0) part.SetFirstMother(part.GetFirstMother() + originalSize); + if(part.GetFirstDaughter() >= 0) part.SetFirstDaughter(part.GetFirstDaughter() + originalSize); + if(part.GetLastDaughter() >= 0) part.SetLastDaughter(part.GetLastDaughter() + originalSize); + mParticles.push_back(part); + // encodeParticleStatusAndTracking method already called in GeneratorEvtGen.C + } + mGeneratorParam->clearParticles(); + } + + mGeneratedEvents++; + return true; + } + + +private: + Generator* mGeneratorParam; + // Control gap-triggering + unsigned long long mGeneratedEvents; + int mInverseTriggerRatio; +}; + +// Predefined generators: +FairGenerator *GeneratorPythia8InjectedBottomoniaGapTriggered(int inputTriggerRatio, int gentype) { + auto myGen = new GeneratorPythia8BottomoniaInjectedGapTriggeredDQ(inputTriggerRatio,gentype); + auto seed = (gRandom->TRandom::GetSeed() % 900000000); + myGen->readString("Random:setSeed on"); + myGen->readString("Random:seed " + std::to_string(seed)); + return myGen; +} diff --git a/MC/config/PWGDQ/external/generator/generator_pythia8_withInjectedPromptSignals_gaptriggered_dq.C b/MC/config/PWGDQ/external/generator/generator_pythia8_withInjectedPromptSignals_gaptriggered_dq.C index 5901729f4..03281b391 100644 --- a/MC/config/PWGDQ/external/generator/generator_pythia8_withInjectedPromptSignals_gaptriggered_dq.C +++ b/MC/config/PWGDQ/external/generator/generator_pythia8_withInjectedPromptSignals_gaptriggered_dq.C @@ -3,7 +3,6 @@ #include "Pythia8/Pythia.h" #include "TRandom.h" #include "GeneratorPromptCharmonia.C" - #include using namespace o2::eventgen; @@ -40,6 +39,36 @@ public: case 5: // generate prompt psi2S at forward rapidity mGeneratorParam = (Generator*)GeneratorParamPromptPsiToMuonEvtGen_pp13TeV("100443"); break; + case 6: // generate prompt ChiC1 + ChiC2 cocktail at midrapidity + mGeneratorParam = (Generator*)GeneratorCocktailChiCToElectronEvtGen_pp13TeV(); + break; + case 7: // generate prompt charmonia cocktail at forward rapidity at 5TeV + mGeneratorParam = (Generator*)GeneratorCocktailPromptCharmoniaToMuonEvtGen_PbPb5TeV(); + break; + case 8: // generate prompt X_1(3872) to Jpsi pi pi at midrapidity + mGeneratorParam = (Generator*)GeneratorParamX3872ToJpsiEvtGen_pp13TeV("9920443"); + break; + case 9: // generate prompt psi2S to Jpsi pi pi at midrapidity + mGeneratorParam = (Generator*)GeneratorParamPromptPsiToJpsiPiPiEvtGen_pp13TeV("100443"); + break; + case 10: // generate cocktail of prompt X_1(3872) and psi2S to Jpsi pi pi at midrapidity + mGeneratorParam = (Generator*)GeneratorCocktailX3872AndPsi2StoJpsi_pp13TeV(); + break; + case 11: // generate prompt charmonium at forward rapidity at 5TeV + mGeneratorParam = (Generator*)GeneratorCocktailPromptCharmoniaToMuonEvtGen_pp5TeV(); + break; + case 12: // generate prompt charmonia cocktail at mid rapidity at 5TeV + mGeneratorParam = (Generator*)GeneratorCocktailPromptCharmoniaToElectronEvtGen_pp5TeV(); + break; + case 13: // generate prompt charmonia cocktail at fwd rapidity at 9.6TeV + mGeneratorParam = (Generator*)GeneratorCocktailPromptCharmoniaToMuonEvtGen_pp96TeV(); + break; + case 14: // generate prompt charmonia cocktail at mid rapidity at 9.6TeV + mGeneratorParam = (Generator*)GeneratorCocktailPromptCharmoniaToElectronEvtGen_pp96TeV(); + break; + case 15: // generate prompt charmonium at forward rapidity at 5TeV for OO + mGeneratorParam = (Generator*)GeneratorCocktailPromptCharmoniaToMuonEvtGen_OO5TeV(); + break; } mGeneratorParam->Init(); } diff --git a/MC/config/PWGDQ/ini/GeneratorHF_JPsiToMuons_fwdy.ini b/MC/config/PWGDQ/ini/GeneratorHF_JPsiToMuons_fwdy.ini index c332b4d98..8337ee3da 100644 --- a/MC/config/PWGDQ/ini/GeneratorHF_JPsiToMuons_fwdy.ini +++ b/MC/config/PWGDQ/ini/GeneratorHF_JPsiToMuons_fwdy.ini @@ -2,5 +2,5 @@ ### This part sets the path of the file and the function call to retrieve it [GeneratorExternal] -fileName = ${O2DPG_ROOT}/MC/config/PWGDQ/external/generator/GeneratorParamPromptJpsiToMuonEvtGen_pp13TeV.C +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/external/generator/GeneratorParamPromptJpsiToMuonEvtGen_pp13TeV.C funcName = GeneratorParamPromptJpsiToMuonEvtGen_pp13TeV() diff --git a/MC/config/PWGDQ/ini/GeneratorHF_bbbarToBplus_midy.ini b/MC/config/PWGDQ/ini/GeneratorHF_bbbarToBplus_midy.ini deleted file mode 100644 index d5dc6a1f6..000000000 --- a/MC/config/PWGDQ/ini/GeneratorHF_bbbarToBplus_midy.ini +++ /dev/null @@ -1,28 +0,0 @@ -### The setup uses an external event generator -### This part sets the path of the file and the function call to retrieve it - -[GeneratorExternal] -fileName = ${O2DPG_ROOT}/MC/config/PWGDQ/external/generator/GeneratorBplusToJpsiKaon_EvtGen.C -funcName = GeneratorBplusToJpsiKaon_EvtGen() - -### The external generator derives from GeneratorPythia8. -### This part configures the bits of the interface: configuration and user hooks - -[GeneratorPythia8] -config = ${O2DPG_ROOT}/MC/config/common/pythia8/generator/pythia8_hf.cfg -hooksFileName = ${O2DPG_ROOT}/MC/config/PWGHF/pythia8/hooks/pythia8_userhooks_qqbar.C -hooksFuncName = pythia8_userhooks_bbbar(-1.5,1.5) - -### The setup uses an external even generator trigger which is -### defined in the following file and it is retrieved and configured -### according to the specified function call - -[TriggerExternal] -fileName = ${O2DPG_ROOT}/MC/config/PWGDQ/trigger/selectDaughterFromHFwithinAcc.C -funcName = selectHFwithinAcc(521,kFALSE,-1.5,1.5) - -### The setup inhibits transport of primary particles which are produce at forward rapidity. -### The settings below only transports particles in the barrel, which is currently defined as |eta| < 2 - -[Stack] -transportPrimary = barrel diff --git a/MC/config/PWGDQ/ini/GeneratorHF_bbbarToBplus_midy_triggerGap.ini b/MC/config/PWGDQ/ini/GeneratorHF_bbbarToBplus_midy_triggerGap.ini index 69f0d4a2b..9b359fb9a 100644 --- a/MC/config/PWGDQ/ini/GeneratorHF_bbbarToBplus_midy_triggerGap.ini +++ b/MC/config/PWGDQ/ini/GeneratorHF_bbbarToBplus_midy_triggerGap.ini @@ -2,19 +2,13 @@ ### This part sets the path of the file and the function call to retrieve it [GeneratorExternal] -fileName = ${O2DPG_ROOT}/MC/config/PWGDQ/external/generator/generator_pythia8_NonPromptSignals_gaptriggered_dq.C -funcName = GeneratorBplusToJpsiKaon_EvtGen() +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/external/generator/generator_pythia8_NonPromptSignals_gaptriggered_dq.C +funcName = GeneratorBplusToJpsiKaon_EvtGen(-1.5,1.5,true) ### The external generator derives from GeneratorPythia8. ### This part configures the bits of the interface: configuration and user hooks [GeneratorPythia8] -config = ${O2DPG_ROOT}/MC/config/common/pythia8/generator/pythia8_hf.cfg -hooksFileName = ${O2DPG_ROOT}/MC/config/PWGHF/pythia8/hooks/pythia8_userhooks_qqbar.C +config = ${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/generator/pythia8_hf.cfg +hooksFileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/hooks/pythia8_userhooks_qqbar.C hooksFuncName = pythia8_userhooks_bbbar(-1.5,1.5) - -### The setup inhibits transport of primary particles which are produce at forward rapidity. -### The settings below only transports particles in the barrel, which is currently defined as |eta| < 2 - -[Stack] -transportPrimary = barrel diff --git a/MC/config/PWGDQ/ini/GeneratorHF_bbbarToDDbarToMuons_fwdy_TriggerGap.ini b/MC/config/PWGDQ/ini/GeneratorHF_bbbarToDDbarToMuons_fwdy_TriggerGap.ini new file mode 100644 index 000000000..3157ddda7 --- /dev/null +++ b/MC/config/PWGDQ/ini/GeneratorHF_bbbarToDDbarToMuons_fwdy_TriggerGap.ini @@ -0,0 +1,14 @@ +### The setup uses an external event generator +### This part sets the path of the file and the function call to retrieve it + +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/external/generator/GeneratorHFToMu_EvtGenFwdY_gaptriggered_dq.C +funcName = GeneratorHFToMu_EvtGenFwdY_gaptriggered_dq(-4.3, -2.3, false, true, true) + +### The external generator derives from GeneratorPythia8. +### This part configures the bits of the interface: configuration and user hooks + +[GeneratorPythia8] +config = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/pythia8/generator/pythia8_hard_cr2.cfg +hooksFileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/hooks/pythia8_userhooks_qqbar.C +hooksFuncName = pythia8_userhooks_bbbar(-4.3, -2.3) diff --git a/MC/config/PWGDQ/ini/GeneratorHF_bbbarToDDbarToMuons_fwdy_TriggerGap_inel.ini b/MC/config/PWGDQ/ini/GeneratorHF_bbbarToDDbarToMuons_fwdy_TriggerGap_inel.ini new file mode 100644 index 000000000..d41df25e2 --- /dev/null +++ b/MC/config/PWGDQ/ini/GeneratorHF_bbbarToDDbarToMuons_fwdy_TriggerGap_inel.ini @@ -0,0 +1,14 @@ +### The setup uses an external event generator +### This part sets the path of the file and the function call to retrieve it + +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/external/generator/GeneratorHFToMu_EvtGenFwdY_gaptriggered_dq.C +funcName = GeneratorHFToMu_EvtGenFwdY_gaptriggered_dq(-4.3, -2.3, false, true, true) + +### The external generator derives from GeneratorPythia8. +### This part configures the bits of the interface: configuration and user hooks + +[GeneratorPythia8] +config = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/pythia8/generator/pythia8_inel_cr2.cfg +hooksFileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/hooks/pythia8_userhooks_qqbar.C +hooksFuncName = pythia8_userhooks_bbbar(-4.3, -2.3) diff --git a/MC/config/PWGDQ/ini/GeneratorHF_bbbarToDDbarToMuons_fwdy_TriggerGap_natural.ini b/MC/config/PWGDQ/ini/GeneratorHF_bbbarToDDbarToMuons_fwdy_TriggerGap_natural.ini new file mode 100644 index 000000000..0cb4391aa --- /dev/null +++ b/MC/config/PWGDQ/ini/GeneratorHF_bbbarToDDbarToMuons_fwdy_TriggerGap_natural.ini @@ -0,0 +1,14 @@ +### The setup uses an external event generator +### This part sets the path of the file and the function call to retrieve it + +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/external/generator/GeneratorHFToMu_EvtGenFwdY_gaptriggered_dq.C +funcName = GeneratorHFToMu_EvtGenFwdY_gaptriggered_dq(-4.3, -2.3, false, true, false) + +### The external generator derives from GeneratorPythia8. +### This part configures the bits of the interface: configuration and user hooks + +[GeneratorPythia8] +config = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/pythia8/generator/pythia8_hard_cr2.cfg +hooksFileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/hooks/pythia8_userhooks_qqbar.C +hooksFuncName = pythia8_userhooks_bbbar(-4.3, -2.3) diff --git a/MC/config/PWGDQ/ini/GeneratorHF_bbbarToDDbarToMuons_fwdy_TriggerGap_natural_inel.ini b/MC/config/PWGDQ/ini/GeneratorHF_bbbarToDDbarToMuons_fwdy_TriggerGap_natural_inel.ini new file mode 100644 index 000000000..7526ca494 --- /dev/null +++ b/MC/config/PWGDQ/ini/GeneratorHF_bbbarToDDbarToMuons_fwdy_TriggerGap_natural_inel.ini @@ -0,0 +1,14 @@ +### The setup uses an external event generator +### This part sets the path of the file and the function call to retrieve it + +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/external/generator/GeneratorHFToMu_EvtGenFwdY_gaptriggered_dq.C +funcName = GeneratorHFToMu_EvtGenFwdY_gaptriggered_dq(-4.3, -2.3, false, true, false) + +### The external generator derives from GeneratorPythia8. +### This part configures the bits of the interface: configuration and user hooks + +[GeneratorPythia8] +config = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/pythia8/generator/pythia8_inel_cr2.cfg +hooksFileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/hooks/pythia8_userhooks_qqbar.C +hooksFuncName = pythia8_userhooks_bbbar(-4.3, -2.3) diff --git a/MC/config/PWGDQ/ini/GeneratorHF_bbbarToMuonsSemileptonic_fwdy.ini b/MC/config/PWGDQ/ini/GeneratorHF_bbbarToMuonsSemileptonic_fwdy.ini deleted file mode 100644 index 454660a57..000000000 --- a/MC/config/PWGDQ/ini/GeneratorHF_bbbarToMuonsSemileptonic_fwdy.ini +++ /dev/null @@ -1,22 +0,0 @@ -### The setup uses an external event generator -### This part sets the path of the file and the function call to retrieve it - -[GeneratorExternal] -fileName = ${O2DPG_ROOT}/MC/config/PWGDQ/external/generator/GeneratorBeautyToMu_EvtGen.C -funcName = GeneratorBeautyToMu_EvtGenFwdY() - -### The external generator derives from GeneratorPythia8. -### This part configures the bits of the interface: configuration and user hooks - -[GeneratorPythia8] -config = ${O2DPG_ROOT}/MC/config/common/pythia8/generator/pythia8_hf.cfg -hooksFileName = ${O2DPG_ROOT}/MC/config/PWGHF/pythia8/hooks/pythia8_userhooks_qqbar.C -hooksFuncName = pythia8_userhooks_bbbar(-4.3,-2.3) - -### The setup uses an external even generator trigger which is -### defined in the following file and it is retrieved and configured -### according to the specified function call - -[TriggerExternal] -fileName = ${O2DPG_ROOT}/MC/config/PWGDQ/trigger/selectDaughterFromHFwithinAcc.C -funcName = selectDaughterFromHFwithinAcc(13,kTRUE,-4.3,-2.3) diff --git a/MC/config/PWGDQ/ini/GeneratorHF_bbbar_Psi2S_fwdy.ini b/MC/config/PWGDQ/ini/GeneratorHF_bbbar_Psi2S_fwdy.ini deleted file mode 100644 index 00d1dcea9..000000000 --- a/MC/config/PWGDQ/ini/GeneratorHF_bbbar_Psi2S_fwdy.ini +++ /dev/null @@ -1,22 +0,0 @@ -### The setup uses an external event generator -### This part sets the path of the file and the function call to retrieve it - -[GeneratorExternal] -fileName = ${O2DPG_ROOT}/MC/config/PWGDQ/external/generator/GeneratorBeautyToPsi_EvtGen.C -funcName = GeneratorBeautyToPsi_EvtGenFwdY() - -### The external generator derives from GeneratorPythia8. -### This part configures the bits of the interface: configuration and user hooks - -[GeneratorPythia8] -config = ${O2DPG_ROOT}/MC/config/common/pythia8/generator/pythia8_hf.cfg -hooksFileName = ${O2DPG_ROOT}/MC/config/PWGHF/pythia8/hooks/pythia8_userhooks_qqbar.C -hooksFuncName = pythia8_userhooks_bbbar(-4.3,-2.3) - -### The setup uses an external even generator trigger which is -### defined in the following file and it is retrieved and configured -### according to the specified function call - -[TriggerExternal] -fileName = ${O2DPG_ROOT}/MC/config/PWGDQ/trigger/selectDaughterFromHFwithinAcc.C -funcName = selectDaughterFromHFwithinAcc(100443,kTRUE,-4.3,-2.3) diff --git a/MC/config/PWGDQ/ini/GeneratorHF_bbbar_Psi2S_midy.ini b/MC/config/PWGDQ/ini/GeneratorHF_bbbar_Psi2S_midy.ini deleted file mode 100644 index a7ddac0e4..000000000 --- a/MC/config/PWGDQ/ini/GeneratorHF_bbbar_Psi2S_midy.ini +++ /dev/null @@ -1,28 +0,0 @@ -### The setup uses an external event generator -### This part sets the path of the file and the function call to retrieve it - -[GeneratorExternal] -fileName = ${O2DPG_ROOT}/MC/config/PWGDQ/external/generator/GeneratorBeautyToPsi_EvtGen.C -funcName = GeneratorBeautyToPsi_EvtGenMidY() - -### The external generator derives from GeneratorPythia8. -### This part configures the bits of the interface: configuration and user hooks - -[GeneratorPythia8] -config = ${O2DPG_ROOT}/MC/config/common/pythia8/generator/pythia8_hf.cfg -hooksFileName = ${O2DPG_ROOT}/MC/config/PWGHF/pythia8/hooks/pythia8_userhooks_qqbar.C -hooksFuncName = pythia8_userhooks_bbbar(-1.5,1.5) - -### The setup uses an external even generator trigger which is -### defined in the following file and it is retrieved and configured -### according to the specified function call - -[TriggerExternal] -fileName = ${O2DPG_ROOT}/MC/config/PWGDQ/trigger/selectDaughterFromHFwithinAcc.C -funcName = selectDaughterFromHFwithinAcc(100443,kTRUE,-1.5,1.5) - -### The setup inhibits transport of primary particles which are produce at forward rapidity. -### The settings below only transports particles in the barrel, which is currently defined as |eta| < 2 - -[Stack] -transportPrimary = barrel diff --git a/MC/config/PWGDQ/ini/GeneratorHF_bbbar_PsiAndJpsi_fwdy.ini b/MC/config/PWGDQ/ini/GeneratorHF_bbbar_PsiAndJpsi_fwdy.ini deleted file mode 100644 index b4c6801e8..000000000 --- a/MC/config/PWGDQ/ini/GeneratorHF_bbbar_PsiAndJpsi_fwdy.ini +++ /dev/null @@ -1,22 +0,0 @@ -### The setup uses an external event generator -### This part sets the path of the file and the function call to retrieve it - -[GeneratorExternal] -fileName = ${O2DPG_ROOT}/MC/config/PWGDQ/external/generator/GeneratorBeautyToPsiAndJpsi_EvtGen.C -funcName = GeneratorBeautyToPsiAndJpsi_EvtGenFwdY() - -### The external generator derives from GeneratorPythia8. -### This part configures the bits of the interface: configuration and user hooks - -[GeneratorPythia8] -config = ${O2DPG_ROOT}/MC/config/common/pythia8/generator/pythia8_hf.cfg -hooksFileName = ${O2DPG_ROOT}/MC/config/PWGHF/pythia8/hooks/pythia8_userhooks_qqbar.C -hooksFuncName = pythia8_userhooks_bbbar(-4.3,-2.3) - -### The setup uses an external even generator trigger which is -### defined in the following file and it is retrieved and configured -### according to the specified function call - -[TriggerExternal] -fileName = ${O2DPG_ROOT}/MC/config/PWGDQ/trigger/selectDaughterFromHFwithinAcc.C -funcName = selecMultipletHFwithinAcc("443;100443",kTRUE,-4.3,-2.3) diff --git a/MC/config/PWGDQ/ini/GeneratorHF_bbbar_PsiAndJpsi_fwdy_triggerGap.ini b/MC/config/PWGDQ/ini/GeneratorHF_bbbar_PsiAndJpsi_fwdy_triggerGap.ini index 7ca57246a..20d6a6006 100644 --- a/MC/config/PWGDQ/ini/GeneratorHF_bbbar_PsiAndJpsi_fwdy_triggerGap.ini +++ b/MC/config/PWGDQ/ini/GeneratorHF_bbbar_PsiAndJpsi_fwdy_triggerGap.ini @@ -2,14 +2,14 @@ ### This part sets the path of the file and the function call to retrieve it [GeneratorExternal] -fileName = ${O2DPG_ROOT}/MC/config/PWGDQ/external/generator/generator_pythia8_NonPromptSignals_gaptriggered_dq.C +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/external/generator/generator_pythia8_NonPromptSignals_gaptriggered_dq.C funcName = GeneratorBeautyToPsiAndJpsi_EvtGenFwdY() ### The external generator derives from GeneratorPythia8. ### This part configures the bits of the interface: configuration and user hooks [GeneratorPythia8] -config = ${O2DPG_ROOT}/MC/config/common/pythia8/generator/pythia8_hf.cfg -hooksFileName = ${O2DPG_ROOT}/MC/config/PWGHF/pythia8/hooks/pythia8_userhooks_qqbar.C +config = ${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/generator/pythia8_hf.cfg +hooksFileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/hooks/pythia8_userhooks_qqbar.C hooksFuncName = pythia8_userhooks_bbbar(-4.3,-2.3) diff --git a/MC/config/PWGDQ/ini/GeneratorHF_bbbar_PsiAndJpsi_fwdy_triggerGap_OO5TeV.ini b/MC/config/PWGDQ/ini/GeneratorHF_bbbar_PsiAndJpsi_fwdy_triggerGap_OO5TeV.ini new file mode 100644 index 000000000..a76404bb9 --- /dev/null +++ b/MC/config/PWGDQ/ini/GeneratorHF_bbbar_PsiAndJpsi_fwdy_triggerGap_OO5TeV.ini @@ -0,0 +1,15 @@ +### The setup uses an external event generator +### This part sets the path of the file and the function call to retrieve it + +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/external/generator/generator_pythia8_NonPromptSignals_OO_gaptriggered_dq.C +funcName = GeneratorBeautyToPsiAndJpsi_EvtGenFwdY_OO() + +### The external generator derives from GeneratorPythia8. +### This part configures the bits of the interface: configuration and user hooks + +[GeneratorPythia8] +config = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/pythia8/generator/pythia8_hf_OO_536.cfg +hooksFileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/hooks/pythia8_userhooks_qqbar.C +hooksFuncName = pythia8_userhooks_bbbar(-4.3,-2.3) + diff --git a/MC/config/PWGDQ/ini/GeneratorHF_bbbar_PsiAndJpsi_fwdy_triggerGap_pO96TeV.ini b/MC/config/PWGDQ/ini/GeneratorHF_bbbar_PsiAndJpsi_fwdy_triggerGap_pO96TeV.ini new file mode 100644 index 000000000..1e4c5b84e --- /dev/null +++ b/MC/config/PWGDQ/ini/GeneratorHF_bbbar_PsiAndJpsi_fwdy_triggerGap_pO96TeV.ini @@ -0,0 +1,15 @@ +### The setup uses an external event generator +### This part sets the path of the file and the function call to retrieve it + +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/external/generator/generator_pythia8_NonPromptSignals_pO_gaptriggered_dq.C +funcName = GeneratorBeautyToPsiAndJpsi_EvtGenFwdY_pO() + +### The external generator derives from GeneratorPythia8. +### This part configures the bits of the interface: configuration and user hooks + +[GeneratorPythia8] +config = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/pythia8/generator/pythia8_hf_pO_961.cfg +hooksFileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/hooks/pythia8_userhooks_qqbar.C +hooksFuncName = pythia8_userhooks_bbbar(-4.3,-2.3) + diff --git a/MC/config/PWGDQ/ini/GeneratorHF_bbbar_PsiAndJpsi_midy.ini b/MC/config/PWGDQ/ini/GeneratorHF_bbbar_PsiAndJpsi_midy.ini deleted file mode 100644 index 0a6d69f54..000000000 --- a/MC/config/PWGDQ/ini/GeneratorHF_bbbar_PsiAndJpsi_midy.ini +++ /dev/null @@ -1,28 +0,0 @@ -### The setup uses an external event generator -### This part sets the path of the file and the function call to retrieve it - -[GeneratorExternal] -fileName = ${O2DPG_ROOT}/MC/config/PWGDQ/external/generator/GeneratorBeautyToPsiAndJpsi_EvtGen.C -funcName = GeneratorBeautyToPsiAndJpsi_EvtGenMidY() - -### The external generator derives from GeneratorPythia8. -### This part configures the bits of the interface: configuration and user hooks - -[GeneratorPythia8] -config = ${O2DPG_ROOT}/MC/config/common/pythia8/generator/pythia8_hf.cfg -hooksFileName = ${O2DPG_ROOT}/MC/config/PWGHF/pythia8/hooks/pythia8_userhooks_qqbar.C -hooksFuncName = pythia8_userhooks_bbbar(-1.5,1.5) - -### The setup uses an external even generator trigger which is -### defined in the following file and it is retrieved and configured -### according to the specified function call - -[TriggerExternal] -fileName = ${O2DPG_ROOT}/MC/config/PWGDQ/trigger/selectDaughterFromHFwithinAcc.C -funcName = selecMultipletHFwithinAcc("443;100443",kTRUE,-1.5,1.5) - -### The setup inhibits transport of primary particles which are produce at forward rapidity. -### The settings below only transports particles in the barrel, which is currently defined as |eta| < 2 - -[Stack] -transportPrimary = barrel diff --git a/MC/config/PWGDQ/ini/GeneratorHF_bbbar_PsiAndJpsi_midy_triggerGap.ini b/MC/config/PWGDQ/ini/GeneratorHF_bbbar_PsiAndJpsi_midy_triggerGap.ini index f014af9c8..2ba063903 100644 --- a/MC/config/PWGDQ/ini/GeneratorHF_bbbar_PsiAndJpsi_midy_triggerGap.ini +++ b/MC/config/PWGDQ/ini/GeneratorHF_bbbar_PsiAndJpsi_midy_triggerGap.ini @@ -2,19 +2,13 @@ ### This part sets the path of the file and the function call to retrieve it [GeneratorExternal] -fileName = ${O2DPG_ROOT}/MC/config/PWGDQ/external/generator/generator_pythia8_NonPromptSignals_gaptriggered_dq.C +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/external/generator/generator_pythia8_NonPromptSignals_gaptriggered_dq.C funcName = GeneratorBeautyToPsiAndJpsi_EvtGenMidY() ### The external generator derives from GeneratorPythia8. ### This part configures the bits of the interface: configuration and user hooks [GeneratorPythia8] -config = ${O2DPG_ROOT}/MC/config/common/pythia8/generator/pythia8_hf.cfg -hooksFileName = ${O2DPG_ROOT}/MC/config/PWGHF/pythia8/hooks/pythia8_userhooks_qqbar.C +config = ${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/generator/pythia8_hf.cfg +hooksFileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/hooks/pythia8_userhooks_qqbar.C hooksFuncName = pythia8_userhooks_bbbar(-1.5,1.5) - -### The setup inhibits transport of primary particles which are produce at forward rapidity. -### The settings below only transports particles in the barrel, which is currently defined as |eta| < 2 - -[Stack] -transportPrimary = barrel diff --git a/MC/config/PWGDQ/ini/GeneratorHF_bbbar_PsiAndJpsi_midy_triggerGap_OO5TeV.ini b/MC/config/PWGDQ/ini/GeneratorHF_bbbar_PsiAndJpsi_midy_triggerGap_OO5TeV.ini new file mode 100644 index 000000000..8643e68d6 --- /dev/null +++ b/MC/config/PWGDQ/ini/GeneratorHF_bbbar_PsiAndJpsi_midy_triggerGap_OO5TeV.ini @@ -0,0 +1,15 @@ +### The setup uses an external event generator +### This part sets the path of the file and the function call to retrieve it + +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/external/generator/generator_pythia8_NonPromptSignals_OO_gaptriggered_dq.C +funcName = GeneratorBeautyToPsiAndJpsi_EvtGenMidY_OO() + +### The external generator derives from GeneratorPythia8. +### This part configures the bits of the interface: configuration and user hooks + +[GeneratorPythia8] +config = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/pythia8/generator/pythia8_hf_OO_536.cfg +hooksFileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/hooks/pythia8_userhooks_qqbar.C +hooksFuncName = pythia8_userhooks_bbbar(-1.5,1.5) + diff --git a/MC/config/PWGDQ/ini/GeneratorHF_bbbar_PsiAndJpsi_midy_triggerGap_pO96TeV.ini b/MC/config/PWGDQ/ini/GeneratorHF_bbbar_PsiAndJpsi_midy_triggerGap_pO96TeV.ini new file mode 100644 index 000000000..ca57f2515 --- /dev/null +++ b/MC/config/PWGDQ/ini/GeneratorHF_bbbar_PsiAndJpsi_midy_triggerGap_pO96TeV.ini @@ -0,0 +1,15 @@ +### The setup uses an external event generator +### This part sets the path of the file and the function call to retrieve it + +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/external/generator/generator_pythia8_NonPromptSignals_pO_gaptriggered_dq.C +funcName = GeneratorBeautyToPsiAndJpsi_EvtGenMidY_pO() + +### The external generator derives from GeneratorPythia8. +### This part configures the bits of the interface: configuration and user hooks + +[GeneratorPythia8] +config = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/pythia8/generator/pythia8_hf_pO_961.cfg +hooksFileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/hooks/pythia8_userhooks_qqbar.C +hooksFuncName = pythia8_userhooks_bbbar(-1.5,1.5) + diff --git a/MC/config/PWGDQ/ini/GeneratorHF_bbbar_PsiToJpsi_midy_triggerGap.ini b/MC/config/PWGDQ/ini/GeneratorHF_bbbar_PsiToJpsi_midy_triggerGap.ini new file mode 100644 index 000000000..a891cc8d1 --- /dev/null +++ b/MC/config/PWGDQ/ini/GeneratorHF_bbbar_PsiToJpsi_midy_triggerGap.ini @@ -0,0 +1,14 @@ +### The setup uses an external event generator +### This part sets the path of the file and the function call to retrieve it + +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/external/generator/generator_pythia8_NonPromptSignals_gaptriggered_dq.C +funcName = GeneratorBeautyToPsiToJpsi_EvtGenMidY() + +### The external generator derives from GeneratorPythia8. +### This part configures the bits of the interface: configuration and user hooks + +[GeneratorPythia8] +config = ${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/generator/pythia8_hf.cfg +hooksFileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/hooks/pythia8_userhooks_qqbar.C +hooksFuncName = pythia8_userhooks_bbbar(-1.5,1.5) \ No newline at end of file diff --git a/MC/config/PWGDQ/ini/GeneratorHF_bbbar_fwdy.ini b/MC/config/PWGDQ/ini/GeneratorHF_bbbar_fwdy.ini deleted file mode 100644 index 8ab92ec6d..000000000 --- a/MC/config/PWGDQ/ini/GeneratorHF_bbbar_fwdy.ini +++ /dev/null @@ -1,22 +0,0 @@ -### The setup uses an external event generator -### This part sets the path of the file and the function call to retrieve it - -[GeneratorExternal] -fileName = ${O2DPG_ROOT}/MC/config/PWGDQ/external/generator/GeneratorBeautyToJpsi_EvtGen.C -funcName = GeneratorBeautyToJpsi_EvtGenFwdY() - -### The external generator derives from GeneratorPythia8. -### This part configures the bits of the interface: configuration and user hooks - -[GeneratorPythia8] -config = ${O2DPG_ROOT}/MC/config/common/pythia8/generator/pythia8_hf.cfg -hooksFileName = ${O2DPG_ROOT}/MC/config/PWGHF/pythia8/hooks/pythia8_userhooks_qqbar.C -hooksFuncName = pythia8_userhooks_bbbar(-4.3,-2.3) - -### The setup uses an external even generator trigger which is -### defined in the following file and it is retrieved and configured -### according to the specified function call - -[TriggerExternal] -fileName = ${O2DPG_ROOT}/MC/config/PWGDQ/trigger/selectDaughterFromHFwithinAcc.C -funcName = selectDaughterFromHFwithinAcc(443,kTRUE,-4.3,-2.3) diff --git a/MC/config/PWGDQ/ini/GeneratorHF_bbbar_midy.ini b/MC/config/PWGDQ/ini/GeneratorHF_bbbar_midy.ini deleted file mode 100644 index 8de765f45..000000000 --- a/MC/config/PWGDQ/ini/GeneratorHF_bbbar_midy.ini +++ /dev/null @@ -1,28 +0,0 @@ -### The setup uses an external event generator -### This part sets the path of the file and the function call to retrieve it - -[GeneratorExternal] -fileName = ${O2DPG_ROOT}/MC/config/PWGDQ/external/generator/GeneratorBeautyToJpsi_EvtGen.C -funcName = GeneratorBeautyToJpsi_EvtGenMidY() - -### The external generator derives from GeneratorPythia8. -### This part configures the bits of the interface: configuration and user hooks - -[GeneratorPythia8] -config = ${O2DPG_ROOT}/MC/config/common/pythia8/generator/pythia8_hf.cfg -hooksFileName = ${O2DPG_ROOT}/MC/config/PWGHF/pythia8/hooks/pythia8_userhooks_qqbar.C -hooksFuncName = pythia8_userhooks_bbbar(-1.5,1.5) - -### The setup uses an external even generator trigger which is -### defined in the following file and it is retrieved and configured -### according to the specified function call - -[TriggerExternal] -fileName = ${O2DPG_ROOT}/MC/config/PWGDQ/trigger/selectDaughterFromHFwithinAcc.C -funcName = selectDaughterFromHFwithinAcc(443,kTRUE,-1.5,1.5) - -### The setup inhibits transport of primary particles which are produce at forward rapidity. -### The settings below only transports particles in the barrel, which is currently defined as |eta| < 2 - -[Stack] -transportPrimary = barrel diff --git a/MC/config/PWGDQ/ini/GeneratorHF_ccbarToMuonsSemileptonic_fwdy.ini b/MC/config/PWGDQ/ini/GeneratorHF_ccbarToMuonsSemileptonic_fwdy.ini deleted file mode 100644 index dbf914826..000000000 --- a/MC/config/PWGDQ/ini/GeneratorHF_ccbarToMuonsSemileptonic_fwdy.ini +++ /dev/null @@ -1,22 +0,0 @@ -### The setup uses an external event generator -### This part sets the path of the file and the function call to retrieve it - -[GeneratorExternal] -fileName = ${O2DPG_ROOT}/MC/config/PWGDQ/external/generator/GeneratorCharmToMu_EvtGen.C -funcName = GeneratorCharmToMu_EvtGenFwdY() - -### The external generator derives from GeneratorPythia8. -### This part configures the bits of the interface: configuration and user hooks - -[GeneratorPythia8] -config = ${O2DPG_ROOT}/MC/config/common/pythia8/generator/pythia8_hf.cfg -hooksFileName = ${O2DPG_ROOT}/MC/config/PWGHF/pythia8/hooks/pythia8_userhooks_qqbar.C -hooksFuncName = pythia8_userhooks_ccbar(-4.3,-2.3) - -### The setup uses an external even generator trigger which is -### defined in the following file and it is retrieved and configured -### according to the specified function call - -[TriggerExternal] -fileName = ${O2DPG_ROOT}/MC/config/PWGDQ/trigger/selectDaughterFromHFwithinAcc.C -funcName = selectDaughterFromHFwithinAcc(13,kTRUE,-4.3,-2.3) diff --git a/MC/config/PWGDQ/ini/GeneratorHF_ccbarToMuonsSemileptonic_fwdy_inel_TriggerGap.ini b/MC/config/PWGDQ/ini/GeneratorHF_ccbarToMuonsSemileptonic_fwdy_inel_TriggerGap.ini new file mode 100644 index 000000000..f09f8fe4f --- /dev/null +++ b/MC/config/PWGDQ/ini/GeneratorHF_ccbarToMuonsSemileptonic_fwdy_inel_TriggerGap.ini @@ -0,0 +1,14 @@ +### The setup uses an external event generator +### This part sets the path of the file and the function call to retrieve it + +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/external/generator/GeneratorHFToMu_EvtGenFwdY_gaptriggered_dq.C +funcName = GeneratorHFToMu_EvtGenFwdY_gaptriggered_dq(-4.3, -2.3, false, false, true) + +### The external generator derives from GeneratorPythia8. +### This part configures the bits of the interface: configuration and user hooks + +[GeneratorPythia8] +config = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/pythia8/generator/pythia8_inel_cr2.cfg +hooksFileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/hooks/pythia8_userhooks_qqbar.C +hooksFuncName = pythia8_userhooks_ccbar(-4.3, -2.3) diff --git a/MC/config/PWGDQ/ini/GeneratorHF_ccbarToMuonsSemileptonic_fwdy_inel_TriggerGap_natural.ini b/MC/config/PWGDQ/ini/GeneratorHF_ccbarToMuonsSemileptonic_fwdy_inel_TriggerGap_natural.ini new file mode 100644 index 000000000..253842c87 --- /dev/null +++ b/MC/config/PWGDQ/ini/GeneratorHF_ccbarToMuonsSemileptonic_fwdy_inel_TriggerGap_natural.ini @@ -0,0 +1,14 @@ +### The setup uses an external event generator +### This part sets the path of the file and the function call to retrieve it + +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/external/generator/GeneratorHFToMu_EvtGenFwdY_gaptriggered_dq.C +funcName = GeneratorHFToMu_EvtGenFwdY_gaptriggered_dq(-4.3, -2.3, false, false, false) + +### The external generator derives from GeneratorPythia8. +### This part configures the bits of the interface: configuration and user hooks + +[GeneratorPythia8] +config = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/pythia8/generator/pythia8_inel_cr2.cfg +hooksFileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/hooks/pythia8_userhooks_qqbar.C +hooksFuncName = pythia8_userhooks_ccbar(-4.3, -2.3) diff --git a/MC/config/PWGDQ/ini/GeneratorJPsiHFCorr_ccbar.ini b/MC/config/PWGDQ/ini/GeneratorJPsiHFCorr_ccbar.ini index 6db692633..b2582c406 100755 --- a/MC/config/PWGDQ/ini/GeneratorJPsiHFCorr_ccbar.ini +++ b/MC/config/PWGDQ/ini/GeneratorJPsiHFCorr_ccbar.ini @@ -1,7 +1,7 @@ ### The external generator derives from GeneratorPythia8. [GeneratorExternal] -fileName=${O2DPG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C -funcName=GeneratorPythia8GapTriggeredCharm(3, -6, 6, -4.5, -2., 443) +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C +funcName=GeneratorPythia8GapTriggeredCharm(3, -6, 6, -4.5, -2., {443}) [GeneratorPythia8] -config=${O2DPG_ROOT}/MC/config/PWGDQ/pythia8/decayer/jpsi_hf_corr.cfg +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/pythia8/decayer/jpsi_hf_corr.cfg diff --git a/MC/config/PWGDQ/ini/GeneratorParamSingleMuon_PbPb_5TeV.ini b/MC/config/PWGDQ/ini/GeneratorParamSingleMuon_PbPb_5TeV.ini new file mode 100644 index 000000000..ee8dccc0e --- /dev/null +++ b/MC/config/PWGDQ/ini/GeneratorParamSingleMuon_PbPb_5TeV.ini @@ -0,0 +1,4 @@ +### The external generator derives from /MC/config/PWGDQ/external/generator/GeneratorParamSingleMuon_PbPb_5TeV.C +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/external/generator/GeneratorParamSingleMuon_PbPb_5TeV.C +funcName=paramMuGen(797.446,0.830278,0.632177,10.2202,-0.000614809,-1.70993,1.87732,0.00658212,-0.0988071,-0.000452746,0.00269782,2,13,-4.,-2.3,0.0,999.,1) diff --git a/MC/config/PWGDQ/ini/Generator_InjectedBottomoniaFwdY_TriggerGap_PbPb_536TeV.ini b/MC/config/PWGDQ/ini/Generator_InjectedBottomoniaFwdY_TriggerGap_PbPb_536TeV.ini new file mode 100644 index 000000000..ec91423b7 --- /dev/null +++ b/MC/config/PWGDQ/ini/Generator_InjectedBottomoniaFwdY_TriggerGap_PbPb_536TeV.ini @@ -0,0 +1,8 @@ +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/external/generator/generator_pythia8_withInjectedBottomoniaSignals_gaptriggered_dq.C + +funcName=GeneratorPythia8InjectedBottomoniaGapTriggered(2,1) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator/pythia8_PbPb_536tev.cfg diff --git a/MC/config/PWGDQ/ini/Generator_InjectedBottomoniaFwdy_TriggerGap.ini b/MC/config/PWGDQ/ini/Generator_InjectedBottomoniaFwdy_TriggerGap.ini new file mode 100755 index 000000000..410ec7738 --- /dev/null +++ b/MC/config/PWGDQ/ini/Generator_InjectedBottomoniaFwdy_TriggerGap.ini @@ -0,0 +1,7 @@ +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/external/generator/generator_pythia8_withInjectedBottomoniaSignals_gaptriggered_dq.C +funcName=GeneratorPythia8InjectedBottomoniaGapTriggered(5,0) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/pythia8/generator/pythia8_inel_triggerGap.cfg diff --git a/MC/config/PWGDQ/ini/Generator_InjectedCharmoniaMidy_PbPb5TeV.ini b/MC/config/PWGDQ/ini/Generator_InjectedCharmoniaMidy_PbPb5TeV.ini new file mode 100755 index 000000000..d3e0e5931 --- /dev/null +++ b/MC/config/PWGDQ/ini/Generator_InjectedCharmoniaMidy_PbPb5TeV.ini @@ -0,0 +1,7 @@ +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/external/generator/generator_pythia8_HadronTriggered_PbPb.C +funcName=GeneratorPromptJpsiPsi2S_EvtGenMidY(1,-1,1,false) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator/pythia8_PbPb_536tev.cfg diff --git a/MC/config/PWGDQ/ini/Generator_InjectedChiCToElectronMidy_TriggerGap.ini b/MC/config/PWGDQ/ini/Generator_InjectedChiCToElectronMidy_TriggerGap.ini new file mode 100755 index 000000000..6e115c8fd --- /dev/null +++ b/MC/config/PWGDQ/ini/Generator_InjectedChiCToElectronMidy_TriggerGap.ini @@ -0,0 +1,7 @@ +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/external/generator/generator_pythia8_withInjectedPromptSignals_gaptriggered_dq.C +funcName=GeneratorPythia8InjectedPromptCharmoniaGapTriggered(5,6) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/pythia8/generator/pythia8_inel_triggerGap.cfg diff --git a/MC/config/PWGDQ/ini/Generator_InjectedCoherentCharmoniaFwdy_PbPb5TeV.ini b/MC/config/PWGDQ/ini/Generator_InjectedCoherentCharmoniaFwdy_PbPb5TeV.ini new file mode 100644 index 000000000..0a6fbdf09 --- /dev/null +++ b/MC/config/PWGDQ/ini/Generator_InjectedCoherentCharmoniaFwdy_PbPb5TeV.ini @@ -0,0 +1,5 @@ +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/external/generator/generator_pythia8_StarlightTriggered_withGap.C +funcName = GeneratorPhotoproductionFwd(2, 3, -4.5, -2.0) +[GeneratorPythia8] +config = ${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator/pythia8_PbPb_536tev.cfg diff --git a/MC/config/PWGDQ/ini/Generator_InjectedCoherentCharmoniaMidy_PbPb5TeV.ini b/MC/config/PWGDQ/ini/Generator_InjectedCoherentCharmoniaMidy_PbPb5TeV.ini new file mode 100644 index 000000000..6393747f4 --- /dev/null +++ b/MC/config/PWGDQ/ini/Generator_InjectedCoherentCharmoniaMidy_PbPb5TeV.ini @@ -0,0 +1,5 @@ +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/external/generator/generator_pythia8_StarlightTriggered_withGap.C +funcName = GeneratorPhotoproduction(2, 1, -1.5, 1.5) +[GeneratorPythia8] +config = ${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator/pythia8_PbPb_536tev.cfg diff --git a/MC/config/PWGDQ/ini/Generator_InjectedInclusiveJpsiMidy_Pythia8_TriggerGap.ini b/MC/config/PWGDQ/ini/Generator_InjectedInclusiveJpsiMidy_Pythia8_TriggerGap.ini new file mode 100755 index 000000000..95c9de638 --- /dev/null +++ b/MC/config/PWGDQ/ini/Generator_InjectedInclusiveJpsiMidy_Pythia8_TriggerGap.ini @@ -0,0 +1,7 @@ +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/external/generator/generator_pythia8_HadronTriggered_withGap.C +funcName=GeneratorInclusiveJpsi_EvtGenMidY(5,-1.5,1.5) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/pythia8/generator/pythia8_inel_triggerGap.cfg \ No newline at end of file diff --git a/MC/config/PWGDQ/ini/Generator_InjectedInclusiveJpsiPsi2SMidy_2026_Pythia8_TriggerGap.ini b/MC/config/PWGDQ/ini/Generator_InjectedInclusiveJpsiPsi2SMidy_2026_Pythia8_TriggerGap.ini new file mode 100644 index 000000000..7843272c2 --- /dev/null +++ b/MC/config/PWGDQ/ini/Generator_InjectedInclusiveJpsiPsi2SMidy_2026_Pythia8_TriggerGap.ini @@ -0,0 +1,7 @@ +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/external/generator/generator_pythia8_HadronTriggered_withGap.C +funcName=GeneratorInclusiveJpsiPsi2SChiC_EvtGenMidY(5,-1.5,1.5) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/pythia8/generator/pythia8_inel_triggerGap.cfg diff --git a/MC/config/PWGDQ/ini/Generator_InjectedInclusiveJpsiPsi2SMidy_Pythia8_TriggerGap.ini b/MC/config/PWGDQ/ini/Generator_InjectedInclusiveJpsiPsi2SMidy_Pythia8_TriggerGap.ini new file mode 100644 index 000000000..1c6f929ac --- /dev/null +++ b/MC/config/PWGDQ/ini/Generator_InjectedInclusiveJpsiPsi2SMidy_Pythia8_TriggerGap.ini @@ -0,0 +1,7 @@ +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/external/generator/generator_pythia8_HadronTriggered_withGap.C +funcName=GeneratorInclusiveJpsiPsi2S_EvtGenMidY(5,-1.5,1.5) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/pythia8/generator/pythia8_inel_triggerGap.cfg \ No newline at end of file diff --git a/MC/config/PWGDQ/ini/Generator_InjectedInclusiveJpsiPsi2SMidy_Pythia8_TriggerGap_pO96TeV.ini b/MC/config/PWGDQ/ini/Generator_InjectedInclusiveJpsiPsi2SMidy_Pythia8_TriggerGap_pO96TeV.ini new file mode 100644 index 000000000..c660aae24 --- /dev/null +++ b/MC/config/PWGDQ/ini/Generator_InjectedInclusiveJpsiPsi2SMidy_Pythia8_TriggerGap_pO96TeV.ini @@ -0,0 +1,7 @@ +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/external/generator/generator_pythia8_HadronTriggered_withGap.C +funcName=GeneratorInclusiveJpsiPsi2S_EvtGenMidY(5,-1.5,1.5) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/generator/pythia8_pO_961.cfg diff --git a/MC/config/PWGDQ/ini/Generator_InjectedIncoherentCharmoniaFwdy_PbPb5TeV.ini b/MC/config/PWGDQ/ini/Generator_InjectedIncoherentCharmoniaFwdy_PbPb5TeV.ini new file mode 100644 index 000000000..9c1d3aaa0 --- /dev/null +++ b/MC/config/PWGDQ/ini/Generator_InjectedIncoherentCharmoniaFwdy_PbPb5TeV.ini @@ -0,0 +1,5 @@ +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/external/generator/generator_pythia8_StarlightTriggered_withGap.C +funcName = GeneratorPhotoproductionFwd(2, 4, -4.5, -2.0) +[GeneratorPythia8] +config = ${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator/pythia8_PbPb_536tev.cfg diff --git a/MC/config/PWGDQ/ini/Generator_InjectedIncoherentCharmoniaMidy_PbPb5TeV.ini b/MC/config/PWGDQ/ini/Generator_InjectedIncoherentCharmoniaMidy_PbPb5TeV.ini new file mode 100644 index 000000000..cf889af81 --- /dev/null +++ b/MC/config/PWGDQ/ini/Generator_InjectedIncoherentCharmoniaMidy_PbPb5TeV.ini @@ -0,0 +1,5 @@ +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/external/generator/generator_pythia8_StarlightTriggered_withGap.C +funcName = GeneratorPhotoproduction(2, 2, -1.5, 1.5) +[GeneratorPythia8] +config = ${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator/pythia8_PbPb_536tev.cfg diff --git a/MC/config/PWGDQ/ini/Generator_InjectedPhotoproucedCharmoniaMidy_PbPb5TeV.ini b/MC/config/PWGDQ/ini/Generator_InjectedPhotoproucedCharmoniaMidy_PbPb5TeV.ini new file mode 100644 index 000000000..f49c6a994 --- /dev/null +++ b/MC/config/PWGDQ/ini/Generator_InjectedPhotoproucedCharmoniaMidy_PbPb5TeV.ini @@ -0,0 +1,5 @@ +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/external/generator/generator_pythia8_StarlightTriggered_withGap.C +funcName = GeneratorPhotoproduction(2, 0, -1.5, 1.5) +[GeneratorPythia8] +config = ${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator/pythia8_PbPb_536tev.cfg diff --git a/MC/config/PWGDQ/ini/Generator_InjectedPromptCharmoniaFwdy_TriggerGap.ini b/MC/config/PWGDQ/ini/Generator_InjectedPromptCharmoniaFwdy_TriggerGap.ini index 927428214..3fbcab660 100755 --- a/MC/config/PWGDQ/ini/Generator_InjectedPromptCharmoniaFwdy_TriggerGap.ini +++ b/MC/config/PWGDQ/ini/Generator_InjectedPromptCharmoniaFwdy_TriggerGap.ini @@ -1,7 +1,7 @@ ### The external generator derives from GeneratorPythia8. [GeneratorExternal] -fileName=${O2DPG_ROOT}/MC/config/PWGDQ/external/generator/generator_pythia8_withInjectedPromptSignals_gaptriggered_dq.C +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/external/generator/generator_pythia8_withInjectedPromptSignals_gaptriggered_dq.C funcName=GeneratorPythia8InjectedPromptCharmoniaGapTriggered(5,3) [GeneratorPythia8] -config=${O2DPG_ROOT}/MC/config/PWGDQ/pythia8/generator/pythia8_inel_triggerGap.cfg +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/pythia8/generator/pythia8_inel_triggerGap.cfg diff --git a/MC/config/PWGDQ/ini/Generator_InjectedPromptCharmoniaFwdy_TriggerGap_NeNe5TeV.ini b/MC/config/PWGDQ/ini/Generator_InjectedPromptCharmoniaFwdy_TriggerGap_NeNe5TeV.ini new file mode 100644 index 000000000..3668ce8df --- /dev/null +++ b/MC/config/PWGDQ/ini/Generator_InjectedPromptCharmoniaFwdy_TriggerGap_NeNe5TeV.ini @@ -0,0 +1,7 @@ +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/external/generator/generator_pythia8_withInjectedPromptSignals_gaptriggered_dq.C +funcName=GeneratorPythia8InjectedPromptCharmoniaGapTriggered(2,11) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/generator/pythia8_NeNe_536.cfg diff --git a/MC/config/PWGDQ/ini/Generator_InjectedPromptCharmoniaFwdy_TriggerGap_OO5TeV.ini b/MC/config/PWGDQ/ini/Generator_InjectedPromptCharmoniaFwdy_TriggerGap_OO5TeV.ini new file mode 100755 index 000000000..6f9e8fce4 --- /dev/null +++ b/MC/config/PWGDQ/ini/Generator_InjectedPromptCharmoniaFwdy_TriggerGap_OO5TeV.ini @@ -0,0 +1,7 @@ +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/external/generator/generator_pythia8_withInjectedPromptSignals_gaptriggered_dq.C +funcName=GeneratorPythia8InjectedPromptCharmoniaGapTriggered(5,15) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/generator/pythia8_OO_536.cfg diff --git a/MC/config/PWGDQ/ini/Generator_InjectedPromptCharmoniaFwdy_TriggerGap_PbPb5TeV.ini b/MC/config/PWGDQ/ini/Generator_InjectedPromptCharmoniaFwdy_TriggerGap_PbPb5TeV.ini new file mode 100644 index 000000000..1a5877afe --- /dev/null +++ b/MC/config/PWGDQ/ini/Generator_InjectedPromptCharmoniaFwdy_TriggerGap_PbPb5TeV.ini @@ -0,0 +1,9 @@ +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/external/generator/generator_pythia8_withInjectedPromptSignals_gaptriggered_dq.C +funcName=GeneratorPythia8InjectedPromptCharmoniaGapTriggered(5,7) + +[GeneratorPythia8] +### config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/pythia8/generator/pythia8_inel_triggerGap_5TeV.cfg +### config=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/generator/pythia8_hi.cfg +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator/pythia8_PbPb_536tev.cfg diff --git a/MC/config/PWGDQ/ini/Generator_InjectedPromptCharmoniaFwdy_TriggerGap_PbPb5TeV_TriggerRatio2.ini b/MC/config/PWGDQ/ini/Generator_InjectedPromptCharmoniaFwdy_TriggerGap_PbPb5TeV_TriggerRatio2.ini new file mode 100644 index 000000000..d3611a518 --- /dev/null +++ b/MC/config/PWGDQ/ini/Generator_InjectedPromptCharmoniaFwdy_TriggerGap_PbPb5TeV_TriggerRatio2.ini @@ -0,0 +1,9 @@ +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/external/generator/generator_pythia8_withInjectedPromptSignals_gaptriggered_dq.C +funcName=GeneratorPythia8InjectedPromptCharmoniaGapTriggered(2,7) + +[GeneratorPythia8] +### config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/pythia8/generator/pythia8_inel_triggerGap_5TeV.cfg +### config=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/generator/pythia8_hi.cfg +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator/pythia8_PbPb_536tev.cfg diff --git a/MC/config/PWGDQ/ini/Generator_InjectedPromptCharmoniaFwdy_TriggerGap_pO96TeV.ini b/MC/config/PWGDQ/ini/Generator_InjectedPromptCharmoniaFwdy_TriggerGap_pO96TeV.ini new file mode 100755 index 000000000..b4822ef37 --- /dev/null +++ b/MC/config/PWGDQ/ini/Generator_InjectedPromptCharmoniaFwdy_TriggerGap_pO96TeV.ini @@ -0,0 +1,7 @@ +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/external/generator/generator_pythia8_withInjectedPromptSignals_gaptriggered_dq.C +funcName=GeneratorPythia8InjectedPromptCharmoniaGapTriggered(5,13) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/generator/pythia8_pO_961.cfg diff --git a/MC/config/PWGDQ/ini/Generator_InjectedPromptCharmoniaFwdy_TriggerGap_pp5TeV.ini b/MC/config/PWGDQ/ini/Generator_InjectedPromptCharmoniaFwdy_TriggerGap_pp5TeV.ini new file mode 100755 index 000000000..50de50adb --- /dev/null +++ b/MC/config/PWGDQ/ini/Generator_InjectedPromptCharmoniaFwdy_TriggerGap_pp5TeV.ini @@ -0,0 +1,7 @@ +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/external/generator/generator_pythia8_withInjectedPromptSignals_gaptriggered_dq.C +funcName=GeneratorPythia8InjectedPromptCharmoniaGapTriggered(5,11) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/pythia8/generator/pythia8_inel_triggerGap_5TeV.cfg diff --git a/MC/config/PWGDQ/ini/Generator_InjectedPromptCharmoniaMidy_TriggerGap.ini b/MC/config/PWGDQ/ini/Generator_InjectedPromptCharmoniaMidy_TriggerGap.ini index f2ed68c40..61b86eb04 100755 --- a/MC/config/PWGDQ/ini/Generator_InjectedPromptCharmoniaMidy_TriggerGap.ini +++ b/MC/config/PWGDQ/ini/Generator_InjectedPromptCharmoniaMidy_TriggerGap.ini @@ -1,7 +1,7 @@ ### The external generator derives from GeneratorPythia8. [GeneratorExternal] -fileName=${O2DPG_ROOT}/MC/config/PWGDQ/external/generator/generator_pythia8_withInjectedPromptSignals_gaptriggered_dq.C +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/external/generator/generator_pythia8_withInjectedPromptSignals_gaptriggered_dq.C funcName=GeneratorPythia8InjectedPromptCharmoniaGapTriggered(5,0) [GeneratorPythia8] -config=${O2DPG_ROOT}/MC/config/PWGDQ/pythia8/generator/pythia8_inel_triggerGap.cfg +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/pythia8/generator/pythia8_inel_triggerGap.cfg diff --git a/MC/config/PWGDQ/ini/Generator_InjectedPromptCharmoniaMidy_TriggerGap_2_OO5TeV.ini b/MC/config/PWGDQ/ini/Generator_InjectedPromptCharmoniaMidy_TriggerGap_2_OO5TeV.ini new file mode 100644 index 000000000..21ba911d3 --- /dev/null +++ b/MC/config/PWGDQ/ini/Generator_InjectedPromptCharmoniaMidy_TriggerGap_2_OO5TeV.ini @@ -0,0 +1,7 @@ +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/external/generator/generator_pythia8_withInjectedPromptSignals_gaptriggered_dq.C +funcName=GeneratorPythia8InjectedPromptCharmoniaGapTriggered(2,12) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/generator/pythia8_OO_536.cfg diff --git a/MC/config/PWGDQ/ini/Generator_InjectedPromptCharmoniaMidy_TriggerGap_OO5TeV.ini b/MC/config/PWGDQ/ini/Generator_InjectedPromptCharmoniaMidy_TriggerGap_OO5TeV.ini new file mode 100644 index 000000000..c8c6d2ee4 --- /dev/null +++ b/MC/config/PWGDQ/ini/Generator_InjectedPromptCharmoniaMidy_TriggerGap_OO5TeV.ini @@ -0,0 +1,7 @@ +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/external/generator/generator_pythia8_withInjectedPromptSignals_gaptriggered_dq.C +funcName=GeneratorPythia8InjectedPromptCharmoniaGapTriggered(5,12) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/generator/pythia8_OO_536.cfg diff --git a/MC/config/PWGDQ/ini/Generator_InjectedPromptCharmoniaMidy_TriggerGap_pO96TeV.ini b/MC/config/PWGDQ/ini/Generator_InjectedPromptCharmoniaMidy_TriggerGap_pO96TeV.ini new file mode 100644 index 000000000..0f184d019 --- /dev/null +++ b/MC/config/PWGDQ/ini/Generator_InjectedPromptCharmoniaMidy_TriggerGap_pO96TeV.ini @@ -0,0 +1,7 @@ +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/external/generator/generator_pythia8_withInjectedPromptSignals_gaptriggered_dq.C +funcName=GeneratorPythia8InjectedPromptCharmoniaGapTriggered(5,14) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/generator/pythia8_pO_961.cfg diff --git a/MC/config/PWGDQ/ini/Generator_InjectedPromptJpsiFwdy_PythiaOnia_TriggerGap.ini b/MC/config/PWGDQ/ini/Generator_InjectedPromptJpsiFwdy_PythiaOnia_TriggerGap.ini new file mode 100644 index 000000000..e6f0d7316 --- /dev/null +++ b/MC/config/PWGDQ/ini/Generator_InjectedPromptJpsiFwdy_PythiaOnia_TriggerGap.ini @@ -0,0 +1,7 @@ +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/external/generator/generator_pythia8Onia_PromptSignals_gaptriggered.C +funcName=GeneratorPromptJpsi_EvtGenFwdy(5,-4.3,-2.3) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/pythia8/generator/pythia8_onia_triggerGap.cfg diff --git a/MC/config/PWGDQ/ini/Generator_InjectedPromptJpsiFwdy_PythiaOnia_TriggerGap_pp5TeV.ini b/MC/config/PWGDQ/ini/Generator_InjectedPromptJpsiFwdy_PythiaOnia_TriggerGap_pp5TeV.ini new file mode 100644 index 000000000..751e1cf68 --- /dev/null +++ b/MC/config/PWGDQ/ini/Generator_InjectedPromptJpsiFwdy_PythiaOnia_TriggerGap_pp5TeV.ini @@ -0,0 +1,7 @@ +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/external/generator/generator_pythia8Onia_PromptSignals_gaptriggered.C +funcName=GeneratorPromptJpsi_EvtGenFwdy(5,-4.3,-2.3) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/pythia8/generator/pythia8_onia_triggerGap_pp5TeV.cfg diff --git a/MC/config/PWGDQ/ini/Generator_InjectedPromptJpsiFwdy_TriggerGap.ini b/MC/config/PWGDQ/ini/Generator_InjectedPromptJpsiFwdy_TriggerGap.ini index 79d17674c..00d003de5 100755 --- a/MC/config/PWGDQ/ini/Generator_InjectedPromptJpsiFwdy_TriggerGap.ini +++ b/MC/config/PWGDQ/ini/Generator_InjectedPromptJpsiFwdy_TriggerGap.ini @@ -1,7 +1,7 @@ ### The external generator derives from GeneratorPythia8. [GeneratorExternal] -fileName=${O2DPG_ROOT}/MC/config/PWGDQ/external/generator/generator_pythia8_withInjectedPromptSignals_gaptriggered_dq.C +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/external/generator/generator_pythia8_withInjectedPromptSignals_gaptriggered_dq.C funcName=GeneratorPythia8InjectedPromptCharmoniaGapTriggered(5,4) [GeneratorPythia8] -config=${O2DPG_ROOT}/MC/config/PWGDQ/pythia8/generator/pythia8_inel_triggerGap.cfg +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/pythia8/generator/pythia8_inel_triggerGap.cfg diff --git a/MC/config/PWGDQ/ini/Generator_InjectedPromptJpsiMidy_PythiaOnia_TriggerGap.ini b/MC/config/PWGDQ/ini/Generator_InjectedPromptJpsiMidy_PythiaOnia_TriggerGap.ini new file mode 100755 index 000000000..139503480 --- /dev/null +++ b/MC/config/PWGDQ/ini/Generator_InjectedPromptJpsiMidy_PythiaOnia_TriggerGap.ini @@ -0,0 +1,7 @@ +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/external/generator/generator_pythia8Onia_PromptSignals_gaptriggered.C +funcName=GeneratorPromptJpsi_EvtGenMidY(5,-1.5,1.5) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/pythia8/generator/pythia8_onia_triggerGap.cfg diff --git a/MC/config/PWGDQ/ini/Generator_InjectedPromptJpsiPsi2SFwdy_Pythia8_TriggerGap.ini b/MC/config/PWGDQ/ini/Generator_InjectedPromptJpsiPsi2SFwdy_Pythia8_TriggerGap.ini new file mode 100644 index 000000000..17e7af031 --- /dev/null +++ b/MC/config/PWGDQ/ini/Generator_InjectedPromptJpsiPsi2SFwdy_Pythia8_TriggerGap.ini @@ -0,0 +1,7 @@ +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/external/generator/generator_pythia8Onia_PromptSignals_gaptriggered.C +funcName=GeneratorPromptJpsiPsi2S_EvtGenFwdY(5,-4.3,-2.3) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/pythia8/generator/pythia8_onia_triggerGap.cfg diff --git a/MC/config/PWGDQ/ini/Generator_InjectedPromptJpsiPsi2SFwdy_Pythia8_TriggerGap_pp5TeV.ini b/MC/config/PWGDQ/ini/Generator_InjectedPromptJpsiPsi2SFwdy_Pythia8_TriggerGap_pp5TeV.ini new file mode 100644 index 000000000..a653d7ba1 --- /dev/null +++ b/MC/config/PWGDQ/ini/Generator_InjectedPromptJpsiPsi2SFwdy_Pythia8_TriggerGap_pp5TeV.ini @@ -0,0 +1,7 @@ +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/external/generator/generator_pythia8Onia_PromptSignals_gaptriggered.C +funcName=GeneratorPromptJpsiPsi2S_EvtGenFwdY(5,-4.3,-2.3) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/pythia8/generator/pythia8_onia_triggerGap_pp5TeV.cfg diff --git a/MC/config/PWGDQ/ini/Generator_InjectedPromptPsi2SFwdy_TriggerGap.ini b/MC/config/PWGDQ/ini/Generator_InjectedPromptPsi2SFwdy_TriggerGap.ini index 2d62ec371..c2fbdf83b 100755 --- a/MC/config/PWGDQ/ini/Generator_InjectedPromptPsi2SFwdy_TriggerGap.ini +++ b/MC/config/PWGDQ/ini/Generator_InjectedPromptPsi2SFwdy_TriggerGap.ini @@ -1,7 +1,7 @@ ### The external generator derives from GeneratorPythia8. [GeneratorExternal] -fileName=${O2DPG_ROOT}/MC/config/PWGDQ/external/generator/generator_pythia8_withInjectedPromptSignals_gaptriggered_dq.C +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/external/generator/generator_pythia8_withInjectedPromptSignals_gaptriggered_dq.C funcName=GeneratorPythia8InjectedPromptCharmoniaGapTriggered(5,5) [GeneratorPythia8] -config=${O2DPG_ROOT}/MC/config/PWGDQ/pythia8/generator/pythia8_inel_triggerGap.cfg +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/pythia8/generator/pythia8_inel_triggerGap.cfg diff --git a/MC/config/PWGDQ/ini/Generator_InjectedPromptPsi2SToJpsiPiPiMidy_TriggerGap.ini b/MC/config/PWGDQ/ini/Generator_InjectedPromptPsi2SToJpsiPiPiMidy_TriggerGap.ini new file mode 100644 index 000000000..30082ec0f --- /dev/null +++ b/MC/config/PWGDQ/ini/Generator_InjectedPromptPsi2SToJpsiPiPiMidy_TriggerGap.ini @@ -0,0 +1,6 @@ +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/external/generator/generator_pythia8_withInjectedPromptSignals_gaptriggered_dq.C +funcName=GeneratorPythia8InjectedPromptCharmoniaGapTriggered(5,9) +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/pythia8/generator/pythia8_inel_triggerGap.cfg diff --git a/MC/config/PWGDQ/ini/Generator_InjectedPromptX3782Midy_TriggerGap.ini b/MC/config/PWGDQ/ini/Generator_InjectedPromptX3782Midy_TriggerGap.ini new file mode 100644 index 000000000..097d4377d --- /dev/null +++ b/MC/config/PWGDQ/ini/Generator_InjectedPromptX3782Midy_TriggerGap.ini @@ -0,0 +1,6 @@ +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/external/generator/generator_pythia8_withInjectedPromptSignals_gaptriggered_dq.C +funcName=GeneratorPythia8InjectedPromptCharmoniaGapTriggered(5,8) +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/pythia8/generator/pythia8_inel_triggerGap.cfg diff --git a/MC/config/PWGDQ/ini/Generator_InjectedPromptX3782andPsi2SMidy_TriggerGap.ini b/MC/config/PWGDQ/ini/Generator_InjectedPromptX3782andPsi2SMidy_TriggerGap.ini new file mode 100644 index 000000000..566002ac8 --- /dev/null +++ b/MC/config/PWGDQ/ini/Generator_InjectedPromptX3782andPsi2SMidy_TriggerGap.ini @@ -0,0 +1,6 @@ +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/external/generator/generator_pythia8_withInjectedPromptSignals_gaptriggered_dq.C +funcName=GeneratorPythia8InjectedPromptCharmoniaGapTriggered(5,10) +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/pythia8/generator/pythia8_inel_triggerGap.cfg diff --git a/MC/config/PWGDQ/ini/Generator_TriggeredInclusiveQuarkonia_Pythia8_TriggerGap.ini b/MC/config/PWGDQ/ini/Generator_TriggeredInclusiveQuarkonia_Pythia8_TriggerGap.ini new file mode 100644 index 000000000..db679dbc5 --- /dev/null +++ b/MC/config/PWGDQ/ini/Generator_TriggeredInclusiveQuarkonia_Pythia8_TriggerGap.ini @@ -0,0 +1,10 @@ +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/external/generator/generator_pythia8_HadronTriggered_withGap.C +funcName=GeneratorInclusiveAllQuarkonia_EvtGenMidY(5,-1.0,1.0,"443:0.5,20443:0.21,445:0.095,10441:0.014,9920443:0.2:prompt",1) +# enhances psi(2S) compared to J/psi and chi_c1,2 +# reduces chi_c so that their feeddown to J/psi is the same as if decay to J/psi was not forced +# 20% of chi_c1(1P) are replaced with chi_c1(3872) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/pythia8/generator/pythia8_oniaAll_triggerGap.cfg diff --git a/MC/config/PWGDQ/ini/Generator_TriggeredInclusiveQuarkonia_enhancedB_Pythia8_TriggerGap.ini b/MC/config/PWGDQ/ini/Generator_TriggeredInclusiveQuarkonia_enhancedB_Pythia8_TriggerGap.ini new file mode 100644 index 000000000..39e84939d --- /dev/null +++ b/MC/config/PWGDQ/ini/Generator_TriggeredInclusiveQuarkonia_enhancedB_Pythia8_TriggerGap.ini @@ -0,0 +1,11 @@ +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/external/generator/generator_pythia8_HadronTriggered_withGap.C +funcName=GeneratorInclusiveAllQuarkonia_EvtGenMidY(5,-1.0,1.0,"non-prompt:0.15,443:0.5:prompt,20443:0.21:prompt,445:0.095:prompt,10441:0.014:prompt,9920443:0.2:prompt",0) +# non-prompt J/psi is enhanced through branching ratio, we decrease a bit the amount of enhancement relative to prompt J/psi +# enhances prompt psi(2S) and Upsilon(1S) compared to J/psi and chi_c1,2 +# reduces prompt chi_c so that their feeddown to J/psi is the same as if decay chi_c to J/psi was not forced +# 20% of chi_c1(1P) are replaced with chi_c1(3872) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/pythia8/generator/pythia8_oniaAll_triggerGap.cfg diff --git a/MC/config/PWGDQ/ini/tests/GeneratorHF_bbbarToDDbarToMuons_fwdy_TriggerGap.C b/MC/config/PWGDQ/ini/tests/GeneratorHF_bbbarToDDbarToMuons_fwdy_TriggerGap.C new file mode 100644 index 000000000..cf8cba658 --- /dev/null +++ b/MC/config/PWGDQ/ini/tests/GeneratorHF_bbbarToDDbarToMuons_fwdy_TriggerGap.C @@ -0,0 +1,77 @@ +int External() +{ + int checkPdgDecay = 13; + std::string path{"o2sim_Kine.root"}; + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + auto tree = (TTree*)file.Get("o2sim"); + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int nLeptons{}; + int nLeptonsInAcceptance{}; + int nLeptonsToBeDone{}; + int nSignalPairs{}; + int nLeptonPairs{}; + int nLeptonPairsInAcceptance{}; + int nLeptonPairsToBeDone{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + int nleptonseinacc = 0; + int nleptonse = 0; + int nleptonseToBeDone = 0; + int nopenHeavy = 0; + for (auto& track : *tracks) { + auto pdg = track.GetPdgCode(); + auto y = track.GetRapidity(); + if (std::abs(pdg) == checkPdgDecay) { + int igmother = track.getMotherTrackId(); + if (igmother > 0) { + auto gmTrack = (*tracks)[igmother]; + int gmpdg = gmTrack.GetPdgCode(); + if ( int(std::abs(gmpdg)/100.) == 4 || int(std::abs(gmpdg)/1000.) == 4 || int(std::abs(gmpdg)/100.) == 5 || int(std::abs(gmpdg)/1000.) == 5 ) { + nLeptons++; + nleptonse++; + if (-4.3 < y && y < -2.2) { + nleptonseinacc++; + nLeptonsInAcceptance++; + } + if (track.getToBeDone()) { + nLeptonsToBeDone++; + nleptonseToBeDone++; + } + } + } + } else if (std::abs(pdg) == 411 || std::abs(pdg) == 421 || std::abs(pdg) == 431 || std::abs(pdg) == 4122 || std::abs(pdg) == 4132 || std::abs(pdg) == 4232 || std::abs(pdg) == 4332 || std::abs(pdg) == 511 || std::abs(pdg) == 521 || std::abs(pdg) == 531 || std::abs(pdg) == 541 || std::abs(pdg) == 5112 || std::abs(pdg) == 5122 || std::abs(pdg) == 5232 || std::abs(pdg) == 5132 || std::abs(pdg) == 5332) { + nopenHeavy++; + } + } + if (nopenHeavy > 1) nSignalPairs++; + if (nleptonse > 1) nLeptonPairs++; + if (nleptonseToBeDone > 1) nLeptonPairsToBeDone++; + if (nleptonseinacc > 1) nLeptonPairsInAcceptance++; + } + std::cout << "#events: " << nEvents << "\n" + << "#muons in acceptance: " << nLeptonsInAcceptance << "\n" + << "#muon pairs in acceptance: " << nLeptonPairsInAcceptance << "\n" + << "#muons: " << nLeptons << "\n" + << "#muons to be done: " << nLeptonsToBeDone << "\n" + << "#signal pairs: " << nSignalPairs << "\n" + << "#muon pairs: " << nLeptonPairs << "\n" + << "#muon pairs to be done: " << nLeptonPairsToBeDone << "\n"; + if (nLeptonPairs != nLeptonPairsToBeDone) { + std::cerr << "The number of muon pairs should be the same as the number of muon pairs which should be transported.\n"; + return 1; + } + if (nLeptons != nLeptonsToBeDone) { + std::cerr << "The number of muons should be the same as the number of muons which should be transported.\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGDQ/ini/tests/GeneratorHF_bbbarToDDbarToMuons_fwdy_TriggerGap_inel.C b/MC/config/PWGDQ/ini/tests/GeneratorHF_bbbarToDDbarToMuons_fwdy_TriggerGap_inel.C new file mode 100644 index 000000000..cf8cba658 --- /dev/null +++ b/MC/config/PWGDQ/ini/tests/GeneratorHF_bbbarToDDbarToMuons_fwdy_TriggerGap_inel.C @@ -0,0 +1,77 @@ +int External() +{ + int checkPdgDecay = 13; + std::string path{"o2sim_Kine.root"}; + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + auto tree = (TTree*)file.Get("o2sim"); + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int nLeptons{}; + int nLeptonsInAcceptance{}; + int nLeptonsToBeDone{}; + int nSignalPairs{}; + int nLeptonPairs{}; + int nLeptonPairsInAcceptance{}; + int nLeptonPairsToBeDone{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + int nleptonseinacc = 0; + int nleptonse = 0; + int nleptonseToBeDone = 0; + int nopenHeavy = 0; + for (auto& track : *tracks) { + auto pdg = track.GetPdgCode(); + auto y = track.GetRapidity(); + if (std::abs(pdg) == checkPdgDecay) { + int igmother = track.getMotherTrackId(); + if (igmother > 0) { + auto gmTrack = (*tracks)[igmother]; + int gmpdg = gmTrack.GetPdgCode(); + if ( int(std::abs(gmpdg)/100.) == 4 || int(std::abs(gmpdg)/1000.) == 4 || int(std::abs(gmpdg)/100.) == 5 || int(std::abs(gmpdg)/1000.) == 5 ) { + nLeptons++; + nleptonse++; + if (-4.3 < y && y < -2.2) { + nleptonseinacc++; + nLeptonsInAcceptance++; + } + if (track.getToBeDone()) { + nLeptonsToBeDone++; + nleptonseToBeDone++; + } + } + } + } else if (std::abs(pdg) == 411 || std::abs(pdg) == 421 || std::abs(pdg) == 431 || std::abs(pdg) == 4122 || std::abs(pdg) == 4132 || std::abs(pdg) == 4232 || std::abs(pdg) == 4332 || std::abs(pdg) == 511 || std::abs(pdg) == 521 || std::abs(pdg) == 531 || std::abs(pdg) == 541 || std::abs(pdg) == 5112 || std::abs(pdg) == 5122 || std::abs(pdg) == 5232 || std::abs(pdg) == 5132 || std::abs(pdg) == 5332) { + nopenHeavy++; + } + } + if (nopenHeavy > 1) nSignalPairs++; + if (nleptonse > 1) nLeptonPairs++; + if (nleptonseToBeDone > 1) nLeptonPairsToBeDone++; + if (nleptonseinacc > 1) nLeptonPairsInAcceptance++; + } + std::cout << "#events: " << nEvents << "\n" + << "#muons in acceptance: " << nLeptonsInAcceptance << "\n" + << "#muon pairs in acceptance: " << nLeptonPairsInAcceptance << "\n" + << "#muons: " << nLeptons << "\n" + << "#muons to be done: " << nLeptonsToBeDone << "\n" + << "#signal pairs: " << nSignalPairs << "\n" + << "#muon pairs: " << nLeptonPairs << "\n" + << "#muon pairs to be done: " << nLeptonPairsToBeDone << "\n"; + if (nLeptonPairs != nLeptonPairsToBeDone) { + std::cerr << "The number of muon pairs should be the same as the number of muon pairs which should be transported.\n"; + return 1; + } + if (nLeptons != nLeptonsToBeDone) { + std::cerr << "The number of muons should be the same as the number of muons which should be transported.\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGDQ/ini/tests/GeneratorHF_bbbarToDDbarToMuons_fwdy_TriggerGap_natural.C b/MC/config/PWGDQ/ini/tests/GeneratorHF_bbbarToDDbarToMuons_fwdy_TriggerGap_natural.C new file mode 100644 index 000000000..cf8cba658 --- /dev/null +++ b/MC/config/PWGDQ/ini/tests/GeneratorHF_bbbarToDDbarToMuons_fwdy_TriggerGap_natural.C @@ -0,0 +1,77 @@ +int External() +{ + int checkPdgDecay = 13; + std::string path{"o2sim_Kine.root"}; + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + auto tree = (TTree*)file.Get("o2sim"); + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int nLeptons{}; + int nLeptonsInAcceptance{}; + int nLeptonsToBeDone{}; + int nSignalPairs{}; + int nLeptonPairs{}; + int nLeptonPairsInAcceptance{}; + int nLeptonPairsToBeDone{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + int nleptonseinacc = 0; + int nleptonse = 0; + int nleptonseToBeDone = 0; + int nopenHeavy = 0; + for (auto& track : *tracks) { + auto pdg = track.GetPdgCode(); + auto y = track.GetRapidity(); + if (std::abs(pdg) == checkPdgDecay) { + int igmother = track.getMotherTrackId(); + if (igmother > 0) { + auto gmTrack = (*tracks)[igmother]; + int gmpdg = gmTrack.GetPdgCode(); + if ( int(std::abs(gmpdg)/100.) == 4 || int(std::abs(gmpdg)/1000.) == 4 || int(std::abs(gmpdg)/100.) == 5 || int(std::abs(gmpdg)/1000.) == 5 ) { + nLeptons++; + nleptonse++; + if (-4.3 < y && y < -2.2) { + nleptonseinacc++; + nLeptonsInAcceptance++; + } + if (track.getToBeDone()) { + nLeptonsToBeDone++; + nleptonseToBeDone++; + } + } + } + } else if (std::abs(pdg) == 411 || std::abs(pdg) == 421 || std::abs(pdg) == 431 || std::abs(pdg) == 4122 || std::abs(pdg) == 4132 || std::abs(pdg) == 4232 || std::abs(pdg) == 4332 || std::abs(pdg) == 511 || std::abs(pdg) == 521 || std::abs(pdg) == 531 || std::abs(pdg) == 541 || std::abs(pdg) == 5112 || std::abs(pdg) == 5122 || std::abs(pdg) == 5232 || std::abs(pdg) == 5132 || std::abs(pdg) == 5332) { + nopenHeavy++; + } + } + if (nopenHeavy > 1) nSignalPairs++; + if (nleptonse > 1) nLeptonPairs++; + if (nleptonseToBeDone > 1) nLeptonPairsToBeDone++; + if (nleptonseinacc > 1) nLeptonPairsInAcceptance++; + } + std::cout << "#events: " << nEvents << "\n" + << "#muons in acceptance: " << nLeptonsInAcceptance << "\n" + << "#muon pairs in acceptance: " << nLeptonPairsInAcceptance << "\n" + << "#muons: " << nLeptons << "\n" + << "#muons to be done: " << nLeptonsToBeDone << "\n" + << "#signal pairs: " << nSignalPairs << "\n" + << "#muon pairs: " << nLeptonPairs << "\n" + << "#muon pairs to be done: " << nLeptonPairsToBeDone << "\n"; + if (nLeptonPairs != nLeptonPairsToBeDone) { + std::cerr << "The number of muon pairs should be the same as the number of muon pairs which should be transported.\n"; + return 1; + } + if (nLeptons != nLeptonsToBeDone) { + std::cerr << "The number of muons should be the same as the number of muons which should be transported.\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGDQ/ini/tests/GeneratorHF_bbbarToDDbarToMuons_fwdy_TriggerGap_natural_inel.C b/MC/config/PWGDQ/ini/tests/GeneratorHF_bbbarToDDbarToMuons_fwdy_TriggerGap_natural_inel.C new file mode 100644 index 000000000..cf8cba658 --- /dev/null +++ b/MC/config/PWGDQ/ini/tests/GeneratorHF_bbbarToDDbarToMuons_fwdy_TriggerGap_natural_inel.C @@ -0,0 +1,77 @@ +int External() +{ + int checkPdgDecay = 13; + std::string path{"o2sim_Kine.root"}; + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + auto tree = (TTree*)file.Get("o2sim"); + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int nLeptons{}; + int nLeptonsInAcceptance{}; + int nLeptonsToBeDone{}; + int nSignalPairs{}; + int nLeptonPairs{}; + int nLeptonPairsInAcceptance{}; + int nLeptonPairsToBeDone{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + int nleptonseinacc = 0; + int nleptonse = 0; + int nleptonseToBeDone = 0; + int nopenHeavy = 0; + for (auto& track : *tracks) { + auto pdg = track.GetPdgCode(); + auto y = track.GetRapidity(); + if (std::abs(pdg) == checkPdgDecay) { + int igmother = track.getMotherTrackId(); + if (igmother > 0) { + auto gmTrack = (*tracks)[igmother]; + int gmpdg = gmTrack.GetPdgCode(); + if ( int(std::abs(gmpdg)/100.) == 4 || int(std::abs(gmpdg)/1000.) == 4 || int(std::abs(gmpdg)/100.) == 5 || int(std::abs(gmpdg)/1000.) == 5 ) { + nLeptons++; + nleptonse++; + if (-4.3 < y && y < -2.2) { + nleptonseinacc++; + nLeptonsInAcceptance++; + } + if (track.getToBeDone()) { + nLeptonsToBeDone++; + nleptonseToBeDone++; + } + } + } + } else if (std::abs(pdg) == 411 || std::abs(pdg) == 421 || std::abs(pdg) == 431 || std::abs(pdg) == 4122 || std::abs(pdg) == 4132 || std::abs(pdg) == 4232 || std::abs(pdg) == 4332 || std::abs(pdg) == 511 || std::abs(pdg) == 521 || std::abs(pdg) == 531 || std::abs(pdg) == 541 || std::abs(pdg) == 5112 || std::abs(pdg) == 5122 || std::abs(pdg) == 5232 || std::abs(pdg) == 5132 || std::abs(pdg) == 5332) { + nopenHeavy++; + } + } + if (nopenHeavy > 1) nSignalPairs++; + if (nleptonse > 1) nLeptonPairs++; + if (nleptonseToBeDone > 1) nLeptonPairsToBeDone++; + if (nleptonseinacc > 1) nLeptonPairsInAcceptance++; + } + std::cout << "#events: " << nEvents << "\n" + << "#muons in acceptance: " << nLeptonsInAcceptance << "\n" + << "#muon pairs in acceptance: " << nLeptonPairsInAcceptance << "\n" + << "#muons: " << nLeptons << "\n" + << "#muons to be done: " << nLeptonsToBeDone << "\n" + << "#signal pairs: " << nSignalPairs << "\n" + << "#muon pairs: " << nLeptonPairs << "\n" + << "#muon pairs to be done: " << nLeptonPairsToBeDone << "\n"; + if (nLeptonPairs != nLeptonPairsToBeDone) { + std::cerr << "The number of muon pairs should be the same as the number of muon pairs which should be transported.\n"; + return 1; + } + if (nLeptons != nLeptonsToBeDone) { + std::cerr << "The number of muons should be the same as the number of muons which should be transported.\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGDQ/ini/tests/GeneratorHF_bbbar_PsiAndJpsi_fwdy_triggerGap_OO5TeV.C b/MC/config/PWGDQ/ini/tests/GeneratorHF_bbbar_PsiAndJpsi_fwdy_triggerGap_OO5TeV.C new file mode 100644 index 000000000..d68eb1bcf --- /dev/null +++ b/MC/config/PWGDQ/ini/tests/GeneratorHF_bbbar_PsiAndJpsi_fwdy_triggerGap_OO5TeV.C @@ -0,0 +1,93 @@ +int External() +{ + int checkPdgSignal[] = {443,100443}; + int checkPdgDecay = 13; + double rapiditymin = -4.3; double rapiditymax = -2.3; + std::string path{"o2sim_Kine.root"}; + std::cout << "Check for\nsignal PDG " << checkPdgSignal << "\ndecay PDG " << checkPdgDecay << "\n"; + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree*)file.Get("o2sim"); + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int nLeptons{}; + int nAntileptons{}; + int nLeptonPairs{}; + int nLeptonPairsToBeDone{}; + int nSignalJpsi{}; + int nSignalPsi2S{}; + int nSignalJpsiWithinAcc{}; + int nSignalPsi2SWithinAcc{}; + auto nEvents = tree->GetEntries(); + o2::steer::MCKinematicsReader mcreader("o2sim", o2::steer::MCKinematicsReader::Mode::kMCKine); + Int_t bpdgs[] = {511, 521, 531, 5112, 5122, 5232, 5132}; + Int_t sizePdg = sizeof(bpdgs)/sizeof(Int_t); + Bool_t hasBeautyMoth = kFALSE; + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + for (auto& track : *tracks) { + auto pdg = track.GetPdgCode(); + auto rapidity = track.GetRapidity(); + auto idMoth = track.getMotherTrackId(); + if (pdg == checkPdgDecay) { + // count leptons + nLeptons++; + } else if(pdg == -checkPdgDecay) { + // count anti-leptons + nAntileptons++; + } else if (pdg == checkPdgSignal[0] || pdg == checkPdgSignal[1]) { + // check if mothers are beauty hadrons + hasBeautyMoth = kFALSE; + if(idMoth){ // check beauty mother + auto tdM = mcreader.getTrack(i, idMoth); + for(int i=0; iGetPdgCode()) == bpdgs[i] ) hasBeautyMoth = kTRUE; } + } + if(hasBeautyMoth){ + // count signal PDG + pdg == checkPdgSignal[0] ? nSignalJpsi++ : nSignalPsi2S++; + // count signal PDG within acceptance + if(rapidity > rapiditymin && rapidity < rapiditymax) { pdg == checkPdgSignal[0] ? nSignalJpsiWithinAcc++ : nSignalPsi2SWithinAcc++;} + } + auto child0 = o2::mcutils::MCTrackNavigator::getDaughter0(track, *tracks); + auto child1 = o2::mcutils::MCTrackNavigator::getDaughter1(track, *tracks); + if (child0 != nullptr && child1 != nullptr) { + // check for parent-child relations + auto pdg0 = child0->GetPdgCode(); + auto pdg1 = child1->GetPdgCode(); + std::cout << "First and last children of parent " << checkPdgSignal << " are PDG0: " << pdg0 << " PDG1: " << pdg1 << "\n"; + if (std::abs(pdg0) == checkPdgDecay && std::abs(pdg1) == checkPdgDecay && pdg0 == -pdg1) { + nLeptonPairs++; + if (child0->getToBeDone() && child1->getToBeDone()) { + nLeptonPairsToBeDone++; + } + } + } + } + } + } + std::cout << "#events: " << nEvents << "\n" + << "#leptons: " << nLeptons << "\n" + << "#antileptons: " << nAntileptons << "\n" + << "#signal (jpsi <- b): " << nSignalJpsi << "; within acceptance " << rapiditymin << " < y < " << rapiditymax << " : " << nSignalJpsiWithinAcc << "\n" + << "#signal (psi2S <- b): " << nSignalPsi2S << "; within acceptance " << rapiditymin << " < y < " << rapiditymax << " : " << nSignalPsi2SWithinAcc << "\n" + << "#lepton pairs: " << nLeptonPairs << "\n" + << "#lepton pairs to be done: " << nLeptonPairs << "\n"; + + + if (nLeptonPairs == 0 || nLeptons == 0 || nAntileptons == 0) { + std::cerr << "Number of leptons, number of anti-leptons as well as number of lepton pairs should all be greater than 1.\n"; + return 1; + } + if (nLeptonPairs != nLeptonPairsToBeDone) { + std::cerr << "The number of lepton pairs should be the same as the number of lepton pairs which should be transported.\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGDQ/ini/tests/GeneratorHF_bbbar_PsiAndJpsi_fwdy_triggerGap_pO96TeV.C b/MC/config/PWGDQ/ini/tests/GeneratorHF_bbbar_PsiAndJpsi_fwdy_triggerGap_pO96TeV.C new file mode 100644 index 000000000..d68eb1bcf --- /dev/null +++ b/MC/config/PWGDQ/ini/tests/GeneratorHF_bbbar_PsiAndJpsi_fwdy_triggerGap_pO96TeV.C @@ -0,0 +1,93 @@ +int External() +{ + int checkPdgSignal[] = {443,100443}; + int checkPdgDecay = 13; + double rapiditymin = -4.3; double rapiditymax = -2.3; + std::string path{"o2sim_Kine.root"}; + std::cout << "Check for\nsignal PDG " << checkPdgSignal << "\ndecay PDG " << checkPdgDecay << "\n"; + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree*)file.Get("o2sim"); + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int nLeptons{}; + int nAntileptons{}; + int nLeptonPairs{}; + int nLeptonPairsToBeDone{}; + int nSignalJpsi{}; + int nSignalPsi2S{}; + int nSignalJpsiWithinAcc{}; + int nSignalPsi2SWithinAcc{}; + auto nEvents = tree->GetEntries(); + o2::steer::MCKinematicsReader mcreader("o2sim", o2::steer::MCKinematicsReader::Mode::kMCKine); + Int_t bpdgs[] = {511, 521, 531, 5112, 5122, 5232, 5132}; + Int_t sizePdg = sizeof(bpdgs)/sizeof(Int_t); + Bool_t hasBeautyMoth = kFALSE; + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + for (auto& track : *tracks) { + auto pdg = track.GetPdgCode(); + auto rapidity = track.GetRapidity(); + auto idMoth = track.getMotherTrackId(); + if (pdg == checkPdgDecay) { + // count leptons + nLeptons++; + } else if(pdg == -checkPdgDecay) { + // count anti-leptons + nAntileptons++; + } else if (pdg == checkPdgSignal[0] || pdg == checkPdgSignal[1]) { + // check if mothers are beauty hadrons + hasBeautyMoth = kFALSE; + if(idMoth){ // check beauty mother + auto tdM = mcreader.getTrack(i, idMoth); + for(int i=0; iGetPdgCode()) == bpdgs[i] ) hasBeautyMoth = kTRUE; } + } + if(hasBeautyMoth){ + // count signal PDG + pdg == checkPdgSignal[0] ? nSignalJpsi++ : nSignalPsi2S++; + // count signal PDG within acceptance + if(rapidity > rapiditymin && rapidity < rapiditymax) { pdg == checkPdgSignal[0] ? nSignalJpsiWithinAcc++ : nSignalPsi2SWithinAcc++;} + } + auto child0 = o2::mcutils::MCTrackNavigator::getDaughter0(track, *tracks); + auto child1 = o2::mcutils::MCTrackNavigator::getDaughter1(track, *tracks); + if (child0 != nullptr && child1 != nullptr) { + // check for parent-child relations + auto pdg0 = child0->GetPdgCode(); + auto pdg1 = child1->GetPdgCode(); + std::cout << "First and last children of parent " << checkPdgSignal << " are PDG0: " << pdg0 << " PDG1: " << pdg1 << "\n"; + if (std::abs(pdg0) == checkPdgDecay && std::abs(pdg1) == checkPdgDecay && pdg0 == -pdg1) { + nLeptonPairs++; + if (child0->getToBeDone() && child1->getToBeDone()) { + nLeptonPairsToBeDone++; + } + } + } + } + } + } + std::cout << "#events: " << nEvents << "\n" + << "#leptons: " << nLeptons << "\n" + << "#antileptons: " << nAntileptons << "\n" + << "#signal (jpsi <- b): " << nSignalJpsi << "; within acceptance " << rapiditymin << " < y < " << rapiditymax << " : " << nSignalJpsiWithinAcc << "\n" + << "#signal (psi2S <- b): " << nSignalPsi2S << "; within acceptance " << rapiditymin << " < y < " << rapiditymax << " : " << nSignalPsi2SWithinAcc << "\n" + << "#lepton pairs: " << nLeptonPairs << "\n" + << "#lepton pairs to be done: " << nLeptonPairs << "\n"; + + + if (nLeptonPairs == 0 || nLeptons == 0 || nAntileptons == 0) { + std::cerr << "Number of leptons, number of anti-leptons as well as number of lepton pairs should all be greater than 1.\n"; + return 1; + } + if (nLeptonPairs != nLeptonPairsToBeDone) { + std::cerr << "The number of lepton pairs should be the same as the number of lepton pairs which should be transported.\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGDQ/ini/tests/GeneratorHF_bbbar_PsiAndJpsi_midy.C b/MC/config/PWGDQ/ini/tests/GeneratorHF_bbbar_PsiAndJpsi_midy_triggerGap_OO5TeV.C similarity index 100% rename from MC/config/PWGDQ/ini/tests/GeneratorHF_bbbar_PsiAndJpsi_midy.C rename to MC/config/PWGDQ/ini/tests/GeneratorHF_bbbar_PsiAndJpsi_midy_triggerGap_OO5TeV.C diff --git a/MC/config/PWGDQ/ini/tests/GeneratorHF_bbbar_PsiAndJpsi_fwdy.C b/MC/config/PWGDQ/ini/tests/GeneratorHF_bbbar_PsiAndJpsi_midy_triggerGap_pO96TeV.C similarity index 92% rename from MC/config/PWGDQ/ini/tests/GeneratorHF_bbbar_PsiAndJpsi_fwdy.C rename to MC/config/PWGDQ/ini/tests/GeneratorHF_bbbar_PsiAndJpsi_midy_triggerGap_pO96TeV.C index 52d699151..8a0964910 100644 --- a/MC/config/PWGDQ/ini/tests/GeneratorHF_bbbar_PsiAndJpsi_fwdy.C +++ b/MC/config/PWGDQ/ini/tests/GeneratorHF_bbbar_PsiAndJpsi_midy_triggerGap_pO96TeV.C @@ -1,7 +1,7 @@ int External() { int checkPdgSignal[] = {443,100443}; - int checkPdgDecay = 13; + int checkPdgDecay = 11; std::string path{"o2sim_Kine.root"}; std::cout << "Check for\nsignal PDG " << checkPdgSignal << "\ndecay PDG " << checkPdgDecay << "\n"; TFile file(path.c_str(), "READ"); @@ -51,7 +51,7 @@ int External() // count signal PDG pdg == checkPdgSignal[0] ? nSignalJpsi++ : nSignalPsi2S++; // count signal PDG within acceptance - if(rapidity > -4.3 && rapidity < -2.3) { pdg == checkPdgSignal[0] ? nSignalJpsiWithinAcc++ : nSignalPsi2SWithinAcc++;} + if(std::abs(rapidity) < 1.0) { pdg == checkPdgSignal[0] ? nSignalJpsiWithinAcc++ : nSignalPsi2SWithinAcc++;} } auto child0 = o2::mcutils::MCTrackNavigator::getDaughter0(track, *tracks); auto child1 = o2::mcutils::MCTrackNavigator::getDaughter1(track, *tracks); @@ -73,8 +73,8 @@ int External() std::cout << "#events: " << nEvents << "\n" << "#leptons: " << nLeptons << "\n" << "#antileptons: " << nAntileptons << "\n" - << "#signal (jpsi <- b): " << nSignalJpsi << "; within acceptance (-4.3 < y < -2.3): " << nSignalJpsiWithinAcc << "\n" - << "#signal (psi2S <- b): " << nSignalPsi2S << "; within acceptance (-4.3 < y < -2.3): " << nSignalPsi2SWithinAcc << "\n" + << "#signal (jpsi <- b): " << nSignalJpsi << "; within acceptance (|y| < 1): " << nSignalJpsiWithinAcc << "\n" + << "#signal (psi2S <- b): " << nSignalPsi2S << "; within acceptance (|y| < 1): " << nSignalPsi2SWithinAcc << "\n" << "#lepton pairs: " << nLeptonPairs << "\n" << "#lepton pairs to be done: " << nLeptonPairs << "\n"; diff --git a/MC/config/PWGDQ/ini/tests/GeneratorHF_bbbar_PsiToJpsi_midy_triggerGap.C b/MC/config/PWGDQ/ini/tests/GeneratorHF_bbbar_PsiToJpsi_midy_triggerGap.C new file mode 100644 index 000000000..5c50cd1af --- /dev/null +++ b/MC/config/PWGDQ/ini/tests/GeneratorHF_bbbar_PsiToJpsi_midy_triggerGap.C @@ -0,0 +1,104 @@ +int External() +{ + int checkPdgSignal[] = {100443}; + int checkPdgDecay[] = {443, 211, -211}; + int leptonPdg = 11; + Double_t rapidityWindow = 1.0; + std::string path{"o2sim_Kine.root"}; + std::cout << "Check for\nsignal PDG " << checkPdgSignal[0] << "\n decay PDG " << checkPdgDecay[0] << ", " << checkPdgDecay[1] << ", " << checkPdgDecay[2] << "\n"; + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + auto tree = (TTree*)file.Get("o2sim"); + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int nLeptons{}; + int nAntileptons{}; + int nLeptonPairs{}; + int nLeptonPairsToBeDone{}; + int nSignalJpsi{}; + int nSignalPionsPos{}; + int nSignalPionsNeg{}; + int nSignalPsi2S{}; + int nSignalJpsiWithinAcc{}; + int nSignalPionsPosWithinAcc{}; + int nSignalPionsNegWithinAcc{}; + auto nEvents = tree->GetEntries(); + o2::steer::MCKinematicsReader mcreader("o2sim", o2::steer::MCKinematicsReader::Mode::kMCKine); + Int_t bpdgs[] = {511, 521, 531, 5112, 5122, 5232, 5132}; + Int_t sizePdg = sizeof(bpdgs)/sizeof(Int_t); + Bool_t hasBeautyMoth = kFALSE; + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + for (auto& track : *tracks) { + auto pdg = track.GetPdgCode(); + auto rapidity = track.GetRapidity(); + auto idMoth = track.getMotherTrackId(); + int idJpsi = -1; int IdChild0 = -1; int IdChild1 = -1; + if (pdg == leptonPdg) { + // count leptons + nLeptons++; + } else if(pdg == -leptonPdg) { + // count anti-leptons + nAntileptons++; + } else if (pdg == checkPdgSignal[0]) { + hasBeautyMoth = kFALSE; + if(idMoth){ // check beauty mother + auto tdM = mcreader.getTrack(i, idMoth); + for(int i=0; iGetPdgCode()) == bpdgs[i] ) hasBeautyMoth = kTRUE; } + } + if(hasBeautyMoth){ + nSignalPsi2S++; + for (int j{track.getFirstDaughterTrackId()}; j <= track.getLastDaughterTrackId(); ++j) { + auto pdgDau = tracks->at(j).GetPdgCode(); + if(TMath::Abs(pdgDau) == checkPdgDecay[0] ) { nSignalJpsi++; if( std::abs(track.GetRapidity()) < rapidityWindow) nSignalJpsiWithinAcc++; idJpsi = j; } + if(pdgDau == checkPdgDecay[1] ) { nSignalPionsPos++; if( std::abs(track.GetRapidity()) < rapidityWindow) nSignalPionsPosWithinAcc++; } + if(pdgDau == checkPdgDecay[2] ) { nSignalPionsNeg++; if( std::abs(track.GetRapidity()) < rapidityWindow) nSignalPionsNegWithinAcc++; } + } + + auto trackJpsi = tracks->at(idJpsi); + for (int j{trackJpsi.getFirstDaughterTrackId()}; j <= trackJpsi.getLastDaughterTrackId(); ++j) { + auto pdgDau = tracks->at(j).GetPdgCode(); + if(pdgDau == leptonPdg ) IdChild0 = j; + if(pdgDau == -leptonPdg ) IdChild1 = j; + } + auto child0 = tracks->at(IdChild0); + auto child1 = tracks->at(IdChild1); + // check for parent-child relations + auto pdg0 = child0.GetPdgCode(); + auto pdg1 = child1.GetPdgCode(); + if (std::abs(pdg0) == leptonPdg && std::abs(pdg1) == leptonPdg && pdg0 == -pdg1) { + nLeptonPairs++; + if (child0.getToBeDone() && child1.getToBeDone()) { + nLeptonPairsToBeDone++; + } + } + } + } + } + } + std::cout << "#events: " << nEvents << "\n" + << "#leptons: " << nLeptons << "\n" + << "#antileptons: " << nAntileptons << "\n" + << "#signal (jpsi <- psi2S): " << nSignalJpsi << "; within acceptance (|y| < " << rapidityWindow << "): " << nSignalJpsiWithinAcc << "\n" + << "#signal (pi+ <- psi2S): " << nSignalPionsPos << "; within acceptance (|y| < " << rapidityWindow << "): " << nSignalPionsPosWithinAcc << "\n" + << "#signal (pi- <- psi2S): " << nSignalPionsNeg << "; within acceptance (|y| < " << rapidityWindow << "): " << nSignalPionsNegWithinAcc << "\n" + << "#lepton pairs: " << nLeptonPairs << "\n" + << "#lepton pairs to be done: " << nLeptonPairs << "\n"; + + + if (nLeptonPairs == 0 || nLeptons == 0 || nAntileptons == 0) { + std::cerr << "Number of leptons, number of anti-leptons as well as number of lepton pairs should all be greater than 1.\n"; + return 1; + } + if (nLeptonPairs != nLeptonPairsToBeDone) { + std::cerr << "The number of lepton pairs should be the same as the number of lepton pairs which should be transported.\n"; + return 1; + } + + return 0; +} \ No newline at end of file diff --git a/MC/config/PWGDQ/ini/tests/GeneratorHF_ccbarToMuonsSemileptonic_fwdy_inel_TriggerGap.C b/MC/config/PWGDQ/ini/tests/GeneratorHF_ccbarToMuonsSemileptonic_fwdy_inel_TriggerGap.C new file mode 100644 index 000000000..369f394d7 --- /dev/null +++ b/MC/config/PWGDQ/ini/tests/GeneratorHF_ccbarToMuonsSemileptonic_fwdy_inel_TriggerGap.C @@ -0,0 +1,77 @@ +int External() +{ + int checkPdgDecay = 13; + std::string path{"o2sim_Kine.root"}; + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + auto tree = (TTree*)file.Get("o2sim"); + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int nLeptons{}; + int nLeptonsInAcceptance{}; + int nLeptonsToBeDone{}; + int nSignalPairs{}; + int nLeptonPairs{}; + int nLeptonPairsInAcceptance{}; + int nLeptonPairsToBeDone{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + int nleptonseinacc = 0; + int nleptonse = 0; + int nleptonseToBeDone = 0; + int nopenHeavy = 0; + for (auto& track : *tracks) { + auto pdg = track.GetPdgCode(); + auto y = track.GetRapidity(); + if (std::abs(pdg) == checkPdgDecay) { + int igmother = track.getMotherTrackId(); + if (igmother > 0) { + auto gmTrack = (*tracks)[igmother]; + int gmpdg = gmTrack.GetPdgCode(); + if ( int(std::abs(gmpdg)/100.) == 4 || int(std::abs(gmpdg)/1000.) == 4 || int(std::abs(gmpdg)/100.) == 5 || int(std::abs(gmpdg)/1000.) == 5 ) { + nLeptons++; + nleptonse++; + if (-4.3 < y && y < -2.2) { + nleptonseinacc++; + nLeptonsInAcceptance++; + } + if (track.getToBeDone()) { + nLeptonsToBeDone++; + nleptonseToBeDone++; + } + } + } + } else if (std::abs(pdg) == 411 || std::abs(pdg) == 421 || std::abs(pdg) == 431 || std::abs(pdg) == 4122 || std::abs(pdg) == 4132 || std::abs(pdg) == 4232 || std::abs(pdg) == 4332) { + nopenHeavy++; + } + } + if (nopenHeavy > 1) nSignalPairs++; + if (nleptonse > 1) nLeptonPairs++; + if (nleptonseToBeDone > 1) nLeptonPairsToBeDone++; + if (nleptonseinacc > 1) nLeptonPairsInAcceptance++; + } + std::cout << "#events: " << nEvents << "\n" + << "#muons in acceptance: " << nLeptonsInAcceptance << "\n" + << "#muon pairs in acceptance: " << nLeptonPairsInAcceptance << "\n" + << "#muons: " << nLeptons << "\n" + << "#muons to be done: " << nLeptonsToBeDone << "\n" + << "#signal pairs: " << nSignalPairs << "\n" + << "#muon pairs: " << nLeptonPairs << "\n" + << "#muon pairs to be done: " << nLeptonPairsToBeDone << "\n"; + if (nLeptonPairs != nLeptonPairsToBeDone) { + std::cerr << "The number of muon pairs should be the same as the number of muon pairs which should be transported.\n"; + return 1; + } + if (nLeptons != nLeptonsToBeDone) { + std::cerr << "The number of muons should be the same as the number of muons which should be transported.\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGDQ/ini/tests/GeneratorHF_ccbarToMuonsSemileptonic_fwdy_inel_TriggerGap_natural.C b/MC/config/PWGDQ/ini/tests/GeneratorHF_ccbarToMuonsSemileptonic_fwdy_inel_TriggerGap_natural.C new file mode 100644 index 000000000..369f394d7 --- /dev/null +++ b/MC/config/PWGDQ/ini/tests/GeneratorHF_ccbarToMuonsSemileptonic_fwdy_inel_TriggerGap_natural.C @@ -0,0 +1,77 @@ +int External() +{ + int checkPdgDecay = 13; + std::string path{"o2sim_Kine.root"}; + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + auto tree = (TTree*)file.Get("o2sim"); + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int nLeptons{}; + int nLeptonsInAcceptance{}; + int nLeptonsToBeDone{}; + int nSignalPairs{}; + int nLeptonPairs{}; + int nLeptonPairsInAcceptance{}; + int nLeptonPairsToBeDone{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + int nleptonseinacc = 0; + int nleptonse = 0; + int nleptonseToBeDone = 0; + int nopenHeavy = 0; + for (auto& track : *tracks) { + auto pdg = track.GetPdgCode(); + auto y = track.GetRapidity(); + if (std::abs(pdg) == checkPdgDecay) { + int igmother = track.getMotherTrackId(); + if (igmother > 0) { + auto gmTrack = (*tracks)[igmother]; + int gmpdg = gmTrack.GetPdgCode(); + if ( int(std::abs(gmpdg)/100.) == 4 || int(std::abs(gmpdg)/1000.) == 4 || int(std::abs(gmpdg)/100.) == 5 || int(std::abs(gmpdg)/1000.) == 5 ) { + nLeptons++; + nleptonse++; + if (-4.3 < y && y < -2.2) { + nleptonseinacc++; + nLeptonsInAcceptance++; + } + if (track.getToBeDone()) { + nLeptonsToBeDone++; + nleptonseToBeDone++; + } + } + } + } else if (std::abs(pdg) == 411 || std::abs(pdg) == 421 || std::abs(pdg) == 431 || std::abs(pdg) == 4122 || std::abs(pdg) == 4132 || std::abs(pdg) == 4232 || std::abs(pdg) == 4332) { + nopenHeavy++; + } + } + if (nopenHeavy > 1) nSignalPairs++; + if (nleptonse > 1) nLeptonPairs++; + if (nleptonseToBeDone > 1) nLeptonPairsToBeDone++; + if (nleptonseinacc > 1) nLeptonPairsInAcceptance++; + } + std::cout << "#events: " << nEvents << "\n" + << "#muons in acceptance: " << nLeptonsInAcceptance << "\n" + << "#muon pairs in acceptance: " << nLeptonPairsInAcceptance << "\n" + << "#muons: " << nLeptons << "\n" + << "#muons to be done: " << nLeptonsToBeDone << "\n" + << "#signal pairs: " << nSignalPairs << "\n" + << "#muon pairs: " << nLeptonPairs << "\n" + << "#muon pairs to be done: " << nLeptonPairsToBeDone << "\n"; + if (nLeptonPairs != nLeptonPairsToBeDone) { + std::cerr << "The number of muon pairs should be the same as the number of muon pairs which should be transported.\n"; + return 1; + } + if (nLeptons != nLeptonsToBeDone) { + std::cerr << "The number of muons should be the same as the number of muons which should be transported.\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGDQ/ini/tests/GeneratorParamSingleMuon_PbPb_5TeV.C b/MC/config/PWGDQ/ini/tests/GeneratorParamSingleMuon_PbPb_5TeV.C new file mode 100644 index 000000000..bbdb872b8 --- /dev/null +++ b/MC/config/PWGDQ/ini/tests/GeneratorParamSingleMuon_PbPb_5TeV.C @@ -0,0 +1,78 @@ +int External() +{ + int checkPdgDecay = 13; + double yMin = -4.3; + double yMax = -2.3; + double ptMin = 0.0; + double ptMax = 999.; + + std::string path{"o2sim_Kine.root"}; + std::cout << "Check for primary muons, PDG +-" << checkPdgDecay << ", " << yMin << " < y < " << yMax << ", " << ptMin << " < pt < " << ptMax << " GeV/c\n"; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree*)file.Get("o2sim"); + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int nMuons{}; + int nAntiMuons{}; + int nOutsideY{}; + int nOutsidePt{}; + int nNonPrimary{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + for (auto& track : *tracks) { + auto pdg = track.GetPdgCode(); + auto rapidity = track.GetRapidity(); + auto pt = track.GetPt(); + auto idMoth = track.getMotherTrackId(); + if (std::abs(pdg) != checkPdgDecay) { + continue; + } + if (pdg == checkPdgDecay) { + nMuons++; //counts muons + } else { + nAntiMuons++; //counts anti-muons + } + if (idMoth >= 0) { + nNonPrimary++; //counts non-primary muons (those with a mother track) + } + + if (rapidity < yMin || rapidity > yMax) { + nOutsideY++; + } + if (pt < ptMin || pt > ptMax) { + nOutsidePt++; + } + } + } + + std::cout << "#events: " << nEvents << "\n" + << "#mu-: " << nMuons << "\n" + << "#mu+: " << nAntiMuons << "\n" + << "#non-primary muons: " << nNonPrimary << "\n" + << "#muons outside y-range: " << nOutsideY << "\n" + << "#muons outside pt-range: " << nOutsidePt << "\n"; + + if (nMuons == 0 || nAntiMuons == 0) { + std::cerr << "Expected both mu- and mu+, found nMuons=" << nMuons << " nAntiMuons=" << nAntiMuons << "\n"; + return 1; + } + if (nNonPrimary != 0) { + std::cerr << "All generated muons should be primaries (no mother track).\n"; + return 1; + } + if (nOutsideY != 0 || nOutsidePt != 0) { + std::cerr << "Some muons were generated outside the configured (y, pt) window.\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGDQ/ini/tests/Generator_InjectedBottomoniaFwdY_TriggerGap_PbPb_536TeV.C b/MC/config/PWGDQ/ini/tests/Generator_InjectedBottomoniaFwdY_TriggerGap_PbPb_536TeV.C new file mode 100644 index 000000000..bdafa97b1 --- /dev/null +++ b/MC/config/PWGDQ/ini/tests/Generator_InjectedBottomoniaFwdY_TriggerGap_PbPb_536TeV.C @@ -0,0 +1,103 @@ +int External() +{ + int checkPdgSignal[] = {553,100553,200553}; + int checkPdgDecay = 13; + double rapiditymin = -4.3; double rapiditymax = -2.3; + std::string path{"o2sim_Kine.root"}; + std::cout << "Check for\nsignal PDG " << checkPdgSignal << "\ndecay PDG " << checkPdgDecay << "\n"; + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree*)file.Get("o2sim"); + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int nLeptons{}; + int nAntileptons{}; + int nLeptonPairs{}; + int nLeptonPairsToBeDone{}; + int nSignalUpsilon1S{}; + int nSignalUpsilon2S{}; + int nSignalUpsilon3S{}; + int nSignalUpsilon1SWithinAcc{}; + int nSignalUpsilon2SWithinAcc{}; + int nSignalUpsilon3SWithinAcc{}; + auto nEvents = tree->GetEntries(); + o2::steer::MCKinematicsReader mcreader("o2sim", o2::steer::MCKinematicsReader::Mode::kMCKine); + Bool_t isInjected = kFALSE; + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + for (auto& track : *tracks) { + auto pdg = track.GetPdgCode(); + auto rapidity = track.GetRapidity(); + auto idMoth = track.getMotherTrackId(); + if (pdg == checkPdgDecay) { + // count leptons + nLeptons++; + } else if(pdg == -checkPdgDecay) { + // count anti-leptons + nAntileptons++; + } else if (pdg == checkPdgSignal[0] || pdg == checkPdgSignal[1] || pdg == checkPdgSignal[2]) { + if(idMoth < 0){ + // count signal PDG + if (pdg == checkPdgSignal[0]) { + nSignalUpsilon1S++; + } else if (pdg == checkPdgSignal[1]) { + nSignalUpsilon2S++; + } else { + nSignalUpsilon3S++; + } + + // count signal PDG within acceptance + if (rapidity > rapiditymin && rapidity < rapiditymax) { + if (pdg == checkPdgSignal[0]) { + nSignalUpsilon1SWithinAcc++; + } else if (pdg == checkPdgSignal[1]) { + nSignalUpsilon2SWithinAcc++; + } else { + nSignalUpsilon3SWithinAcc++; + } + } + } + auto child0 = o2::mcutils::MCTrackNavigator::getDaughter0(track, *tracks); + auto child1 = o2::mcutils::MCTrackNavigator::getDaughter1(track, *tracks); + if (child0 != nullptr && child1 != nullptr) { + // check for parent-child relations + auto pdg0 = child0->GetPdgCode(); + auto pdg1 = child1->GetPdgCode(); + std::cout << "First and last children of parent " << checkPdgSignal << " are PDG0: " << pdg0 << " PDG1: " << pdg1 << "\n"; + if (std::abs(pdg0) == checkPdgDecay && std::abs(pdg1) == checkPdgDecay && pdg0 == -pdg1) { + nLeptonPairs++; + if (child0->getToBeDone() && child1->getToBeDone()) { + nLeptonPairsToBeDone++; + } + } + } + } + } + } + std::cout << "#events: " << nEvents << "\n" + << "#leptons: " << nLeptons << "\n" + << "#antileptons: " << nAntileptons << "\n" + << "#signal (Upsilon(1S)): " << nSignalUpsilon1S << "; within acceptance " << rapiditymin << " < y < " << rapiditymax << " : " << nSignalUpsilon1SWithinAcc << "\n" + << "#signal (Upsilon(2S)): " << nSignalUpsilon2S << "; within acceptance " << rapiditymin << " < y < " << rapiditymax << " : " << nSignalUpsilon2SWithinAcc << "\n" + << "#signal (Upsilon(2S)): " << nSignalUpsilon3S << "; within acceptance " << rapiditymin << " < y < " << rapiditymax << " : " << nSignalUpsilon3SWithinAcc << "\n" + << "#lepton pairs: " << nLeptonPairs << "\n" + << "#lepton pairs to be done: " << nLeptonPairs << "\n"; + + + if (nLeptonPairs == 0 || nLeptons == 0 || nAntileptons == 0) { + std::cerr << "Number of leptons, number of anti-leptons as well as number of lepton pairs should all be greater than 1.\n"; + return 1; + } + if (nLeptonPairs != nLeptonPairsToBeDone) { + std::cerr << "The number of lepton pairs should be the same as the number of lepton pairs which should be transported.\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGDQ/ini/tests/Generator_InjectedBottomoniaFwdy_TriggerGap.C b/MC/config/PWGDQ/ini/tests/Generator_InjectedBottomoniaFwdy_TriggerGap.C new file mode 100644 index 000000000..b7a166069 --- /dev/null +++ b/MC/config/PWGDQ/ini/tests/Generator_InjectedBottomoniaFwdy_TriggerGap.C @@ -0,0 +1,103 @@ +int External() +{ + int checkPdgSignal[] = {553,100553,200553}; + int checkPdgDecay = 13; + double rapiditymin = -4.3; double rapiditymax = -2.3; + std::string path{"o2sim_Kine.root"}; + std::cout << "Check for\nsignal PDG " << checkPdgSignal << "\ndecay PDG " << checkPdgDecay << "\n"; + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree*)file.Get("o2sim"); + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int nLeptons{}; + int nAntileptons{}; + int nLeptonPairs{}; + int nLeptonPairsToBeDone{}; + int nSignalUpsilon1S{}; + int nSignalUpsilon2S{}; + int nSignalUpsilon3S{}; + int nSignalUpsilon1SWithinAcc{}; + int nSignalUpsilon2SWithinAcc{}; + int nSignalUpsilon3SWithinAcc{}; + auto nEvents = tree->GetEntries(); + o2::steer::MCKinematicsReader mcreader("o2sim", o2::steer::MCKinematicsReader::Mode::kMCKine); + Bool_t isInjected = kFALSE; + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + for (auto& track : *tracks) { + auto pdg = track.GetPdgCode(); + auto rapidity = track.GetRapidity(); + auto idMoth = track.getMotherTrackId(); + if (pdg == checkPdgDecay) { + // count leptons + nLeptons++; + } else if(pdg == -checkPdgDecay) { + // count anti-leptons + nAntileptons++; + } else if (pdg == checkPdgSignal[0] || pdg == checkPdgSignal[1] || pdg == checkPdgSignal[2]) { + if(idMoth < 0){ + // count signal PDG + if (pdg == checkPdgSignal[0]) { + nSignalUpsilon1S++; + } else if (pdg == checkPdgSignal[1]) { + nSignalUpsilon2S++; + } else { + nSignalUpsilon3S++; + } + + // count signal PDG within acceptance + if (rapidity > rapiditymin && rapidity < rapiditymax) { + if (pdg == checkPdgSignal[0]) { + nSignalUpsilon1SWithinAcc++; + } else if (pdg == checkPdgSignal[1]) { + nSignalUpsilon2SWithinAcc++; + } else { + nSignalUpsilon3SWithinAcc++; + } + } + } + auto child0 = o2::mcutils::MCTrackNavigator::getDaughter0(track, *tracks); + auto child1 = o2::mcutils::MCTrackNavigator::getDaughter1(track, *tracks); + if (child0 != nullptr && child1 != nullptr) { + // check for parent-child relations + auto pdg0 = child0->GetPdgCode(); + auto pdg1 = child1->GetPdgCode(); + std::cout << "First and last children of parent " << checkPdgSignal << " are PDG0: " << pdg0 << " PDG1: " << pdg1 << "\n"; + if (std::abs(pdg0) == checkPdgDecay && std::abs(pdg1) == checkPdgDecay && pdg0 == -pdg1) { + nLeptonPairs++; + if (child0->getToBeDone() && child1->getToBeDone()) { + nLeptonPairsToBeDone++; + } + } + } + } + } + } + std::cout << "#events: " << nEvents << "\n" + << "#leptons: " << nLeptons << "\n" + << "#antileptons: " << nAntileptons << "\n" + << "#signal (Upsilon(1S)): " << nSignalUpsilon1S << "; within acceptance " << rapiditymin << " < y < " << rapiditymax << " : " << nSignalUpsilon1SWithinAcc << "\n" + << "#signal (Upsilon(2S)): " << nSignalUpsilon2S << "; within acceptance " << rapiditymin << " < y < " << rapiditymax << " : " << nSignalUpsilon2SWithinAcc << "\n" + << "#signal (Upsilon(2S)): " << nSignalUpsilon3S << "; within acceptance " << rapiditymin << " < y < " << rapiditymax << " : " << nSignalUpsilon3SWithinAcc << "\n" + << "#lepton pairs: " << nLeptonPairs << "\n" + << "#lepton pairs to be done: " << nLeptonPairs << "\n"; + + + if (nLeptonPairs == 0 || nLeptons == 0 || nAntileptons == 0) { + std::cerr << "Number of leptons, number of anti-leptons as well as number of lepton pairs should all be greater than 1.\n"; + return 1; + } + if (nLeptonPairs != nLeptonPairsToBeDone) { + std::cerr << "The number of lepton pairs should be the same as the number of lepton pairs which should be transported.\n"; + return 1; + } + + return 0; +} \ No newline at end of file diff --git a/MC/config/PWGDQ/ini/tests/Generator_InjectedCharmoniaMidy_PbPb5TeV.C b/MC/config/PWGDQ/ini/tests/Generator_InjectedCharmoniaMidy_PbPb5TeV.C new file mode 100644 index 000000000..b155ec9ac --- /dev/null +++ b/MC/config/PWGDQ/ini/tests/Generator_InjectedCharmoniaMidy_PbPb5TeV.C @@ -0,0 +1,84 @@ +int External() +{ + int checkPdgSignal[] = {443,100443}; + int checkPdgDecay = 11; + std::string path{"o2sim_Kine.root"}; + std::cout << "Check for\nsignal PDG " << checkPdgSignal << "\ndecay PDG " << checkPdgDecay << "\n"; + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree*)file.Get("o2sim"); + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int nLeptons{}; + int nAntileptons{}; + int nLeptonPairs{}; + int nLeptonPairsToBeDone{}; + int nSignalJpsi{}; + int nSignalPsi2S{}; + int nSignalJpsiWithinAcc{}; + int nSignalPsi2SWithinAcc{}; + auto nEvents = tree->GetEntries(); + o2::steer::MCKinematicsReader mcreader("o2sim", o2::steer::MCKinematicsReader::Mode::kMCKine); + Bool_t isInjected = kFALSE; + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + for (auto& track : *tracks) { + auto pdg = track.GetPdgCode(); + auto rapidity = track.GetRapidity(); + auto idMoth = track.getMotherTrackId(); + if (pdg == checkPdgDecay) { + // count leptons + nLeptons++; + } else if(pdg == -checkPdgDecay) { + // count anti-leptons + nAntileptons++; + } else if (pdg == checkPdgSignal[0] || pdg == checkPdgSignal[1]) { + if(idMoth < 0){ + // count signal PDG + pdg == checkPdgSignal[0] ? nSignalJpsi++ : nSignalPsi2S++; + // count signal PDG within acceptance + if(std::abs(rapidity) < 1.0) { pdg == checkPdgSignal[0] ? nSignalJpsiWithinAcc++ : nSignalPsi2SWithinAcc++;} + } + auto child0 = o2::mcutils::MCTrackNavigator::getDaughter0(track, *tracks); + auto child1 = o2::mcutils::MCTrackNavigator::getDaughter1(track, *tracks); + if (child0 != nullptr && child1 != nullptr) { + // check for parent-child relations + auto pdg0 = child0->GetPdgCode(); + auto pdg1 = child1->GetPdgCode(); + std::cout << "First and last children of parent " << checkPdgSignal << " are PDG0: " << pdg0 << " PDG1: " << pdg1 << "\n"; + if (std::abs(pdg0) == checkPdgDecay && std::abs(pdg1) == checkPdgDecay && pdg0 == -pdg1) { + nLeptonPairs++; + if (child0->getToBeDone() && child1->getToBeDone()) { + nLeptonPairsToBeDone++; + } + } + } + } + } + } + std::cout << "#events: " << nEvents << "\n" + << "#leptons: " << nLeptons << "\n" + << "#antileptons: " << nAntileptons << "\n" + << "#signal (prompt Jpsi): " << nSignalJpsi << "; within acceptance (|y| < 1): " << nSignalJpsiWithinAcc << "\n" + << "#signal (prompt Psi(2S)): " << nSignalPsi2S << "; within acceptance (|y| < 1): " << nSignalPsi2SWithinAcc << "\n" + << "#lepton pairs: " << nLeptonPairs << "\n" + << "#lepton pairs to be done: " << nLeptonPairs << "\n"; + + + if (nLeptonPairs == 0 || nLeptons == 0 || nAntileptons == 0) { + std::cerr << "Number of leptons, number of anti-leptons as well as number of lepton pairs should all be greater than 1.\n"; + return 1; + } + if (nLeptonPairs != nLeptonPairsToBeDone) { + std::cerr << "The number of lepton pairs should be the same as the number of lepton pairs which should be transported.\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGDQ/ini/tests/Generator_InjectedChiCToElectronMidy_TriggerGap.C b/MC/config/PWGDQ/ini/tests/Generator_InjectedChiCToElectronMidy_TriggerGap.C new file mode 100644 index 000000000..0ca9d149f --- /dev/null +++ b/MC/config/PWGDQ/ini/tests/Generator_InjectedChiCToElectronMidy_TriggerGap.C @@ -0,0 +1,173 @@ +int External() +{ + int checkPdgSignal[] = {20443,445}; + int checkPdgDecay = 11; + int checkPdgDecayOther = 22; + int checkPdgDecayFirst = 443; + std::string path{"o2sim_Kine.root"}; + std::cout << "Check for\nsignal PDG " << checkPdgSignal[0] << " and "<< checkPdgSignal[1] << "\ndecay PDG " << checkPdgDecayOther << " and" << checkPdgDecayFirst << "\n"; + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + int hasElectron=0; + int hasPositron=0; + + auto tree = (TTree*)file.Get("o2sim"); + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int nPhotons{}; + int nLeptons{}; + int nSignalJpsi{}; + int nAntileptons{}; + int nLeptonPairsChild0{}; + int nLeptonPairsChild0ToBeDone{}; + int nLeptonPairsChild1{}; + int nLeptonPairsChild1ToBeDone{}; + int nGammaJpsiPairs{}; + int nGammaJpsiPairsToBeDone{}; + int nSignalChiC1{}; + int nSignalChiC2{}; + int nSignalChiC1WithinAcc{}; + int nSignalChiC2WithinAcc{}; + auto nEvents = tree->GetEntries(); + o2::steer::MCKinematicsReader mcreader("o2sim", o2::steer::MCKinematicsReader::Mode::kMCKine); + Bool_t isInjected = kFALSE; + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + for (auto& track : *tracks) { + auto pdg = track.GetPdgCode(); + auto rapidity = track.GetRapidity(); + auto idMoth = track.getMotherTrackId(); + if (pdg == checkPdgDecay) { + // count leptons + nLeptons++; + } else if(pdg == -checkPdgDecay) { + // count anti-leptons + nAntileptons++; + } else if(pdg == checkPdgDecayFirst) { + // count J/psi + nSignalJpsi++; + } else if(pdg == checkPdgDecayOther) { + // count photons + nPhotons++; + } else if (pdg == checkPdgSignal[0] || pdg == checkPdgSignal[1]) { + if(idMoth < 0){ + // count signal PDG + pdg == checkPdgSignal[0] ? nSignalChiC1++ : nSignalChiC2++; + // count signal PDG within acceptance + if(std::abs(rapidity) < 1.0) { pdg == checkPdgSignal[0] ? nSignalChiC1WithinAcc++ : nSignalChiC2WithinAcc++;} + } + auto child0 = o2::mcutils::MCTrackNavigator::getDaughter0(track, *tracks); + auto child1 = o2::mcutils::MCTrackNavigator::getDaughter1(track, *tracks); + if (child0 != nullptr && child1 != nullptr) { + // check for parent-child relations + auto pdg0 = child0->GetPdgCode(); + auto pdg1 = child1->GetPdgCode(); +// std::cout << "First and last children of parent " << checkPdgSignal << " are PDG0: " << pdg0 << " PDG1: " << pdg1 << "\n"; + if ( (std::abs(pdg0) == checkPdgDecayFirst && std::abs(pdg1) == checkPdgDecayOther ) || + (std::abs(pdg0) == checkPdgDecayOther && std::abs(pdg1) == checkPdgDecayFirst ) ) { + nGammaJpsiPairs++; + + + hasElectron=0; + hasPositron=0; + + if (child0->getToBeDone() == 0) { + + auto grandChild0FromChild0 = &(*tracks).at(child0-> getFirstDaughterTrackId()); + auto grandChild1FromChild0 = &(*tracks).at(child0-> getLastDaughterTrackId()); + + for(int ii = child0-> getFirstDaughterTrackId() ; ii< child0-> getLastDaughterTrackId()+1 ;ii++){ + auto grandChildFromChild0 = &(*tracks).at(ii); + if ( grandChildFromChild0->GetPdgCode() == -checkPdgDecay ){ + hasElectron = 1; + auto grandChild0FromChild0 = &(*tracks).at(ii); + } + if ( grandChildFromChild0->GetPdgCode() == checkPdgDecay ){ + hasPositron = 1; + auto grandChild1FromChild0 = &(*tracks).at(ii); + + } + } + if (hasElectron && hasPositron) { + nLeptonPairsChild0++; + if (grandChild0FromChild0->getToBeDone() && grandChild1FromChild0->getToBeDone()) { + nLeptonPairsChild0ToBeDone++; + } + } + } + + hasElectron=0; + hasPositron=0; + + if (child1->getToBeDone() == 0) { + auto grandChild0FromChild1 = &(*tracks).at(child1-> getFirstDaughterTrackId()); + auto grandChild1FromChild1 = &(*tracks).at(child1-> getLastDaughterTrackId()); + + for(int ii = child1-> getFirstDaughterTrackId() ; ii< child1-> getLastDaughterTrackId()+1 ;ii++){ + auto grandChildFromChild1 = &(*tracks).at(ii); + if ( grandChildFromChild1->GetPdgCode() == -checkPdgDecay ){ + hasElectron = 1; + auto grandChild0FromChild1 = &(*tracks).at(ii); + } + if ( grandChildFromChild1->GetPdgCode() == checkPdgDecay ){ + hasPositron = 1; + auto grandChild1FromChild1 = &(*tracks).at(ii); + + } + } + if (hasElectron && hasPositron) { + nLeptonPairsChild1++; + if (grandChild0FromChild1->getToBeDone() && grandChild1FromChild1->getToBeDone()) { + nLeptonPairsChild1ToBeDone++; + } + } + } + } + } + } + } + } + nGammaJpsiPairsToBeDone = nLeptonPairsChild0ToBeDone + nLeptonPairsChild1ToBeDone; + + std::cout << "#events: " << nEvents << "\n" + << "#leptons: " << nLeptons << "\n" + << "#antileptons: " << nAntileptons << "\n" + << "#signal photon " << nPhotons << "\n" + << "#signal (prompt Jpsi): " << nSignalJpsi << "\n" + << "#signal (prompt ChiC1): " << nSignalChiC1 << "; within acceptance (|y| < 1): " << nSignalChiC1WithinAcc << "\n" + << "#signal (prompt ChiC2): " << nSignalChiC2 << "; within acceptance (|y| < 1): " << nSignalChiC2WithinAcc << "\n" + << "#GammaJpsi pairs: " << nGammaJpsiPairs << "\n" + << "#GammaJpsi pairs to be done: " << nGammaJpsiPairsToBeDone << "\n" + << "#lepton pairs to be done from child0: " << nLeptonPairsChild0 << " " << nLeptonPairsChild0ToBeDone << "\n" + << "#lepton pairs to be done from Child 1: " << nLeptonPairsChild1 << " " << nLeptonPairsChild1ToBeDone << "\n"; + + + + if (nGammaJpsiPairs == 0 || nPhotons == 0 || nSignalJpsi == 0) { + std::cerr << "Number of photons, number of J/psi as well as number of Gamma-Jpsi pairs should all be greater than 1.\n"; + return 1; + } + if (nLeptonPairsChild0 != nLeptonPairsChild0ToBeDone){ + std::cerr << "The number of gamma J/psi pairs should be the same as the number of Gamma Jpsi pairs which should be transported.\n"; + return 1; + } + + if (nLeptonPairsChild1 != nLeptonPairsChild1ToBeDone){ + std::cerr << "The number of gamma J/psi pairs should be the same as the number of Gamma Jpsi pairs which should be transported.\n"; + return 1; + } + + + if (nGammaJpsiPairs < nGammaJpsiPairsToBeDone) { + std::cerr << "The number of gamma J/psi pairs should be the same as the number of Gamma Jpsi pairs which should be transported.\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGDQ/ini/tests/Generator_InjectedCoherentCharmoniaFwdy_PbPb5TeV.C b/MC/config/PWGDQ/ini/tests/Generator_InjectedCoherentCharmoniaFwdy_PbPb5TeV.C new file mode 100644 index 000000000..05d937161 --- /dev/null +++ b/MC/config/PWGDQ/ini/tests/Generator_InjectedCoherentCharmoniaFwdy_PbPb5TeV.C @@ -0,0 +1,132 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) + { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int checkPdgSignal[2] = {443, 100443}; + int checkPdgDecay = 13; + double rapiditymin = -4.5; double rapiditymax = -2.0; + + int nLeptons{}; + int nAntileptons{}; + int nLeptonPairs{}; + int nPrimaryLepton{}; + int nPrimaryAntilepton{}; + int nLeptonPairsToBeDone{}; + int nSignalJpsi{}; + int nSignalPsi2S{}; + int nPions{}; + int nPhotonsFromPsi{}; + int nPhotonsFromJPsi{}; + int nSignalJpsiWithinAcc{}; + int nSignalPsi2SWithinAcc{}; + int nDaughtersWithWrongMother{}; + + auto nEvents = tree->GetEntries(); + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + for (auto& track : *tracks) { + int trackId = static_cast(&track - tracks->data()); + auto pdg = track.GetPdgCode(); + auto rapidity = track.GetRapidity(); + auto idMoth = track.getMotherTrackId(); + if (pdg == checkPdgDecay) { + // count leptons + nLeptons++; + if (idMoth == -1) nPrimaryLepton++; + } else if (pdg == -checkPdgDecay) { + // count anti-leptons + nAntileptons++; + if (idMoth == -1) nPrimaryAntilepton++; + } else if (pdg == checkPdgSignal[0] || pdg == checkPdgSignal[1]) { + if (idMoth < 0) { + // count signal PDG + pdg == checkPdgSignal[0] ? nSignalJpsi++ : nSignalPsi2S++; + // count signal PDG within acceptance + if (rapidity > rapiditymin && rapidity < rapiditymax) { + pdg == checkPdgSignal[0] ? nSignalJpsiWithinAcc++ : nSignalPsi2SWithinAcc++; + } + } + int child0Id = track.getFirstDaughterTrackId(); + int child1Id = track.getLastDaughterTrackId(); + if (child0Id < 0 || child1Id < child0Id || + child1Id >= static_cast(tracks->size())) { + std::cout << "Skip charmonium without valid daughters:" + << " pdg=" << pdg + << " mother=" << idMoth + << " firstDaughter=" << child0Id + << " lastDaughter=" << child1Id + << " y=" << rapidity << '\n'; + continue; + } + + for (int iDaugh = child0Id; iDaugh <= child1Id; iDaugh++) { + const auto& daugh = tracks->at(iDaugh); + if (daugh.getMotherTrackId() != trackId) { + nDaughtersWithWrongMother++; + std::cerr << "Daughter does not point back to charmonium:" + << " daughter=" << iDaugh + << " daughterPdg=" << daugh.GetPdgCode() + << " mother=" << daugh.getMotherTrackId() + << " expectedMother=" << trackId + << " expectedMotherPdg=" << pdg << '\n'; + } + if (daugh.GetPdgCode() == 22) { + if (pdg == checkPdgSignal[0]) { + nPhotonsFromJPsi++; + } else if (pdg == checkPdgSignal[1]) { + nPhotonsFromPsi++; + } + } + if (TMath::Abs(daugh.GetPdgCode()) == 211) nPions++; + } + auto child0 = o2::mcutils::MCTrackNavigator::getDaughter0(track, *tracks); + auto child1 = o2::mcutils::MCTrackNavigator::getDaughter1(track, *tracks); + if (child0 != nullptr && child1 != nullptr) { + // check for parent-child relations + auto pdg0 = child0->GetPdgCode(); + auto pdg1 = child1->GetPdgCode(); + // std::cout << "First and last children of parent " << checkPdgSignal << " are PDG0: " << pdg0 << " PDG1: " << pdg1 << "\n"; + if (pdg == checkPdgSignal[0]) std::cout << "First and last children of parent Jpsi are PDG0: " << pdg0 << " PDG1: " << pdg1 << "\n"; + if (pdg == checkPdgSignal[1]) std::cout << "First and last children of parent Psi(2S) are PDG0: " << pdg0 << " PDG1: " << pdg1 << "\n"; + if (std::abs(pdg0) == checkPdgDecay && std::abs(pdg1) == checkPdgDecay && pdg0 == -pdg1) { + nLeptonPairs++; + if (child0->getToBeDone() && child1->getToBeDone()) { + nLeptonPairsToBeDone++; + } + } + } + } + } + } + + std::cout << "#events: " << nEvents << "\n" + << "#leptons: " << nLeptons << "\n" + << "#antileptons: " << nAntileptons << "\n" + << "#primary leptons: " << nPrimaryLepton << "\n" + << "#primary antileptons: " << nPrimaryAntilepton << "\n" + << "#signal (prompt Jpsi): " << nSignalJpsi << "; within acceptance " << rapiditymin << " < y < " << rapiditymax << " : " << nSignalJpsiWithinAcc << "\n" + << "#signal (prompt Psi(2S)): " << nSignalPsi2S << "; within acceptance " << rapiditymin << " < y < " << rapiditymax << " : " << nSignalPsi2SWithinAcc << "\n" + << "#photons from Jpsi: " << nPhotonsFromJPsi <<" #photons from Psi(2S): "<< nPhotonsFromPsi<<"\n" + << "#pions from Psi(2S): "<< nPions<<"\n" + << "#daughters with wrong mother: " << nDaughtersWithWrongMother << "\n" + << "#lepton pairs: " << nLeptonPairs <<" #lepton pairs to be done: "<< nLeptonPairsToBeDone<<"\n"; + + if (nDaughtersWithWrongMother != 0) { + std::cerr << "Some charmonium daughters do not point back to their expected mother.\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGDQ/ini/tests/Generator_InjectedCoherentCharmoniaMidy_PbPb5TeV.C b/MC/config/PWGDQ/ini/tests/Generator_InjectedCoherentCharmoniaMidy_PbPb5TeV.C new file mode 100644 index 000000000..760f7827a --- /dev/null +++ b/MC/config/PWGDQ/ini/tests/Generator_InjectedCoherentCharmoniaMidy_PbPb5TeV.C @@ -0,0 +1,131 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) + { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int checkPdgSignal[2] = {443, 100443}; + int checkPdgDecay = 11; + double rapiditymin = -1.5; double rapiditymax = 1.5; + + int nLeptons{}; + int nAntileptons{}; + int nLeptonPairs{}; + int nPrimaryLepton{}; + int nPrimaryAntilepton{}; + int nLeptonPairsToBeDone{}; + int nSignalJpsi{}; + int nSignalPsi2S{}; + int nPions{}; + int nPhotonsFromPsi{}; + int nPhotonsFromJPsi{}; + int nSignalJpsiWithinAcc{}; + int nSignalPsi2SWithinAcc{}; + int nDaughtersWithWrongMother{}; + + auto nEvents = tree->GetEntries(); + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + for (auto& track : *tracks) { + int trackId = static_cast(&track - tracks->data()); + auto pdg = track.GetPdgCode(); + auto rapidity = track.GetRapidity(); + auto idMoth = track.getMotherTrackId(); + if (pdg == checkPdgDecay) { + // count leptons + nLeptons++; + if (idMoth == -1) nPrimaryLepton++; + } else if (pdg == -checkPdgDecay) { + // count anti-leptons + nAntileptons++; + if (idMoth == -1) nPrimaryAntilepton++; + } else if (pdg == checkPdgSignal[0] || pdg == checkPdgSignal[1]) { + if (idMoth < 0) { + // count signal PDG + pdg == checkPdgSignal[0] ? nSignalJpsi++ : nSignalPsi2S++; + // count signal PDG within acceptance + if (rapidity > rapiditymin && rapidity < rapiditymax) { + pdg == checkPdgSignal[0] ? nSignalJpsiWithinAcc++ : nSignalPsi2SWithinAcc++; + } + } + int child0Id = track.getFirstDaughterTrackId(); + int child1Id = track.getLastDaughterTrackId(); + if (child0Id < 0 || child1Id < child0Id || + child1Id >= static_cast(tracks->size())) { + std::cout << "Skip charmonium without valid daughters:" + << " pdg=" << pdg + << " mother=" << idMoth + << " firstDaughter=" << child0Id + << " lastDaughter=" << child1Id + << " y=" << rapidity << '\n'; + continue; + } + for (int iDaugh = child0Id; iDaugh <= child1Id; iDaugh++) { + const auto& daugh = tracks->at(iDaugh); + if (daugh.getMotherTrackId() != trackId) { + nDaughtersWithWrongMother++; + std::cerr << "Daughter does not point back to charmonium:" + << " daughter=" << iDaugh + << " daughterPdg=" << daugh.GetPdgCode() + << " mother=" << daugh.getMotherTrackId() + << " expectedMother=" << trackId + << " expectedMotherPdg=" << pdg << '\n'; + } + if (daugh.GetPdgCode() == 22) { + if (pdg == checkPdgSignal[0]) { + nPhotonsFromJPsi++; + } else if (pdg == checkPdgSignal[1]) { + nPhotonsFromPsi++; + } + } + if (TMath::Abs(daugh.GetPdgCode()) == 211) nPions++; + } + auto child0 = o2::mcutils::MCTrackNavigator::getDaughter0(track, *tracks); + auto child1 = o2::mcutils::MCTrackNavigator::getDaughter1(track, *tracks); + if (child0 != nullptr && child1 != nullptr) { + // check for parent-child relations + auto pdg0 = child0->GetPdgCode(); + auto pdg1 = child1->GetPdgCode(); + // std::cout << "First and last children of parent " << checkPdgSignal << " are PDG0: " << pdg0 << " PDG1: " << pdg1 << "\n"; + if (pdg == checkPdgSignal[0]) std::cout << "First and last children of parent Jpsi are PDG0: " << pdg0 << " PDG1: " << pdg1 << "\n"; + if (pdg == checkPdgSignal[1]) std::cout << "First and last children of parent Psi(2S) are PDG0: " << pdg0 << " PDG1: " << pdg1 << "\n"; + if (std::abs(pdg0) == checkPdgDecay && std::abs(pdg1) == checkPdgDecay && pdg0 == -pdg1) { + nLeptonPairs++; + if (child0->getToBeDone() && child1->getToBeDone()) { + nLeptonPairsToBeDone++; + } + } + } + } + } + } + + std::cout << "#events: " << nEvents << "\n" + << "#leptons: " << nLeptons << "\n" + << "#antileptons: " << nAntileptons << "\n" + << "#primary leptons: " << nPrimaryLepton << "\n" + << "#primary antileptons: " << nPrimaryAntilepton << "\n" + << "#signal (prompt Jpsi): " << nSignalJpsi << "; within acceptance " << rapiditymin << " < y < " << rapiditymax << " : " << nSignalJpsiWithinAcc << "\n" + << "#signal (prompt Psi(2S)): " << nSignalPsi2S << "; within acceptance " << rapiditymin << " < y < " << rapiditymax << " : " << nSignalPsi2SWithinAcc << "\n" + << "#photons from Jpsi: " << nPhotonsFromJPsi <<" #photons from Psi(2S): "<< nPhotonsFromPsi<<"\n" + << "#pions from Psi(2S): "<< nPions<<"\n" + << "#daughters with wrong mother: " << nDaughtersWithWrongMother << "\n" + << "#lepton pairs: " << nLeptonPairs <<" #lepton pairs to be done: "<< nLeptonPairsToBeDone<<"\n"; + + if (nDaughtersWithWrongMother != 0) { + std::cerr << "Some charmonium daughters do not point back to their expected mother.\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGDQ/ini/tests/Generator_InjectedInclusiveJpsiMidy_Pythia8_TriggerGap.C b/MC/config/PWGDQ/ini/tests/Generator_InjectedInclusiveJpsiMidy_Pythia8_TriggerGap.C new file mode 100644 index 000000000..45dafa928 --- /dev/null +++ b/MC/config/PWGDQ/ini/tests/Generator_InjectedInclusiveJpsiMidy_Pythia8_TriggerGap.C @@ -0,0 +1,102 @@ +int External() +{ + int checkPdgSignal[] = {443, 100443}; + int checkPdgDecay = 11; + double rapiditymin = -4.3; + double rapiditymax = -2.3; + std::string path{"o2sim_Kine.root"}; + std::cout << "Check for\nsignal PDG " << checkPdgSignal << "\ndecay PDG " << checkPdgDecay << "\n"; + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) + { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int nLeptons{}; + int nAntileptons{}; + int nLeptonPairs{}; + int nLeptonPairsToBeDone{}; + int nSignalJpsi{}; + int nSignalPsi2S{}; + int nSignalJpsiWithinAcc{}; + int nSignalPsi2SWithinAcc{}; + auto nEvents = tree->GetEntries(); + o2::steer::MCKinematicsReader mcreader("o2sim", o2::steer::MCKinematicsReader::Mode::kMCKine); + Bool_t isInjected = kFALSE; + + for (int i = 0; i < nEvents; i++) + { + tree->GetEntry(i); + for (auto &track : *tracks) + { + auto pdg = track.GetPdgCode(); + auto rapidity = track.GetRapidity(); + auto idMoth = track.getMotherTrackId(); + if (pdg == checkPdgDecay) + { + // count leptons + nLeptons++; + } + else if (pdg == -checkPdgDecay) + { + // count anti-leptons + nAntileptons++; + } + else if (pdg == checkPdgSignal[0] || pdg == checkPdgSignal[1]) + { + if (idMoth < 0) + { + // count signal PDG + pdg == checkPdgSignal[0] ? nSignalJpsi++ : nSignalPsi2S++; + // count signal PDG within acceptance + if (rapidity > rapiditymin && rapidity < rapiditymax) + { + pdg == checkPdgSignal[0] ? nSignalJpsiWithinAcc++ : nSignalPsi2SWithinAcc++; + } + } + auto child0 = o2::mcutils::MCTrackNavigator::getDaughter0(track, *tracks); + auto child1 = o2::mcutils::MCTrackNavigator::getDaughter1(track, *tracks); + if (child0 != nullptr && child1 != nullptr) + { + // check for parent-child relations + auto pdg0 = child0->GetPdgCode(); + auto pdg1 = child1->GetPdgCode(); + std::cout << "First and last children of parent " << checkPdgSignal << " are PDG0: " << pdg0 << " PDG1: " << pdg1 << "\n"; + if (std::abs(pdg0) == checkPdgDecay && std::abs(pdg1) == checkPdgDecay && pdg0 == -pdg1) + { + nLeptonPairs++; + if (child0->getToBeDone() && child1->getToBeDone()) + { + nLeptonPairsToBeDone++; + } + } + } + } + } + } + std::cout << "#events: " << nEvents << "\n" + << "#leptons: " << nLeptons << "\n" + << "#antileptons: " << nAntileptons << "\n" + << "#signal (prompt Jpsi): " << nSignalJpsi << "; within acceptance " << rapiditymin << " < y < " << rapiditymax << " : " << nSignalJpsiWithinAcc << "\n" + << "#signal (prompt Psi(2S)): " << nSignalPsi2S << "; within acceptance " << rapiditymin << " < y < " << rapiditymax << " : " << nSignalPsi2SWithinAcc << "\n" + << "#lepton pairs: " << nLeptonPairs << "\n" + << "#lepton pairs to be done: " << nLeptonPairs << "\n"; + + if (nLeptonPairs == 0 || nLeptons == 0 || nAntileptons == 0) + { + std::cerr << "Number of leptons, number of anti-leptons as well as number of lepton pairs should all be greater than 1.\n"; + return 1; + } + if (nLeptonPairs != nLeptonPairsToBeDone) + { + std::cerr << "The number of lepton pairs should be the same as the number of lepton pairs which should be transported.\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGDQ/ini/tests/Generator_InjectedInclusiveJpsiPsi2SMidy_2026_Pythia8_TriggerGap.C b/MC/config/PWGDQ/ini/tests/Generator_InjectedInclusiveJpsiPsi2SMidy_2026_Pythia8_TriggerGap.C new file mode 100644 index 000000000..45dafa928 --- /dev/null +++ b/MC/config/PWGDQ/ini/tests/Generator_InjectedInclusiveJpsiPsi2SMidy_2026_Pythia8_TriggerGap.C @@ -0,0 +1,102 @@ +int External() +{ + int checkPdgSignal[] = {443, 100443}; + int checkPdgDecay = 11; + double rapiditymin = -4.3; + double rapiditymax = -2.3; + std::string path{"o2sim_Kine.root"}; + std::cout << "Check for\nsignal PDG " << checkPdgSignal << "\ndecay PDG " << checkPdgDecay << "\n"; + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) + { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int nLeptons{}; + int nAntileptons{}; + int nLeptonPairs{}; + int nLeptonPairsToBeDone{}; + int nSignalJpsi{}; + int nSignalPsi2S{}; + int nSignalJpsiWithinAcc{}; + int nSignalPsi2SWithinAcc{}; + auto nEvents = tree->GetEntries(); + o2::steer::MCKinematicsReader mcreader("o2sim", o2::steer::MCKinematicsReader::Mode::kMCKine); + Bool_t isInjected = kFALSE; + + for (int i = 0; i < nEvents; i++) + { + tree->GetEntry(i); + for (auto &track : *tracks) + { + auto pdg = track.GetPdgCode(); + auto rapidity = track.GetRapidity(); + auto idMoth = track.getMotherTrackId(); + if (pdg == checkPdgDecay) + { + // count leptons + nLeptons++; + } + else if (pdg == -checkPdgDecay) + { + // count anti-leptons + nAntileptons++; + } + else if (pdg == checkPdgSignal[0] || pdg == checkPdgSignal[1]) + { + if (idMoth < 0) + { + // count signal PDG + pdg == checkPdgSignal[0] ? nSignalJpsi++ : nSignalPsi2S++; + // count signal PDG within acceptance + if (rapidity > rapiditymin && rapidity < rapiditymax) + { + pdg == checkPdgSignal[0] ? nSignalJpsiWithinAcc++ : nSignalPsi2SWithinAcc++; + } + } + auto child0 = o2::mcutils::MCTrackNavigator::getDaughter0(track, *tracks); + auto child1 = o2::mcutils::MCTrackNavigator::getDaughter1(track, *tracks); + if (child0 != nullptr && child1 != nullptr) + { + // check for parent-child relations + auto pdg0 = child0->GetPdgCode(); + auto pdg1 = child1->GetPdgCode(); + std::cout << "First and last children of parent " << checkPdgSignal << " are PDG0: " << pdg0 << " PDG1: " << pdg1 << "\n"; + if (std::abs(pdg0) == checkPdgDecay && std::abs(pdg1) == checkPdgDecay && pdg0 == -pdg1) + { + nLeptonPairs++; + if (child0->getToBeDone() && child1->getToBeDone()) + { + nLeptonPairsToBeDone++; + } + } + } + } + } + } + std::cout << "#events: " << nEvents << "\n" + << "#leptons: " << nLeptons << "\n" + << "#antileptons: " << nAntileptons << "\n" + << "#signal (prompt Jpsi): " << nSignalJpsi << "; within acceptance " << rapiditymin << " < y < " << rapiditymax << " : " << nSignalJpsiWithinAcc << "\n" + << "#signal (prompt Psi(2S)): " << nSignalPsi2S << "; within acceptance " << rapiditymin << " < y < " << rapiditymax << " : " << nSignalPsi2SWithinAcc << "\n" + << "#lepton pairs: " << nLeptonPairs << "\n" + << "#lepton pairs to be done: " << nLeptonPairs << "\n"; + + if (nLeptonPairs == 0 || nLeptons == 0 || nAntileptons == 0) + { + std::cerr << "Number of leptons, number of anti-leptons as well as number of lepton pairs should all be greater than 1.\n"; + return 1; + } + if (nLeptonPairs != nLeptonPairsToBeDone) + { + std::cerr << "The number of lepton pairs should be the same as the number of lepton pairs which should be transported.\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGDQ/ini/tests/Generator_InjectedInclusiveJpsiPsi2SMidy_Pythia8_TriggerGap.C b/MC/config/PWGDQ/ini/tests/Generator_InjectedInclusiveJpsiPsi2SMidy_Pythia8_TriggerGap.C new file mode 100644 index 000000000..80ae5d71f --- /dev/null +++ b/MC/config/PWGDQ/ini/tests/Generator_InjectedInclusiveJpsiPsi2SMidy_Pythia8_TriggerGap.C @@ -0,0 +1,72 @@ +int External() +{ + int checkPdgSignal = 443; + int checkPdgDecay = 11; + std::string path{"o2sim_Kine.root"}; + std::cout << "Check for\nsignal PDG " << checkPdgSignal << "\ndecay PDG " << checkPdgDecay << "\n"; + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree*)file.Get("o2sim"); + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int nLeptons{}; + int nAntileptons{}; + int nLeptonPairs{}; + int nLeptonPairsToBeDone{}; + int nSignal{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + for (auto& track : *tracks) { + auto pdg = track.GetPdgCode(); + if (pdg == checkPdgDecay) { + // count leptons + nLeptons++; + } else if(pdg == -checkPdgDecay) { + // count anti-leptons + nAntileptons++; + } else if (pdg == checkPdgSignal) { + // count signal PDG + nSignal++; + auto child0 = o2::mcutils::MCTrackNavigator::getDaughter0(track, *tracks); + auto child1 = o2::mcutils::MCTrackNavigator::getDaughter1(track, *tracks); + if (child0 != nullptr && child1 != nullptr) { + // check for parent-child relations + auto pdg0 = child0->GetPdgCode(); + auto pdg1 = child1->GetPdgCode(); + std::cout << "First and last children of parent " << checkPdgSignal << " are PDG0: " << pdg0 << " PDG1: " << pdg1 << "\n"; + if (std::abs(pdg0) == checkPdgDecay && std::abs(pdg1) == checkPdgDecay && pdg0 == -pdg1) { + nLeptonPairs++; + if (child0->getToBeDone() && child1->getToBeDone()) { + nLeptonPairsToBeDone++; + } + } + } + } + } + } + std::cout << "#events: " << nEvents << "\n" + << "#leptons: " << nLeptons << "\n" + << "#antileptons: " << nAntileptons << "\n" + << "#signal: " << nSignal << "\n" + << "#lepton pairs: " << nLeptonPairs << "\n" + << "#lepton pairs to be done: " << nLeptonPairs << "\n"; + + + if (nLeptonPairs == 0 || nLeptons == 0 || nAntileptons == 0) { + std::cerr << "Number of leptons, number of anti-leptons as well as number of lepton pairs should all be greater than 1.\n"; + return 1; + } + if (nLeptonPairs != nLeptonPairsToBeDone) { + std::cerr << "The number of lepton pairs should be the same as the number of lepton pairs which should be transported.\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGDQ/ini/tests/Generator_InjectedInclusiveJpsiPsi2SMidy_Pythia8_TriggerGap_pO96TeV.C b/MC/config/PWGDQ/ini/tests/Generator_InjectedInclusiveJpsiPsi2SMidy_Pythia8_TriggerGap_pO96TeV.C new file mode 100644 index 000000000..b155ec9ac --- /dev/null +++ b/MC/config/PWGDQ/ini/tests/Generator_InjectedInclusiveJpsiPsi2SMidy_Pythia8_TriggerGap_pO96TeV.C @@ -0,0 +1,84 @@ +int External() +{ + int checkPdgSignal[] = {443,100443}; + int checkPdgDecay = 11; + std::string path{"o2sim_Kine.root"}; + std::cout << "Check for\nsignal PDG " << checkPdgSignal << "\ndecay PDG " << checkPdgDecay << "\n"; + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree*)file.Get("o2sim"); + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int nLeptons{}; + int nAntileptons{}; + int nLeptonPairs{}; + int nLeptonPairsToBeDone{}; + int nSignalJpsi{}; + int nSignalPsi2S{}; + int nSignalJpsiWithinAcc{}; + int nSignalPsi2SWithinAcc{}; + auto nEvents = tree->GetEntries(); + o2::steer::MCKinematicsReader mcreader("o2sim", o2::steer::MCKinematicsReader::Mode::kMCKine); + Bool_t isInjected = kFALSE; + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + for (auto& track : *tracks) { + auto pdg = track.GetPdgCode(); + auto rapidity = track.GetRapidity(); + auto idMoth = track.getMotherTrackId(); + if (pdg == checkPdgDecay) { + // count leptons + nLeptons++; + } else if(pdg == -checkPdgDecay) { + // count anti-leptons + nAntileptons++; + } else if (pdg == checkPdgSignal[0] || pdg == checkPdgSignal[1]) { + if(idMoth < 0){ + // count signal PDG + pdg == checkPdgSignal[0] ? nSignalJpsi++ : nSignalPsi2S++; + // count signal PDG within acceptance + if(std::abs(rapidity) < 1.0) { pdg == checkPdgSignal[0] ? nSignalJpsiWithinAcc++ : nSignalPsi2SWithinAcc++;} + } + auto child0 = o2::mcutils::MCTrackNavigator::getDaughter0(track, *tracks); + auto child1 = o2::mcutils::MCTrackNavigator::getDaughter1(track, *tracks); + if (child0 != nullptr && child1 != nullptr) { + // check for parent-child relations + auto pdg0 = child0->GetPdgCode(); + auto pdg1 = child1->GetPdgCode(); + std::cout << "First and last children of parent " << checkPdgSignal << " are PDG0: " << pdg0 << " PDG1: " << pdg1 << "\n"; + if (std::abs(pdg0) == checkPdgDecay && std::abs(pdg1) == checkPdgDecay && pdg0 == -pdg1) { + nLeptonPairs++; + if (child0->getToBeDone() && child1->getToBeDone()) { + nLeptonPairsToBeDone++; + } + } + } + } + } + } + std::cout << "#events: " << nEvents << "\n" + << "#leptons: " << nLeptons << "\n" + << "#antileptons: " << nAntileptons << "\n" + << "#signal (prompt Jpsi): " << nSignalJpsi << "; within acceptance (|y| < 1): " << nSignalJpsiWithinAcc << "\n" + << "#signal (prompt Psi(2S)): " << nSignalPsi2S << "; within acceptance (|y| < 1): " << nSignalPsi2SWithinAcc << "\n" + << "#lepton pairs: " << nLeptonPairs << "\n" + << "#lepton pairs to be done: " << nLeptonPairs << "\n"; + + + if (nLeptonPairs == 0 || nLeptons == 0 || nAntileptons == 0) { + std::cerr << "Number of leptons, number of anti-leptons as well as number of lepton pairs should all be greater than 1.\n"; + return 1; + } + if (nLeptonPairs != nLeptonPairsToBeDone) { + std::cerr << "The number of lepton pairs should be the same as the number of lepton pairs which should be transported.\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGDQ/ini/tests/Generator_InjectedIncoherentCharmoniaFwdy_PbPb5TeV.C b/MC/config/PWGDQ/ini/tests/Generator_InjectedIncoherentCharmoniaFwdy_PbPb5TeV.C new file mode 100644 index 000000000..d958f5d90 --- /dev/null +++ b/MC/config/PWGDQ/ini/tests/Generator_InjectedIncoherentCharmoniaFwdy_PbPb5TeV.C @@ -0,0 +1,131 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) + { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int checkPdgSignal[2] = {443, 100443}; + int checkPdgDecay = 13; + double rapiditymin = -4.5; double rapiditymax = -2.0; + + int nLeptons{}; + int nAntileptons{}; + int nLeptonPairs{}; + int nPrimaryLepton{}; + int nPrimaryAntilepton{}; + int nLeptonPairsToBeDone{}; + int nSignalJpsi{}; + int nSignalPsi2S{}; + int nPions{}; + int nPhotonsFromPsi{}; + int nPhotonsFromJPsi{}; + int nSignalJpsiWithinAcc{}; + int nSignalPsi2SWithinAcc{}; + int nDaughtersWithWrongMother{}; + + auto nEvents = tree->GetEntries(); + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + for (auto& track : *tracks) { + int trackId = static_cast(&track - tracks->data()); + auto pdg = track.GetPdgCode(); + auto rapidity = track.GetRapidity(); + auto idMoth = track.getMotherTrackId(); + if (pdg == checkPdgDecay) { + // count leptons + nLeptons++; + if (idMoth == -1) nPrimaryLepton++; + } else if (pdg == -checkPdgDecay) { + // count anti-leptons + nAntileptons++; + if (idMoth == -1) nPrimaryAntilepton++; + } else if (pdg == checkPdgSignal[0] || pdg == checkPdgSignal[1]) { + if (idMoth < 0) { + // count signal PDG + pdg == checkPdgSignal[0] ? nSignalJpsi++ : nSignalPsi2S++; + // count signal PDG within acceptance + if (rapidity > rapiditymin && rapidity < rapiditymax) { + pdg == checkPdgSignal[0] ? nSignalJpsiWithinAcc++ : nSignalPsi2SWithinAcc++; + } + } + int child0Id = track.getFirstDaughterTrackId(); + int child1Id = track.getLastDaughterTrackId(); + if (child0Id < 0 || child1Id < child0Id || + child1Id >= static_cast(tracks->size())) { + std::cout << "Skip charmonium without valid daughters:" + << " pdg=" << pdg + << " mother=" << idMoth + << " firstDaughter=" << child0Id + << " lastDaughter=" << child1Id + << " y=" << rapidity << '\n'; + continue; + } + for (int iDaugh = child0Id; iDaugh <= child1Id; iDaugh++) { + const auto& daugh = tracks->at(iDaugh); + if (daugh.getMotherTrackId() != trackId) { + nDaughtersWithWrongMother++; + std::cerr << "Daughter does not point back to charmonium:" + << " daughter=" << iDaugh + << " daughterPdg=" << daugh.GetPdgCode() + << " mother=" << daugh.getMotherTrackId() + << " expectedMother=" << trackId + << " expectedMotherPdg=" << pdg << '\n'; + } + if (daugh.GetPdgCode() == 22) { + if (pdg == checkPdgSignal[0]) { + nPhotonsFromJPsi++; + } else if (pdg == checkPdgSignal[1]) { + nPhotonsFromPsi++; + } + } + if (TMath::Abs(daugh.GetPdgCode()) == 211) nPions++; + } + auto child0 = o2::mcutils::MCTrackNavigator::getDaughter0(track, *tracks); + auto child1 = o2::mcutils::MCTrackNavigator::getDaughter1(track, *tracks); + if (child0 != nullptr && child1 != nullptr) { + // check for parent-child relations + auto pdg0 = child0->GetPdgCode(); + auto pdg1 = child1->GetPdgCode(); + // std::cout << "First and last children of parent " << checkPdgSignal << " are PDG0: " << pdg0 << " PDG1: " << pdg1 << "\n"; + if (pdg == checkPdgSignal[0]) std::cout << "First and last children of parent Jpsi are PDG0: " << pdg0 << " PDG1: " << pdg1 << "\n"; + if (pdg == checkPdgSignal[1]) std::cout << "First and last children of parent Psi(2S) are PDG0: " << pdg0 << " PDG1: " << pdg1 << "\n"; + if (std::abs(pdg0) == checkPdgDecay && std::abs(pdg1) == checkPdgDecay && pdg0 == -pdg1) { + nLeptonPairs++; + if (child0->getToBeDone() && child1->getToBeDone()) { + nLeptonPairsToBeDone++; + } + } + } + } + } + } + + std::cout << "#events: " << nEvents << "\n" + << "#leptons: " << nLeptons << "\n" + << "#antileptons: " << nAntileptons << "\n" + << "#primary leptons: " << nPrimaryLepton << "\n" + << "#primary antileptons: " << nPrimaryAntilepton << "\n" + << "#signal (prompt Jpsi): " << nSignalJpsi << "; within acceptance " << rapiditymin << " < y < " << rapiditymax << " : " << nSignalJpsiWithinAcc << "\n" + << "#signal (prompt Psi(2S)): " << nSignalPsi2S << "; within acceptance " << rapiditymin << " < y < " << rapiditymax << " : " << nSignalPsi2SWithinAcc << "\n" + << "#photons from Jpsi: " << nPhotonsFromJPsi <<" #photons from Psi(2S): "<< nPhotonsFromPsi<<"\n" + << "#pions from Psi(2S): "<< nPions<<"\n" + << "#daughters with wrong mother: " << nDaughtersWithWrongMother << "\n" + << "#lepton pairs: " << nLeptonPairs <<" #lepton pairs to be done: "<< nLeptonPairsToBeDone<<"\n"; + + if (nDaughtersWithWrongMother != 0) { + std::cerr << "Some charmonium daughters do not point back to their expected mother.\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGDQ/ini/tests/Generator_InjectedIncoherentCharmoniaMidy_PbPb5TeV.C b/MC/config/PWGDQ/ini/tests/Generator_InjectedIncoherentCharmoniaMidy_PbPb5TeV.C new file mode 100644 index 000000000..760f7827a --- /dev/null +++ b/MC/config/PWGDQ/ini/tests/Generator_InjectedIncoherentCharmoniaMidy_PbPb5TeV.C @@ -0,0 +1,131 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) + { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int checkPdgSignal[2] = {443, 100443}; + int checkPdgDecay = 11; + double rapiditymin = -1.5; double rapiditymax = 1.5; + + int nLeptons{}; + int nAntileptons{}; + int nLeptonPairs{}; + int nPrimaryLepton{}; + int nPrimaryAntilepton{}; + int nLeptonPairsToBeDone{}; + int nSignalJpsi{}; + int nSignalPsi2S{}; + int nPions{}; + int nPhotonsFromPsi{}; + int nPhotonsFromJPsi{}; + int nSignalJpsiWithinAcc{}; + int nSignalPsi2SWithinAcc{}; + int nDaughtersWithWrongMother{}; + + auto nEvents = tree->GetEntries(); + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + for (auto& track : *tracks) { + int trackId = static_cast(&track - tracks->data()); + auto pdg = track.GetPdgCode(); + auto rapidity = track.GetRapidity(); + auto idMoth = track.getMotherTrackId(); + if (pdg == checkPdgDecay) { + // count leptons + nLeptons++; + if (idMoth == -1) nPrimaryLepton++; + } else if (pdg == -checkPdgDecay) { + // count anti-leptons + nAntileptons++; + if (idMoth == -1) nPrimaryAntilepton++; + } else if (pdg == checkPdgSignal[0] || pdg == checkPdgSignal[1]) { + if (idMoth < 0) { + // count signal PDG + pdg == checkPdgSignal[0] ? nSignalJpsi++ : nSignalPsi2S++; + // count signal PDG within acceptance + if (rapidity > rapiditymin && rapidity < rapiditymax) { + pdg == checkPdgSignal[0] ? nSignalJpsiWithinAcc++ : nSignalPsi2SWithinAcc++; + } + } + int child0Id = track.getFirstDaughterTrackId(); + int child1Id = track.getLastDaughterTrackId(); + if (child0Id < 0 || child1Id < child0Id || + child1Id >= static_cast(tracks->size())) { + std::cout << "Skip charmonium without valid daughters:" + << " pdg=" << pdg + << " mother=" << idMoth + << " firstDaughter=" << child0Id + << " lastDaughter=" << child1Id + << " y=" << rapidity << '\n'; + continue; + } + for (int iDaugh = child0Id; iDaugh <= child1Id; iDaugh++) { + const auto& daugh = tracks->at(iDaugh); + if (daugh.getMotherTrackId() != trackId) { + nDaughtersWithWrongMother++; + std::cerr << "Daughter does not point back to charmonium:" + << " daughter=" << iDaugh + << " daughterPdg=" << daugh.GetPdgCode() + << " mother=" << daugh.getMotherTrackId() + << " expectedMother=" << trackId + << " expectedMotherPdg=" << pdg << '\n'; + } + if (daugh.GetPdgCode() == 22) { + if (pdg == checkPdgSignal[0]) { + nPhotonsFromJPsi++; + } else if (pdg == checkPdgSignal[1]) { + nPhotonsFromPsi++; + } + } + if (TMath::Abs(daugh.GetPdgCode()) == 211) nPions++; + } + auto child0 = o2::mcutils::MCTrackNavigator::getDaughter0(track, *tracks); + auto child1 = o2::mcutils::MCTrackNavigator::getDaughter1(track, *tracks); + if (child0 != nullptr && child1 != nullptr) { + // check for parent-child relations + auto pdg0 = child0->GetPdgCode(); + auto pdg1 = child1->GetPdgCode(); + // std::cout << "First and last children of parent " << checkPdgSignal << " are PDG0: " << pdg0 << " PDG1: " << pdg1 << "\n"; + if (pdg == checkPdgSignal[0]) std::cout << "First and last children of parent Jpsi are PDG0: " << pdg0 << " PDG1: " << pdg1 << "\n"; + if (pdg == checkPdgSignal[1]) std::cout << "First and last children of parent Psi(2S) are PDG0: " << pdg0 << " PDG1: " << pdg1 << "\n"; + if (std::abs(pdg0) == checkPdgDecay && std::abs(pdg1) == checkPdgDecay && pdg0 == -pdg1) { + nLeptonPairs++; + if (child0->getToBeDone() && child1->getToBeDone()) { + nLeptonPairsToBeDone++; + } + } + } + } + } + } + + std::cout << "#events: " << nEvents << "\n" + << "#leptons: " << nLeptons << "\n" + << "#antileptons: " << nAntileptons << "\n" + << "#primary leptons: " << nPrimaryLepton << "\n" + << "#primary antileptons: " << nPrimaryAntilepton << "\n" + << "#signal (prompt Jpsi): " << nSignalJpsi << "; within acceptance " << rapiditymin << " < y < " << rapiditymax << " : " << nSignalJpsiWithinAcc << "\n" + << "#signal (prompt Psi(2S)): " << nSignalPsi2S << "; within acceptance " << rapiditymin << " < y < " << rapiditymax << " : " << nSignalPsi2SWithinAcc << "\n" + << "#photons from Jpsi: " << nPhotonsFromJPsi <<" #photons from Psi(2S): "<< nPhotonsFromPsi<<"\n" + << "#pions from Psi(2S): "<< nPions<<"\n" + << "#daughters with wrong mother: " << nDaughtersWithWrongMother << "\n" + << "#lepton pairs: " << nLeptonPairs <<" #lepton pairs to be done: "<< nLeptonPairsToBeDone<<"\n"; + + if (nDaughtersWithWrongMother != 0) { + std::cerr << "Some charmonium daughters do not point back to their expected mother.\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGDQ/ini/tests/Generator_InjectedPhotoproucedCharmoniaMidy_PbPb5TeV.C b/MC/config/PWGDQ/ini/tests/Generator_InjectedPhotoproucedCharmoniaMidy_PbPb5TeV.C new file mode 100644 index 000000000..fe55e024a --- /dev/null +++ b/MC/config/PWGDQ/ini/tests/Generator_InjectedPhotoproucedCharmoniaMidy_PbPb5TeV.C @@ -0,0 +1,104 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) + { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int checkPdgSignal[2] = {443, 100443}; + int checkPdgDecay = 11; + double rapiditymin = -1.5; double rapiditymax = 1.5; + + int nLeptons{}; + int nAntileptons{}; + int nLeptonPairs{}; + int nPrimaryLepton{}; + int nPrimaryAntilepton{}; + int nLeptonPairsToBeDone{}; + int nSignalJpsi{}; + int nSignalPsi2S{}; + int nPions{}; + int nPhotonsFromPsi{}; + int nPhotonsFromJPsi{}; + int nSignalJpsiWithinAcc{}; + int nSignalPsi2SWithinAcc{}; + + auto nEvents = tree->GetEntries(); + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + for (auto& track : *tracks) { + auto pdg = track.GetPdgCode(); + auto rapidity = track.GetRapidity(); + auto idMoth = track.getMotherTrackId(); + if (pdg == checkPdgDecay) { + // count leptons + nLeptons++; + if (idMoth == -1) nPrimaryLepton++; + } else if (pdg == -checkPdgDecay) { + // count anti-leptons + nAntileptons++; + if (idMoth == -1) nPrimaryAntilepton++; + } else if (pdg == checkPdgSignal[0] || pdg == checkPdgSignal[1]) { + if (idMoth < 0) { + // count signal PDG + pdg == checkPdgSignal[0] ? nSignalJpsi++ : nSignalPsi2S++; + // count signal PDG within acceptance + if (rapidity > rapiditymin && rapidity < rapiditymax) { + pdg == checkPdgSignal[0] ? nSignalJpsiWithinAcc++ : nSignalPsi2SWithinAcc++; + } + } + int child0Id = track.getFirstDaughterTrackId(); + int child1Id = track.getLastDaughterTrackId(); + for (int iDaugh = child0Id; iDaugh <= child1Id; iDaugh++) { + auto daugh = tracks->at(iDaugh); + if (daugh.GetPdgCode() == 22) { + if (pdg == checkPdgSignal[0]) { + nPhotonsFromJPsi++; + } else if (pdg == checkPdgSignal[1]) { + nPhotonsFromPsi++; + } + } + if (TMath::Abs(daugh.GetPdgCode()) == 211) nPions++; + } + auto child0 = o2::mcutils::MCTrackNavigator::getDaughter0(track, *tracks); + auto child1 = o2::mcutils::MCTrackNavigator::getDaughter1(track, *tracks); + if (child0 != nullptr && child1 != nullptr) { + // check for parent-child relations + auto pdg0 = child0->GetPdgCode(); + auto pdg1 = child1->GetPdgCode(); + // std::cout << "First and last children of parent " << checkPdgSignal << " are PDG0: " << pdg0 << " PDG1: " << pdg1 << "\n"; + if (pdg == checkPdgSignal[0]) std::cout << "First and last children of parent Jpsi are PDG0: " << pdg0 << " PDG1: " << pdg1 << "\n"; + if (pdg == checkPdgSignal[1]) std::cout << "First and last children of parent Psi(2S) are PDG0: " << pdg0 << " PDG1: " << pdg1 << "\n"; + if (std::abs(pdg0) == checkPdgDecay && std::abs(pdg1) == checkPdgDecay && pdg0 == -pdg1) { + nLeptonPairs++; + if (child0->getToBeDone() && child1->getToBeDone()) { + nLeptonPairsToBeDone++; + } + } + } + } + } + } + + std::cout << "#events: " << nEvents << "\n" + << "#leptons: " << nLeptons << "\n" + << "#antileptons: " << nAntileptons << "\n" + << "#primary leptons: " << nPrimaryLepton << "\n" + << "#primary antileptons: " << nPrimaryAntilepton << "\n" + << "#signal (prompt Jpsi): " << nSignalJpsi << "; within acceptance " << rapiditymin << " < y < " << rapiditymax << " : " << nSignalJpsiWithinAcc << "\n" + << "#signal (prompt Psi(2S)): " << nSignalPsi2S << "; within acceptance " << rapiditymin << " < y < " << rapiditymax << " : " << nSignalPsi2SWithinAcc << "\n" + << "#photons from Jpsi: " << nPhotonsFromJPsi <<" #photons from Psi(2S): "<< nPhotonsFromPsi<<"\n" + << "#pions from Psi(2S): "<< nPions<<"\n" + << "#lepton pairs: " << nLeptonPairs <<" #lepton pairs to be done: "<< nLeptonPairsToBeDone<<"\n"; + + return 0; +} \ No newline at end of file diff --git a/MC/config/PWGDQ/ini/tests/Generator_InjectedPromptCharmoniaFwdy_TriggerGap_NeNe5TeV.C b/MC/config/PWGDQ/ini/tests/Generator_InjectedPromptCharmoniaFwdy_TriggerGap_NeNe5TeV.C new file mode 100644 index 000000000..5dd26f49d --- /dev/null +++ b/MC/config/PWGDQ/ini/tests/Generator_InjectedPromptCharmoniaFwdy_TriggerGap_NeNe5TeV.C @@ -0,0 +1,85 @@ +int External() +{ + int checkPdgSignal[] = {443,100443}; + int checkPdgDecay = 13; + double rapiditymin = -4.3; double rapiditymax = -2.3; + std::string path{"o2sim_Kine.root"}; + std::cout << "Check for\nsignal PDG " << checkPdgSignal << "\ndecay PDG " << checkPdgDecay << "\n"; + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree*)file.Get("o2sim"); + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int nLeptons{}; + int nAntileptons{}; + int nLeptonPairs{}; + int nLeptonPairsToBeDone{}; + int nSignalJpsi{}; + int nSignalPsi2S{}; + int nSignalJpsiWithinAcc{}; + int nSignalPsi2SWithinAcc{}; + auto nEvents = tree->GetEntries(); + o2::steer::MCKinematicsReader mcreader("o2sim", o2::steer::MCKinematicsReader::Mode::kMCKine); + Bool_t isInjected = kFALSE; + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + for (auto& track : *tracks) { + auto pdg = track.GetPdgCode(); + auto rapidity = track.GetRapidity(); + auto idMoth = track.getMotherTrackId(); + if (pdg == checkPdgDecay) { + // count leptons + nLeptons++; + } else if(pdg == -checkPdgDecay) { + // count anti-leptons + nAntileptons++; + } else if (pdg == checkPdgSignal[0] || pdg == checkPdgSignal[1]) { + if(idMoth < 0) { + // count signal PDG + pdg == checkPdgSignal[0] ? nSignalJpsi++ : nSignalPsi2S++; + // count signal PDG within acceptance + if(rapidity > rapiditymin && rapidity < rapiditymax) { pdg == checkPdgSignal[0] ? nSignalJpsiWithinAcc++ : nSignalPsi2SWithinAcc++;} + } + auto child0 = o2::mcutils::MCTrackNavigator::getDaughter0(track, *tracks); + auto child1 = o2::mcutils::MCTrackNavigator::getDaughter1(track, *tracks); + if (child0 != nullptr && child1 != nullptr) { + // check for parent-child relations + auto pdg0 = child0->GetPdgCode(); + auto pdg1 = child1->GetPdgCode(); + std::cout << "First and last children of parent " << checkPdgSignal << " are PDG0: " << pdg0 << " PDG1: " << pdg1 << "\n"; + if (std::abs(pdg0) == checkPdgDecay && std::abs(pdg1) == checkPdgDecay && pdg0 == -pdg1) { + nLeptonPairs++; + if (child0->getToBeDone() && child1->getToBeDone()) { + nLeptonPairsToBeDone++; + } + } + } + } + } + } + std::cout << "#events: " << nEvents << "\n" + << "#leptons: " << nLeptons << "\n" + << "#antileptons: " << nAntileptons << "\n" + << "#signal (prompt Jpsi): " << nSignalJpsi << "; within acceptance " << rapiditymin << " < y < " << rapiditymax << " : " << nSignalJpsiWithinAcc << "\n" + << "#signal (prompt Psi(2S)): " << nSignalPsi2S << "; within acceptance " << rapiditymin << " < y < " << rapiditymax << " : " << nSignalPsi2SWithinAcc << "\n" + << "#lepton pairs: " << nLeptonPairs << "\n" + << "#lepton pairs to be done: " << nLeptonPairs << "\n"; + + + if (nLeptonPairs == 0 || nLeptons == 0 || nAntileptons == 0) { + std::cerr << "Number of leptons, number of anti-leptons as well as number of lepton pairs should all be greater than 1.\n"; + return 1; + } + if (nLeptonPairs != nLeptonPairsToBeDone) { + std::cerr << "The number of lepton pairs should be the same as the number of lepton pairs which should be transported.\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGDQ/ini/tests/Generator_InjectedPromptCharmoniaFwdy_TriggerGap_OO5TeV.C b/MC/config/PWGDQ/ini/tests/Generator_InjectedPromptCharmoniaFwdy_TriggerGap_OO5TeV.C new file mode 100644 index 000000000..5dd26f49d --- /dev/null +++ b/MC/config/PWGDQ/ini/tests/Generator_InjectedPromptCharmoniaFwdy_TriggerGap_OO5TeV.C @@ -0,0 +1,85 @@ +int External() +{ + int checkPdgSignal[] = {443,100443}; + int checkPdgDecay = 13; + double rapiditymin = -4.3; double rapiditymax = -2.3; + std::string path{"o2sim_Kine.root"}; + std::cout << "Check for\nsignal PDG " << checkPdgSignal << "\ndecay PDG " << checkPdgDecay << "\n"; + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree*)file.Get("o2sim"); + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int nLeptons{}; + int nAntileptons{}; + int nLeptonPairs{}; + int nLeptonPairsToBeDone{}; + int nSignalJpsi{}; + int nSignalPsi2S{}; + int nSignalJpsiWithinAcc{}; + int nSignalPsi2SWithinAcc{}; + auto nEvents = tree->GetEntries(); + o2::steer::MCKinematicsReader mcreader("o2sim", o2::steer::MCKinematicsReader::Mode::kMCKine); + Bool_t isInjected = kFALSE; + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + for (auto& track : *tracks) { + auto pdg = track.GetPdgCode(); + auto rapidity = track.GetRapidity(); + auto idMoth = track.getMotherTrackId(); + if (pdg == checkPdgDecay) { + // count leptons + nLeptons++; + } else if(pdg == -checkPdgDecay) { + // count anti-leptons + nAntileptons++; + } else if (pdg == checkPdgSignal[0] || pdg == checkPdgSignal[1]) { + if(idMoth < 0) { + // count signal PDG + pdg == checkPdgSignal[0] ? nSignalJpsi++ : nSignalPsi2S++; + // count signal PDG within acceptance + if(rapidity > rapiditymin && rapidity < rapiditymax) { pdg == checkPdgSignal[0] ? nSignalJpsiWithinAcc++ : nSignalPsi2SWithinAcc++;} + } + auto child0 = o2::mcutils::MCTrackNavigator::getDaughter0(track, *tracks); + auto child1 = o2::mcutils::MCTrackNavigator::getDaughter1(track, *tracks); + if (child0 != nullptr && child1 != nullptr) { + // check for parent-child relations + auto pdg0 = child0->GetPdgCode(); + auto pdg1 = child1->GetPdgCode(); + std::cout << "First and last children of parent " << checkPdgSignal << " are PDG0: " << pdg0 << " PDG1: " << pdg1 << "\n"; + if (std::abs(pdg0) == checkPdgDecay && std::abs(pdg1) == checkPdgDecay && pdg0 == -pdg1) { + nLeptonPairs++; + if (child0->getToBeDone() && child1->getToBeDone()) { + nLeptonPairsToBeDone++; + } + } + } + } + } + } + std::cout << "#events: " << nEvents << "\n" + << "#leptons: " << nLeptons << "\n" + << "#antileptons: " << nAntileptons << "\n" + << "#signal (prompt Jpsi): " << nSignalJpsi << "; within acceptance " << rapiditymin << " < y < " << rapiditymax << " : " << nSignalJpsiWithinAcc << "\n" + << "#signal (prompt Psi(2S)): " << nSignalPsi2S << "; within acceptance " << rapiditymin << " < y < " << rapiditymax << " : " << nSignalPsi2SWithinAcc << "\n" + << "#lepton pairs: " << nLeptonPairs << "\n" + << "#lepton pairs to be done: " << nLeptonPairs << "\n"; + + + if (nLeptonPairs == 0 || nLeptons == 0 || nAntileptons == 0) { + std::cerr << "Number of leptons, number of anti-leptons as well as number of lepton pairs should all be greater than 1.\n"; + return 1; + } + if (nLeptonPairs != nLeptonPairsToBeDone) { + std::cerr << "The number of lepton pairs should be the same as the number of lepton pairs which should be transported.\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGDQ/ini/tests/Generator_InjectedPromptCharmoniaFwdy_TriggerGap_PbPb5TeV.C b/MC/config/PWGDQ/ini/tests/Generator_InjectedPromptCharmoniaFwdy_TriggerGap_PbPb5TeV.C new file mode 100644 index 000000000..e88864af3 --- /dev/null +++ b/MC/config/PWGDQ/ini/tests/Generator_InjectedPromptCharmoniaFwdy_TriggerGap_PbPb5TeV.C @@ -0,0 +1,85 @@ +int External() +{ + int checkPdgSignal[] = {443,100443}; + int checkPdgDecay = 13; + double rapiditymin = -4.3; double rapiditymax = -2.3; + std::string path{"o2sim_Kine.root"}; + std::cout << "Check for\nsignal PDG " << checkPdgSignal << "\ndecay PDG " << checkPdgDecay << "\n"; + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree*)file.Get("o2sim"); + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int nLeptons{}; + int nAntileptons{}; + int nLeptonPairs{}; + int nLeptonPairsToBeDone{}; + int nSignalJpsi{}; + int nSignalPsi2S{}; + int nSignalJpsiWithinAcc{}; + int nSignalPsi2SWithinAcc{}; + auto nEvents = tree->GetEntries(); + o2::steer::MCKinematicsReader mcreader("o2sim", o2::steer::MCKinematicsReader::Mode::kMCKine); + Bool_t isInjected = kFALSE; + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + for (auto& track : *tracks) { + auto pdg = track.GetPdgCode(); + auto rapidity = track.GetRapidity(); + auto idMoth = track.getMotherTrackId(); + if (pdg == checkPdgDecay) { + // count leptons + nLeptons++; + } else if(pdg == -checkPdgDecay) { + // count anti-leptons + nAntileptons++; + } else if (pdg == checkPdgSignal[0] || pdg == checkPdgSignal[1]) { + if(idMoth < 0){ + // count signal PDG + pdg == checkPdgSignal[0] ? nSignalJpsi++ : nSignalPsi2S++; + // count signal PDG within acceptance + if(rapidity > rapiditymin && rapidity < rapiditymax) { pdg == checkPdgSignal[0] ? nSignalJpsiWithinAcc++ : nSignalPsi2SWithinAcc++;} + } + auto child0 = o2::mcutils::MCTrackNavigator::getDaughter0(track, *tracks); + auto child1 = o2::mcutils::MCTrackNavigator::getDaughter1(track, *tracks); + if (child0 != nullptr && child1 != nullptr) { + // check for parent-child relations + auto pdg0 = child0->GetPdgCode(); + auto pdg1 = child1->GetPdgCode(); + std::cout << "First and last children of parent " << checkPdgSignal << " are PDG0: " << pdg0 << " PDG1: " << pdg1 << "\n"; + if (std::abs(pdg0) == checkPdgDecay && std::abs(pdg1) == checkPdgDecay && pdg0 == -pdg1) { + nLeptonPairs++; + if (child0->getToBeDone() && child1->getToBeDone()) { + nLeptonPairsToBeDone++; + } + } + } + } + } + } + std::cout << "#events: " << nEvents << "\n" + << "#leptons: " << nLeptons << "\n" + << "#antileptons: " << nAntileptons << "\n" + << "#signal (prompt Jpsi): " << nSignalJpsi << "; within acceptance " << rapiditymin << " < y < " << rapiditymax << " : " << nSignalJpsiWithinAcc << "\n" + << "#signal (prompt Psi(2S)): " << nSignalPsi2S << "; within acceptance " << rapiditymin << " < y < " << rapiditymax << " : " << nSignalPsi2SWithinAcc << "\n" + << "#lepton pairs: " << nLeptonPairs << "\n" + << "#lepton pairs to be done: " << nLeptonPairs << "\n"; + + + if (nLeptonPairs == 0 || nLeptons == 0 || nAntileptons == 0) { + std::cerr << "Number of leptons, number of anti-leptons as well as number of lepton pairs should all be greater than 1.\n"; + return 1; + } + if (nLeptonPairs != nLeptonPairsToBeDone) { + std::cerr << "The number of lepton pairs should be the same as the number of lepton pairs which should be transported.\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGDQ/ini/tests/Generator_InjectedPromptCharmoniaFwdy_TriggerGap_PbPb5TeV_TriggerRatio2.C b/MC/config/PWGDQ/ini/tests/Generator_InjectedPromptCharmoniaFwdy_TriggerGap_PbPb5TeV_TriggerRatio2.C new file mode 100644 index 000000000..e88864af3 --- /dev/null +++ b/MC/config/PWGDQ/ini/tests/Generator_InjectedPromptCharmoniaFwdy_TriggerGap_PbPb5TeV_TriggerRatio2.C @@ -0,0 +1,85 @@ +int External() +{ + int checkPdgSignal[] = {443,100443}; + int checkPdgDecay = 13; + double rapiditymin = -4.3; double rapiditymax = -2.3; + std::string path{"o2sim_Kine.root"}; + std::cout << "Check for\nsignal PDG " << checkPdgSignal << "\ndecay PDG " << checkPdgDecay << "\n"; + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree*)file.Get("o2sim"); + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int nLeptons{}; + int nAntileptons{}; + int nLeptonPairs{}; + int nLeptonPairsToBeDone{}; + int nSignalJpsi{}; + int nSignalPsi2S{}; + int nSignalJpsiWithinAcc{}; + int nSignalPsi2SWithinAcc{}; + auto nEvents = tree->GetEntries(); + o2::steer::MCKinematicsReader mcreader("o2sim", o2::steer::MCKinematicsReader::Mode::kMCKine); + Bool_t isInjected = kFALSE; + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + for (auto& track : *tracks) { + auto pdg = track.GetPdgCode(); + auto rapidity = track.GetRapidity(); + auto idMoth = track.getMotherTrackId(); + if (pdg == checkPdgDecay) { + // count leptons + nLeptons++; + } else if(pdg == -checkPdgDecay) { + // count anti-leptons + nAntileptons++; + } else if (pdg == checkPdgSignal[0] || pdg == checkPdgSignal[1]) { + if(idMoth < 0){ + // count signal PDG + pdg == checkPdgSignal[0] ? nSignalJpsi++ : nSignalPsi2S++; + // count signal PDG within acceptance + if(rapidity > rapiditymin && rapidity < rapiditymax) { pdg == checkPdgSignal[0] ? nSignalJpsiWithinAcc++ : nSignalPsi2SWithinAcc++;} + } + auto child0 = o2::mcutils::MCTrackNavigator::getDaughter0(track, *tracks); + auto child1 = o2::mcutils::MCTrackNavigator::getDaughter1(track, *tracks); + if (child0 != nullptr && child1 != nullptr) { + // check for parent-child relations + auto pdg0 = child0->GetPdgCode(); + auto pdg1 = child1->GetPdgCode(); + std::cout << "First and last children of parent " << checkPdgSignal << " are PDG0: " << pdg0 << " PDG1: " << pdg1 << "\n"; + if (std::abs(pdg0) == checkPdgDecay && std::abs(pdg1) == checkPdgDecay && pdg0 == -pdg1) { + nLeptonPairs++; + if (child0->getToBeDone() && child1->getToBeDone()) { + nLeptonPairsToBeDone++; + } + } + } + } + } + } + std::cout << "#events: " << nEvents << "\n" + << "#leptons: " << nLeptons << "\n" + << "#antileptons: " << nAntileptons << "\n" + << "#signal (prompt Jpsi): " << nSignalJpsi << "; within acceptance " << rapiditymin << " < y < " << rapiditymax << " : " << nSignalJpsiWithinAcc << "\n" + << "#signal (prompt Psi(2S)): " << nSignalPsi2S << "; within acceptance " << rapiditymin << " < y < " << rapiditymax << " : " << nSignalPsi2SWithinAcc << "\n" + << "#lepton pairs: " << nLeptonPairs << "\n" + << "#lepton pairs to be done: " << nLeptonPairs << "\n"; + + + if (nLeptonPairs == 0 || nLeptons == 0 || nAntileptons == 0) { + std::cerr << "Number of leptons, number of anti-leptons as well as number of lepton pairs should all be greater than 1.\n"; + return 1; + } + if (nLeptonPairs != nLeptonPairsToBeDone) { + std::cerr << "The number of lepton pairs should be the same as the number of lepton pairs which should be transported.\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGDQ/ini/tests/Generator_InjectedPromptCharmoniaFwdy_TriggerGap_pO96TeV.C b/MC/config/PWGDQ/ini/tests/Generator_InjectedPromptCharmoniaFwdy_TriggerGap_pO96TeV.C new file mode 100644 index 000000000..5dd26f49d --- /dev/null +++ b/MC/config/PWGDQ/ini/tests/Generator_InjectedPromptCharmoniaFwdy_TriggerGap_pO96TeV.C @@ -0,0 +1,85 @@ +int External() +{ + int checkPdgSignal[] = {443,100443}; + int checkPdgDecay = 13; + double rapiditymin = -4.3; double rapiditymax = -2.3; + std::string path{"o2sim_Kine.root"}; + std::cout << "Check for\nsignal PDG " << checkPdgSignal << "\ndecay PDG " << checkPdgDecay << "\n"; + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree*)file.Get("o2sim"); + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int nLeptons{}; + int nAntileptons{}; + int nLeptonPairs{}; + int nLeptonPairsToBeDone{}; + int nSignalJpsi{}; + int nSignalPsi2S{}; + int nSignalJpsiWithinAcc{}; + int nSignalPsi2SWithinAcc{}; + auto nEvents = tree->GetEntries(); + o2::steer::MCKinematicsReader mcreader("o2sim", o2::steer::MCKinematicsReader::Mode::kMCKine); + Bool_t isInjected = kFALSE; + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + for (auto& track : *tracks) { + auto pdg = track.GetPdgCode(); + auto rapidity = track.GetRapidity(); + auto idMoth = track.getMotherTrackId(); + if (pdg == checkPdgDecay) { + // count leptons + nLeptons++; + } else if(pdg == -checkPdgDecay) { + // count anti-leptons + nAntileptons++; + } else if (pdg == checkPdgSignal[0] || pdg == checkPdgSignal[1]) { + if(idMoth < 0) { + // count signal PDG + pdg == checkPdgSignal[0] ? nSignalJpsi++ : nSignalPsi2S++; + // count signal PDG within acceptance + if(rapidity > rapiditymin && rapidity < rapiditymax) { pdg == checkPdgSignal[0] ? nSignalJpsiWithinAcc++ : nSignalPsi2SWithinAcc++;} + } + auto child0 = o2::mcutils::MCTrackNavigator::getDaughter0(track, *tracks); + auto child1 = o2::mcutils::MCTrackNavigator::getDaughter1(track, *tracks); + if (child0 != nullptr && child1 != nullptr) { + // check for parent-child relations + auto pdg0 = child0->GetPdgCode(); + auto pdg1 = child1->GetPdgCode(); + std::cout << "First and last children of parent " << checkPdgSignal << " are PDG0: " << pdg0 << " PDG1: " << pdg1 << "\n"; + if (std::abs(pdg0) == checkPdgDecay && std::abs(pdg1) == checkPdgDecay && pdg0 == -pdg1) { + nLeptonPairs++; + if (child0->getToBeDone() && child1->getToBeDone()) { + nLeptonPairsToBeDone++; + } + } + } + } + } + } + std::cout << "#events: " << nEvents << "\n" + << "#leptons: " << nLeptons << "\n" + << "#antileptons: " << nAntileptons << "\n" + << "#signal (prompt Jpsi): " << nSignalJpsi << "; within acceptance " << rapiditymin << " < y < " << rapiditymax << " : " << nSignalJpsiWithinAcc << "\n" + << "#signal (prompt Psi(2S)): " << nSignalPsi2S << "; within acceptance " << rapiditymin << " < y < " << rapiditymax << " : " << nSignalPsi2SWithinAcc << "\n" + << "#lepton pairs: " << nLeptonPairs << "\n" + << "#lepton pairs to be done: " << nLeptonPairs << "\n"; + + + if (nLeptonPairs == 0 || nLeptons == 0 || nAntileptons == 0) { + std::cerr << "Number of leptons, number of anti-leptons as well as number of lepton pairs should all be greater than 1.\n"; + return 1; + } + if (nLeptonPairs != nLeptonPairsToBeDone) { + std::cerr << "The number of lepton pairs should be the same as the number of lepton pairs which should be transported.\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGDQ/ini/tests/Generator_InjectedPromptCharmoniaFwdy_TriggerGap_pp5TeV.C b/MC/config/PWGDQ/ini/tests/Generator_InjectedPromptCharmoniaFwdy_TriggerGap_pp5TeV.C new file mode 100644 index 000000000..5dd26f49d --- /dev/null +++ b/MC/config/PWGDQ/ini/tests/Generator_InjectedPromptCharmoniaFwdy_TriggerGap_pp5TeV.C @@ -0,0 +1,85 @@ +int External() +{ + int checkPdgSignal[] = {443,100443}; + int checkPdgDecay = 13; + double rapiditymin = -4.3; double rapiditymax = -2.3; + std::string path{"o2sim_Kine.root"}; + std::cout << "Check for\nsignal PDG " << checkPdgSignal << "\ndecay PDG " << checkPdgDecay << "\n"; + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree*)file.Get("o2sim"); + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int nLeptons{}; + int nAntileptons{}; + int nLeptonPairs{}; + int nLeptonPairsToBeDone{}; + int nSignalJpsi{}; + int nSignalPsi2S{}; + int nSignalJpsiWithinAcc{}; + int nSignalPsi2SWithinAcc{}; + auto nEvents = tree->GetEntries(); + o2::steer::MCKinematicsReader mcreader("o2sim", o2::steer::MCKinematicsReader::Mode::kMCKine); + Bool_t isInjected = kFALSE; + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + for (auto& track : *tracks) { + auto pdg = track.GetPdgCode(); + auto rapidity = track.GetRapidity(); + auto idMoth = track.getMotherTrackId(); + if (pdg == checkPdgDecay) { + // count leptons + nLeptons++; + } else if(pdg == -checkPdgDecay) { + // count anti-leptons + nAntileptons++; + } else if (pdg == checkPdgSignal[0] || pdg == checkPdgSignal[1]) { + if(idMoth < 0) { + // count signal PDG + pdg == checkPdgSignal[0] ? nSignalJpsi++ : nSignalPsi2S++; + // count signal PDG within acceptance + if(rapidity > rapiditymin && rapidity < rapiditymax) { pdg == checkPdgSignal[0] ? nSignalJpsiWithinAcc++ : nSignalPsi2SWithinAcc++;} + } + auto child0 = o2::mcutils::MCTrackNavigator::getDaughter0(track, *tracks); + auto child1 = o2::mcutils::MCTrackNavigator::getDaughter1(track, *tracks); + if (child0 != nullptr && child1 != nullptr) { + // check for parent-child relations + auto pdg0 = child0->GetPdgCode(); + auto pdg1 = child1->GetPdgCode(); + std::cout << "First and last children of parent " << checkPdgSignal << " are PDG0: " << pdg0 << " PDG1: " << pdg1 << "\n"; + if (std::abs(pdg0) == checkPdgDecay && std::abs(pdg1) == checkPdgDecay && pdg0 == -pdg1) { + nLeptonPairs++; + if (child0->getToBeDone() && child1->getToBeDone()) { + nLeptonPairsToBeDone++; + } + } + } + } + } + } + std::cout << "#events: " << nEvents << "\n" + << "#leptons: " << nLeptons << "\n" + << "#antileptons: " << nAntileptons << "\n" + << "#signal (prompt Jpsi): " << nSignalJpsi << "; within acceptance " << rapiditymin << " < y < " << rapiditymax << " : " << nSignalJpsiWithinAcc << "\n" + << "#signal (prompt Psi(2S)): " << nSignalPsi2S << "; within acceptance " << rapiditymin << " < y < " << rapiditymax << " : " << nSignalPsi2SWithinAcc << "\n" + << "#lepton pairs: " << nLeptonPairs << "\n" + << "#lepton pairs to be done: " << nLeptonPairs << "\n"; + + + if (nLeptonPairs == 0 || nLeptons == 0 || nAntileptons == 0) { + std::cerr << "Number of leptons, number of anti-leptons as well as number of lepton pairs should all be greater than 1.\n"; + return 1; + } + if (nLeptonPairs != nLeptonPairsToBeDone) { + std::cerr << "The number of lepton pairs should be the same as the number of lepton pairs which should be transported.\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGDQ/ini/tests/Generator_InjectedPromptCharmoniaMidy_TriggerGap_2_OO5TeV.C b/MC/config/PWGDQ/ini/tests/Generator_InjectedPromptCharmoniaMidy_TriggerGap_2_OO5TeV.C new file mode 100644 index 000000000..b155ec9ac --- /dev/null +++ b/MC/config/PWGDQ/ini/tests/Generator_InjectedPromptCharmoniaMidy_TriggerGap_2_OO5TeV.C @@ -0,0 +1,84 @@ +int External() +{ + int checkPdgSignal[] = {443,100443}; + int checkPdgDecay = 11; + std::string path{"o2sim_Kine.root"}; + std::cout << "Check for\nsignal PDG " << checkPdgSignal << "\ndecay PDG " << checkPdgDecay << "\n"; + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree*)file.Get("o2sim"); + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int nLeptons{}; + int nAntileptons{}; + int nLeptonPairs{}; + int nLeptonPairsToBeDone{}; + int nSignalJpsi{}; + int nSignalPsi2S{}; + int nSignalJpsiWithinAcc{}; + int nSignalPsi2SWithinAcc{}; + auto nEvents = tree->GetEntries(); + o2::steer::MCKinematicsReader mcreader("o2sim", o2::steer::MCKinematicsReader::Mode::kMCKine); + Bool_t isInjected = kFALSE; + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + for (auto& track : *tracks) { + auto pdg = track.GetPdgCode(); + auto rapidity = track.GetRapidity(); + auto idMoth = track.getMotherTrackId(); + if (pdg == checkPdgDecay) { + // count leptons + nLeptons++; + } else if(pdg == -checkPdgDecay) { + // count anti-leptons + nAntileptons++; + } else if (pdg == checkPdgSignal[0] || pdg == checkPdgSignal[1]) { + if(idMoth < 0){ + // count signal PDG + pdg == checkPdgSignal[0] ? nSignalJpsi++ : nSignalPsi2S++; + // count signal PDG within acceptance + if(std::abs(rapidity) < 1.0) { pdg == checkPdgSignal[0] ? nSignalJpsiWithinAcc++ : nSignalPsi2SWithinAcc++;} + } + auto child0 = o2::mcutils::MCTrackNavigator::getDaughter0(track, *tracks); + auto child1 = o2::mcutils::MCTrackNavigator::getDaughter1(track, *tracks); + if (child0 != nullptr && child1 != nullptr) { + // check for parent-child relations + auto pdg0 = child0->GetPdgCode(); + auto pdg1 = child1->GetPdgCode(); + std::cout << "First and last children of parent " << checkPdgSignal << " are PDG0: " << pdg0 << " PDG1: " << pdg1 << "\n"; + if (std::abs(pdg0) == checkPdgDecay && std::abs(pdg1) == checkPdgDecay && pdg0 == -pdg1) { + nLeptonPairs++; + if (child0->getToBeDone() && child1->getToBeDone()) { + nLeptonPairsToBeDone++; + } + } + } + } + } + } + std::cout << "#events: " << nEvents << "\n" + << "#leptons: " << nLeptons << "\n" + << "#antileptons: " << nAntileptons << "\n" + << "#signal (prompt Jpsi): " << nSignalJpsi << "; within acceptance (|y| < 1): " << nSignalJpsiWithinAcc << "\n" + << "#signal (prompt Psi(2S)): " << nSignalPsi2S << "; within acceptance (|y| < 1): " << nSignalPsi2SWithinAcc << "\n" + << "#lepton pairs: " << nLeptonPairs << "\n" + << "#lepton pairs to be done: " << nLeptonPairs << "\n"; + + + if (nLeptonPairs == 0 || nLeptons == 0 || nAntileptons == 0) { + std::cerr << "Number of leptons, number of anti-leptons as well as number of lepton pairs should all be greater than 1.\n"; + return 1; + } + if (nLeptonPairs != nLeptonPairsToBeDone) { + std::cerr << "The number of lepton pairs should be the same as the number of lepton pairs which should be transported.\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGDQ/ini/tests/Generator_InjectedPromptCharmoniaMidy_TriggerGap_OO5TeV.C b/MC/config/PWGDQ/ini/tests/Generator_InjectedPromptCharmoniaMidy_TriggerGap_OO5TeV.C new file mode 100644 index 000000000..b155ec9ac --- /dev/null +++ b/MC/config/PWGDQ/ini/tests/Generator_InjectedPromptCharmoniaMidy_TriggerGap_OO5TeV.C @@ -0,0 +1,84 @@ +int External() +{ + int checkPdgSignal[] = {443,100443}; + int checkPdgDecay = 11; + std::string path{"o2sim_Kine.root"}; + std::cout << "Check for\nsignal PDG " << checkPdgSignal << "\ndecay PDG " << checkPdgDecay << "\n"; + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree*)file.Get("o2sim"); + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int nLeptons{}; + int nAntileptons{}; + int nLeptonPairs{}; + int nLeptonPairsToBeDone{}; + int nSignalJpsi{}; + int nSignalPsi2S{}; + int nSignalJpsiWithinAcc{}; + int nSignalPsi2SWithinAcc{}; + auto nEvents = tree->GetEntries(); + o2::steer::MCKinematicsReader mcreader("o2sim", o2::steer::MCKinematicsReader::Mode::kMCKine); + Bool_t isInjected = kFALSE; + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + for (auto& track : *tracks) { + auto pdg = track.GetPdgCode(); + auto rapidity = track.GetRapidity(); + auto idMoth = track.getMotherTrackId(); + if (pdg == checkPdgDecay) { + // count leptons + nLeptons++; + } else if(pdg == -checkPdgDecay) { + // count anti-leptons + nAntileptons++; + } else if (pdg == checkPdgSignal[0] || pdg == checkPdgSignal[1]) { + if(idMoth < 0){ + // count signal PDG + pdg == checkPdgSignal[0] ? nSignalJpsi++ : nSignalPsi2S++; + // count signal PDG within acceptance + if(std::abs(rapidity) < 1.0) { pdg == checkPdgSignal[0] ? nSignalJpsiWithinAcc++ : nSignalPsi2SWithinAcc++;} + } + auto child0 = o2::mcutils::MCTrackNavigator::getDaughter0(track, *tracks); + auto child1 = o2::mcutils::MCTrackNavigator::getDaughter1(track, *tracks); + if (child0 != nullptr && child1 != nullptr) { + // check for parent-child relations + auto pdg0 = child0->GetPdgCode(); + auto pdg1 = child1->GetPdgCode(); + std::cout << "First and last children of parent " << checkPdgSignal << " are PDG0: " << pdg0 << " PDG1: " << pdg1 << "\n"; + if (std::abs(pdg0) == checkPdgDecay && std::abs(pdg1) == checkPdgDecay && pdg0 == -pdg1) { + nLeptonPairs++; + if (child0->getToBeDone() && child1->getToBeDone()) { + nLeptonPairsToBeDone++; + } + } + } + } + } + } + std::cout << "#events: " << nEvents << "\n" + << "#leptons: " << nLeptons << "\n" + << "#antileptons: " << nAntileptons << "\n" + << "#signal (prompt Jpsi): " << nSignalJpsi << "; within acceptance (|y| < 1): " << nSignalJpsiWithinAcc << "\n" + << "#signal (prompt Psi(2S)): " << nSignalPsi2S << "; within acceptance (|y| < 1): " << nSignalPsi2SWithinAcc << "\n" + << "#lepton pairs: " << nLeptonPairs << "\n" + << "#lepton pairs to be done: " << nLeptonPairs << "\n"; + + + if (nLeptonPairs == 0 || nLeptons == 0 || nAntileptons == 0) { + std::cerr << "Number of leptons, number of anti-leptons as well as number of lepton pairs should all be greater than 1.\n"; + return 1; + } + if (nLeptonPairs != nLeptonPairsToBeDone) { + std::cerr << "The number of lepton pairs should be the same as the number of lepton pairs which should be transported.\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGDQ/ini/tests/Generator_InjectedPromptCharmoniaMidy_TriggerGap_pO96TeV.C b/MC/config/PWGDQ/ini/tests/Generator_InjectedPromptCharmoniaMidy_TriggerGap_pO96TeV.C new file mode 100644 index 000000000..b155ec9ac --- /dev/null +++ b/MC/config/PWGDQ/ini/tests/Generator_InjectedPromptCharmoniaMidy_TriggerGap_pO96TeV.C @@ -0,0 +1,84 @@ +int External() +{ + int checkPdgSignal[] = {443,100443}; + int checkPdgDecay = 11; + std::string path{"o2sim_Kine.root"}; + std::cout << "Check for\nsignal PDG " << checkPdgSignal << "\ndecay PDG " << checkPdgDecay << "\n"; + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree*)file.Get("o2sim"); + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int nLeptons{}; + int nAntileptons{}; + int nLeptonPairs{}; + int nLeptonPairsToBeDone{}; + int nSignalJpsi{}; + int nSignalPsi2S{}; + int nSignalJpsiWithinAcc{}; + int nSignalPsi2SWithinAcc{}; + auto nEvents = tree->GetEntries(); + o2::steer::MCKinematicsReader mcreader("o2sim", o2::steer::MCKinematicsReader::Mode::kMCKine); + Bool_t isInjected = kFALSE; + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + for (auto& track : *tracks) { + auto pdg = track.GetPdgCode(); + auto rapidity = track.GetRapidity(); + auto idMoth = track.getMotherTrackId(); + if (pdg == checkPdgDecay) { + // count leptons + nLeptons++; + } else if(pdg == -checkPdgDecay) { + // count anti-leptons + nAntileptons++; + } else if (pdg == checkPdgSignal[0] || pdg == checkPdgSignal[1]) { + if(idMoth < 0){ + // count signal PDG + pdg == checkPdgSignal[0] ? nSignalJpsi++ : nSignalPsi2S++; + // count signal PDG within acceptance + if(std::abs(rapidity) < 1.0) { pdg == checkPdgSignal[0] ? nSignalJpsiWithinAcc++ : nSignalPsi2SWithinAcc++;} + } + auto child0 = o2::mcutils::MCTrackNavigator::getDaughter0(track, *tracks); + auto child1 = o2::mcutils::MCTrackNavigator::getDaughter1(track, *tracks); + if (child0 != nullptr && child1 != nullptr) { + // check for parent-child relations + auto pdg0 = child0->GetPdgCode(); + auto pdg1 = child1->GetPdgCode(); + std::cout << "First and last children of parent " << checkPdgSignal << " are PDG0: " << pdg0 << " PDG1: " << pdg1 << "\n"; + if (std::abs(pdg0) == checkPdgDecay && std::abs(pdg1) == checkPdgDecay && pdg0 == -pdg1) { + nLeptonPairs++; + if (child0->getToBeDone() && child1->getToBeDone()) { + nLeptonPairsToBeDone++; + } + } + } + } + } + } + std::cout << "#events: " << nEvents << "\n" + << "#leptons: " << nLeptons << "\n" + << "#antileptons: " << nAntileptons << "\n" + << "#signal (prompt Jpsi): " << nSignalJpsi << "; within acceptance (|y| < 1): " << nSignalJpsiWithinAcc << "\n" + << "#signal (prompt Psi(2S)): " << nSignalPsi2S << "; within acceptance (|y| < 1): " << nSignalPsi2SWithinAcc << "\n" + << "#lepton pairs: " << nLeptonPairs << "\n" + << "#lepton pairs to be done: " << nLeptonPairs << "\n"; + + + if (nLeptonPairs == 0 || nLeptons == 0 || nAntileptons == 0) { + std::cerr << "Number of leptons, number of anti-leptons as well as number of lepton pairs should all be greater than 1.\n"; + return 1; + } + if (nLeptonPairs != nLeptonPairsToBeDone) { + std::cerr << "The number of lepton pairs should be the same as the number of lepton pairs which should be transported.\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGDQ/ini/tests/Generator_InjectedPromptJpsiFwdy_PythiaOnia_TriggerGap.C b/MC/config/PWGDQ/ini/tests/Generator_InjectedPromptJpsiFwdy_PythiaOnia_TriggerGap.C new file mode 100644 index 000000000..2b75fcdbd --- /dev/null +++ b/MC/config/PWGDQ/ini/tests/Generator_InjectedPromptJpsiFwdy_PythiaOnia_TriggerGap.C @@ -0,0 +1,82 @@ +int External() +{ + int checkPdgSignal[] = {443}; + int checkPdgDecay = 13; + double rapiditymin = -4.3; double rapiditymax = -2.3; + std::string path{"o2sim_Kine.root"}; + std::cout << "Check for\nsignal PDG " << checkPdgSignal[0] << "\n decay PDG " << checkPdgDecay << "\n"; + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree*)file.Get("o2sim"); + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int nLeptons{}; + int nAntileptons{}; + int nLeptonPairs{}; + int nLeptonPairsToBeDone{}; + int nSignalJpsi{}; + int nSignalJpsiWithinAcc{}; + auto nEvents = tree->GetEntries(); + o2::steer::MCKinematicsReader mcreader("o2sim", o2::steer::MCKinematicsReader::Mode::kMCKine); + Bool_t isInjected = kFALSE; + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + for (auto& track : *tracks) { + auto pdg = track.GetPdgCode(); + auto rapidity = track.GetRapidity(); + auto idMoth = track.getMotherTrackId(); + if (pdg == checkPdgDecay) { + // count leptons + nLeptons++; + } else if(pdg == -checkPdgDecay) { + // count anti-leptons + nAntileptons++; + } else if (pdg == checkPdgSignal[0]) { + if(idMoth < 0){ + // count signal PDG + nSignalJpsi++; + // count signal PDG within acceptance + if(rapidity > rapiditymin && rapidity < rapiditymax) nSignalJpsiWithinAcc++; + } + auto child0 = o2::mcutils::MCTrackNavigator::getDaughter0(track, *tracks); + auto child1 = o2::mcutils::MCTrackNavigator::getDaughter1(track, *tracks); + if (child0 != nullptr && child1 != nullptr) { + // check for parent-child relations + auto pdg0 = child0->GetPdgCode(); + auto pdg1 = child1->GetPdgCode(); + std::cout << "First and last children of parent " << checkPdgSignal << " are PDG0: " << pdg0 << " PDG1: " << pdg1 << "\n"; + if (std::abs(pdg0) == checkPdgDecay && std::abs(pdg1) == checkPdgDecay && pdg0 == -pdg1) { + nLeptonPairs++; + if (child0->getToBeDone() && child1->getToBeDone()) { + nLeptonPairsToBeDone++; + } + } + } + } + } + } + std::cout << "#events: " << nEvents << "\n" + << "#leptons: " << nLeptons << "\n" + << "#antileptons: " << nAntileptons << "\n" + << "#signal (prompt Jpsi): " << nSignalJpsi << "; within acceptance " << rapiditymin << " < y < " << rapiditymax << " : " << nSignalJpsiWithinAcc << "\n" + << "#lepton pairs: " << nLeptonPairs << "\n" + << "#lepton pairs to be done: " << nLeptonPairs << "\n"; + + + if (nLeptonPairs == 0 || nLeptons == 0 || nAntileptons == 0) { + std::cerr << "Number of leptons, number of anti-leptons as well as number of lepton pairs should all be greater than 1.\n"; + return 1; + } + if (nLeptonPairs != nLeptonPairsToBeDone) { + std::cerr << "The number of lepton pairs should be the same as the number of lepton pairs which should be transported.\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGDQ/ini/tests/Generator_InjectedPromptJpsiFwdy_PythiaOnia_TriggerGap_pp5TeV.C b/MC/config/PWGDQ/ini/tests/Generator_InjectedPromptJpsiFwdy_PythiaOnia_TriggerGap_pp5TeV.C new file mode 100644 index 000000000..2b75fcdbd --- /dev/null +++ b/MC/config/PWGDQ/ini/tests/Generator_InjectedPromptJpsiFwdy_PythiaOnia_TriggerGap_pp5TeV.C @@ -0,0 +1,82 @@ +int External() +{ + int checkPdgSignal[] = {443}; + int checkPdgDecay = 13; + double rapiditymin = -4.3; double rapiditymax = -2.3; + std::string path{"o2sim_Kine.root"}; + std::cout << "Check for\nsignal PDG " << checkPdgSignal[0] << "\n decay PDG " << checkPdgDecay << "\n"; + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree*)file.Get("o2sim"); + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int nLeptons{}; + int nAntileptons{}; + int nLeptonPairs{}; + int nLeptonPairsToBeDone{}; + int nSignalJpsi{}; + int nSignalJpsiWithinAcc{}; + auto nEvents = tree->GetEntries(); + o2::steer::MCKinematicsReader mcreader("o2sim", o2::steer::MCKinematicsReader::Mode::kMCKine); + Bool_t isInjected = kFALSE; + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + for (auto& track : *tracks) { + auto pdg = track.GetPdgCode(); + auto rapidity = track.GetRapidity(); + auto idMoth = track.getMotherTrackId(); + if (pdg == checkPdgDecay) { + // count leptons + nLeptons++; + } else if(pdg == -checkPdgDecay) { + // count anti-leptons + nAntileptons++; + } else if (pdg == checkPdgSignal[0]) { + if(idMoth < 0){ + // count signal PDG + nSignalJpsi++; + // count signal PDG within acceptance + if(rapidity > rapiditymin && rapidity < rapiditymax) nSignalJpsiWithinAcc++; + } + auto child0 = o2::mcutils::MCTrackNavigator::getDaughter0(track, *tracks); + auto child1 = o2::mcutils::MCTrackNavigator::getDaughter1(track, *tracks); + if (child0 != nullptr && child1 != nullptr) { + // check for parent-child relations + auto pdg0 = child0->GetPdgCode(); + auto pdg1 = child1->GetPdgCode(); + std::cout << "First and last children of parent " << checkPdgSignal << " are PDG0: " << pdg0 << " PDG1: " << pdg1 << "\n"; + if (std::abs(pdg0) == checkPdgDecay && std::abs(pdg1) == checkPdgDecay && pdg0 == -pdg1) { + nLeptonPairs++; + if (child0->getToBeDone() && child1->getToBeDone()) { + nLeptonPairsToBeDone++; + } + } + } + } + } + } + std::cout << "#events: " << nEvents << "\n" + << "#leptons: " << nLeptons << "\n" + << "#antileptons: " << nAntileptons << "\n" + << "#signal (prompt Jpsi): " << nSignalJpsi << "; within acceptance " << rapiditymin << " < y < " << rapiditymax << " : " << nSignalJpsiWithinAcc << "\n" + << "#lepton pairs: " << nLeptonPairs << "\n" + << "#lepton pairs to be done: " << nLeptonPairs << "\n"; + + + if (nLeptonPairs == 0 || nLeptons == 0 || nAntileptons == 0) { + std::cerr << "Number of leptons, number of anti-leptons as well as number of lepton pairs should all be greater than 1.\n"; + return 1; + } + if (nLeptonPairs != nLeptonPairsToBeDone) { + std::cerr << "The number of lepton pairs should be the same as the number of lepton pairs which should be transported.\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGDQ/ini/tests/Generator_InjectedPromptJpsiMidy_PythiaOnia_TriggerGap.C b/MC/config/PWGDQ/ini/tests/Generator_InjectedPromptJpsiMidy_PythiaOnia_TriggerGap.C new file mode 100644 index 000000000..45dafa928 --- /dev/null +++ b/MC/config/PWGDQ/ini/tests/Generator_InjectedPromptJpsiMidy_PythiaOnia_TriggerGap.C @@ -0,0 +1,102 @@ +int External() +{ + int checkPdgSignal[] = {443, 100443}; + int checkPdgDecay = 11; + double rapiditymin = -4.3; + double rapiditymax = -2.3; + std::string path{"o2sim_Kine.root"}; + std::cout << "Check for\nsignal PDG " << checkPdgSignal << "\ndecay PDG " << checkPdgDecay << "\n"; + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) + { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int nLeptons{}; + int nAntileptons{}; + int nLeptonPairs{}; + int nLeptonPairsToBeDone{}; + int nSignalJpsi{}; + int nSignalPsi2S{}; + int nSignalJpsiWithinAcc{}; + int nSignalPsi2SWithinAcc{}; + auto nEvents = tree->GetEntries(); + o2::steer::MCKinematicsReader mcreader("o2sim", o2::steer::MCKinematicsReader::Mode::kMCKine); + Bool_t isInjected = kFALSE; + + for (int i = 0; i < nEvents; i++) + { + tree->GetEntry(i); + for (auto &track : *tracks) + { + auto pdg = track.GetPdgCode(); + auto rapidity = track.GetRapidity(); + auto idMoth = track.getMotherTrackId(); + if (pdg == checkPdgDecay) + { + // count leptons + nLeptons++; + } + else if (pdg == -checkPdgDecay) + { + // count anti-leptons + nAntileptons++; + } + else if (pdg == checkPdgSignal[0] || pdg == checkPdgSignal[1]) + { + if (idMoth < 0) + { + // count signal PDG + pdg == checkPdgSignal[0] ? nSignalJpsi++ : nSignalPsi2S++; + // count signal PDG within acceptance + if (rapidity > rapiditymin && rapidity < rapiditymax) + { + pdg == checkPdgSignal[0] ? nSignalJpsiWithinAcc++ : nSignalPsi2SWithinAcc++; + } + } + auto child0 = o2::mcutils::MCTrackNavigator::getDaughter0(track, *tracks); + auto child1 = o2::mcutils::MCTrackNavigator::getDaughter1(track, *tracks); + if (child0 != nullptr && child1 != nullptr) + { + // check for parent-child relations + auto pdg0 = child0->GetPdgCode(); + auto pdg1 = child1->GetPdgCode(); + std::cout << "First and last children of parent " << checkPdgSignal << " are PDG0: " << pdg0 << " PDG1: " << pdg1 << "\n"; + if (std::abs(pdg0) == checkPdgDecay && std::abs(pdg1) == checkPdgDecay && pdg0 == -pdg1) + { + nLeptonPairs++; + if (child0->getToBeDone() && child1->getToBeDone()) + { + nLeptonPairsToBeDone++; + } + } + } + } + } + } + std::cout << "#events: " << nEvents << "\n" + << "#leptons: " << nLeptons << "\n" + << "#antileptons: " << nAntileptons << "\n" + << "#signal (prompt Jpsi): " << nSignalJpsi << "; within acceptance " << rapiditymin << " < y < " << rapiditymax << " : " << nSignalJpsiWithinAcc << "\n" + << "#signal (prompt Psi(2S)): " << nSignalPsi2S << "; within acceptance " << rapiditymin << " < y < " << rapiditymax << " : " << nSignalPsi2SWithinAcc << "\n" + << "#lepton pairs: " << nLeptonPairs << "\n" + << "#lepton pairs to be done: " << nLeptonPairs << "\n"; + + if (nLeptonPairs == 0 || nLeptons == 0 || nAntileptons == 0) + { + std::cerr << "Number of leptons, number of anti-leptons as well as number of lepton pairs should all be greater than 1.\n"; + return 1; + } + if (nLeptonPairs != nLeptonPairsToBeDone) + { + std::cerr << "The number of lepton pairs should be the same as the number of lepton pairs which should be transported.\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGDQ/ini/tests/Generator_InjectedPromptJpsiPsi2SFwdy_Pythia8_TriggerGap.C b/MC/config/PWGDQ/ini/tests/Generator_InjectedPromptJpsiPsi2SFwdy_Pythia8_TriggerGap.C new file mode 100644 index 000000000..e88864af3 --- /dev/null +++ b/MC/config/PWGDQ/ini/tests/Generator_InjectedPromptJpsiPsi2SFwdy_Pythia8_TriggerGap.C @@ -0,0 +1,85 @@ +int External() +{ + int checkPdgSignal[] = {443,100443}; + int checkPdgDecay = 13; + double rapiditymin = -4.3; double rapiditymax = -2.3; + std::string path{"o2sim_Kine.root"}; + std::cout << "Check for\nsignal PDG " << checkPdgSignal << "\ndecay PDG " << checkPdgDecay << "\n"; + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree*)file.Get("o2sim"); + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int nLeptons{}; + int nAntileptons{}; + int nLeptonPairs{}; + int nLeptonPairsToBeDone{}; + int nSignalJpsi{}; + int nSignalPsi2S{}; + int nSignalJpsiWithinAcc{}; + int nSignalPsi2SWithinAcc{}; + auto nEvents = tree->GetEntries(); + o2::steer::MCKinematicsReader mcreader("o2sim", o2::steer::MCKinematicsReader::Mode::kMCKine); + Bool_t isInjected = kFALSE; + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + for (auto& track : *tracks) { + auto pdg = track.GetPdgCode(); + auto rapidity = track.GetRapidity(); + auto idMoth = track.getMotherTrackId(); + if (pdg == checkPdgDecay) { + // count leptons + nLeptons++; + } else if(pdg == -checkPdgDecay) { + // count anti-leptons + nAntileptons++; + } else if (pdg == checkPdgSignal[0] || pdg == checkPdgSignal[1]) { + if(idMoth < 0){ + // count signal PDG + pdg == checkPdgSignal[0] ? nSignalJpsi++ : nSignalPsi2S++; + // count signal PDG within acceptance + if(rapidity > rapiditymin && rapidity < rapiditymax) { pdg == checkPdgSignal[0] ? nSignalJpsiWithinAcc++ : nSignalPsi2SWithinAcc++;} + } + auto child0 = o2::mcutils::MCTrackNavigator::getDaughter0(track, *tracks); + auto child1 = o2::mcutils::MCTrackNavigator::getDaughter1(track, *tracks); + if (child0 != nullptr && child1 != nullptr) { + // check for parent-child relations + auto pdg0 = child0->GetPdgCode(); + auto pdg1 = child1->GetPdgCode(); + std::cout << "First and last children of parent " << checkPdgSignal << " are PDG0: " << pdg0 << " PDG1: " << pdg1 << "\n"; + if (std::abs(pdg0) == checkPdgDecay && std::abs(pdg1) == checkPdgDecay && pdg0 == -pdg1) { + nLeptonPairs++; + if (child0->getToBeDone() && child1->getToBeDone()) { + nLeptonPairsToBeDone++; + } + } + } + } + } + } + std::cout << "#events: " << nEvents << "\n" + << "#leptons: " << nLeptons << "\n" + << "#antileptons: " << nAntileptons << "\n" + << "#signal (prompt Jpsi): " << nSignalJpsi << "; within acceptance " << rapiditymin << " < y < " << rapiditymax << " : " << nSignalJpsiWithinAcc << "\n" + << "#signal (prompt Psi(2S)): " << nSignalPsi2S << "; within acceptance " << rapiditymin << " < y < " << rapiditymax << " : " << nSignalPsi2SWithinAcc << "\n" + << "#lepton pairs: " << nLeptonPairs << "\n" + << "#lepton pairs to be done: " << nLeptonPairs << "\n"; + + + if (nLeptonPairs == 0 || nLeptons == 0 || nAntileptons == 0) { + std::cerr << "Number of leptons, number of anti-leptons as well as number of lepton pairs should all be greater than 1.\n"; + return 1; + } + if (nLeptonPairs != nLeptonPairsToBeDone) { + std::cerr << "The number of lepton pairs should be the same as the number of lepton pairs which should be transported.\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGDQ/ini/tests/Generator_InjectedPromptJpsiPsi2SFwdy_Pythia8_TriggerGap_pp5TeV.C b/MC/config/PWGDQ/ini/tests/Generator_InjectedPromptJpsiPsi2SFwdy_Pythia8_TriggerGap_pp5TeV.C new file mode 100644 index 000000000..d559770ed --- /dev/null +++ b/MC/config/PWGDQ/ini/tests/Generator_InjectedPromptJpsiPsi2SFwdy_Pythia8_TriggerGap_pp5TeV.C @@ -0,0 +1,102 @@ +int External() +{ + int checkPdgSignal[] = {443, 100443}; + int checkPdgDecay = 13; + double rapiditymin = -4.3; + double rapiditymax = -2.3; + std::string path{"o2sim_Kine.root"}; + std::cout << "Check for\nsignal PDG " << checkPdgSignal << "\ndecay PDG " << checkPdgDecay << "\n"; + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) + { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int nLeptons{}; + int nAntileptons{}; + int nLeptonPairs{}; + int nLeptonPairsToBeDone{}; + int nSignalJpsi{}; + int nSignalPsi2S{}; + int nSignalJpsiWithinAcc{}; + int nSignalPsi2SWithinAcc{}; + auto nEvents = tree->GetEntries(); + o2::steer::MCKinematicsReader mcreader("o2sim", o2::steer::MCKinematicsReader::Mode::kMCKine); + Bool_t isInjected = kFALSE; + + for (int i = 0; i < nEvents; i++) + { + tree->GetEntry(i); + for (auto &track : *tracks) + { + auto pdg = track.GetPdgCode(); + auto rapidity = track.GetRapidity(); + auto idMoth = track.getMotherTrackId(); + if (pdg == checkPdgDecay) + { + // count leptons + nLeptons++; + } + else if (pdg == -checkPdgDecay) + { + // count anti-leptons + nAntileptons++; + } + else if (pdg == checkPdgSignal[0] || pdg == checkPdgSignal[1]) + { + if (idMoth < 0) + { + // count signal PDG + pdg == checkPdgSignal[0] ? nSignalJpsi++ : nSignalPsi2S++; + // count signal PDG within acceptance + if (rapidity > rapiditymin && rapidity < rapiditymax) + { + pdg == checkPdgSignal[0] ? nSignalJpsiWithinAcc++ : nSignalPsi2SWithinAcc++; + } + } + auto child0 = o2::mcutils::MCTrackNavigator::getDaughter0(track, *tracks); + auto child1 = o2::mcutils::MCTrackNavigator::getDaughter1(track, *tracks); + if (child0 != nullptr && child1 != nullptr) + { + // check for parent-child relations + auto pdg0 = child0->GetPdgCode(); + auto pdg1 = child1->GetPdgCode(); + std::cout << "First and last children of parent " << checkPdgSignal << " are PDG0: " << pdg0 << " PDG1: " << pdg1 << "\n"; + if (std::abs(pdg0) == checkPdgDecay && std::abs(pdg1) == checkPdgDecay && pdg0 == -pdg1) + { + nLeptonPairs++; + if (child0->getToBeDone() && child1->getToBeDone()) + { + nLeptonPairsToBeDone++; + } + } + } + } + } + } + std::cout << "#events: " << nEvents << "\n" + << "#leptons: " << nLeptons << "\n" + << "#antileptons: " << nAntileptons << "\n" + << "#signal (prompt Jpsi): " << nSignalJpsi << "; within acceptance " << rapiditymin << " < y < " << rapiditymax << " : " << nSignalJpsiWithinAcc << "\n" + << "#signal (prompt Psi(2S)): " << nSignalPsi2S << "; within acceptance " << rapiditymin << " < y < " << rapiditymax << " : " << nSignalPsi2SWithinAcc << "\n" + << "#lepton pairs: " << nLeptonPairs << "\n" + << "#lepton pairs to be done: " << nLeptonPairs << "\n"; + + if (nLeptonPairs == 0 || nLeptons == 0 || nAntileptons == 0) + { + std::cerr << "Number of leptons, number of anti-leptons as well as number of lepton pairs should all be greater than 1.\n"; + return 1; + } + if (nLeptonPairs != nLeptonPairsToBeDone) + { + std::cerr << "The number of lepton pairs should be the same as the number of lepton pairs which should be transported.\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGDQ/ini/tests/Generator_InjectedPromptPsi2SToJpsiPiPiMidy_TriggerGap.C b/MC/config/PWGDQ/ini/tests/Generator_InjectedPromptPsi2SToJpsiPiPiMidy_TriggerGap.C new file mode 100644 index 000000000..9254fdab1 --- /dev/null +++ b/MC/config/PWGDQ/ini/tests/Generator_InjectedPromptPsi2SToJpsiPiPiMidy_TriggerGap.C @@ -0,0 +1,99 @@ +int External() +{ + int checkPdgSignal[] = {100443}; + int checkPdgDecay[] = {443, 211, -211}; + int leptonPdg = 11; + Double_t rapidityWindow = 1.0; + std::string path{"o2sim_Kine.root"}; + std::cout << "Check for\nsignal PDG " << checkPdgSignal[0] << "\n decay PDG " << checkPdgDecay[0] << ", " << checkPdgDecay[1] << ", " << checkPdgDecay[2] << "\n"; + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + auto tree = (TTree*)file.Get("o2sim"); + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int nLeptons{}; + int nAntileptons{}; + int nLeptonPairs{}; + int nLeptonPairsToBeDone{}; + int nSignalJpsi{}; + int nSignalPionsPos{}; + int nSignalPionsNeg{}; + int nSignalPsi2S{}; + int nSignalJpsiWithinAcc{}; + int nSignalPionsPosWithinAcc{}; + int nSignalPionsNegWithinAcc{}; + auto nEvents = tree->GetEntries(); + o2::steer::MCKinematicsReader mcreader("o2sim", o2::steer::MCKinematicsReader::Mode::kMCKine); + Bool_t hasPsi2SMoth = kFALSE; + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + for (auto& track : *tracks) { + auto pdg = track.GetPdgCode(); + auto rapidity = track.GetRapidity(); + auto idMoth = track.getMotherTrackId(); + int idJpsi = -1; int IdChild0 = -1; int IdChild1 = -1; + if (pdg == leptonPdg) { + // count leptons + nLeptons++; + } else if(pdg == -leptonPdg) { + // count anti-leptons + nAntileptons++; + } else if (pdg == checkPdgSignal[0]) { + // check daughters + std::cout << "Signal PDG: " << pdg << "\n"; + for (int j{track.getFirstDaughterTrackId()}; j <= track.getLastDaughterTrackId(); ++j) { + auto pdgDau = tracks->at(j).GetPdgCode(); + std::cout << "Daughter " << j << " is: " << pdgDau << "\n"; + if(TMath::Abs(pdgDau) == checkPdgDecay[0] ) { nSignalJpsi++; if( std::abs(track.GetRapidity()) < rapidityWindow) nSignalJpsiWithinAcc++; idJpsi = j; } + if(pdgDau == checkPdgDecay[1] ) { nSignalPionsPos++; if( std::abs(track.GetRapidity()) < rapidityWindow) nSignalPionsPosWithinAcc++; } + if(pdgDau == checkPdgDecay[2] ) { nSignalPionsNeg++; if( std::abs(track.GetRapidity()) < rapidityWindow) nSignalPionsNegWithinAcc++; } + } + + auto trackJpsi = tracks->at(idJpsi); + for (int j{trackJpsi.getFirstDaughterTrackId()}; j <= trackJpsi.getLastDaughterTrackId(); ++j) { + auto pdgDau = tracks->at(j).GetPdgCode(); + if(pdgDau == leptonPdg ) IdChild0 = j; + if(pdgDau == -leptonPdg ) IdChild1 = j; + } + auto child0 = tracks->at(IdChild0); + auto child1 = tracks->at(IdChild1); + // check for parent-child relations + auto pdg0 = child0.GetPdgCode(); + auto pdg1 = child1.GetPdgCode(); + std::cout << "Lepton daughter particles of mother " << trackJpsi.GetPdgCode() << " are PDG0: " << pdg0 << " PDG1: " << pdg1 << "\n"; + if (std::abs(pdg0) == leptonPdg && std::abs(pdg1) == leptonPdg && pdg0 == -pdg1) { + nLeptonPairs++; + if (child0.getToBeDone() && child1.getToBeDone()) { + nLeptonPairsToBeDone++; + } + } + //} + } + } + } + std::cout << "#events: " << nEvents << "\n" + << "#leptons: " << nLeptons << "\n" + << "#antileptons: " << nAntileptons << "\n" + << "#signal (jpsi <- psi2S): " << nSignalJpsi << "; within acceptance (|y| < " << rapidityWindow << "): " << nSignalJpsiWithinAcc << "\n" + << "#signal (pi+ <- psi2S): " << nSignalPionsPos << "; within acceptance (|y| < " << rapidityWindow << "): " << nSignalPionsPosWithinAcc << "\n" + << "#signal (pi- <- psi2S): " << nSignalPionsNeg << "; within acceptance (|y| < " << rapidityWindow << "): " << nSignalPionsNegWithinAcc << "\n" + << "#lepton pairs: " << nLeptonPairs << "\n" + << "#lepton pairs to be done: " << nLeptonPairs << "\n"; + + + if (nLeptonPairs == 0 || nLeptons == 0 || nAntileptons == 0) { + std::cerr << "Number of leptons, number of anti-leptons as well as number of lepton pairs should all be greater than 1.\n"; + return 1; + } + if (nLeptonPairs != nLeptonPairsToBeDone) { + std::cerr << "The number of lepton pairs should be the same as the number of lepton pairs which should be transported.\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGDQ/ini/tests/Generator_InjectedPromptX3782Midy_TriggerGap.C b/MC/config/PWGDQ/ini/tests/Generator_InjectedPromptX3782Midy_TriggerGap.C new file mode 100644 index 000000000..836c8df30 --- /dev/null +++ b/MC/config/PWGDQ/ini/tests/Generator_InjectedPromptX3782Midy_TriggerGap.C @@ -0,0 +1,99 @@ +int External() +{ + int checkPdgSignal[] = {9920443}; // pdg code X3872 + int checkPdgDecay[] = {443, 211, -211}; + int leptonPdg = 11; + Double_t rapidityWindow = 1.0; + std::string path{"o2sim_Kine.root"}; + std::cout << "Check for\nsignal PDG " << checkPdgSignal[0] << "\n decay PDG " << checkPdgDecay[0] << ", " << checkPdgDecay[1] << ", " << checkPdgDecay[2] << "\n"; + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + auto tree = (TTree*)file.Get("o2sim"); + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int nLeptons{}; + int nAntileptons{}; + int nLeptonPairs{}; + int nLeptonPairsToBeDone{}; + int nSignalX3872{}; + int nSignalPionsPos{}; + int nSignalPionsNeg{}; + int nSignalPsi2S{}; + int nSignalX3872WithinAcc{}; + int nSignalPionsPosWithinAcc{}; + int nSignalPionsNegWithinAcc{}; + auto nEvents = tree->GetEntries(); + o2::steer::MCKinematicsReader mcreader("o2sim", o2::steer::MCKinematicsReader::Mode::kMCKine); + Bool_t hasPsi2SMoth = kFALSE; + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + for (auto& track : *tracks) { + auto pdg = track.GetPdgCode(); + auto rapidity = track.GetRapidity(); + auto idMoth = track.getMotherTrackId(); + int idX3872 = -1; int IdChild0 = -1; int IdChild1 = -1; + if (pdg == leptonPdg) { + // count leptons + nLeptons++; + } else if(pdg == -leptonPdg) { + // count anti-leptons + nAntileptons++; + } else if (pdg == checkPdgSignal[0]) { + // check daughters + std::cout << "Signal PDG: " << pdg << "\n"; + for (int j{track.getFirstDaughterTrackId()}; j <= track.getLastDaughterTrackId(); ++j) { + auto pdgDau = tracks->at(j).GetPdgCode(); + std::cout << "Daughter " << j << " is: " << pdgDau << "\n"; + if(TMath::Abs(pdgDau) == checkPdgDecay[0] ) { nSignalX3872++; if( std::abs(track.GetRapidity()) < rapidityWindow) nSignalX3872WithinAcc++; idX3872 = j; } + if(pdgDau == checkPdgDecay[1] ) { nSignalPionsPos++; if( std::abs(track.GetRapidity()) < rapidityWindow) nSignalPionsPosWithinAcc++; } + if(pdgDau == checkPdgDecay[2] ) { nSignalPionsNeg++; if( std::abs(track.GetRapidity()) < rapidityWindow) nSignalPionsNegWithinAcc++; } + } + + auto trackX3872 = tracks->at(idX3872); + for (int j{trackX3872.getFirstDaughterTrackId()}; j <= trackX3872.getLastDaughterTrackId(); ++j) { + auto pdgDau = tracks->at(j).GetPdgCode(); + if(pdgDau == leptonPdg ) IdChild0 = j; + if(pdgDau == -leptonPdg ) IdChild1 = j; + } + auto child0 = tracks->at(IdChild0); + auto child1 = tracks->at(IdChild1); + // check for parent-child relations + auto pdg0 = child0.GetPdgCode(); + auto pdg1 = child1.GetPdgCode(); + std::cout << "Lepton daughter particles of mother " << trackX3872.GetPdgCode() << " are PDG0: " << pdg0 << " PDG1: " << pdg1 << "\n"; + if (std::abs(pdg0) == leptonPdg && std::abs(pdg1) == leptonPdg && pdg0 == -pdg1) { + nLeptonPairs++; + if (child0.getToBeDone() && child1.getToBeDone()) { + nLeptonPairsToBeDone++; + } + } + //} + } + } + } + std::cout << "#events: " << nEvents << "\n" + << "#leptons: " << nLeptons << "\n" + << "#antileptons: " << nAntileptons << "\n" + << "#signal (jpsi <- X3872): " << nSignalX3872 << "; within acceptance (|y| < " << rapidityWindow << "): " << nSignalX3872WithinAcc << "\n" + << "#signal (pi+ <- X3872): " << nSignalPionsPos << "; within acceptance (|y| < " << rapidityWindow << "): " << nSignalPionsPosWithinAcc << "\n" + << "#signal (pi- <- X3872): " << nSignalPionsNeg << "; within acceptance (|y| < " << rapidityWindow << "): " << nSignalPionsNegWithinAcc << "\n" + << "#lepton pairs: " << nLeptonPairs << "\n" + << "#lepton pairs to be done: " << nLeptonPairs << "\n"; + + + if (nLeptonPairs == 0 || nLeptons == 0 || nAntileptons == 0) { + std::cerr << "Number of leptons, number of anti-leptons as well as number of lepton pairs should all be greater than 1.\n"; + return 1; + } + if (nLeptonPairs != nLeptonPairsToBeDone) { + std::cerr << "The number of lepton pairs should be the same as the number of lepton pairs which should be transported.\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGDQ/ini/tests/Generator_InjectedPromptX3782andPsi2SMidy_TriggerGap.C b/MC/config/PWGDQ/ini/tests/Generator_InjectedPromptX3782andPsi2SMidy_TriggerGap.C new file mode 100644 index 000000000..87674d2f1 --- /dev/null +++ b/MC/config/PWGDQ/ini/tests/Generator_InjectedPromptX3782andPsi2SMidy_TriggerGap.C @@ -0,0 +1,104 @@ +int External() +{ + int checkPdgSignal[] = {9920443,100443}; // pdg code X3872 + TString PdgSignalName[] = {"X(3872)", "Psi2S"}; + int checkPdgDecay[] = {443, 211, -211}; + int leptonPdg = 11; + Double_t rapidityWindow = 1.0; + std::string path{"o2sim_Kine.root"}; + for(int iSig =0; iSig < 2; iSig++) std::cout << "Check for\nsignal PDG " << checkPdgSignal[iSig] << "\n decay PDG " << checkPdgDecay[0] << ", " << checkPdgDecay[1] << ", " << checkPdgDecay[2] << "\n"; + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + auto tree = (TTree*)file.Get("o2sim"); + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int nLeptons[]={0,0}; + int nAntileptons[]={0,0}; + int nLeptonPairs[]={0,0}; + int nLeptonPairsToBeDone[]={0,0}; + int nSignalX3872[]={0,0}; + int nSignalPionsPos[]={0,0}; + int nSignalPionsNeg[]={0,0}; + int nSignalPsi2S{}; + int nSignalX3872WithinAcc[]={0,0}; + int nSignalPionsPosWithinAcc[]={0,0}; + int nSignalPionsNegWithinAcc[]={0,0}; + auto nEvents = tree->GetEntries(); + o2::steer::MCKinematicsReader mcreader("o2sim", o2::steer::MCKinematicsReader::Mode::kMCKine); + Bool_t hasPsi2SMoth = kFALSE; + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + for (auto& track : *tracks) { + auto pdg = track.GetPdgCode(); + auto rapidity = track.GetRapidity(); + auto idMoth = track.getMotherTrackId(); + int idX3872 = -1; int IdChild0 = -1; int IdChild1 = -1; + for(int iSig=0; iSig<2; iSig++) { + if (pdg == leptonPdg) { + // count leptons + nLeptons[iSig]++; + } else if(pdg == -leptonPdg) { + // count anti-leptons + nAntileptons[iSig]++; + } else if (pdg == checkPdgSignal[iSig]) { + // check daughters + std::cout << "Signal PDG: " << pdg << "\n"; + for (int j{track.getFirstDaughterTrackId()}; j <= track.getLastDaughterTrackId(); ++j) { + auto pdgDau = tracks->at(j).GetPdgCode(); + std::cout << "Daughter " << j << " is: " << pdgDau << "\n"; + if(TMath::Abs(pdgDau) == checkPdgDecay[0] ) { nSignalX3872[iSig]++; if( std::abs(track.GetRapidity()) < rapidityWindow) nSignalX3872WithinAcc[iSig]++; idX3872 = j; } + if(pdgDau == checkPdgDecay[1] ) { nSignalPionsPos[iSig]++; if( std::abs(track.GetRapidity()) < rapidityWindow) nSignalPionsPosWithinAcc[iSig]++; } + if(pdgDau == checkPdgDecay[2] ) { nSignalPionsNeg[iSig]++; if( std::abs(track.GetRapidity()) < rapidityWindow) nSignalPionsNegWithinAcc[iSig]++; } + } + + auto trackX3872 = tracks->at(idX3872); + for (int j{trackX3872.getFirstDaughterTrackId()}; j <= trackX3872.getLastDaughterTrackId(); ++j) { + auto pdgDau = tracks->at(j).GetPdgCode(); + if(pdgDau == leptonPdg ) IdChild0 = j; + if(pdgDau == -leptonPdg ) IdChild1 = j; + } + auto child0 = tracks->at(IdChild0); + auto child1 = tracks->at(IdChild1); + // check for parent-child relations + auto pdg0 = child0.GetPdgCode(); + auto pdg1 = child1.GetPdgCode(); + std::cout << "Lepton daughter particles of mother " << trackX3872.GetPdgCode() << " are PDG0: " << pdg0 << " PDG1: " << pdg1 << "\n"; + if (std::abs(pdg0) == leptonPdg && std::abs(pdg1) == leptonPdg && pdg0 == -pdg1) { + nLeptonPairs[iSig]++; + if (child0.getToBeDone() && child1.getToBeDone()) { + nLeptonPairsToBeDone[iSig]++; + } + } + } + } + } + } + + std::cout << "#events: " << nEvents << "\n"; + for(int iSig=0; iSig < 2; iSig++){ + std::cout << "#leptons from " << PdgSignalName[iSig] << ": " << nLeptons[iSig] << "\n" + << "#antileptons from " << PdgSignalName[iSig] << ": " << nAntileptons[iSig] << "\n" + << "#signal (jpsi <-" << PdgSignalName[iSig] <<"): " << nSignalX3872[iSig] << "; within acceptance (|y| < " << rapidityWindow << "): " << nSignalX3872WithinAcc[iSig] << "\n" + << "#signal (pi+ <-" << PdgSignalName[iSig] <<"): " << nSignalPionsPos[iSig] << "; within acceptance (|y| < " << rapidityWindow << "): " << nSignalPionsPosWithinAcc[iSig] << "\n" + << "#signal (pi- <-" << PdgSignalName[iSig] <<"): " << nSignalPionsNeg[iSig] << "; within acceptance (|y| < " << rapidityWindow << "): " << nSignalPionsNegWithinAcc[iSig] << "\n" + << "#lepton pairs from " << PdgSignalName[iSig] <<": " << nLeptonPairs[iSig] << "\n" + << "#lepton pairs to be done from " << PdgSignalName[iSig] <<": " << nLeptonPairs[iSig] << "\n"; + + + if (nLeptonPairs[iSig] == 0 || nLeptons[iSig] == 0 || nAntileptons[iSig] == 0) { + std::cerr << "Number of leptons, number of anti-leptons as well as number of lepton pairs should all be greater than 1.\n"; + return 1; + } + if (nLeptonPairs[iSig] != nLeptonPairsToBeDone[iSig]) { + std::cerr << "The number of lepton pairs should be the same as the number of lepton pairs which should be transported.\n"; + return 1; + } + + } + return 0; +} diff --git a/MC/config/PWGDQ/ini/tests/Generator_TriggeredInclusiveQuarkonia_Pythia8_TriggerGap.C b/MC/config/PWGDQ/ini/tests/Generator_TriggeredInclusiveQuarkonia_Pythia8_TriggerGap.C new file mode 100644 index 000000000..d23d3e654 --- /dev/null +++ b/MC/config/PWGDQ/ini/tests/Generator_TriggeredInclusiveQuarkonia_Pythia8_TriggerGap.C @@ -0,0 +1,77 @@ +int External() { + int checkPdgSignal = 443; + int checkPdgDecay = 11; + std::string path{"o2sim_Kine.root"}; + std::cout << "Check for\nsignal PDG " << checkPdgSignal << "\ndecay PDG " + << checkPdgDecay << "\n"; + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int nLeptons{}; + int nAntileptons{}; + int nLeptonPairs{}; + int nLeptonPairsToBeDone{}; + int nSignal{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + for (auto &track : *tracks) { + auto pdg = track.GetPdgCode(); + if (pdg == checkPdgDecay) { + // count leptons + nLeptons++; + } else if (pdg == -checkPdgDecay) { + // count anti-leptons + nAntileptons++; + } else if (pdg == checkPdgSignal) { + // count signal PDG + nSignal++; + auto child0 = + o2::mcutils::MCTrackNavigator::getDaughter0(track, *tracks); + auto child1 = + o2::mcutils::MCTrackNavigator::getDaughter1(track, *tracks); + if (child0 != nullptr && child1 != nullptr) { + // check for parent-child relations + auto pdg0 = child0->GetPdgCode(); + auto pdg1 = child1->GetPdgCode(); + std::cout << "First and last children of parent " << checkPdgSignal + << " are PDG0: " << pdg0 << " PDG1: " << pdg1 << "\n"; + if (std::abs(pdg0) == checkPdgDecay && + std::abs(pdg1) == checkPdgDecay && pdg0 == -pdg1) { + nLeptonPairs++; + if (child0->getToBeDone() && child1->getToBeDone()) { + nLeptonPairsToBeDone++; + } + } + } + } + } + } + std::cout << "#events: " << nEvents << "\n" + << "#leptons: " << nLeptons << "\n" + << "#antileptons: " << nAntileptons << "\n" + << "#signal: " << nSignal << "\n" + << "#lepton pairs: " << nLeptonPairs << "\n" + << "#lepton pairs to be done: " << nLeptonPairs << "\n"; + + if (nLeptonPairs == 0 || nLeptons == 0 || nAntileptons == 0) { + std::cerr << "Number of leptons, number of anti-leptons as well as number " + "of lepton pairs should all be greater than 1.\n"; + return 1; + } + if (nLeptonPairs != nLeptonPairsToBeDone) { + std::cerr << "The number of lepton pairs should be the same as the number " + "of lepton pairs which should be transported.\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGDQ/ini/tests/Generator_TriggeredInclusiveQuarkonia_enhancedB_Pythia8_TriggerGap.C b/MC/config/PWGDQ/ini/tests/Generator_TriggeredInclusiveQuarkonia_enhancedB_Pythia8_TriggerGap.C new file mode 100644 index 000000000..d23d3e654 --- /dev/null +++ b/MC/config/PWGDQ/ini/tests/Generator_TriggeredInclusiveQuarkonia_enhancedB_Pythia8_TriggerGap.C @@ -0,0 +1,77 @@ +int External() { + int checkPdgSignal = 443; + int checkPdgDecay = 11; + std::string path{"o2sim_Kine.root"}; + std::cout << "Check for\nsignal PDG " << checkPdgSignal << "\ndecay PDG " + << checkPdgDecay << "\n"; + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int nLeptons{}; + int nAntileptons{}; + int nLeptonPairs{}; + int nLeptonPairsToBeDone{}; + int nSignal{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + for (auto &track : *tracks) { + auto pdg = track.GetPdgCode(); + if (pdg == checkPdgDecay) { + // count leptons + nLeptons++; + } else if (pdg == -checkPdgDecay) { + // count anti-leptons + nAntileptons++; + } else if (pdg == checkPdgSignal) { + // count signal PDG + nSignal++; + auto child0 = + o2::mcutils::MCTrackNavigator::getDaughter0(track, *tracks); + auto child1 = + o2::mcutils::MCTrackNavigator::getDaughter1(track, *tracks); + if (child0 != nullptr && child1 != nullptr) { + // check for parent-child relations + auto pdg0 = child0->GetPdgCode(); + auto pdg1 = child1->GetPdgCode(); + std::cout << "First and last children of parent " << checkPdgSignal + << " are PDG0: " << pdg0 << " PDG1: " << pdg1 << "\n"; + if (std::abs(pdg0) == checkPdgDecay && + std::abs(pdg1) == checkPdgDecay && pdg0 == -pdg1) { + nLeptonPairs++; + if (child0->getToBeDone() && child1->getToBeDone()) { + nLeptonPairsToBeDone++; + } + } + } + } + } + } + std::cout << "#events: " << nEvents << "\n" + << "#leptons: " << nLeptons << "\n" + << "#antileptons: " << nAntileptons << "\n" + << "#signal: " << nSignal << "\n" + << "#lepton pairs: " << nLeptonPairs << "\n" + << "#lepton pairs to be done: " << nLeptonPairs << "\n"; + + if (nLeptonPairs == 0 || nLeptons == 0 || nAntileptons == 0) { + std::cerr << "Number of leptons, number of anti-leptons as well as number " + "of lepton pairs should all be greater than 1.\n"; + return 1; + } + if (nLeptonPairs != nLeptonPairsToBeDone) { + std::cerr << "The number of lepton pairs should be the same as the number " + "of lepton pairs which should be transported.\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGDQ/pythia8/decayer/jpsiPsi2Sdielectron.cfg b/MC/config/PWGDQ/pythia8/decayer/jpsiPsi2Sdielectron.cfg new file mode 100644 index 000000000..145dc8cec --- /dev/null +++ b/MC/config/PWGDQ/pythia8/decayer/jpsiPsi2Sdielectron.cfg @@ -0,0 +1,9 @@ +### Force J/psi and psi(2S) -> e+ e- + +### J/psi -> e+ e- +443:onMode = off # switch off all decay channels +443:onIfAll = 11 -11 # switch on e+ e- decay channel + +### psi(2S) -> e+ e- +100443:onMode = off # switch off all decay channels +100443:onIfAll = 11 -11 # switch on e+ e- decay channel diff --git a/MC/config/PWGDQ/pythia8/decayer/switchOffAllQuarkonia.cfg b/MC/config/PWGDQ/pythia8/decayer/switchOffAllQuarkonia.cfg new file mode 100644 index 000000000..2d5c57ade --- /dev/null +++ b/MC/config/PWGDQ/pythia8/decayer/switchOffAllQuarkonia.cfg @@ -0,0 +1,10 @@ +#switch off all quarkonia and B hadrons decays + +443:mayDecay off # J/psi +100443:mayDecay off # psi(2S) +10441:mayDecay off # chic0 +20443:mayDecay off # chic1 +445:mayDecay off # chic2 +553:mayDecay off # upsilon1S +100553:mayDecay off # upsilon2S +200553:mayDecay off # upsilon3S diff --git a/MC/config/PWGDQ/pythia8/decayer/switchOffAllQuarkoniaAndBhadrons.cfg b/MC/config/PWGDQ/pythia8/decayer/switchOffAllQuarkoniaAndBhadrons.cfg new file mode 100644 index 000000000..2711096f1 --- /dev/null +++ b/MC/config/PWGDQ/pythia8/decayer/switchOffAllQuarkoniaAndBhadrons.cfg @@ -0,0 +1,18 @@ +#switch off all quarkonia and B hadrons decays + +443:mayDecay off # J/psi +100443:mayDecay off # psi(2S) +10441:mayDecay off # chic0 +20443:mayDecay off # chic1 +445:mayDecay off # chic2 +553:mayDecay off # upsilon1S +100553:mayDecay off # upsilon2S +200553:mayDecay off # upsilon3S +511:mayDecay off # B0 +521:mayDecay off # B+ +531:mayDecay off # Bs +541:mayDecay off # Bc +5122:mayDecay off # Lambdab +5132:mayDecay off # Xib+ +5232:mayDecay off # Xib0 +5332:mayDecay off # Omegab diff --git a/MC/config/PWGDQ/pythia8/decayer/switchOffJpsi.cfg b/MC/config/PWGDQ/pythia8/decayer/switchOffJpsi.cfg new file mode 100644 index 000000000..4bc262efc --- /dev/null +++ b/MC/config/PWGDQ/pythia8/decayer/switchOffJpsi.cfg @@ -0,0 +1,4 @@ +#switch off jpsi decays + +443:mayDecay off # J/psi +100443:mayDecay off # psi(2S) diff --git a/MC/config/PWGDQ/pythia8/decayer/switchOffJpsiUpsilon.cfg b/MC/config/PWGDQ/pythia8/decayer/switchOffJpsiUpsilon.cfg new file mode 100644 index 000000000..e63c5365f --- /dev/null +++ b/MC/config/PWGDQ/pythia8/decayer/switchOffJpsiUpsilon.cfg @@ -0,0 +1,7 @@ +#switch off jpsi decays + +443:mayDecay off # J/psi +100443:mayDecay off # psi(2S) +553:mayDecay off # Upsilon(1S) +100553:mayDecay off # Upsilon(2S) +200553:mayDecay off # Upsilon(3S) diff --git a/MC/config/PWGDQ/pythia8/generator/pythia8_charmoniumAll_triggerGap.cfg b/MC/config/PWGDQ/pythia8/generator/pythia8_charmoniumAll_triggerGap.cfg new file mode 100644 index 000000000..d1c46cd7e --- /dev/null +++ b/MC/config/PWGDQ/pythia8/generator/pythia8_charmoniumAll_triggerGap.cfg @@ -0,0 +1,11 @@ +### beams +Beams:idA 2212 # proton +Beams:idB 2212 # proton +Beams:eCM 13600. # GeV + +### processes +Charmonium:all = on + +### decays +ParticleDecays:limitTau0 on +ParticleDecays:tau0Max 10. diff --git a/MC/config/PWGDQ/pythia8/generator/pythia8_hard_cr2.cfg b/MC/config/PWGDQ/pythia8/generator/pythia8_hard_cr2.cfg new file mode 100644 index 000000000..f8a606021 --- /dev/null +++ b/MC/config/PWGDQ/pythia8/generator/pythia8_hard_cr2.cfg @@ -0,0 +1,38 @@ +### beams +Beams:idA 2212 # proton +Beams:idB 2212 # proton +Beams:eCM 13600. # GeV + +### processes +HardQCD:hardccbar on +HardQCD:hardbbbar on + +### decays +ParticleDecays:limitTau0 on +ParticleDecays:tau0Max 10. + +### switching on Pythia Mode2 +ColourReconnection:mode 1 +ColourReconnection:allowDoubleJunRem off +ColourReconnection:m0 0.3 +ColourReconnection:allowJunctions on +ColourReconnection:junctionCorrection 1.20 +ColourReconnection:timeDilationMode 2 +ColourReconnection:timeDilationPar 0.18 +StringPT:sigma 0.335 +StringZ:aLund 0.36 +StringZ:bLund 0.56 +StringFlav:probQQtoQ 0.078 +StringFlav:ProbStoUD 0.2 +StringFlav:probQQ1toQQ0join 0.0275,0.0275,0.0275,0.0275 +MultiPartonInteractions:pT0Ref 2.15 +BeamRemnants:remnantMode 1 +BeamRemnants:saturation 5 + +# Correct decay lengths (wrong in PYTHIA8 decay table) +# Lb +5122:tau0 = 0.4390 +# Xic0 +4132:tau0 = 0.0455 +# OmegaC +4332:tau0 = 0.0803 diff --git a/MC/config/PWGDQ/pythia8/generator/pythia8_hf_OO_536.cfg b/MC/config/PWGDQ/pythia8/generator/pythia8_hf_OO_536.cfg new file mode 100644 index 000000000..c2f4e063d --- /dev/null +++ b/MC/config/PWGDQ/pythia8/generator/pythia8_hf_OO_536.cfg @@ -0,0 +1,19 @@ +### OO beams +Beams:idA = 1000080160 +Beams:idB = 1000080160 +Beams:eCM = 5360.0 ### energy + +Beams:frameType = 1 +ParticleDecays:limitTau0 = on +ParticleDecays:tau0Max = 10. ### match alice: 1cm/c = 10.0mm/c + +### Save some CPU at init of jobs +### To avoid refitting, add the following lines to your configuration file: +HeavyIon:SigFitNGen = 0 +HeavyIon:SigFitDefPar = 2.15,18.42,0.33 + +Random:setSeed = on + +### processes +HardQCD:hardccbar on # scatterings g-g / q-qbar -> c-cbar +HardQCD:hardbbbar on # scatterings g-g / q-qbar -> b-bbar diff --git a/MC/config/PWGDQ/pythia8/generator/pythia8_hf_pO_961.cfg b/MC/config/PWGDQ/pythia8/generator/pythia8_hf_pO_961.cfg new file mode 100644 index 000000000..4a1b25f2e --- /dev/null +++ b/MC/config/PWGDQ/pythia8/generator/pythia8_hf_pO_961.cfg @@ -0,0 +1,19 @@ +### OO beams +Beams:frameType 2 # back-to-back beams of different energies and particles +Beams:idA 2212 # proton +Beams:idB 1000080160 # Oxygen +Beams:eA 6800. # Energy of proton beam in GeV moving in the +z direction +Beams:eB 3400. # Energy in GeV per Oxygen nucleon (6.8 Z TeV) moving in the -z direction + +ParticleDecays:limitTau0 = on +ParticleDecays:tau0Max = 10. ### match alice: 1cm/c = 10.0mm/c + +### To avoid refitting, add the following lines to your configuration file: +HeavyIon:SigFitNGen = 0 +HeavyIon:SigFitDefPar = 2.17,17.56,0.30 + +Random:setSeed = on + +### processes +HardQCD:hardccbar on # scatterings g-g / q-qbar -> c-cbar +HardQCD:hardbbbar on # scatterings g-g / q-qbar -> b-bbar diff --git a/MC/config/PWGDQ/pythia8/generator/pythia8_inel_cr2.cfg b/MC/config/PWGDQ/pythia8/generator/pythia8_inel_cr2.cfg new file mode 100644 index 000000000..92932f7bf --- /dev/null +++ b/MC/config/PWGDQ/pythia8/generator/pythia8_inel_cr2.cfg @@ -0,0 +1,36 @@ +Beams:idA 2212 # proton +Beams:idB 2212 # proton +Beams:eCM 13600. # GeV + +### processes +SoftQCD:inelastic on # all inelastic processes + +### decays +ParticleDecays:limitTau0 on +ParticleDecays:tau0Max 10. + +### switching on Pythia Mode2 +ColourReconnection:mode 1 +ColourReconnection:allowDoubleJunRem off +ColourReconnection:m0 0.3 +ColourReconnection:allowJunctions on +ColourReconnection:junctionCorrection 1.20 +ColourReconnection:timeDilationMode 2 +ColourReconnection:timeDilationPar 0.18 +StringPT:sigma 0.335 +StringZ:aLund 0.36 +StringZ:bLund 0.56 +StringFlav:probQQtoQ 0.078 +StringFlav:ProbStoUD 0.2 +StringFlav:probQQ1toQQ0join 0.0275,0.0275,0.0275,0.0275 +MultiPartonInteractions:pT0Ref 2.15 +BeamRemnants:remnantMode 1 +BeamRemnants:saturation 5 + +# Correct decay lengths (wrong in PYTHIA8 decay table) +# Lb +5122:tau0 = 0.4390 +# Xic0 +4132:tau0 = 0.0455 +# OmegaC +4332:tau0 = 0.0803 diff --git a/MC/config/PWGDQ/pythia8/generator/pythia8_inel_triggerGap_5TeV.cfg b/MC/config/PWGDQ/pythia8/generator/pythia8_inel_triggerGap_5TeV.cfg new file mode 100644 index 000000000..149fe1231 --- /dev/null +++ b/MC/config/PWGDQ/pythia8/generator/pythia8_inel_triggerGap_5TeV.cfg @@ -0,0 +1,11 @@ +### beams +Beams:idA 2212 # proton +Beams:idB 2212 # proton +Beams:eCM 5360. # GeV + +### processes +SoftQCD:inelastic on # all inelastic processes + +### decays +ParticleDecays:limitTau0 on +ParticleDecays:tau0Max 10. \ No newline at end of file diff --git a/MC/config/PWGDQ/pythia8/generator/pythia8_inel_triggerGap_OO5TeV.cfg b/MC/config/PWGDQ/pythia8/generator/pythia8_inel_triggerGap_OO5TeV.cfg new file mode 100644 index 000000000..ff4c5ae6f --- /dev/null +++ b/MC/config/PWGDQ/pythia8/generator/pythia8_inel_triggerGap_OO5TeV.cfg @@ -0,0 +1,16 @@ +### beams +Beams:idA 1000080160 # oxygen +Beams:idB 1000080160 # oxygen +Beams:eCM 5360. # GeV +Beams:frameType = 1 + +### processes +SoftQCD:inelastic on # all inelastic processes + +### decays +ParticleDecays:limitTau0 on +ParticleDecays:tau0Max 10. + +#### Heavy ion settings +HeavyIon:SigFitErr = 0.02,0.02,0.1,0.05,0.05,0.0,0.1,0.0 +HeavyIon:SigFitDefPar = 17.24,2.15,0.33,0.0,0.0,0.0,0.0,0.0 \ No newline at end of file diff --git a/MC/config/PWGDQ/pythia8/generator/pythia8_oniaAll_triggerGap.cfg b/MC/config/PWGDQ/pythia8/generator/pythia8_oniaAll_triggerGap.cfg new file mode 100644 index 000000000..6358e615d --- /dev/null +++ b/MC/config/PWGDQ/pythia8/generator/pythia8_oniaAll_triggerGap.cfg @@ -0,0 +1,13 @@ +### beams +Beams:idA 2212 # proton +Beams:idB 2212 # proton +Beams:eCM 13600. # GeV + +### processes +SoftQCD:inelastic on # all inelastic processes +OniaShower:all = on +OniaShower:ldmeFac = 5. + +### decays +ParticleDecays:limitTau0 on +ParticleDecays:tau0Max 10. diff --git a/MC/config/PWGDQ/pythia8/generator/pythia8_onia_triggerGap.cfg b/MC/config/PWGDQ/pythia8/generator/pythia8_onia_triggerGap.cfg new file mode 100644 index 000000000..841f84d70 --- /dev/null +++ b/MC/config/PWGDQ/pythia8/generator/pythia8_onia_triggerGap.cfg @@ -0,0 +1,12 @@ +### beams +Beams:idA 2212 # proton +Beams:idB 2212 # proton +Beams:eCM 13600. # GeV + +### processes +SoftQCD:inelastic on # all inelastic processes +CharmoniumShower:all = on + +### decays +ParticleDecays:limitTau0 on +ParticleDecays:tau0Max 10. diff --git a/MC/config/PWGDQ/pythia8/generator/pythia8_onia_triggerGap_pp5TeV.cfg b/MC/config/PWGDQ/pythia8/generator/pythia8_onia_triggerGap_pp5TeV.cfg new file mode 100644 index 000000000..3b4d393b4 --- /dev/null +++ b/MC/config/PWGDQ/pythia8/generator/pythia8_onia_triggerGap_pp5TeV.cfg @@ -0,0 +1,12 @@ +### beams +Beams:idA 2212 # proton +Beams:idB 2212 # proton +Beams:eCM 5360. # GeV + +### processes +SoftQCD:inelastic on # all inelastic processes +CharmoniumShower:all = on + +### decays +ParticleDecays:limitTau0 on +ParticleDecays:tau0Max 10. diff --git a/MC/config/PWGDQ/trigger/selectDaughterFromHFwithinAcc.C b/MC/config/PWGDQ/trigger/selectDaughterFromHFwithinAcc.C index 44035a834..f7b406849 100644 --- a/MC/config/PWGDQ/trigger/selectDaughterFromHFwithinAcc.C +++ b/MC/config/PWGDQ/trigger/selectDaughterFromHFwithinAcc.C @@ -1,4 +1,4 @@ -R__ADD_INCLUDE_PATH($O2DPG_ROOT) +R__ADD_INCLUDE_PATH($O2DPG_MC_CONFIG_ROOT) #include #include "Generators/Trigger.h" diff --git a/MC/config/PWGEM/decaytables/decaytable_LMee.dat b/MC/config/PWGEM/decaytables/decaytable_LMee.dat index f35b10657..03f88f65a 100644 --- a/MC/config/PWGEM/decaytables/decaytable_LMee.dat +++ b/MC/config/PWGEM/decaytables/decaytable_LMee.dat @@ -277,66 +277,69 @@ 111 pi0 0 0 0 0.13498 0.000000 0.00000 2.56000E-05 0 1 1 0 0.988230 22 22 0 0 0 1 2 0.011740 22 -11 11 0 0 - 1 0 0.000033 -11 11 -11 11 0 - 221 eta 0 0 0 0.54786 0.000001 0.00000 1.50886E-07 0 1 - 1 0 0.394074 22 22 0 0 0 - 1 0 0.326777 111 111 111 0 0 - 1 0 0.000256 111 22 22 0 0 - 1 0 0.229189 211 -211 111 0 0 - 1 0 0.042199 22 211 -211 0 0 - 1 2 0.006899 22 11 -11 0 0 - 1 0 0.000309 22 13 -13 0 0 - 1 0 0.000006 -13 13 0 0 0 - 1 0 0.000024 -11 11 -11 11 0 - 1 0 0.000268 211 -211 11 -11 0 - 331 eta' 0 0 0 0.95778 0.000197 0.002 1.00336E-09 0 1 - 1 0 0.428744 211 -211 221 0 0 - 1 0 0.290822 22 113 0 0 0 - 1 0 0.221809 111 111 221 0 0 - 1 0 0.027458 22 223 0 0 0 - 1 0 0.000200 223 -11 11 0 0 - 1 0 0.021980 22 22 0 0 0 - 1 0 0.002136 111 111 111 0 0 - 1 0 0.000108 22 -13 13 0 0 - 1 0 0.003794 211 -211 111 0 0 - 1 0 0.000001 211 -211 211 -211 0 - 1 0 0.000180 211 -211 111 111 0 - 1 0 0.002384 211 -211 -11 11 0 - 1 2 0.000473 22 11 -11 0 0 - 113 rho0 0 0 0 0.77526 0.149100 0.40000 1.33000E-12 0 1 + 1 0 0.000000 -11 11 -11 11 0 + 1 0 0.000000 -11 11 0 0 0 + 221 eta 0 0 0 0.54786 0.001308 0.00000 1.50886E-07 0 1 + 1 0 0.393588 22 22 0 0 0 + 1 0 0.325618 111 111 111 0 0 + 1 0 0.000255 111 22 22 0 0 + 1 0 0.230188 211 -211 111 0 0 + 1 0 0.042769 22 211 -211 0 0 + 1 2 0.007004 22 11 -11 0 0 + 1 0 0.000310 22 13 -13 0 0 + 1 0 0.000006 -13 13 0 0 0 + 1 0 0.000001 -11 11 0 0 0 + 1 0 0.000000 -11 11 -11 11 0 + 1 0 0.000000 211 -211 11 -11 0 + 331 eta' 0 0 0 0.95778 0.000188 0.002 1.00336E-09 0 1 + 1 0 0.424687 211 -211 221 0 0 + 1 0 0.294809 22 113 0 0 0 + 1 0 0.223714 111 111 221 0 0 + 1 0 0.025190 22 223 0 0 0 + 1 0 0.000197 223 -11 11 0 0 + 1 0 0.023068 22 22 0 0 0 + 1 0 0.002499 111 111 111 0 0 + 1 0 0.000113 22 -13 13 0 0 + 1 0 0.003606 211 -211 111 0 0 + 1 0 0.000001 211 -211 211 -211 0 + 1 0 0.000180 211 -211 111 111 0 + 1 0 0.000000 211 -211 -11 11 0 + 1 2 0.000480 22 11 -11 0 0 + 1 0 0.000000 -11 11 -11 11 0 + 113 rho0 0 0 0 0.77526 0.14739 0.40000 1.33000E-12 0 1 1 3 0.988927 211 -211 0 0 0 1 0 0.009900 211 -211 22 0 0 - 1 0 0.000600 111 22 0 0 0 - 1 0 0.000300 221 22 0 0 0 - 1 0 0.000045 111 111 22 0 0 - 1 0 0.000047 11 -11 0 0 0 - 1 0 0.000046 13 -13 0 0 0 + 1 0 0.000467 111 22 0 0 0 + 1 0 0.000300 221 22 0 0 0 + 1 0 0.000045 111 111 22 0 0 + 1 0 0.000047 11 -11 0 0 0 + 1 0 0.000046 13 -13 0 0 0 1 0 0.000101 211 -211 111 0 0 1 0 0.000018 -211 211 -211 211 0 1 0 0.000016 -211 211 111 111 0 - 223 omega 0 0 0 0.78265 0.008490 0.10000 2.33000E-11 0 1 - 1 1 0.899500 211 -211 111 0 0 - 1 0 0.083500 22 111 0 0 0 - 1 3 0.015380 211 -211 0 0 0 - 1 0 0.000460 221 22 0 0 0 - 1 0 0.000140 111 13 -13 0 0 - 1 0 0.000770 111 11 -11 0 0 - 1 0 0.000074 11 -11 0 0 0 - 1 0 0.000070 111 111 22 0 0 - 1 0 0.000100 13 -13 0 0 0 - 333 phi 0 0 0 1.01946 0.004266 0.01500 4.63000E-11 0 1 - 1 3 0.489092 321 -321 0 0 0 - 1 3 0.342092 130 310 0 0 0 + 223 omega 0 0 0 0.78266 0.00868 0.10000 2.33000E-11 0 1 + 1 1 0.892458 211 -211 111 0 0 + 1 0 0.083264 22 111 0 0 0 + 1 3 0.015349 211 -211 0 0 0 + 1 0 0.000448 221 22 0 0 0 + 1 0 0.000134 111 13 -13 0 0 + 1 0 0.000767 111 11 -11 0 0 + 1 0 0.000074 11 -11 0 0 0 + 1 0 0.000067 111 111 22 0 0 + 1 0 0.000074 13 -13 0 0 0 + 333 phi 0 0 0 1.01946 0.004249 0.01500 4.63000E-11 0 1 + 1 3 0.498884 321 -321 0 0 0 + 1 3 0.336407 130 310 0 0 0 1 0 0.042259 213 -211 0 0 0 1 0 0.042259 -213 211 0 0 0 1 0 0.042259 113 111 0 0 0 1 1 0.026534 211 -211 111 0 0 - 1 0 0.013108 221 22 0 0 0 - 1 0 0.001274 111 22 0 0 0 - 1 0 0.000297 11 -11 0 0 0 - 1 0 0.000283 13 -13 0 0 0 + 1 0 0.013062 221 22 0 0 0 + 1 0 0.001328 111 22 0 0 0 + 1 0 0.000296 11 -11 0 0 0 + 1 0 0.000286 13 -13 0 0 0 1 0 0.000108 221 -11 11 0 0 - 1 0 0.000075 211 -211 0 0 0 + 1 0 0.000095 211 -211 0 0 0 1 0 0.000047 223 111 0 0 0 1 0 0.000041 -211 211 22 0 0 1 0 0.000113 111 111 22 0 0 @@ -344,11 +347,17 @@ 1 0 0.000013 111 -11 11 0 0 1 0 0.000073 111 221 22 0 0 1 0 0.000063 331 22 0 0 0 - 1 0 0.000014 -13 13 22 0 0 + 1 0 0.000014 -13 13 22 0 0 443 J/Psi 0 0 0 3.09690 0.000093 0.00000 2.13000E-09 0 1 1 12 0.881200 82 -82 0 0 0 1 0 0.059710 11 -11 0 0 0 1 0 0.059400 13 -13 0 0 0 + 100443 psi' 0 0 0 3.68609 0.000304 0.00000 6.52000E-10 0 1 + 1 0 0.007720 11 -11 0 0 0 + 1 0 0.007700 13 -13 0 0 0 + 553 Upsilon 0 0 0 9.39870 0.000054 0.00000 3.67000E-09 0 1 + 1 0 0.023900 11 -11 0 0 0 + 1 0 0.024800 13 -13 0 0 0 310 K0s 0 0 0 0.497611 0.00000 0.00000 2.68000E+01 0 1 1 0 0.306540 111 111 0 0 0 1 0 0.690449 -211 211 0 0 0 diff --git a/MC/config/PWGEM/external/generator/GeneratorBeautyToEle_EvtGen.C b/MC/config/PWGEM/external/generator/GeneratorBeautyToEle_EvtGen.C index b33e44cf1..4973b24ae 100644 --- a/MC/config/PWGEM/external/generator/GeneratorBeautyToEle_EvtGen.C +++ b/MC/config/PWGEM/external/generator/GeneratorBeautyToEle_EvtGen.C @@ -4,8 +4,8 @@ //o2-sim -j 4 -n 10 -g external -t external -m "PIPE ITS TPC" -o sgn --configFile GeneratorHF_bbbar_midy.ini // // -R__ADD_INCLUDE_PATH($O2DPG_ROOT/MC/config/PWGDQ/EvtGen) -R__ADD_INCLUDE_PATH($O2DPG_ROOT/MC/config/PWGHF/external/generator) +R__ADD_INCLUDE_PATH($O2DPG_MC_CONFIG_ROOT/MC/config/PWGDQ/EvtGen) +R__ADD_INCLUDE_PATH($O2DPG_MC_CONFIG_ROOT/MC/config/PWGHF/external/generator) #include "GeneratorEvtGen.C" #include "GeneratorHF.C" @@ -16,7 +16,7 @@ GeneratorBeautyToEle_EvtGen(double rapidityMin = -2., double rapidityMax = 2., b auto gen = new o2::eventgen::GeneratorEvtGen(); gen->setRapidity(rapidityMin,rapidityMax); gen->setPDG(5); - TString pathO2table = gSystem->ExpandPathName("$O2DPG_ROOT/MC/config/PWGDQ/pythia8/decayer/switchOffCBhadrons.cfg"); + TString pathO2table = gSystem->ExpandPathName("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/pythia8/decayer/switchOffCBhadrons.cfg"); gen->readFile(pathO2table.Data()); diff --git a/MC/config/PWGEM/external/generator/GeneratorCharmToEle_EvtGen.C b/MC/config/PWGEM/external/generator/GeneratorCharmToEle_EvtGen.C index 60082d27c..71210eca7 100644 --- a/MC/config/PWGEM/external/generator/GeneratorCharmToEle_EvtGen.C +++ b/MC/config/PWGEM/external/generator/GeneratorCharmToEle_EvtGen.C @@ -4,8 +4,8 @@ //o2-sim -j 4 -n 10 -g external -t external -m "PIPE ITS TPC" -o sgn --configFile GeneratorHF_bbbar_midy.ini // // -R__ADD_INCLUDE_PATH($O2DPG_ROOT/MC/config/PWGDQ/EvtGen) -R__ADD_INCLUDE_PATH($O2DPG_ROOT/MC/config/PWGHF/external/generator) +R__ADD_INCLUDE_PATH($O2DPG_MC_CONFIG_ROOT/MC/config/PWGDQ/EvtGen) +R__ADD_INCLUDE_PATH($O2DPG_MC_CONFIG_ROOT/MC/config/PWGHF/external/generator) #include "GeneratorEvtGen.C" #include "GeneratorHF.C" @@ -16,7 +16,7 @@ GeneratorCharmToEle_EvtGen(double rapidityMin = -2., double rapidityMax = 2., bo auto gen = new o2::eventgen::GeneratorEvtGen(); gen->setRapidity(rapidityMin,rapidityMax); gen->setPDG(4); - TString pathO2table = gSystem->ExpandPathName("$O2DPG_ROOT/MC/config/PWGDQ/pythia8/decayer/switchOffChadrons.cfg"); + TString pathO2table = gSystem->ExpandPathName("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/pythia8/decayer/switchOffChadrons.cfg"); gen->readFile(pathO2table.Data()); gen->setVerbose(verbose); diff --git a/MC/config/PWGEM/external/generator/GeneratorCocktailLF.C b/MC/config/PWGEM/external/generator/GeneratorCocktailLF.C index becd04ab3..7f35f49d8 100644 --- a/MC/config/PWGEM/external/generator/GeneratorCocktailLF.C +++ b/MC/config/PWGEM/external/generator/GeneratorCocktailLF.C @@ -2,8 +2,8 @@ // // -R__ADD_INCLUDE_PATH($O2DPG_ROOT/MC/config/PWGDQ/EvtGen) -R__ADD_INCLUDE_PATH($O2DPG_ROOT/MC/config/PWGDQ/external/generator) +R__ADD_INCLUDE_PATH($O2DPG_MC_CONFIG_ROOT/MC/config/PWGDQ/EvtGen) +R__ADD_INCLUDE_PATH($O2DPG_MC_CONFIG_ROOT/MC/config/PWGDQ/external/generator) #include "GeneratorCocktail.C" #include "GeneratorEvtGen.C" @@ -88,12 +88,12 @@ GeneratorCocktailLF(int nPart, bool ispp) auto genCocktailEvtGen = new o2::eventgen::GeneratorEvtGen(); // EXODUS decayer - TString O2DPG_ROOT = TString(getenv("O2DPG_ROOT")); + TString O2DPG_ROOT = TString(getenv("O2DPG_MC_CONFIG_ROOT")); auto decayer = new PythiaDecayerConfig(); decayer->SetDecayerExodus(); - TString useLMeeDecaytable = "$O2DPG_ROOT/MC/config/PWGEM/decaytables/decaytable_LMee.dat"; - useLMeeDecaytable=useLMeeDecaytable.ReplaceAll("$O2DPG_ROOT",O2DPG_ROOT); - useLMeeDecaytable=useLMeeDecaytable.ReplaceAll("${O2DPG_ROOT}",O2DPG_ROOT); + TString useLMeeDecaytable = "${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/decaytables/decaytable_LMee.dat"; + useLMeeDecaytable=useLMeeDecaytable.ReplaceAll("$O2DPG_MC_CONFIG_ROOT",O2DPG_ROOT); + useLMeeDecaytable=useLMeeDecaytable.ReplaceAll("${O2DPG_MC_CONFIG_ROOT}",O2DPG_ROOT); decayer->SetDecayTableFile(useLMeeDecaytable.Data()); decayer->ReadDecayTable(); diff --git a/MC/config/PWGEM/external/generator/GeneratorEMCocktailV2.C b/MC/config/PWGEM/external/generator/GeneratorEMCocktailV2.C index 9e2deac45..0d35b4378 100644 --- a/MC/config/PWGEM/external/generator/GeneratorEMCocktailV2.C +++ b/MC/config/PWGEM/external/generator/GeneratorEMCocktailV2.C @@ -1,4 +1,5 @@ -R__ADD_INCLUDE_PATH($O2DPG_ROOT/MC/config/PWGDQ/external/generator) +R__ADD_INCLUDE_PATH($O2DPG_MC_CONFIG_ROOT/MC/config/PWGDQ/external/generator) +R__LOAD_LIBRARY(libpythia6) #include "GeneratorCocktail.C" namespace o2 { @@ -47,33 +48,31 @@ public: {GeneratorParamEMlibV2::kEtaprime, "Etaprime", 331, 0x00010}, {GeneratorParamEMlibV2::kPhi, "Phi", 333, 0x00020}, {GeneratorParamEMlibV2::kJpsi, "Jpsi", 443, 0x00040}, - {GeneratorParamEMlibV2::kSigma0, "Sigma0", 3212, 0x00080}, - {GeneratorParamEMlibV2::kK0s, "K0short", 310, 0x00100}, - {GeneratorParamEMlibV2::kDeltaPlPl, "DeltaPlPl", 2224, 0x00200}, - {GeneratorParamEMlibV2::kDeltaPl, "DeltaPl", 2214, 0x00400}, - {GeneratorParamEMlibV2::kDeltaMi, "DeltaMi", 1114, 0x00800}, - {GeneratorParamEMlibV2::kDeltaZero, "DeltaZero", 2114, 0x01000}, - {GeneratorParamEMlibV2::kRhoPl, "RhoPl", 213, 0x02000}, - {GeneratorParamEMlibV2::kRhoMi, "RhoMi", 213, 0x04000}, - {GeneratorParamEMlibV2::kK0star, "K0star", 313, 0x08000}, - {GeneratorParamEMlibV2::kK0l, "K0long", 130, 0x10000}, - {GeneratorParamEMlibV2::kLambda, "Lambda", 3122, 0x20000}, - {GeneratorParamEMlibV2::kKPl, "KPl", 321, 0x40000}, - {GeneratorParamEMlibV2::kKMi, "KMi", 321, 0x80000}, - {GeneratorParamEMlibV2::kOmegaPl, "OmegaPl", -3334, 0x100000}, - {GeneratorParamEMlibV2::kOmegaMi, "OmegaMi", 3334, 0x200000}, - {GeneratorParamEMlibV2::kXiPl, "XiPl", -3312, 0x400000}, - {GeneratorParamEMlibV2::kXiMi, "XiMi", 3312, 0x800000}, - {GeneratorParamEMlibV2::kSigmaPl, "SigamPl", 3224, 0x1000000}, - {GeneratorParamEMlibV2::kSigmaMi, "SigmaMi", 3114, 0x2000000}, + {GeneratorParamEMlibV2::kPsi2S, "Psi2S", 100443, 0x00080}, + {GeneratorParamEMlibV2::kUpsilon, "Upsilon", 553, 0x00100}, + {GeneratorParamEMlibV2::kSigma0, "Sigma0", 3212, 0x00200}, + {GeneratorParamEMlibV2::kK0s, "K0short", 310, 0x00400}, + {GeneratorParamEMlibV2::kDeltaPlPl, "DeltaPlPl", 2224, 0x00800}, + {GeneratorParamEMlibV2::kDeltaPl, "DeltaPl", 2214, 0x01000}, + {GeneratorParamEMlibV2::kDeltaMi, "DeltaMi", 1114, 0x02000}, + {GeneratorParamEMlibV2::kDeltaZero, "DeltaZero", 2114, 0x04000}, + {GeneratorParamEMlibV2::kRhoPl, "RhoPl", 213, 0x08000}, + {GeneratorParamEMlibV2::kRhoMi, "RhoMi", 213, 0x10000}, + {GeneratorParamEMlibV2::kK0star, "K0star", 313, 0x20000}, + {GeneratorParamEMlibV2::kK0l, "K0long", 130, 0x40000}, + {GeneratorParamEMlibV2::kLambda, "Lambda", 3122, 0x80000}, + {GeneratorParamEMlibV2::kKPl, "KPl", 321, 0x100000}, + {GeneratorParamEMlibV2::kKMi, "KMi", 321, 0x200000}, + {GeneratorParamEMlibV2::kOmegaPl, "OmegaPl", -3334, 0x400000}, + {GeneratorParamEMlibV2::kOmegaMi, "OmegaMi", 3334, 0x800000}, + {GeneratorParamEMlibV2::kXiPl, "XiPl", -3312, 0x1000000}, + {GeneratorParamEMlibV2::kXiMi, "XiMi", 3312, 0x2000000}, + {GeneratorParamEMlibV2::kSigmaPl, "SigamPl", 3224, 0x4000000}, + {GeneratorParamEMlibV2::kSigmaMi, "SigmaMi", 3114, 0x8000000}, {GeneratorParamEMlibV2::kDirectRealGamma, "DirectRealGamma", 22, - 0x4000000}, + 0x10000000}, {GeneratorParamEMlibV2::kDirectVirtGamma, "DirectVirtGamma", 22, - 0x8000000}}; - - static const int nHadrons = - GeneratorParamEMlibV2::kNParticles - - 2; // total number of particles minus DirectRealGamma and DirectVirtGamma + 0x20000000}}; void SetUseYWeighting(Bool_t useYWeighting) { fUseYWeighting = useYWeighting; @@ -121,11 +120,11 @@ public: Bool_t SetPtParametrizations() { TF1 *tempFct = NULL; - for (Int_t i = 0; i < nHadrons + 1; i++) { + for (Int_t i = 0; i < GeneratorParamEMlibV2::kNHadrons + 1; i++) { tempFct = GeneratorParamEMlibV2::GetPtParametrization(i); if (!tempFct) return kFALSE; - if (i < nHadrons) + if (i < GeneratorParamEMlibV2::kNHadrons) fPtParametrization[i] = new TF1(*tempFct); else fParametrizationProton = new TF1(*tempFct); @@ -142,7 +141,7 @@ public: //_________________________________________________________________________ Bool_t SetPtYDistributions() { TH2F *tempPtY = NULL; - for (Int_t i = 0; i < nHadrons; i++) { + for (Int_t i = 0; i < GeneratorParamEMlibV2::kNHadrons; i++) { tempPtY = GeneratorParamEMlibV2::GetPtYDistribution(i); if (tempPtY) fPtYDistribution[i] = new TH2F(*tempPtY); @@ -164,9 +163,9 @@ public: }*/ TF1 *GetPtParametrization(Int_t np) { - if (np < nHadrons) + if (np < GeneratorParamEMlibV2::kNHadrons) return fPtParametrization[np]; - else if (np == nHadrons) + else if (np == GeneratorParamEMlibV2::kNHadrons) return fParametrizationProton; else return NULL; @@ -175,7 +174,7 @@ public: TH1D *GetMtScalingFactors() { return fMtScalingFactorHisto; } TH2F *GetPtYDistribution(Int_t np) { - if (np < nHadrons) + if (np < GeneratorParamEMlibV2::kNHadrons) return fPtYDistribution[np]; else return NULL; @@ -386,10 +385,10 @@ private: Int_t fNPart; // multiplicity of each source per event Double_t fYieldArray[GeneratorParamEMlibV2::kNParticles]; // array of dN/dy // for each source - TF1 *fPtParametrization[nHadrons]; // pt paramtrizations + TF1 *fPtParametrization[GeneratorParamEMlibV2::kNHadrons]; // pt paramtrizations TF1 *fParametrizationProton; // TH1D *fMtScalingFactorHisto; // mt scaling factors - TH2F *fPtYDistribution[nHadrons]; // pt-y distribution + TH2F *fPtYDistribution[GeneratorParamEMlibV2::kNHadrons]; // pt-y distribution Double_t fPtMin; Double_t fPtMax; Double_t fYMin; @@ -434,11 +433,11 @@ GenerateEMCocktail(Int_t collisionsSystem = GeneratorParamEMlibV2::kpp7TeV, Double_t yGenRange = 0.1, TString useLMeeDecaytable = "", Int_t weightingMode = 1) { - TString O2DPG_ROOT = TString(getenv("O2DPG_ROOT")); - paramFile=paramFile.ReplaceAll("$O2DPG_ROOT",O2DPG_ROOT); - paramFile=paramFile.ReplaceAll("${O2DPG_ROOT}",O2DPG_ROOT); - useLMeeDecaytable=useLMeeDecaytable.ReplaceAll("$O2DPG_ROOT",O2DPG_ROOT); - useLMeeDecaytable=useLMeeDecaytable.ReplaceAll("${O2DPG_ROOT}",O2DPG_ROOT); + TString O2DPG_ROOT = TString(getenv("O2DPG_MC_CONFIG_ROOT")); + paramFile=paramFile.ReplaceAll("$O2DPG_MC_CONFIG_ROOT",O2DPG_ROOT); + paramFile=paramFile.ReplaceAll("${O2DPG_MC_CONFIG_ROOT}",O2DPG_ROOT); + useLMeeDecaytable=useLMeeDecaytable.ReplaceAll("$O2DPG_MC_CONFIG_ROOT",O2DPG_ROOT); + useLMeeDecaytable=useLMeeDecaytable.ReplaceAll("${O2DPG_MC_CONFIG_ROOT}",O2DPG_ROOT); if (paramFile.BeginsWith("alien://")){ TGrid::Connect("alien://"); } diff --git a/MC/config/PWGEM/external/generator/GeneratorHFToEleFull_EvtGen.C b/MC/config/PWGEM/external/generator/GeneratorHFToEleFull_EvtGen.C index 0cebace65..9862591b0 100644 --- a/MC/config/PWGEM/external/generator/GeneratorHFToEleFull_EvtGen.C +++ b/MC/config/PWGEM/external/generator/GeneratorHFToEleFull_EvtGen.C @@ -1,6 +1,6 @@ // // -R__ADD_INCLUDE_PATH($O2DPG_ROOT/MC/config/PWGDQ/EvtGen) +R__ADD_INCLUDE_PATH($O2DPG_MC_CONFIG_ROOT/MC/config/PWGDQ/EvtGen) #include "GeneratorEvtGen.C" @@ -17,9 +17,9 @@ GeneratorHFToEleFull_EvtGen(bool forcedecay = true, bool allHF = true) auto gen = new o2::eventgen::GeneratorEvtGen(); TString pathO2table; if (allHF) { - pathO2table = gSystem->ExpandPathName("$O2DPG_ROOT/MC/config/PWGDQ/pythia8/decayer/switchOffCBhadrons.cfg"); + pathO2table = gSystem->ExpandPathName("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/pythia8/decayer/switchOffCBhadrons.cfg"); } else { - pathO2table = gSystem->ExpandPathName("$O2DPG_ROOT/MC/config/PWGDQ/pythia8/decayer/switchOffChadrons.cfg"); + pathO2table = gSystem->ExpandPathName("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/pythia8/decayer/switchOffChadrons.cfg"); } gen->readFile(pathO2table.Data()); diff --git a/MC/config/PWGEM/external/generator/Generator_pythia8_GapTriggered_DYll.C b/MC/config/PWGEM/external/generator/Generator_pythia8_GapTriggered_DYll.C new file mode 100644 index 000000000..3a1ff8df1 --- /dev/null +++ b/MC/config/PWGEM/external/generator/Generator_pythia8_GapTriggered_DYll.C @@ -0,0 +1,151 @@ +#include "Pythia8/Pythia.h" +#include "Pythia8/HeavyIons.h" +#include "FairGenerator.h" +#include "FairPrimaryGenerator.h" +#include "Generators/GeneratorPythia8.h" +#include "TString.h" +#include "TRandom3.h" +#include "TParticlePDG.h" +#include "TDatabasePDG.h" + +#include +#include + +using namespace Pythia8; + +class GeneratorPythia8GapTriggeredDY : public o2::eventgen::GeneratorPythia8 +{ +public: + /// default constructor + GeneratorPythia8GapTriggeredDY() = default; + + /// constructor + GeneratorPythia8GapTriggeredDY(TString configsignal, int leptonPdg = 11, int lInputTriggerRatio = 5, int lInputExternalID = 1, int idA = 2212, int idB = 2212, float eCM = 13600.0) + { + lGeneratedEvents = 0; + lInverseTriggerRatio = lInputTriggerRatio; + lExternalID = lInputExternalID; + mLeptonPdg = leptonPdg; + + auto seed = (gRandom->TRandom::GetSeed() % 900000000); + + int offset = (int)(gRandom->Uniform(lInverseTriggerRatio)); // create offset to mitigate edge effects due to small number of events per job + lGeneratedEvents += offset; + + cout << "Initalizing extra PYTHIA object used to generate min-bias events..." << endl; + TString pathconfigMB = gSystem->ExpandPathName("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/generator/pythia8_MB_gapevent.cfg"); + pythiaObjectMinimumBias.readFile(pathconfigMB.Data()); + pythiaObjectMinimumBias.readString("Random:setSeed on"); + pythiaObjectMinimumBias.readString("Random:seed " + std::to_string(seed)); + // overwrite basic configration + pythiaObjectMinimumBias.readString(Form("Beams:idA %d", idA)); + pythiaObjectMinimumBias.readString(Form("Beams:idB %d", idB)); + pythiaObjectMinimumBias.readString(Form("Beams:eCM %f", eCM)); + pythiaObjectMinimumBias.init(); + cout << "Initalization complete" << endl; + cout << "Initalizing extra PYTHIA object used to generate signal events..." << endl; + TString pathconfigSignal = gSystem->ExpandPathName(configsignal.Data()); + pythiaObjectSignal.readFile(pathconfigSignal.Data()); + pythiaObjectSignal.readString("Random:setSeed on"); + pythiaObjectSignal.readString("Random:seed " + std::to_string(seed)); + pythiaObjectSignal.readString(Form("23:onIfMatch %d %d", -mLeptonPdg, mLeptonPdg)); + // overwrite basic configration + pythiaObjectSignal.readString(Form("Beams:idA %d", idA)); + pythiaObjectSignal.readString(Form("Beams:idB %d", idB)); + pythiaObjectSignal.readString(Form("Beams:eCM %f", eCM)); + + pythiaObjectSignal.init(); + cout << "Initalization complete" << endl; + addSubGenerator(0, "default generator"); + addSubGenerator(lExternalID, "Drell-Yan"); + } + + /// Destructor + ~GeneratorPythia8GapTriggeredDY() = default; + + void setZRapidity(float yMin, float yMax) + { + mZRapidityMin = yMin; + mZRapidityMax = yMax; + }; + +protected: + //__________________________________________________________________ + Bool_t generateEvent() override + { + /// reset event + mPythia.event.reset(); + + // Simple straightforward check to alternate generators + if (lGeneratedEvents % lInverseTriggerRatio == 0) { + // Generate event of interest + Bool_t lGenerationOK = kFALSE; + while (!lGenerationOK) { + if (pythiaObjectSignal.next()) { + lGenerationOK = selectEvent(pythiaObjectSignal.event); + } + } + mPythia.event = pythiaObjectSignal.event; + notifySubGenerator(lExternalID); + } else { + // Generate minimum-bias event + Bool_t lGenerationOK = kFALSE; + while (!lGenerationOK) { + lGenerationOK = pythiaObjectMinimumBias.next(); + } + mPythia.event = pythiaObjectMinimumBias.event; + notifySubGenerator(0); + } + + lGeneratedEvents++; + // mPythia.next(); + + return true; + } + + bool selectEvent(const Pythia8::Event& event) + { + for (size_t iPart = 0; iPart < event.size(); ++iPart) { + if (event[iPart].id() == 23 && event[iPart].daughterList().size() == 2 && (mZRapidityMin < event[iPart].y() && event[iPart].y() < mZRapidityMax) + && std::abs(event[event[iPart].daughter1()].id()) == mLeptonPdg && std::abs(event[event[iPart].daughter2()].id()) == mLeptonPdg && event[event[iPart].daughter1()].id() * event[event[iPart].daughter2()].id() < 0) { // Z/gamma* -> l+l- + printf("Z/gamma* is found. rapidity = %f, event[iPart].daughterList().size() = %zu\n", event[iPart].y(), event[iPart].daughterList().size()); + printf("event[event[iPart].daughter1()].id() = %d\n", event[event[iPart].daughter1()].id()); + printf("event[event[iPart].daughter2()].id() = %d\n", event[event[iPart].daughter2()].id()); + return true; + } + } // end of particle loop + return false; + }; + +private: + // Interface to override import particles + Pythia8::Event mOutputEvent; + + // Properties of selection + int mLeptonPdg; + float mZRapidityMin; + float mZRapidityMax; + + // Control gap-triggering + Long64_t lGeneratedEvents; + int lInverseTriggerRatio; + // ID for different generators + int lExternalID; + + // Base event generators + Pythia8::Pythia pythiaObjectMinimumBias; ///Minimum bias collision generator + Pythia8::Pythia pythiaObjectSignal; ///Signal collision generator +}; + +// Predefined generators: + +FairGenerator* GeneratorPythia8GapTriggeredDYll(int inputTriggerRatio, int inputExternalID, int pdgLepton = 11, float yMin = -1.5, float yMax = 1.5, int idA = 2212, int idB = 2212, float eCM = 13600.0) +{ + auto myGen = new GeneratorPythia8GapTriggeredDY("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/generator/pythia8_DY.cfg", pdgLepton, inputTriggerRatio, inputExternalID, idA, idB, eCM); + auto seed = (gRandom->TRandom::GetSeed() % 900000000); + myGen->readString("Random:setSeed on"); + myGen->readString("Random:seed " + std::to_string(seed)); + myGen->setZRapidity(yMin, yMax); + return myGen; +} + diff --git a/MC/config/PWGEM/external/generator/Generator_pythia8_GapTriggered_HFLepton.C b/MC/config/PWGEM/external/generator/Generator_pythia8_GapTriggered_HFLepton.C index 2b90ca1b9..76e00b406 100644 --- a/MC/config/PWGEM/external/generator/Generator_pythia8_GapTriggered_HFLepton.C +++ b/MC/config/PWGEM/external/generator/Generator_pythia8_GapTriggered_HFLepton.C @@ -13,28 +13,34 @@ using namespace Pythia8; -class GeneratorPythia8GapTriggeredHFLepton : public o2::eventgen::GeneratorPythia8{ +class GeneratorPythia8GapTriggeredHFLepton : public o2::eventgen::GeneratorPythia8 +{ public: /// default constructor GeneratorPythia8GapTriggeredHFLepton() = default; /// constructor - GeneratorPythia8GapTriggeredHFLepton( TString configsignal, int quarkPdg = 4, int lInputTriggerRatio = 5){ + GeneratorPythia8GapTriggeredHFLepton(TString configsignal, int quarkPdg = 4, int lInputTriggerRatio = 5, int lInputExternalID = 0) + { - lGeneratedEvents=0; - lInverseTriggerRatio=lInputTriggerRatio; - mQuarkPdg=quarkPdg; + lGeneratedEvents = 0; + lInverseTriggerRatio = lInputTriggerRatio; + lExternalID = lInputExternalID; + mQuarkPdg = quarkPdg; auto seed = (gRandom->TRandom::GetSeed() % 900000000); - cout<<"Initalizing extra PYTHIA object used to generate min-bias events..."<ExpandPathName("$O2DPG_ROOT/MC/config/PWGEM/pythia8/generator/pythia8_MB_gapevent.cfg"); + int offset = (int)(gRandom->Uniform(lInverseTriggerRatio)); // create offset to mitigate edge effects due to small number of events per job + lGeneratedEvents += offset; + + cout << "Initalizing extra PYTHIA object used to generate min-bias events..." << endl; + TString pathconfigMB = gSystem->ExpandPathName("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/generator/pythia8_MB_gapevent.cfg"); pythiaObjectMinimumBias.readFile(pathconfigMB.Data()); pythiaObjectMinimumBias.readString("Random:setSeed on"); pythiaObjectMinimumBias.readString("Random:seed " + std::to_string(seed)); pythiaObjectMinimumBias.init(); cout << "Initalization complete" << endl; - cout<<"Initalizing extra PYTHIA object used to generate signal events..."<ExpandPathName(configsignal.Data()); pythiaObjectSignal.readFile(pathconfigSignal.Data()); pythiaObjectSignal.readString("Random:setSeed on"); @@ -44,13 +50,21 @@ public: // flag the generators using type // addCocktailConstituent(type, "interesting"); // addCocktailConstitent(0, "minbias"); - + // Add Sub generators + addSubGenerator(0, "default generator"); + addSubGenerator(1, "charm lepton"); + addSubGenerator(2, "beauty forced decay"); + addSubGenerator(3, "beauty no foced decay"); } /// Destructor ~GeneratorPythia8GapTriggeredHFLepton() = default; - void addTriggerOnDaughter(int nb, int pdg) { mNbDaughter = nb; mPdgDaughter = pdg; }; + void addTriggerOnDaughter(int nb, int pdg) + { + mNbDaughter = nb; + mPdgDaughter = pdg; + }; void setQuarkRapidity(float yMin, float yMax) { mQuarkRapidityMin = yMin; @@ -64,27 +78,30 @@ public: protected: //__________________________________________________________________ - Bool_t generateEvent() override { + Bool_t generateEvent() override + { /// reset event mPythia.event.reset(); // Simple straightforward check to alternate generators - if( lGeneratedEvents % lInverseTriggerRatio == 0 ){ + if (lGeneratedEvents % lInverseTriggerRatio == 0) { // Generate event of interest Bool_t lGenerationOK = kFALSE; - while (!lGenerationOK){ + while (!lGenerationOK) { if (pythiaObjectSignal.next()) { - lGenerationOK = selectEvent(pythiaObjectSignal.event); + lGenerationOK = selectEvent(pythiaObjectSignal.event); } } mPythia.event = pythiaObjectSignal.event; - }else{ + notifySubGenerator(lExternalID); + } else { // Generate minimum-bias event Bool_t lGenerationOK = kFALSE; while (!lGenerationOK) { lGenerationOK = pythiaObjectMinimumBias.next(); } mPythia.event = pythiaObjectMinimumBias.event; + notifySubGenerator(0); } lGeneratedEvents++; @@ -93,10 +110,10 @@ protected: return true; } - bool selectEvent(const Pythia8::Event& event) + bool selectEvent(const Pythia8::Event& event) { bool isGoodAtPartonLevel = false, isGoodAtDaughterLevel = (mPdgDaughter != 0) ? false : true; - int nbDaughter = 0; + int nbDaughter = 0; for (auto iPart{0}; iPart < event.size(); ++iPart) { // search for Q-Qbar mother with at least one Q in rapidity window if (!isGoodAtPartonLevel) { @@ -104,11 +121,11 @@ protected: bool hasQ = false, hasQbar = false, atSelectedY = false; for (auto iDau : daughterList) { if (event[iDau].id() == mQuarkPdg) { - hasQ = true; - } + hasQ = true; + } if (event[iDau].id() == -mQuarkPdg) { - hasQbar = true; - } + hasQbar = true; + } if ((std::abs(event[iDau].id()) == mQuarkPdg) && (event[iDau].y() > mQuarkRapidityMin) && (event[iDau].y() < mQuarkRapidityMax)) atSelectedY = true; } @@ -121,21 +138,21 @@ protected: int id = std::abs(event[iPart].id()); float rap = event[iPart].y(); if (id == mPdgDaughter) { - int motherindexa = event[iPart].mother1(); - if (motherindexa > 0) { - int idmother = std::abs(event[motherindexa].id()); - if (int(std::abs(idmother)/100.) == 4 || int(std::abs(idmother)/1000.) == 4 || int(std::abs(idmother)/100.) == 5 || int(std::abs(idmother)/1000.) == 5) { - if (rap > mDaughterRapidityMin && rap < mDaughterRapidityMax) { - nbDaughter++; - if (nbDaughter >= mNbDaughter) isGoodAtDaughterLevel = true; - } + int motherindexa = event[iPart].mother1(); + if (motherindexa > 0) { + int idmother = std::abs(event[motherindexa].id()); + if (int(std::abs(idmother) / 100.) == 4 || int(std::abs(idmother) / 1000.) == 4 || int(std::abs(idmother) / 100.) == 5 || int(std::abs(idmother) / 1000.) == 5) { + if (rap > mDaughterRapidityMin && rap < mDaughterRapidityMax) { + nbDaughter++; + if (nbDaughter >= mNbDaughter) isGoodAtDaughterLevel = true; } - } + } + } } } // we send the trigger if (isGoodAtPartonLevel && isGoodAtDaughterLevel) { - return true; + return true; } } return false; @@ -157,46 +174,51 @@ private: // Control gap-triggering Long64_t lGeneratedEvents; int lInverseTriggerRatio; + // ID for different generators + int lExternalID; // Base event generators - Pythia pythiaObjectMinimumBias; ///Minimum bias collision generator - Pythia pythiaObjectSignal; ///Signal collision generator + Pythia8::Pythia pythiaObjectMinimumBias; ///Minimum bias collision generator + Pythia8::Pythia pythiaObjectSignal; ///Signal collision generator }; // Predefined generators: // Charm-enriched forced decay -FairGenerator *GeneratorPythia8GapTriggeredCharmLepton(int inputTriggerRatio, float yMin=-1.5, float yMax=1.5) { - auto myGen = new GeneratorPythia8GapTriggeredHFLepton("$O2DPG_ROOT/MC/config/PWGEM/pythia8/generator/pythia8_pp_cr2_forceddecayscharm.cfg", 4, inputTriggerRatio); +FairGenerator* GeneratorPythia8GapTriggeredCharmLepton(int inputTriggerRatio, int inputExternalID, float yMin = -1.5, float yMax = 1.5) +{ + auto myGen = new GeneratorPythia8GapTriggeredHFLepton("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/generator/pythia8_pp_cr2_forceddecayscharm.cfg", 4, inputTriggerRatio, inputExternalID); auto seed = (gRandom->TRandom::GetSeed() % 900000000); myGen->readString("Random:setSeed on"); myGen->readString("Random:seed " + std::to_string(seed)); myGen->setQuarkRapidity(yMin, yMax); myGen->addTriggerOnDaughter(2, 11); - myGen->setDaughterRapidity(-1.,1.); + myGen->setDaughterRapidity(-1., 1.); return myGen; } // Beauty-enriched forced decay -FairGenerator *GeneratorPythia8GapTriggeredBeautyForcedDecays(int inputTriggerRatio, float yMin=-1.5, float yMax=1.5) { - auto myGen = new GeneratorPythia8GapTriggeredHFLepton("$O2DPG_ROOT/MC/config/PWGEM/pythia8/generator/pythia8_bbbar_forceddecayscharmbeauty.cfg", 5, inputTriggerRatio); +FairGenerator* GeneratorPythia8GapTriggeredBeautyForcedDecays(int inputTriggerRatio, int inputExternalID, float yMin = -1.5, float yMax = 1.5) +{ + auto myGen = new GeneratorPythia8GapTriggeredHFLepton("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/generator/pythia8_bbbar_forceddecayscharmbeauty.cfg", 5, inputTriggerRatio, inputExternalID); auto seed = (gRandom->TRandom::GetSeed() % 900000000); myGen->readString("Random:setSeed on"); myGen->readString("Random:seed " + std::to_string(seed)); myGen->setQuarkRapidity(yMin, yMax); myGen->addTriggerOnDaughter(2, 11); - myGen->setDaughterRapidity(-1.,1.); + myGen->setDaughterRapidity(-1., 1.); return myGen; } // Beauty-enriched no forced decay -FairGenerator *GeneratorPythia8GapTriggeredBeautyNoForcedDecays(int inputTriggerRatio, float yMin=-1.5, float yMax=1.5) { - auto myGen = new GeneratorPythia8GapTriggeredHFLepton("$O2DPG_ROOT/MC/config/PWGEM/pythia8/generator/pythia8_bbbar.cfg", 5, inputTriggerRatio); +FairGenerator* GeneratorPythia8GapTriggeredBeautyNoForcedDecays(int inputTriggerRatio, int inputExternalID, float yMin = -1.5, float yMax = 1.5) +{ + auto myGen = new GeneratorPythia8GapTriggeredHFLepton("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/generator/pythia8_bbbar.cfg", 5, inputTriggerRatio, inputExternalID); auto seed = (gRandom->TRandom::GetSeed() % 900000000); myGen->readString("Random:setSeed on"); myGen->readString("Random:seed " + std::to_string(seed)); myGen->setQuarkRapidity(yMin, yMax); myGen->addTriggerOnDaughter(2, 11); - myGen->setDaughterRapidity(-1.,1.); + myGen->setDaughterRapidity(-1., 1.); return myGen; } diff --git a/MC/config/PWGEM/external/generator/Generator_pythia8_GapTriggered_HFLepton_OO.C b/MC/config/PWGEM/external/generator/Generator_pythia8_GapTriggered_HFLepton_OO.C new file mode 100644 index 000000000..439d7e3ce --- /dev/null +++ b/MC/config/PWGEM/external/generator/Generator_pythia8_GapTriggered_HFLepton_OO.C @@ -0,0 +1,227 @@ +#include "Pythia8/Pythia.h" +#include "Pythia8/HeavyIons.h" +#include "FairGenerator.h" +#include "FairPrimaryGenerator.h" +#include "Generators/GeneratorPythia8.h" +#include "TRandom3.h" +#include "TParticlePDG.h" +#include "TDatabasePDG.h" + +#include +#include +//#include // for std::pair + +using namespace Pythia8; + +class GeneratorPythia8GapTriggeredHFLeptonOO : public o2::eventgen::GeneratorPythia8 +{ +public: + /// default constructor + GeneratorPythia8GapTriggeredHFLeptonOO() = default; + + /// constructor + GeneratorPythia8GapTriggeredHFLeptonOO(TString configsignal, int quarkPdg = 4, int lInputTriggerRatio = 5, int lInputExternalID = 0) + { + + lGeneratedEvents = 0; + lInverseTriggerRatio = lInputTriggerRatio; + lExternalID = lInputExternalID; + mQuarkPdg = quarkPdg; + + auto seed = (gRandom->TRandom::GetSeed() % 900000000); + + int offset = (int)(gRandom->Uniform(lInverseTriggerRatio)); // create offset to mitigate edge effects due to small number of events per job + lGeneratedEvents += offset; + + cout << "Initalizing extra PYTHIA object used to generate min-bias events..." << endl; + TString pathconfigMB = gSystem->ExpandPathName("${O2DPG_MC_CONFIG_ROOT}//MC/config/common/pythia8/generator/pythia8_OO_536.cfg"); + pythiaObjectMinimumBias.readFile(pathconfigMB.Data()); + pythiaObjectMinimumBias.readString("Random:setSeed on"); + pythiaObjectMinimumBias.readString("Random:seed " + std::to_string(seed)); + pythiaObjectMinimumBias.init(); + cout << "Initalization of gap event is complete" << endl; + + cout << "Initalizing extra PYTHIA object used to generate signal events..." << endl; + TString pathconfigSignal = gSystem->ExpandPathName(configsignal.Data()); + pythiaObjectSignal.readFile(pathconfigSignal.Data()); + pythiaObjectSignal.readString("Random:setSeed on"); + pythiaObjectSignal.readString("Random:seed " + std::to_string(seed)); + pythiaObjectSignal.init(); + cout << "Initalization of signal event is complete" << endl; + + // flag the generators using type + // addCocktailConstituent(type, "interesting"); + // addCocktailConstitent(0, "minbias"); + // Add Sub generators + addSubGenerator(0, "default generator"); + addSubGenerator(1, "charm lepton"); + addSubGenerator(2, "beauty forced decay"); + addSubGenerator(3, "beauty no foced decay"); + } + + /// Destructor + ~GeneratorPythia8GapTriggeredHFLeptonOO() = default; + + void addTriggerOnDaughter(int nb, int pdg) + { + mNbDaughter = nb; + mPdgDaughter = pdg; + }; + void setQuarkRapidity(float yMin, float yMax) + { + mQuarkRapidityMin = yMin; + mQuarkRapidityMax = yMax; + }; + void setDaughterRapidity(float yMin, float yMax) + { + mDaughterRapidityMin = yMin; + mDaughterRapidityMax = yMax; + }; + +protected: + //__________________________________________________________________ + Bool_t generateEvent() override + { + /// reset event + mPythia.event.reset(); + + // Simple straightforward check to alternate generators + if (lGeneratedEvents % lInverseTriggerRatio == 0) { + // Generate event of interest + Bool_t lGenerationOK = kFALSE; + while (!lGenerationOK) { + if (pythiaObjectSignal.next()) { + lGenerationOK = selectEvent(pythiaObjectSignal.event); + } + } + mPythia.event = pythiaObjectSignal.event; + notifySubGenerator(lExternalID); + } else { + // Generate minimum-bias event + Bool_t lGenerationOK = kFALSE; + while (!lGenerationOK) { + lGenerationOK = pythiaObjectMinimumBias.next(); + } + mPythia.event = pythiaObjectMinimumBias.event; + notifySubGenerator(0); + } + + lGeneratedEvents++; + // mPythia.next(); + + return true; + } + + bool selectEvent(const Pythia8::Event& event) + { + bool isGoodAtPartonLevel = false, isGoodAtDaughterLevel = (mPdgDaughter != 0) ? false : true; + int nbDaughter = 0; + for (auto iPart{0}; iPart < event.size(); ++iPart) { + // search for Q-Qbar mother with at least one Q in rapidity window + if (!isGoodAtPartonLevel) { + auto daughterList = event[iPart].daughterList(); + bool hasQ = false, hasQbar = false, atSelectedY = false; + for (auto iDau : daughterList) { + if (event[iDau].id() == mQuarkPdg) { + hasQ = true; + } + if (event[iDau].id() == -mQuarkPdg) { + hasQbar = true; + } + if ((std::abs(event[iDau].id()) == mQuarkPdg) && (event[iDau].y() > mQuarkRapidityMin) && (event[iDau].y() < mQuarkRapidityMax)) + atSelectedY = true; + } + if (hasQ && hasQbar && atSelectedY) { + isGoodAtPartonLevel = true; + } + } + // search for mNbDaughter daughters of type mPdgDaughter in rapidity window + if (!isGoodAtDaughterLevel) { + int id = std::abs(event[iPart].id()); + float rap = event[iPart].y(); + if (id == mPdgDaughter) { + int motherindexa = event[iPart].mother1(); + if (motherindexa > 0) { + int idmother = std::abs(event[motherindexa].id()); + if (int(std::abs(idmother) / 100.) == 4 || int(std::abs(idmother) / 1000.) == 4 || int(std::abs(idmother) / 100.) == 5 || int(std::abs(idmother) / 1000.) == 5) { + if (rap > mDaughterRapidityMin && rap < mDaughterRapidityMax) { + nbDaughter++; + if (nbDaughter >= mNbDaughter) isGoodAtDaughterLevel = true; + } + } + } + } + } + // we send the trigger + if (isGoodAtPartonLevel && isGoodAtDaughterLevel) { + return true; + } + } + return false; + }; + +private: + // Interface to override import particles + Pythia8::Event mOutputEvent; + + // Properties of selection + int mQuarkPdg; + float mQuarkRapidityMin; + float mQuarkRapidityMax; + int mPdgDaughter; + int mNbDaughter; + float mDaughterRapidityMin; + float mDaughterRapidityMax; + + // Control gap-triggering + Long64_t lGeneratedEvents; + int lInverseTriggerRatio; + // ID for different generators + int lExternalID; + + // Base event generators + Pythia8::Pythia pythiaObjectMinimumBias; ///Minimum bias collision generator + Pythia8::Pythia pythiaObjectSignal; ///Signal collision generator +}; + +// Predefined generators: + +// Charm-enriched forced decay +FairGenerator* GeneratorPythia8GapTriggeredCharmLepton(int inputTriggerRatio, int inputExternalID, int pdgLepton, float yMinQ = -1.5, float yMaxQ = 1.5, float yMinL = -1, float yMaxL = 1) +{ + auto myGen = new GeneratorPythia8GapTriggeredHFLeptonOO("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/generator/pythia8_OO_536_ccbar.cfg", 4, inputTriggerRatio, inputExternalID); + auto seed = (gRandom->TRandom::GetSeed() % 900000000); + myGen->readString("Random:setSeed on"); + myGen->readString("Random:seed " + std::to_string(seed)); + myGen->setQuarkRapidity(yMinQ, yMaxQ); + myGen->addTriggerOnDaughter(2, pdgLepton); + myGen->setDaughterRapidity(yMinL, yMaxL); + return myGen; +} + + +// Beauty-enriched forced decay +FairGenerator* GeneratorPythia8GapTriggeredBeautyForcedDecays(int inputTriggerRatio, int inputExternalID, int pdgLepton, float yMinQ = -1.5, float yMaxQ = 1.5, float yMinL = -1, float yMaxL = 1) +{ + auto myGen = new GeneratorPythia8GapTriggeredHFLeptonOO("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/generator/pythia8_bbbar_OO_536_forceddecayscharmbeauty.cfg", 5, inputTriggerRatio, inputExternalID); + auto seed = (gRandom->TRandom::GetSeed() % 900000000); + myGen->readString("Random:setSeed on"); + myGen->readString("Random:seed " + std::to_string(seed)); + myGen->setQuarkRapidity(yMinQ, yMaxQ); + myGen->addTriggerOnDaughter(2, pdgLepton); + myGen->setDaughterRapidity(yMinL, yMaxL); + return myGen; +} + +// Beauty-enriched no forced decay +FairGenerator* GeneratorPythia8GapTriggeredBeautyNoForcedDecays(int inputTriggerRatio, int inputExternalID, int pdgLepton, float yMinQ = -1.5, float yMaxQ = 1.5, float yMinL = -1, float yMaxL = 1) +{ + auto myGen = new GeneratorPythia8GapTriggeredHFLeptonOO("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/generator/pythia8_OO_536_bbbar.cfg", 5, inputTriggerRatio, inputExternalID); + auto seed = (gRandom->TRandom::GetSeed() % 900000000); + myGen->readString("Random:setSeed on"); + myGen->readString("Random:seed " + std::to_string(seed)); + myGen->setQuarkRapidity(yMinQ, yMaxQ); + myGen->addTriggerOnDaughter(2, pdgLepton); + myGen->setDaughterRapidity(yMinL, yMaxL); + return myGen; +} diff --git a/MC/config/PWGEM/external/generator/Generator_pythia8_GapTriggered_HFLepton_pp5360.C b/MC/config/PWGEM/external/generator/Generator_pythia8_GapTriggered_HFLepton_pp5360.C new file mode 100644 index 000000000..3daa246ae --- /dev/null +++ b/MC/config/PWGEM/external/generator/Generator_pythia8_GapTriggered_HFLepton_pp5360.C @@ -0,0 +1,224 @@ +#include "Pythia8/Pythia.h" +#include "Pythia8/HeavyIons.h" +#include "FairGenerator.h" +#include "FairPrimaryGenerator.h" +#include "Generators/GeneratorPythia8.h" +#include "TRandom3.h" +#include "TParticlePDG.h" +#include "TDatabasePDG.h" + +#include +#include +//#include // for std::pair + +using namespace Pythia8; + +class GeneratorPythia8GapTriggeredHFLepton : public o2::eventgen::GeneratorPythia8 +{ +public: + /// default constructor + GeneratorPythia8GapTriggeredHFLepton() = default; + + /// constructor + GeneratorPythia8GapTriggeredHFLepton(TString configsignal, int quarkPdg = 4, int lInputTriggerRatio = 5, int lInputExternalID = 0) + { + + lGeneratedEvents = 0; + lInverseTriggerRatio = lInputTriggerRatio; + lExternalID = lInputExternalID; + mQuarkPdg = quarkPdg; + + auto seed = (gRandom->TRandom::GetSeed() % 900000000); + + int offset = (int)(gRandom->Uniform(lInverseTriggerRatio)); // create offset to mitigate edge effects due to small number of events per job + lGeneratedEvents += offset; + + cout << "Initalizing extra PYTHIA object used to generate min-bias events..." << endl; + TString pathconfigMB = gSystem->ExpandPathName("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/generator/pythia8_pp_5360_MB_gapevent.cfg"); + pythiaObjectMinimumBias.readFile(pathconfigMB.Data()); + pythiaObjectMinimumBias.readString("Random:setSeed on"); + pythiaObjectMinimumBias.readString("Random:seed " + std::to_string(seed)); + pythiaObjectMinimumBias.init(); + cout << "Initalization complete" << endl; + cout << "Initalizing extra PYTHIA object used to generate signal events..." << endl; + TString pathconfigSignal = gSystem->ExpandPathName(configsignal.Data()); + pythiaObjectSignal.readFile(pathconfigSignal.Data()); + pythiaObjectSignal.readString("Random:setSeed on"); + pythiaObjectSignal.readString("Random:seed " + std::to_string(seed)); + pythiaObjectSignal.init(); + cout << "Initalization complete" << endl; + // flag the generators using type + // addCocktailConstituent(type, "interesting"); + // addCocktailConstitent(0, "minbias"); + // Add Sub generators + addSubGenerator(0, "default generator"); + addSubGenerator(1, "charm lepton"); + addSubGenerator(2, "beauty forced decay"); + addSubGenerator(3, "beauty no foced decay"); + } + + /// Destructor + ~GeneratorPythia8GapTriggeredHFLepton() = default; + + void addTriggerOnDaughter(int nb, int pdg) + { + mNbDaughter = nb; + mPdgDaughter = pdg; + }; + void setQuarkRapidity(float yMin, float yMax) + { + mQuarkRapidityMin = yMin; + mQuarkRapidityMax = yMax; + }; + void setDaughterRapidity(float yMin, float yMax) + { + mDaughterRapidityMin = yMin; + mDaughterRapidityMax = yMax; + }; + +protected: + //__________________________________________________________________ + Bool_t generateEvent() override + { + /// reset event + mPythia.event.reset(); + + // Simple straightforward check to alternate generators + if (lGeneratedEvents % lInverseTriggerRatio == 0) { + // Generate event of interest + Bool_t lGenerationOK = kFALSE; + while (!lGenerationOK) { + if (pythiaObjectSignal.next()) { + lGenerationOK = selectEvent(pythiaObjectSignal.event); + } + } + mPythia.event = pythiaObjectSignal.event; + notifySubGenerator(lExternalID); + } else { + // Generate minimum-bias event + Bool_t lGenerationOK = kFALSE; + while (!lGenerationOK) { + lGenerationOK = pythiaObjectMinimumBias.next(); + } + mPythia.event = pythiaObjectMinimumBias.event; + notifySubGenerator(0); + } + + lGeneratedEvents++; + // mPythia.next(); + + return true; + } + + bool selectEvent(const Pythia8::Event& event) + { + bool isGoodAtPartonLevel = false, isGoodAtDaughterLevel = (mPdgDaughter != 0) ? false : true; + int nbDaughter = 0; + for (auto iPart{0}; iPart < event.size(); ++iPart) { + // search for Q-Qbar mother with at least one Q in rapidity window + if (!isGoodAtPartonLevel) { + auto daughterList = event[iPart].daughterList(); + bool hasQ = false, hasQbar = false, atSelectedY = false; + for (auto iDau : daughterList) { + if (event[iDau].id() == mQuarkPdg) { + hasQ = true; + } + if (event[iDau].id() == -mQuarkPdg) { + hasQbar = true; + } + if ((std::abs(event[iDau].id()) == mQuarkPdg) && (event[iDau].y() > mQuarkRapidityMin) && (event[iDau].y() < mQuarkRapidityMax)) + atSelectedY = true; + } + if (hasQ && hasQbar && atSelectedY) { + isGoodAtPartonLevel = true; + } + } + // search for mNbDaughter daughters of type mPdgDaughter in rapidity window + if (!isGoodAtDaughterLevel) { + int id = std::abs(event[iPart].id()); + float rap = event[iPart].y(); + if (id == mPdgDaughter) { + int motherindexa = event[iPart].mother1(); + if (motherindexa > 0) { + int idmother = std::abs(event[motherindexa].id()); + if (int(std::abs(idmother) / 100.) == 4 || int(std::abs(idmother) / 1000.) == 4 || int(std::abs(idmother) / 100.) == 5 || int(std::abs(idmother) / 1000.) == 5) { + if (rap > mDaughterRapidityMin && rap < mDaughterRapidityMax) { + nbDaughter++; + if (nbDaughter >= mNbDaughter) isGoodAtDaughterLevel = true; + } + } + } + } + } + // we send the trigger + if (isGoodAtPartonLevel && isGoodAtDaughterLevel) { + return true; + } + } + return false; + }; + +private: + // Interface to override import particles + Pythia8::Event mOutputEvent; + + // Properties of selection + int mQuarkPdg; + float mQuarkRapidityMin; + float mQuarkRapidityMax; + int mPdgDaughter; + int mNbDaughter; + float mDaughterRapidityMin; + float mDaughterRapidityMax; + + // Control gap-triggering + Long64_t lGeneratedEvents; + int lInverseTriggerRatio; + // ID for different generators + int lExternalID; + + // Base event generators + Pythia8::Pythia pythiaObjectMinimumBias; ///Minimum bias collision generator + Pythia8::Pythia pythiaObjectSignal; ///Signal collision generator +}; + +// Predefined generators: + +// Charm-enriched forced decay +FairGenerator* GeneratorPythia8GapTriggeredCharmLepton(int inputTriggerRatio, int inputExternalID, float yMin = -1.5, float yMax = 1.5) +{ + auto myGen = new GeneratorPythia8GapTriggeredHFLepton("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/generator/pythia8_pp_5360_cr2_forceddecayscharm.cfg", 4, inputTriggerRatio, inputExternalID); + auto seed = (gRandom->TRandom::GetSeed() % 900000000); + myGen->readString("Random:setSeed on"); + myGen->readString("Random:seed " + std::to_string(seed)); + myGen->setQuarkRapidity(yMin, yMax); + myGen->addTriggerOnDaughter(2, 11); + myGen->setDaughterRapidity(-1., 1.); + return myGen; +} + +// Beauty-enriched forced decay +FairGenerator* GeneratorPythia8GapTriggeredBeautyForcedDecays(int inputTriggerRatio, int inputExternalID, float yMin = -1.5, float yMax = 1.5) +{ + auto myGen = new GeneratorPythia8GapTriggeredHFLepton("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/generator/pythia8_pp_5360_bbbar_forceddecayscharmbeauty.cfg", 5, inputTriggerRatio, inputExternalID); + auto seed = (gRandom->TRandom::GetSeed() % 900000000); + myGen->readString("Random:setSeed on"); + myGen->readString("Random:seed " + std::to_string(seed)); + myGen->setQuarkRapidity(yMin, yMax); + myGen->addTriggerOnDaughter(2, 11); + myGen->setDaughterRapidity(-1., 1.); + return myGen; +} + +// Beauty-enriched no forced decay +FairGenerator* GeneratorPythia8GapTriggeredBeautyNoForcedDecays(int inputTriggerRatio, int inputExternalID, float yMin = -1.5, float yMax = 1.5) +{ + auto myGen = new GeneratorPythia8GapTriggeredHFLepton("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/generator/pythia8_pp_5360_bbbar.cfg", 5, inputTriggerRatio, inputExternalID); + auto seed = (gRandom->TRandom::GetSeed() % 900000000); + myGen->readString("Random:setSeed on"); + myGen->readString("Random:seed " + std::to_string(seed)); + myGen->setQuarkRapidity(yMin, yMax); + myGen->addTriggerOnDaughter(2, 11); + myGen->setDaughterRapidity(-1., 1.); + return myGen; +} diff --git a/MC/config/PWGEM/external/generator/Generator_pythia8_GapTriggered_HFMuon.C b/MC/config/PWGEM/external/generator/Generator_pythia8_GapTriggered_HFMuon.C new file mode 100644 index 000000000..b84608346 --- /dev/null +++ b/MC/config/PWGEM/external/generator/Generator_pythia8_GapTriggered_HFMuon.C @@ -0,0 +1,224 @@ +#include "Pythia8/Pythia.h" +#include "Pythia8/HeavyIons.h" +#include "FairGenerator.h" +#include "FairPrimaryGenerator.h" +#include "Generators/GeneratorPythia8.h" +#include "TRandom3.h" +#include "TParticlePDG.h" +#include "TDatabasePDG.h" + +#include +#include +//#include // for std::pair + +using namespace Pythia8; + +class GeneratorPythia8GapTriggeredHFLepton : public o2::eventgen::GeneratorPythia8 +{ +public: + /// default constructor + GeneratorPythia8GapTriggeredHFLepton() = default; + + /// constructor + GeneratorPythia8GapTriggeredHFLepton(TString configsignal, int quarkPdg = 4, int lInputTriggerRatio = 5, int lInputExternalID = 0) + { + + lGeneratedEvents = 0; + lInverseTriggerRatio = lInputTriggerRatio; + lExternalID = lInputExternalID; + mQuarkPdg = quarkPdg; + + auto seed = (gRandom->TRandom::GetSeed() % 900000000); + + int offset = (int)(gRandom->Uniform(lInverseTriggerRatio)); // create offset to mitigate edge effects due to small number of events per job + lGeneratedEvents += offset; + + cout << "Initalizing extra PYTHIA object used to generate min-bias events..." << endl; + TString pathconfigMB = gSystem->ExpandPathName("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/generator/pythia8_MB_gapevent.cfg"); + pythiaObjectMinimumBias.readFile(pathconfigMB.Data()); + pythiaObjectMinimumBias.readString("Random:setSeed on"); + pythiaObjectMinimumBias.readString("Random:seed " + std::to_string(seed)); + pythiaObjectMinimumBias.init(); + cout << "Initalization complete" << endl; + cout << "Initalizing extra PYTHIA object used to generate signal events..." << endl; + TString pathconfigSignal = gSystem->ExpandPathName(configsignal.Data()); + pythiaObjectSignal.readFile(pathconfigSignal.Data()); + pythiaObjectSignal.readString("Random:setSeed on"); + pythiaObjectSignal.readString("Random:seed " + std::to_string(seed)); + pythiaObjectSignal.init(); + cout << "Initalization complete" << endl; + // flag the generators using type + // addCocktailConstituent(type, "interesting"); + // addCocktailConstitent(0, "minbias"); + // Add Sub generators + addSubGenerator(0, "default generator"); + addSubGenerator(1, "charm lepton"); + addSubGenerator(2, "beauty forced decay"); + addSubGenerator(3, "beauty no foced decay"); + } + + /// Destructor + ~GeneratorPythia8GapTriggeredHFLepton() = default; + + void addTriggerOnDaughter(int nb, int pdg) + { + mNbDaughter = nb; + mPdgDaughter = pdg; + }; + void setQuarkRapidity(float yMin, float yMax) + { + mQuarkRapidityMin = yMin; + mQuarkRapidityMax = yMax; + }; + void setDaughterRapidity(float yMin, float yMax) + { + mDaughterRapidityMin = yMin; + mDaughterRapidityMax = yMax; + }; + +protected: + //__________________________________________________________________ + Bool_t generateEvent() override + { + /// reset event + mPythia.event.reset(); + + // Simple straightforward check to alternate generators + if (lGeneratedEvents % lInverseTriggerRatio == 0) { + // Generate event of interest + Bool_t lGenerationOK = kFALSE; + while (!lGenerationOK) { + if (pythiaObjectSignal.next()) { + lGenerationOK = selectEvent(pythiaObjectSignal.event); + } + } + mPythia.event = pythiaObjectSignal.event; + notifySubGenerator(lExternalID); + } else { + // Generate minimum-bias event + Bool_t lGenerationOK = kFALSE; + while (!lGenerationOK) { + lGenerationOK = pythiaObjectMinimumBias.next(); + } + mPythia.event = pythiaObjectMinimumBias.event; + notifySubGenerator(0); + } + + lGeneratedEvents++; + // mPythia.next(); + + return true; + } + + bool selectEvent(const Pythia8::Event& event) + { + bool isGoodAtPartonLevel = false, isGoodAtDaughterLevel = (mPdgDaughter != 0) ? false : true; + int nbDaughter = 0; + for (auto iPart{0}; iPart < event.size(); ++iPart) { + // search for Q-Qbar mother with at least one Q in rapidity window + if (!isGoodAtPartonLevel) { + auto daughterList = event[iPart].daughterList(); + bool hasQ = false, hasQbar = false, atSelectedY = false; + for (auto iDau : daughterList) { + if (event[iDau].id() == mQuarkPdg) { + hasQ = true; + } + if (event[iDau].id() == -mQuarkPdg) { + hasQbar = true; + } + if ((std::abs(event[iDau].id()) == mQuarkPdg) && (event[iDau].y() > mQuarkRapidityMin) && (event[iDau].y() < mQuarkRapidityMax)) + atSelectedY = true; + } + if (hasQ && hasQbar && atSelectedY) { + isGoodAtPartonLevel = true; + } + } + // search for mNbDaughter daughters of type mPdgDaughter in rapidity window + if (!isGoodAtDaughterLevel) { + int id = std::abs(event[iPart].id()); + float rap = event[iPart].y(); + if (id == mPdgDaughter) { + int motherindexa = event[iPart].mother1(); + if (motherindexa > 0) { + int idmother = std::abs(event[motherindexa].id()); + if (int(std::abs(idmother) / 100.) == 4 || int(std::abs(idmother) / 1000.) == 4 || int(std::abs(idmother) / 100.) == 5 || int(std::abs(idmother) / 1000.) == 5) { + if (rap > mDaughterRapidityMin && rap < mDaughterRapidityMax) { + nbDaughter++; + if (nbDaughter >= mNbDaughter) isGoodAtDaughterLevel = true; + } + } + } + } + } + // we send the trigger + if (isGoodAtPartonLevel && isGoodAtDaughterLevel) { + return true; + } + } + return false; + }; + +private: + // Interface to override import particles + Pythia8::Event mOutputEvent; + + // Properties of selection + int mQuarkPdg; + float mQuarkRapidityMin; + float mQuarkRapidityMax; + int mPdgDaughter; + int mNbDaughter; + float mDaughterRapidityMin; + float mDaughterRapidityMax; + + // Control gap-triggering + Long64_t lGeneratedEvents; + int lInverseTriggerRatio; + // ID for different generators + int lExternalID; + + // Base event generators + Pythia8::Pythia pythiaObjectMinimumBias; ///Minimum bias collision generator + Pythia8::Pythia pythiaObjectSignal; ///Signal collision generator +}; + +// Predefined generators: + +// Charm-enriched forced decay +FairGenerator* GeneratorPythia8GapTriggeredCharmLepton(int inputTriggerRatio, int inputExternalID, float yMin = -1.5, float yMax = 1.5) +{ + auto myGen = new GeneratorPythia8GapTriggeredHFLepton("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/generator/pythia8_pp_cr2_forceddecayscharm_semimuonic.cfg", 4, inputTriggerRatio, inputExternalID); + auto seed = (gRandom->TRandom::GetSeed() % 900000000); + myGen->readString("Random:setSeed on"); + myGen->readString("Random:seed " + std::to_string(seed)); + myGen->setQuarkRapidity(yMin, yMax); + myGen->addTriggerOnDaughter(2, 13); + myGen->setDaughterRapidity(-6., -1.); + return myGen; +} + +// Beauty-enriched forced decay +FairGenerator* GeneratorPythia8GapTriggeredBeautyForcedDecays(int inputTriggerRatio, int inputExternalID, float yMin = -1.5, float yMax = 1.5) +{ + auto myGen = new GeneratorPythia8GapTriggeredHFLepton("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/generator/pythia8_bbbar_forceddecayscharmbeauty_semimuonic.cfg", 5, inputTriggerRatio, inputExternalID); + auto seed = (gRandom->TRandom::GetSeed() % 900000000); + myGen->readString("Random:setSeed on"); + myGen->readString("Random:seed " + std::to_string(seed)); + myGen->setQuarkRapidity(yMin, yMax); + myGen->addTriggerOnDaughter(2, 13); + myGen->setDaughterRapidity(-6., -1.); + return myGen; +} + +// Beauty-enriched no forced decay +FairGenerator* GeneratorPythia8GapTriggeredBeautyNoForcedDecays(int inputTriggerRatio, int inputExternalID, float yMin = -1.5, float yMax = 1.5) +{ + auto myGen = new GeneratorPythia8GapTriggeredHFLepton("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/generator/pythia8_bbbar.cfg", 5, inputTriggerRatio, inputExternalID); + auto seed = (gRandom->TRandom::GetSeed() % 900000000); + myGen->readString("Random:setSeed on"); + myGen->readString("Random:seed " + std::to_string(seed)); + myGen->setQuarkRapidity(yMin, yMax); + myGen->addTriggerOnDaughter(2, 13); + myGen->setDaughterRapidity(-6., -1.); + return myGen; +} diff --git a/MC/config/PWGEM/external/generator/Generator_pythia8_GapTriggered_LFgamma.C b/MC/config/PWGEM/external/generator/Generator_pythia8_GapTriggered_LFgamma.C new file mode 100644 index 000000000..d4c940e2e --- /dev/null +++ b/MC/config/PWGEM/external/generator/Generator_pythia8_GapTriggered_LFgamma.C @@ -0,0 +1,169 @@ +R__ADD_INCLUDE_PATH($O2DPG_MC_CONFIG_ROOT/MC/config/PWGDQ/EvtGen) +R__ADD_INCLUDE_PATH($O2DPG_MC_CONFIG_ROOT/MC/config/PWGDQ/external/generator) +R__ADD_INCLUDE_PATH($O2DPG_MC_CONFIG_ROOT/MC/config/PWGEM/external/generator) +R__LOAD_LIBRARY(libpythia6) +R__LOAD_LIBRARY(libEGPythia6) +#include "GeneratorEvtGen.C" +#include "GeneratorCocktail.C" +#include "Generators/GeneratorPythia8.h" +#include "Pythia8/Pythia.h" + +using namespace std; +using namespace Pythia8; + +namespace o2 { +namespace eventgen { + +class CocktailParam : public GeneratorTGenerator { +public: + CocktailParam(GeneratorParam *thisGenerator) + : GeneratorTGenerator(thisGenerator->GetName()) { + setTGenerator(thisGenerator); + }; + + ~CocktailParam() { delete thisGenerator; }; + +private: + GeneratorParam *thisGenerator = nullptr; +}; + +//my generator class +class GeneratorPythia8GapTriggeredLFgamma : public GeneratorPythia8 { + + public: + GeneratorPythia8GapTriggeredLFgamma() : GeneratorPythia8() { + mGeneratedEvents = 0; + mInverseTriggerRatio = 1; + fGeneratorCocktail = 0x0; + }; + + GeneratorPythia8GapTriggeredLFgamma(int lInputTriggerRatio, float yMin, float yMax, int nPart) : GeneratorPythia8() { + mGeneratedEvents = 0; + mInverseTriggerRatio = lInputTriggerRatio; + // LMee cocktail settings: + float minPt = 0; + float maxPt = 25; + float phiMin = 0.; + float phiMax = 360.; + Weighting_t weightMode = kNonAnalog; + + //create cocktail generator : pi0, eta + fGeneratorCocktail = new o2::eventgen::GeneratorEvtGen(); + + auto decayer = new PythiaDecayerConfig(); + + //Param + GeneratorParamEMlib *emlib = new GeneratorParamEMlib(); + + // pi0 + auto genPizero = new GeneratorParam(nPart, emlib, GeneratorParamEMlib::kPizero, "pizero"); // 111 + genPizero->SetName("pizero"); + genPizero->SetMomentumRange(0., 25.); + genPizero->SetPtRange(minPt, maxPt); + genPizero->SetYRange(yMin, yMax); + genPizero->SetPhiRange(phiMin, phiMax); + genPizero->SetWeighting(weightMode); // flat pt, y and v2 zero + genPizero->SetSelectAll(kTRUE); + genPizero->SetDecayer(decayer); + genPizero->Init(); + CocktailParam *newgenpizero = new CocktailParam(genPizero); + + // eta + auto geneta = new GeneratorParam(nPart,emlib,GeneratorParamEMlib::kEta,"eta"); // 221 + geneta->SetName("eta"); + geneta->SetMomentumRange(0., 25.); + geneta->SetPtRange(minPt, maxPt); + geneta->SetYRange(yMin, yMax); + geneta->SetPhiRange(phiMin, phiMax); + geneta->SetWeighting(weightMode); // flat pt, y and v2 zero + geneta->SetSelectAll(kTRUE); + geneta->SetDecayer(decayer); + geneta->Init(); + CocktailParam *newgeneta = new CocktailParam(geneta); + + // eta + auto genk0s = new GeneratorParam(nPart,emlib,GeneratorParamEMlib::kK0s,"k0s"); // 310 for feed down + genk0s->SetName("k0s"); + genk0s->SetMomentumRange(0., 25.); + genk0s->SetPtRange(minPt, maxPt); + genk0s->SetYRange(yMin, yMax); + genk0s->SetPhiRange(phiMin, phiMax); + genk0s->SetWeighting(weightMode); // flat pt, y and v2 zero + genk0s->SetSelectAll(kTRUE); + genk0s->SetDecayer(decayer); + genk0s->Init(); + CocktailParam *newgenk0s = new CocktailParam(genk0s); + + cout << "add pi0 for signal" << endl; + fGeneratorCocktail->AddGenerator(newgenpizero, 1); + cout << "add eta for signal" << endl; + fGeneratorCocktail->AddGenerator(newgeneta, 1); + cout << "add k0s for signal" << endl; + fGeneratorCocktail->AddGenerator(newgenk0s, 1); + + // print debug + fGeneratorCocktail->PrintDebug(); + fGeneratorCocktail->Init(); + + addSubGenerator(0, "gap mb pythia"); + addSubGenerator(1, "event with injected signals"); + + }; + + ~GeneratorPythia8GapTriggeredLFgamma() = default; + + protected: + bool generateEvent() override + { + GeneratorPythia8::generateEvent(); + + if (mGeneratedEvents % mInverseTriggerRatio == 0){ // add injected prompt signals to the stack + fGeneratorCocktail->generateEvent(); + notifySubGenerator(1); + } else { // gap event + notifySubGenerator(0); + } + mGeneratedEvents++; + return true; + } + + bool importParticles() override + { + GeneratorPythia8::importParticles(); + + bool genOk = false; + if ((mGeneratedEvents-1) % mInverseTriggerRatio == 0){ // add injected prompt signals to the stack + fGeneratorCocktail->importParticles(); + int originalSize = mParticles.size(); + for(int ipart=0; ipart < fGeneratorCocktail->getParticles().size(); ipart++){ + TParticle part = TParticle(fGeneratorCocktail->getParticles().at(ipart)); + if(part.GetFirstMother() >= 0) part.SetFirstMother(part.GetFirstMother() + originalSize); + if(part.GetFirstDaughter() >= 0) part.SetFirstDaughter(part.GetFirstDaughter() + originalSize); + if(part.GetLastDaughter() >= 0) part.SetLastDaughter(part.GetLastDaughter() + originalSize); + mParticles.push_back(part); + // encodeParticleStatusAndTracking method already called in GeneratorEvtGen.C + } + fGeneratorCocktail->clearParticles(); + } + + return true; + } + + private: + GeneratorEvtGen *fGeneratorCocktail; + // Control gap-triggering + unsigned long long mGeneratedEvents; + int mInverseTriggerRatio; +}; + +} // close eventgen +} // close o2 + +// Predefined generators: // this function should be called in ini file. +FairGenerator *GeneratorPythia8GapTriggeredLFgamma_ForEM(int inputTriggerRatio = 5, float yMin=-1.2, float yMax=1.2, int nPart = 1) { + auto myGen = new GeneratorPythia8GapTriggeredLFgamma(inputTriggerRatio, yMin, yMax, nPart); + auto seed = (gRandom->TRandom::GetSeed() % 900000000); + myGen->readString("Random:setSeed on"); + myGen->readString("Random:seed " + std::to_string(seed)); + return myGen; +} diff --git a/MC/config/PWGEM/external/generator/Generator_pythia8_GapTriggered_LFmumu.C b/MC/config/PWGEM/external/generator/Generator_pythia8_GapTriggered_LFmumu.C new file mode 100644 index 000000000..5fb499d0a --- /dev/null +++ b/MC/config/PWGEM/external/generator/Generator_pythia8_GapTriggered_LFmumu.C @@ -0,0 +1,287 @@ +R__ADD_INCLUDE_PATH($O2DPG_MC_CONFIG_ROOT/MC/config/PWGDQ/EvtGen) +R__ADD_INCLUDE_PATH($O2DPG_MC_CONFIG_ROOT/MC/config/PWGDQ/external/generator) +R__ADD_INCLUDE_PATH($O2DPG_MC_CONFIG_ROOT/MC/config/PWGEM/external/generator) +R__LOAD_LIBRARY(libpythia6) +R__LOAD_LIBRARY(libEGPythia6) +#include "GeneratorEvtGen.C" +#include "GeneratorCocktail.C" +#include "Generators/GeneratorPythia8.h" +#include "Pythia8/Pythia.h" + +using namespace std; +using namespace Pythia8; + +namespace o2 { +namespace eventgen { + +class CocktailParam : public GeneratorTGenerator { +public: + CocktailParam(GeneratorParam *thisGenerator) + : GeneratorTGenerator(thisGenerator->GetName()) { + setTGenerator(thisGenerator); + }; + + ~CocktailParam() { delete thisGenerator; }; + +private: + GeneratorParam *thisGenerator = nullptr; +}; + + +//my generator class +class GeneratorPythia8GapTriggeredLFmumu : public GeneratorPythia8 { + + public: + GeneratorPythia8GapTriggeredLFmumu() : GeneratorPythia8() { + mGeneratedEvents = 0; + mInverseTriggerRatio = 1; + fGeneratorCocktail = 0x0; + mMode = -1; + mTargetPDG = 0; + }; + + GeneratorPythia8GapTriggeredLFmumu(int lInputTriggerRatio, float yMin, float yMax, int nPart, int mode) : GeneratorPythia8() { + mGeneratedEvents = 0; + mInverseTriggerRatio = lInputTriggerRatio; + mMode = mode; + // LMee cocktail settings: + float minPt = 0; + float maxPt = 25; + float phiMin = 0.; + float phiMax = 360.; + Weighting_t weightMode = kNonAnalog; + + //create cocktail generator : eta, eta', rho, omega, phi + fGeneratorCocktail = new o2::eventgen::GeneratorEvtGen(); + + // EXODUS decayer + TString O2DPG_ROOT = TString(getenv("O2DPG_MC_CONFIG_ROOT")); + auto decayer = new PythiaDecayerConfig(); + decayer->SetDecayerExodus(); + TString useLMeeDecaytable = "${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/decaytables/decaytable_LMee.dat"; + useLMeeDecaytable=useLMeeDecaytable.ReplaceAll("$O2DPG_MC_CONFIG_ROOT",O2DPG_ROOT); + useLMeeDecaytable=useLMeeDecaytable.ReplaceAll("${O2DPG_MC_CONFIG_ROOT}",O2DPG_ROOT); + decayer->SetDecayTableFile(useLMeeDecaytable.Data()); + decayer->ReadDecayTable(); + decayer->DecayToDimuons(); + + //Param + GeneratorParamEMlib *emlib = new GeneratorParamEMlib(); + + // eta + auto geneta = new GeneratorParam(nPart,emlib,GeneratorParamEMlib::kEta,"eta"); // 221 + geneta->SetName("eta"); + geneta->SetMomentumRange(0., 200.); + geneta->SetPtRange(minPt, maxPt); + geneta->SetYRange(yMin, yMax); + geneta->SetPhiRange(phiMin, phiMax); + geneta->SetWeighting(weightMode); // flat pt, y and v2 zero + geneta->SetDecayer(decayer); // EXOUS; + geneta->SetForceDecay(kDiMuon); // Dielectrons + geneta->SetForceGammaConversion(kFALSE); + geneta->SetSelectAll(kTRUE); // Store also the gamma in pi0->e+e-gamma + geneta->Init(); + CocktailParam *newgeneta = new CocktailParam(geneta); + + // etaprime + auto genetaprime = new GeneratorParam(nPart,emlib,GeneratorParamEMlib::kEtaprime,"etaprime"); // 331 + genetaprime->SetName("etaprime"); + genetaprime->SetMomentumRange(0., 200.); + genetaprime->SetPtRange(minPt, maxPt); + genetaprime->SetYRange(yMin, yMax); + genetaprime->SetPhiRange(phiMin, phiMax); + genetaprime->SetWeighting(weightMode); // flat pt, y and v2 zero + genetaprime->SetDecayer(decayer); // EXOUS; + genetaprime->SetForceDecay(kDiMuon); // Dielectrons + genetaprime->SetForceGammaConversion(kFALSE); + genetaprime->SetSelectAll(kTRUE); // Store also the gamma in pi0->e+e-gamma + genetaprime->Init(); + CocktailParam *newgenetaprime = new CocktailParam(genetaprime); + + // rho + auto genrho = new GeneratorParam(nPart,emlib,GeneratorParamEMlib::kRho0,"rho"); // 113 + genrho->SetName("rho"); + genrho->SetMomentumRange(0., 200.); + genrho->SetPtRange(minPt, maxPt); + genrho->SetYRange(yMin, yMax); + genrho->SetPhiRange(phiMin, phiMax); + genrho->SetWeighting(weightMode); // flat pt, y and v2 zero + genrho->SetDecayer(decayer); // EXOUS; + genrho->SetForceDecay(kDiMuon); // Dielectrons + genrho->SetForceGammaConversion(kFALSE); + genrho->SetSelectAll(kTRUE); // Store also the gamma in pi0->e+e-gamma + genrho->Init(); + CocktailParam *newgenrho = new CocktailParam(genrho); + + // Omega + auto genomega = new GeneratorParam(nPart,emlib,GeneratorParamEMlib::kOmega,"omega"); //223 + genomega->SetName("omega"); + genomega->SetMomentumRange(0., 200.); + genomega->SetPtRange(minPt, maxPt); + genomega->SetYRange(yMin, yMax); + genomega->SetPhiRange(phiMin, phiMax); + genomega->SetWeighting(weightMode); // flat pt, y and v2 zero + genomega->SetDecayer(decayer); // EXOUS; + genomega->SetForceDecay(kDiMuon); // Dielectrons + genomega->SetForceGammaConversion(kFALSE); + genomega->SetSelectAll(kTRUE); // Store also the gamma in pi0->e+e-gamma + genomega->Init(); + CocktailParam *newgenomega = new CocktailParam(genomega); + + // phi + auto genphi = new GeneratorParam(nPart,emlib,GeneratorParamEMlib::kPhi,"phi"); //333 + genphi->SetName("phi"); + genphi->SetMomentumRange(0., 200.); + genphi->SetPtRange(minPt, maxPt); + genphi->SetYRange(yMin, yMax); + genphi->SetPhiRange(phiMin, phiMax); + genphi->SetWeighting(weightMode); // flat pt, y and v2 zero + genphi->SetDecayer(decayer); // EXOUS; + genphi->SetForceDecay(kDiMuon); // Dielectrons + genphi->SetForceGammaConversion(kFALSE); + genphi->SetSelectAll(kTRUE); // Store also the gamma in pi0->e+e-gamma + genphi->Init(); + CocktailParam *newgenphi = new CocktailParam(genphi); + + int target_pdg = 1; + + if (mMode < 0) { + target_pdg = 1; + cout << "all-particle mode is selected. all 5 mesons are injected in each event" << endl; + cout << "add eta for signal" << endl; + fGeneratorCocktail->AddGenerator(newgeneta, 1); + cout << "add etaprime for signal" << endl; + fGeneratorCocktail->AddGenerator(newgenetaprime, 1); + cout << "add rho for signal" << endl; + fGeneratorCocktail->AddGenerator(newgenrho, 1); + cout << "add omega for signal" << endl; + fGeneratorCocktail->AddGenerator(newgenomega, 1); + cout << "add phi for signal" << endl; + fGeneratorCocktail->AddGenerator(newgenphi, 1); + } else if (mMode < 100) { + cout << "1-particle Mode is selected. 1 meson selected randomly per job is injected in each event" << endl; + TRandom3 *r3 = new TRandom3(0); + double rndm = r3->Rndm(); + printf("rndm = %f\n", rndm); + + if (rndm < 1/5.) { + cout << "add eta for signal" << endl; + fGeneratorCocktail->AddGenerator(newgeneta, 1); + target_pdg = 221; + } else if (rndm < 2/5.) { + cout << "add etaprime for signal" << endl; + fGeneratorCocktail->AddGenerator(newgenetaprime, 1); + target_pdg = 331; + } else if (rndm < 3/5.) { + cout << "add rho for signal" << endl; + fGeneratorCocktail->AddGenerator(newgenrho, 1); + target_pdg = 113; + } else if (rndm < 4/5.) { + cout << "add omega for signal" << endl; + fGeneratorCocktail->AddGenerator(newgenomega, 1); + target_pdg = 223; + } else if (rndm < 5/5.) { + cout << "add phi for signal" << endl; + fGeneratorCocktail->AddGenerator(newgenphi, 1); + target_pdg = 333; + } + delete r3; + } else { //directly select meson pdg + target_pdg = mMode; + switch (mMode) { + case 221 : + cout << "add eta for signal" << endl; + fGeneratorCocktail->AddGenerator(newgeneta, 1); + break; + case 331 : + cout << "add etaprime for signal" << endl; + fGeneratorCocktail->AddGenerator(newgenetaprime, 1); + break; + case 113 : + cout << "add rho for signal" << endl; + fGeneratorCocktail->AddGenerator(newgenrho, 1); + break; + case 223 : + cout << "add omega for signal" << endl; + fGeneratorCocktail->AddGenerator(newgenomega, 1); + break; + case 333 : + cout << "add phi for signal" << endl; + fGeneratorCocktail->AddGenerator(newgenphi, 1); + break; + default: + cout << "!WARNING! default : nothing is added to cocktail generator" << endl; + target_pdg = 1; + break; + } + } + + // print debug + fGeneratorCocktail->PrintDebug(); + fGeneratorCocktail->Init(); + + cout << "target_pdg for subGeneratorId is " << target_pdg << endl; + addSubGenerator(0, "gap mb pythia"); + addSubGenerator(target_pdg, "injected cocktail"); + mTargetPDG = target_pdg; + }; + + ~GeneratorPythia8GapTriggeredLFmumu() = default; + + protected: + bool generateEvent() override + { + GeneratorPythia8::generateEvent(); + + if (mGeneratedEvents % mInverseTriggerRatio == 0){ // add injected prompt signals to the stack + fGeneratorCocktail->generateEvent(); + notifySubGenerator(mTargetPDG); + } else { // gap event + notifySubGenerator(0); + } + mGeneratedEvents++; + return true; + } + + bool importParticles() override + { + GeneratorPythia8::importParticles(); + + bool genOk = false; + if ((mGeneratedEvents-1) % mInverseTriggerRatio == 0){ // add injected prompt signals to the stack + fGeneratorCocktail->importParticles(); + int originalSize = mParticles.size(); + for(int ipart=0; ipart < fGeneratorCocktail->getParticles().size(); ipart++){ + TParticle part = TParticle(fGeneratorCocktail->getParticles().at(ipart)); + if(part.GetFirstMother() >= 0) part.SetFirstMother(part.GetFirstMother() + originalSize); + if(part.GetFirstDaughter() >= 0) part.SetFirstDaughter(part.GetFirstDaughter() + originalSize); + if(part.GetLastDaughter() >= 0) part.SetLastDaughter(part.GetLastDaughter() + originalSize); + mParticles.push_back(part); + // encodeParticleStatusAndTracking method already called in GeneratorEvtGen.C + } + fGeneratorCocktail->clearParticles(); + } + + return true; + } + + private: + GeneratorEvtGen *fGeneratorCocktail; + // Control gap-triggering + unsigned long long mGeneratedEvents; + int mInverseTriggerRatio; + int mMode; + int mTargetPDG; +}; + +} // close eventgen +} // close o2 + +// Predefined generators: // this function should be called in ini file. +FairGenerator *GeneratorPythia8GapTriggeredLFmumu_ForEM(int inputTriggerRatio = 5, float yMin=-4.3, float yMax=-2.2, int nPart = 1, int mode = -1) { + auto myGen = new GeneratorPythia8GapTriggeredLFmumu(inputTriggerRatio, yMin, yMax, nPart, mode); + auto seed = (gRandom->TRandom::GetSeed() % 900000000); + myGen->readString("Random:setSeed on"); + myGen->readString("Random:seed " + std::to_string(seed)); + return myGen; +} diff --git a/MC/config/PWGEM/external/generator/Generator_pythia8_HFLepton_pp.C b/MC/config/PWGEM/external/generator/Generator_pythia8_HFLepton_pp.C new file mode 100644 index 000000000..854d6ff42 --- /dev/null +++ b/MC/config/PWGEM/external/generator/Generator_pythia8_HFLepton_pp.C @@ -0,0 +1,202 @@ +#include "Pythia8/Pythia.h" +#include "Pythia8/HeavyIons.h" +#include "FairGenerator.h" +#include "FairPrimaryGenerator.h" +#include "Generators/GeneratorPythia8.h" +#include "TRandom3.h" +#include "TParticlePDG.h" +#include "TDatabasePDG.h" + +#include +#include + +using namespace Pythia8; + +class GeneratorPythia8HFLeptonpp : public o2::eventgen::GeneratorPythia8 +{ +public: + /// default constructor + GeneratorPythia8HFLeptonpp() = default; + + /// constructor + GeneratorPythia8HFLeptonpp(TString configsignal, int quarkPdg = 4, int lInputExternalID = 0) + { + + lGeneratedEvents = 0; + lExternalID = lInputExternalID; + mQuarkPdg = quarkPdg; + + auto seed = (gRandom->TRandom::GetSeed() % 900000000); + + int offset = (int)(gRandom->Uniform(1)); // create offset to mitigate edge effects due to small number of events per job + lGeneratedEvents += offset; + + cout << "Initalizing PYTHIA object used to generate signal events..." << endl; + TString pathconfigSignal = gSystem->ExpandPathName(configsignal.Data()); + pythiaObjectSignal.readFile(pathconfigSignal.Data()); + pythiaObjectSignal.readString("Random:setSeed on"); + pythiaObjectSignal.readString("Random:seed " + std::to_string(seed)); + pythiaObjectSignal.readString("Beams:eCM = 5360.0"); + pythiaObjectSignal.init(); + cout << "Initalization of signal event is complete" << endl; + + // flag the generators using type + // addCocktailConstituent(type, "interesting"); + // addCocktailConstitent(0, "minbias"); + // Add Sub generators + addSubGenerator(1, "charm lepton"); + addSubGenerator(2, "beauty forced decay"); + addSubGenerator(3, "beauty no foced decay"); + } + + /// Destructor + ~GeneratorPythia8HFLeptonpp() = default; + + void addTriggerOnDaughter(int nb, int pdg) + { + mNbDaughter = nb; + mPdgDaughter = pdg; + }; + void setQuarkRapidity(float yMin, float yMax) + { + mQuarkRapidityMin = yMin; + mQuarkRapidityMax = yMax; + }; + void setDaughterRapidity(float yMin, float yMax) + { + mDaughterRapidityMin = yMin; + mDaughterRapidityMax = yMax; + }; + +protected: + //__________________________________________________________________ + Bool_t generateEvent() override + { + /// reset event + mPythia.event.reset(); + + // Generate event of interest + Bool_t lGenerationOK = kFALSE; + while (!lGenerationOK) { + if (pythiaObjectSignal.next()) { + lGenerationOK = selectEvent(pythiaObjectSignal.event); + } + } + mPythia.event = pythiaObjectSignal.event; + notifySubGenerator(lExternalID); + + lGeneratedEvents++; + // mPythia.next(); + + return true; + } + + bool selectEvent(const Pythia8::Event& event) + { + bool isGoodAtPartonLevel = false, isGoodAtDaughterLevel = (mPdgDaughter != 0) ? false : true; + int nbDaughter = 0; + for (auto iPart{0}; iPart < event.size(); ++iPart) { + // search for Q-Qbar mother with at least one Q in rapidity window + if (!isGoodAtPartonLevel) { + auto daughterList = event[iPart].daughterList(); + bool hasQ = false, hasQbar = false, atSelectedY = false; + for (auto iDau : daughterList) { + if (event[iDau].id() == mQuarkPdg) { + hasQ = true; + } + if (event[iDau].id() == -mQuarkPdg) { + hasQbar = true; + } + if ((std::abs(event[iDau].id()) == mQuarkPdg) && (event[iDau].y() > mQuarkRapidityMin) && (event[iDau].y() < mQuarkRapidityMax)) + atSelectedY = true; + } + if (hasQ && hasQbar && atSelectedY) { + isGoodAtPartonLevel = true; + } + } + // search for mNbDaughter daughters of type mPdgDaughter in rapidity window + if (!isGoodAtDaughterLevel) { + int id = std::abs(event[iPart].id()); + float rap = event[iPart].y(); + if (id == mPdgDaughter) { + int motherindexa = event[iPart].mother1(); + if (motherindexa > 0) { + int idmother = std::abs(event[motherindexa].id()); + if (int(std::abs(idmother) / 100.) == 4 || int(std::abs(idmother) / 1000.) == 4 || int(std::abs(idmother) / 100.) == 5 || int(std::abs(idmother) / 1000.) == 5) { + if (rap > mDaughterRapidityMin && rap < mDaughterRapidityMax) { + nbDaughter++; + if (nbDaughter >= mNbDaughter) isGoodAtDaughterLevel = true; + } + } + } + } + } + // we send the trigger + if (isGoodAtPartonLevel && isGoodAtDaughterLevel) { + return true; + } + } + return false; + }; + +private: + // Interface to override import particles + Pythia8::Event mOutputEvent; + + // Properties of selection + int mQuarkPdg; + float mQuarkRapidityMin; + float mQuarkRapidityMax; + int mPdgDaughter; + int mNbDaughter; + float mDaughterRapidityMin; + float mDaughterRapidityMax; + + Long64_t lGeneratedEvents; + // ID for different generators + int lExternalID; + + // Base event generators + Pythia8::Pythia pythiaObjectSignal; ///Signal collision generator +}; + +// Predefined generators: + +// Charm-enriched forced decay +FairGenerator* GeneratorPythia8CharmLepton(int inputExternalID, int pdgLepton, float yMinQ = -1.5, float yMaxQ = 1.5, float yMinL = -1, float yMaxL = 1) +{ + auto myGen = new GeneratorPythia8HFLeptonpp("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/generator/pythia8_pp_cr2_forceddecayscharm.cfg", 4, inputExternalID); + auto seed = (gRandom->TRandom::GetSeed() % 900000000); + myGen->readString("Random:setSeed on"); + myGen->readString("Random:seed " + std::to_string(seed)); + myGen->setQuarkRapidity(yMinQ, yMaxQ); + myGen->addTriggerOnDaughter(2, pdgLepton); + myGen->setDaughterRapidity(yMinL, yMaxL); + return myGen; +} + +// Beauty-enriched forced decay +FairGenerator* GeneratorPythia8BeautyForcedDecays(int inputExternalID, int pdgLepton, float yMinQ = -1.5, float yMaxQ = 1.5, float yMinL = -1, float yMaxL = 1) +{ + auto myGen = new GeneratorPythia8HFLeptonpp("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/generator/pythia8_bbbar_forceddecayscharmbeauty.cfg", 5, inputExternalID); + auto seed = (gRandom->TRandom::GetSeed() % 900000000); + myGen->readString("Random:setSeed on"); + myGen->readString("Random:seed " + std::to_string(seed)); + myGen->setQuarkRapidity(yMinQ, yMaxQ); + myGen->addTriggerOnDaughter(2, pdgLepton); + myGen->setDaughterRapidity(yMinL, yMaxL); + return myGen; +} + +// Beauty-enriched no forced decay +FairGenerator* GeneratorPythia8BeautyNoForcedDecays(int inputExternalID, int pdgLepton, float yMinQ = -1.5, float yMaxQ = 1.5, float yMinL = -1, float yMaxL = 1) +{ + auto myGen = new GeneratorPythia8HFLeptonpp("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/generator/pythia8_bbbar.cfg", 5, inputExternalID); + auto seed = (gRandom->TRandom::GetSeed() % 900000000); + myGen->readString("Random:setSeed on"); + myGen->readString("Random:seed " + std::to_string(seed)); + myGen->setQuarkRapidity(yMinQ, yMaxQ); + myGen->addTriggerOnDaughter(2, pdgLepton); + myGen->setDaughterRapidity(yMinL, yMaxL); + return myGen; +} diff --git a/MC/config/PWGEM/external/generator/Generator_pythia8_embed_HFLepton.C b/MC/config/PWGEM/external/generator/Generator_pythia8_embed_HFLepton.C new file mode 100644 index 000000000..f82693d89 --- /dev/null +++ b/MC/config/PWGEM/external/generator/Generator_pythia8_embed_HFLepton.C @@ -0,0 +1,396 @@ +/////////////////////////////////////////////////////////////////////////////// +/// /// +/// HF MC generator for Pb-Pb /// +/// Option 1: generate N PYTHIA events triggered on ccbar and/or bbbar /// +/// to be embedded with a underlying Pb-Pb event /// +/// /// +/////////////////////////////////////////////////////////////////////////////// + +#include "Generator_pythia8_HFLepton_pp.C" + +using namespace Pythia8; + +#include +namespace hf_generators +{ + enum GenType : int { + Charm = 0, // --> GeneratorPythia8CharmLepton: charm enriched + Beauty, // --> GeneratorPythia8BeautyForcedDecays: beauty enriched with forced decays + BeautyNoForcedDecays, // --> GeneratorPythia8BeautyNoForcedDecays: beauty enriched no forced decays + NGenType + }; +} + +class GeneratorPythia8EmbedHFLepton : public o2::eventgen::GeneratorPythia8 +{ +public: + + /// default constructor + GeneratorPythia8EmbedHFLepton() = default; + + /// Destructor + ~GeneratorPythia8EmbedHFLepton() { + // Clean up the internally created HF generator if any + if (mGeneratorEvHFLepton) { + delete mGeneratorEvHFLepton; + mGeneratorEvHFLepton = nullptr; + } + } + + /// Init + bool Init() override + { + return o2::eventgen::GeneratorPythia8::Init(); + } + + /// @brief setup the event generator for HF signals + /// \param gentype generator type (only ccbar, only bbbar, both) + /// \param usePtHardBins flag to enable/disable pt-hard bins + /// \param yQuarkMin minimum quark rapidity + /// \param yQuarkMax maximum quark rapidity + /// \param yHadronMin minimum hadron rapidity + /// \param yHadronMax maximum hadron rapidity + /// \param hadronPdgList list of PDG codes for hadrons to be used in trigger + /// \param quarkPdgList list of PDG codes for quarks to be enriched in the trigger + void setupGeneratorEvHFLepton(int genType, int inputExternalID, int pdgLepton, float yMinQ = -1.5, float yMaxQ = 1.5, float yMinL = -1, float yMaxL = 1) { + mGeneratorEvHFLepton = nullptr; + switch (genType) + { + case hf_generators::Charm: + LOG(info) << "********** [GeneratorPythia8EmbedHFLepton] configuring GeneratorPythia8CharmLepton **********"; + LOG(info) << "********** Default number of HF signal events to be merged (updated by notifyEmbedding): " << mNumSigEvs; + mGeneratorEvHFLepton = dynamic_cast(GeneratorPythia8CharmLepton(inputExternalID, pdgLepton, yMinQ, yMaxQ, yMinL, yMaxL)); + break; + case hf_generators::Beauty: + LOG(info) << "********** [GeneratorPythia8EmbedHFLepton] configuring GeneratorPythia8BeautyForcedDecays **********"; + LOG(info) << "********** Default number of HF signal events to be merged (updated by notifyEmbedding): " << mNumSigEvs; + mGeneratorEvHFLepton = dynamic_cast(GeneratorPythia8BeautyForcedDecays(inputExternalID, pdgLepton, yMinQ, yMaxQ, yMinL, yMaxL)); + break; + case hf_generators::BeautyNoForcedDecays: + LOG(info) << "********** [GeneratorPythia8EmbedHFLepton] configuring GeneratorPythia8BeautyNoForcedDecays **********"; + LOG(info) << "********** Default number of HF signal events to be merged (updated by notifyEmbedding): " << mNumSigEvs; + mGeneratorEvHFLepton = dynamic_cast(GeneratorPythia8BeautyNoForcedDecays(inputExternalID, pdgLepton, yMinQ, yMaxQ, yMinL, yMaxL)); + break; + default: + LOG(fatal) << "********** [GeneratorPythia8EmbedHFLepton] bad configuration, fix it! **********"; + break; + } + mGeneratorEvHFLepton->Init(); + } + + // This function is called by the primary generator + // for each event in case we are in embedding mode. + // We use it to setup the number of signal events + // to be generated and to be embedded on the background. + void notifyEmbedding(const o2::dataformats::MCEventHeader* bkgHeader) override + { + LOG(info) << "[notifyEmbedding] ----- Function called"; + + /// Impact parameter between the two nuclei + const float x = bkgHeader->GetB(); + LOG(info) << "[notifyEmbedding] ----- Collision impact parameter: " << x; + + /// number of events to be embedded in a background event + // gen->setFormula("max(1.,120.*(x<5.)+80.*(1.-x/20.)*(x>5.)*(x<11.)+240.*(1.-x/13.)*(x>11.))"); + mNumSigEvs = static_cast(std::lround(5.0 + 0.886202881 * std::pow(std::max(0.0f, 17.5f - x), 1.7))); + //mNumSigEvs = static_cast(std::max(1.,120.*(x<5.)+80.*(1.-x/20.)*(x>5.)*(x<11.)+240.*(1.-x/13.)*(x>11.))); + //mNumSigEvs = 1; + LOG(info) << "[notifyEmbedding] ----- generating " << mNumSigEvs << " signal events " << std::endl; + }; + +protected: + +/// @brief Main function for event generation +bool generateEvent() override +{ + /// Overriding that from GeneratorPythia8, to avoid the simulation of an untriggered event as first + return true; +} + +/// @brief Main function to find out whether the particle comes charm or beauty quark +/// @param partId is the index of the particle under study +/// @param particles are the particles of the full event +bool isFromCharmOrBeauty(const int partId, std::vector const& particles) { + + // Let's check wheter this is already a c or b quark? + const TParticle& part = particles.at(partId); + const int pdgAbs = std::abs(part.GetPdgCode()); + if(pdgAbs == 4 || pdgAbs == 5) { + return true; + } + + // Let's check the mother particles of the hadron at all stages + // and look for the charm or beauty quark + std::vector> arrayIds{}; + std::vector initVec{partId}; + arrayIds.push_back(initVec); // the first vector contains the index of the original particle + int stage = 0; + while(arrayIds[-stage].size() > 0) { + + //LOG(info) << "### stage " << stage << ", arrayIds[-stage].size() = " << arrayIds[-stage].size(); + + std::vector arrayIdsStage{}; + + for (auto& iPart : arrayIds[-stage]) { // check all the particles that were the mothers at the previous stage + const TParticle& partStage = particles.at(iPart); + + // check the first mother + const int firstMotherId = partStage.GetFirstMother(); + if( firstMotherId >= 0) { + const TParticle& firstMother = particles.at(firstMotherId); + const int pdgAbsFirstMother = std::abs(firstMother.GetPdgCode()); + if(pdgAbsFirstMother == 4 || pdgAbsFirstMother == 5) { + return true; + } + // the first mother is not a charm or beauty quark + arrayIdsStage.push_back(firstMotherId); + } + + // let's check all other mothers, if any + const int lastMotherId = partStage.GetSecondMother(); + if(lastMotherId >=0 && lastMotherId != firstMotherId) { + for(int motherId = firstMotherId+1 /*first mother already considered*/; motherId <= lastMotherId; motherId++) { + const TParticle& mother = particles.at(motherId); + const int pdgAbsMother = std::abs(mother.GetPdgCode()); + if(pdgAbsMother == 4 || pdgAbsMother == 5) { + return true; + } + // this mother is not a charm or beauty quark + arrayIdsStage.push_back(motherId); + } + } + + } + + /* + All light-flavour mothers are not considered with this approach + eg: D+ coming from c and uBar --> uBar lost + --> TODO: check if the current particle has a charm or beauty hadron as daughter. If yes, keep it + >>> we can ignore it! This might be useful only for jet analyses, however this approach of embedding N pp events into a Pb-Pb one might be not ideal for them + */ + + // none of the particle mothers is a charm or beauty quark, let's consider their indices for the next stage + arrayIds.push_back(arrayIdsStage); + stage--; // ready to go to next stage + + } /// end while(arrayIds[-stage].size() > 0) + + return false; +} + + +void printParticleVector(std::vector v) { + for(int id=0; id idFirstMother=" << idFirstMother << ", idLastMother=" << idLastMother << ", idFirstDaughter=" << idFirstDaughter << ", idLastDaughter=" << idLastDaughter; + } +} + + +int findKey(const std::map& m, int value) { + for(std::pair p : m) { + if(p.second /*index*/ == value) { + return p.first; // key --> it becomes the new index + } + } + return -1; +} + + +/// @brief Main function to copy the generated particles in mPythia.event into the stack (this.mParticles) +Bool_t importParticles() override +{ + /// Import particles from generated event + /// This should not do anything now, since we override generateEvent + GeneratorPythia8::importParticles(); + + LOG(info) << ""; + LOG(info) << "*************************************************************"; + LOG(info) << "************** New signal event considered **************"; + LOG(info) << "*************************************************************"; + LOG(info) << ""; + + /// Generate mNumSigEvs HF events to be merged in one + int nEvsHF = 0; + while(nEvsHF < mNumSigEvs) { + + /// generate the HF event + bool genOk = false; + while(!genOk) { + genOk = (mGeneratorEvHFLepton->generateEvent() && mGeneratorEvHFLepton->importParticles() /*copy particles from mGeneratorEvHF.mPythia.event to mGeneratorEvHF.mParticles*/ ); + } + + int originalSize = mParticles.size(); // stack of this event generator + + // for debug + // LOG(info) << ""; + // LOG(info) << "============ Before HF event " << nEvsHF; + // LOG(info) << "Full stack (size " << originalSize << "):"; + // printParticleVector(mParticles); + + /// copy the particles from the HF event in the particle stack + auto particlesHfEvent = mGeneratorEvHFLepton->getParticles(); + std::map mapHfParticles = {}; + int counterHfParticles = 0; + + // for debug + // LOG(info) << "-----------------------------------------------"; + // LOG(info) << ">>> HF event " << nEvsHF; + // LOG(info) << " HF event stack:"; + // printParticleVector(particlesHfEvent); + + for(int iPart=0; iPart>>"; + // LOG(info) << " >>> printing mapHfParticles:"; + // for(auto& p : mapHfParticles) { + // const int pdgCodeFromMap = particlesHfEvent.at(p.second).GetPdgCode(); + // LOG(info) << " >>> entry " << p.first << ", original id = " << p.second << ", pdgCode=" << pdgCodeFromMap << " --> firstMotherId=" << particlesHfEvent.at(p.second).GetFirstMother() << ", lastMotherId=" << particlesHfEvent.at(p.second).GetSecondMother() << ", firstDaughterId=" << particlesHfEvent.at(p.second).GetFirstDaughter() << ", lastDaughterId=" << particlesHfEvent.at(p.second).GetLastDaughter(); + // } + + + // In the map we have only the particles from charm or beauty + // Let's readapt the mother/daughter indices accordingly + int offset = originalSize; + for(int iHfPart=0; iHfPart= 0) { + idFirstMother = findKey(mapHfParticles, idFirstMother); + /// If idFirstMother>=0, the 1st mother is from charm or beauty, i.e. is not a light-flavoured parton + /// Instead, if idFirstMother==-1 from findKey this means that the first mother was a light-flavoured parton --> not stored in the map + if(idFirstMother >=0) { + /// the 1st mother is from charm or beauty, i.e. is not a light-flavoured parton + if(idLastMother != idFirstMotherOrig) { + if(idLastMother != -1) { + /// idLastMother is >= 0 + } + } else { + /// idLastMother is equal to idFirstMother + idLastMother = idFirstMother; + } + isFirstMotherOk = true; + } + } + if(!isFirstMotherOk) { + /// - If we are here, it means that the 1st mother was not from charm or beauty + /// - No need to check whether idLastMother>=0, + /// because this would mean that none of the mother is from charm or beauty and this was checked already in isFromCharmOrBeauty + /// - Need to loop between 1st and last mother, to treat cases like these + /// [11:52:13][INFO] id = 565, pdgCode = -2 --> idFirstMother=519, idLastMother=519 + /// [11:52:13][INFO] id = 566, pdgCode = -4 --> idFirstMother=520, idLastMother=520 + /// [11:52:13][INFO] id = 567, pdgCode = -1 --> idFirstMother=518, idLastMother=518 + /// [11:52:13][INFO] id = 568, pdgCode = -311 --> idFirstMother=565, idLastMother=567 + /// [11:52:13][INFO] id = 569, pdgCode = -4212 --> idFirstMother=565, idLastMother=567 + /// --> w/o loop between 1st and last mother, the mother Ids assigned to this Sc+ (4212) by findKey are -1, both first and last + bool foundAnyMother = false; + for(int idMotherOrig=(idFirstMotherOrig+1); idMotherOrig<=idLastMother; idMotherOrig++) { + const int idMother = findKey(mapHfParticles, idMotherOrig); + if(idMother >= 0) { + /// this should mean that the mother is from HF, i.e. that we found the correct one + idFirstMother = idMother; + idLastMother = idFirstMother; + foundAnyMother = true; + break; + } + } + // set last mother to -1 if no mother has been found so far + if (!foundAnyMother) { + idLastMother = -1; + } + } + + /// fix daughter indices + idFirstDaughter = findKey(mapHfParticles, idFirstDaughter); + idLastDaughter = findKey(mapHfParticles, idLastDaughter); + + /// adjust the particle mother and daughter indices + particle.SetFirstMother((idFirstMother >= 0) ? idFirstMother + offset : idFirstMother); + particle.SetLastMother((idLastMother >= 0) ? idLastMother + offset : idLastMother); + particle.SetFirstDaughter((idFirstDaughter >= 0) ? idFirstDaughter + offset : idFirstDaughter); + particle.SetLastDaughter((idLastDaughter >= 0) ? idLastDaughter + offset : idLastDaughter); + + /// copy inside this.mParticles from mGeneratorEvHF.mParticles, i.e. the particles generated in mGeneratorEvHF + mParticles.push_back(particle); + + } + + // for debug + // LOG(info) << "-----------------------------------------------"; + // LOG(info) << "============ After HF event " << nEvsHF; + // LOG(info) << "Full stack:"; + // printParticleVector(mParticles); + + /// one more event generated, let's update the counter and clear it, to allow the next generation + nEvsHF++; + //mGeneratedEvents++; + mGeneratorEvHFLepton->clearParticles(); + } + + return true; +} + +private: + + Generator* mGeneratorEvHFLepton; // to generate HF signal events + + int mNumSigEvs{1}; // number of HF signal events to be merged in one Pythia event + //unsigned long long mGeneratedEvents; + +}; + +// Charm enriched +FairGenerator * GeneratorPythia8EmbedHFLeptonCharm(int inputExternalID, int pdgLepton, float yMinQ = -1.5, float yMaxQ = 1.5, float yMinL = -1, float yMaxL = 1) +{ + auto myGen = new GeneratorPythia8EmbedHFLepton(); + + /// setup the internal generator for HF events + myGen->setupGeneratorEvHFLepton(hf_generators::Charm, inputExternalID, pdgLepton, yMinQ = -1.5, yMaxQ = 1.5, yMinL = -1, yMaxL = 1); + + return myGen; +} + +// Beauty enriched +FairGenerator * GeneratorPythia8EmbedHFLeptonBeauty(int inputExternalID, int pdgLepton, float yMinQ = -1.5, float yMaxQ = 1.5, float yMinL = -1, float yMaxL = 1) +{ + auto myGen = new GeneratorPythia8EmbedHFLepton(); + + /// setup the internal generator for HF events + myGen->setupGeneratorEvHFLepton(hf_generators::Beauty, inputExternalID, pdgLepton, yMinQ = -1.5, yMaxQ = 1.5, yMinL = -1, yMaxL = 1); + + return myGen; +} + +// Charm and beauty enriched (with same ratio) +FairGenerator * GeneratorPythia8EmbedHFLeptonBeautyNoForcedDecays(int inputExternalID, int pdgLepton, float yMinQ = -1.5, float yMaxQ = 1.5, float yMinL = -1, float yMaxL = 1) +{ + auto myGen = new GeneratorPythia8EmbedHFLepton(); + + /// setup the internal generator for HF events + myGen->setupGeneratorEvHFLepton(hf_generators::BeautyNoForcedDecays, inputExternalID, pdgLepton, yMinQ = -1.5, yMaxQ = 1.5, yMinL = -1, yMaxL = 1); + + return myGen; +} diff --git a/MC/config/PWGEM/external/generator/Generator_pythia8_forcedDecays.C b/MC/config/PWGEM/external/generator/Generator_pythia8_forcedDecays.C new file mode 100644 index 000000000..d6117e0b8 --- /dev/null +++ b/MC/config/PWGEM/external/generator/Generator_pythia8_forcedDecays.C @@ -0,0 +1,179 @@ +#include "Generators/DecayerPythia8.h" +#include "Generators/GeneratorPythia8.h" +#include "SimulationDataFormat/MCGenProperties.h" +#include "SimulationDataFormat/ParticleStatus.h" +#include "TLorentzVector.h" + +namespace o2 { +namespace eventgen { + +class DecayerPythia8ForceDecays : public DecayerPythia8 { +public: + DecayerPythia8ForceDecays(){ + mPythia.readString("Random:setSeed = on"); + char* alien_proc_id = getenv("ALIEN_PROC_ID"); + int seed; + if (alien_proc_id != NULL) { + seed = atoi(alien_proc_id); + LOG(info) << "Seed for DecayerPythia8 set to ALIEN_PROC_ID: " << seed; + } else { + LOG(info) << "Unable to retrieve ALIEN_PROC_ID"; + LOG(info) << "Setting seed for DecayerPyhtia8 to 0 (random)"; + seed = 0; + } + mPythia.readString("Random:seed = "+std::to_string(seed)); + } + ~DecayerPythia8ForceDecays() = default; + + + void calculateWeights(std::vector &pdgs) { + TLorentzVector mom = TLorentzVector(0., 0., 0., 9999999.); + for (int pdg : pdgs) { + Decay(pdg, &mom); // do one fake decay to initalize everything correctly + auto particleData = mPythia.particleData.particleDataEntryPtr(pdg); + double weight = 0.; + for (int c = 0; c < particleData->sizeChannels(); c++) { + weight += particleData->channel(c).currentBR(); + } + LOG(info) << "PDG = " << pdg + << ": sum of branching ratios of active decay channels = " + << weight; + mWeights[pdg] = weight; + mPythia.particleData.mayDecay(pdg, false); + } + } + + void forceDecays(std::vector &mParticles, int mother_pos) { + TParticle *p = &mParticles[mother_pos]; + int pdg = p->GetPdgCode(); + TLorentzVector mom = TLorentzVector(p->Px(), p->Py(), p->Pz(), p->Energy()); + Decay(pdg, &mom); + TClonesArray daughters = TClonesArray("TParticle"); + int nParticles = ImportParticles(&daughters); + int mcGenStatus = o2::mcgenstatus::getGenStatusCode(p->GetStatusCode()); + p->SetStatusCode(o2::mcgenstatus::MCGenStatusEncoding(2, -mcGenStatus).fullEncoding); + p->SetBit(ParticleStatus::kToBeDone, false); + double mother_weight = p->GetWeight(); + TParticle *mother = static_cast(daughters[0]); + int mParticles_size = mParticles.size(); + p->SetFirstDaughter(mother->GetFirstDaughter() + mParticles_size - 1); + p->SetLastDaughter(mother->GetLastDaughter() + mParticles_size - 1); + for (int j = 1; j < nParticles; + j++) { // start loop at 1 to not include mother + TParticle *d = static_cast(daughters[j]); + double decay_weight = mWeights[abs(pdg)]; + if (decay_weight == 0) { + LOG(error) << "Decaying particle (PDG = " << pdg + << ") with decay weight = 0. Did you set the pdg codes for " + "calculating weights correctly?"; + } + d->SetWeight(decay_weight * mother_weight); + if (d->GetStatusCode() == 1) { + d->SetStatusCode( + o2::mcgenstatus::MCGenStatusEncoding(1, 91).fullEncoding); + d->SetBit(ParticleStatus::kToBeDone, true); + } else { + d->SetStatusCode( + o2::mcgenstatus::MCGenStatusEncoding(2, -91).fullEncoding); + d->SetBit(ParticleStatus::kToBeDone, false); + } + int firstmother = d->GetFirstMother(); + int firstdaughter = d->GetFirstDaughter(); + int lastdaughter = d->GetLastDaughter(); + if (firstmother == 0) { + d->SetFirstMother(mother_pos); + d->SetLastMother(mother_pos); + } else { + d->SetFirstMother(firstmother + mParticles_size - 1); + d->SetLastMother(firstmother + mParticles_size - 1); + } + if (firstdaughter == 0) { + d->SetFirstDaughter(-1); + } else { + d->SetFirstDaughter(firstdaughter + mParticles_size - 1); + } + if (lastdaughter == 0) { + d->SetLastDaughter(-1); + } else { + d->SetLastDaughter(lastdaughter + mParticles_size - 1); + } + mParticles.push_back(*d); + } + } + +private: + std::map mWeights; +}; + +class GeneratorPythia8ForcedDecays : public GeneratorPythia8 { + +public: + GeneratorPythia8ForcedDecays(){ + mPythia.readString("Random:setSeed = on"); + char* alien_proc_id = getenv("ALIEN_PROC_ID"); + int seed; + if (alien_proc_id != NULL) { + seed = atoi(alien_proc_id); + LOG(info) << "Seed for GeneratorPythia8 set to ALIEN_PROC_ID: " << seed; + } else { + LOG(info) << "Unable to retrieve ALIEN_PROC_ID"; + LOG(info) << "Setting seed for GeneratorPyhtia8 to 0 (random)"; + seed = 0; + } + mPythia.readString("Random:seed = "+std::to_string(seed)); + } + ~GeneratorPythia8ForcedDecays() = default; + + // overriden methods + bool Init() override { return GeneratorPythia8::Init() && InitDecayer(); }; + bool importParticles() override { + return GeneratorPythia8::importParticles() && makeForcedDecays(); + }; + + void setPDGs(TString pdgs) { + TObjArray *obj = pdgs.Tokenize(";"); + for (int i = 0; i < obj->GetEntriesFast(); i++) { + std::string spdg = obj->At(i)->GetName(); + int pdg = std::stoi(spdg); + mPdgCodes.push_back(pdg); + LOG(info) << "Force decay of PDG = " << pdg; + } + } + +protected: + bool InitDecayer() { + mDecayer = new DecayerPythia8ForceDecays(); + mDecayer->Init(); + mDecayer->calculateWeights(mPdgCodes); + for (int pdg : mPdgCodes) { + mPythia.particleData.mayDecay(pdg, false); + } + return true; + } + + bool makeForcedDecays() { + int mParticles_size = mParticles.size(); + for (int i = 0; i < mParticles_size; i++) { + int pdg = mParticles[i].GetPdgCode(); + if (std::find(mPdgCodes.begin(), mPdgCodes.end(), abs(pdg)) != + mPdgCodes.end()) { + mDecayer->forceDecays(mParticles, i); + mParticles_size = mParticles.size(); + } + } + return true; + } + +private: + DecayerPythia8ForceDecays *mDecayer; + std::vector mPdgCodes; +}; + +} // namespace eventgen +} // namespace o2 + +FairGenerator *GeneratePythia8ForcedDecays(TString pdgs) { + auto gen = new o2::eventgen::GeneratorPythia8ForcedDecays(); + gen->setPDGs(pdgs); + return gen; +} \ No newline at end of file diff --git a/MC/config/PWGEM/ini/GeneratorDYee_GapTriggered_Gap2_OO5360GeV.ini b/MC/config/PWGEM/ini/GeneratorDYee_GapTriggered_Gap2_OO5360GeV.ini new file mode 100644 index 000000000..a83045099 --- /dev/null +++ b/MC/config/PWGEM/ini/GeneratorDYee_GapTriggered_Gap2_OO5360GeV.ini @@ -0,0 +1,7 @@ +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/external/generator/Generator_pythia8_GapTriggered_DYll.C +funcName = GeneratorPythia8GapTriggeredDYll(2, 1, 11, -1.5, +1.5, 1000080160, 1000080160, 5360.0) + +[GeneratorPythia8] +config = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/generator/configPythiaEmpty.cfg +includePartonEvent = true diff --git a/MC/config/PWGEM/ini/GeneratorDYee_GapTriggered_Gap5_OO5360GeV.ini b/MC/config/PWGEM/ini/GeneratorDYee_GapTriggered_Gap5_OO5360GeV.ini new file mode 100644 index 000000000..f07e285dd --- /dev/null +++ b/MC/config/PWGEM/ini/GeneratorDYee_GapTriggered_Gap5_OO5360GeV.ini @@ -0,0 +1,7 @@ +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/external/generator/Generator_pythia8_GapTriggered_DYll.C +funcName = GeneratorPythia8GapTriggeredDYll(5, 1, 11, -1.5, +1.5, 1000080160, 1000080160, 5360.0) + +[GeneratorPythia8] +config = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/generator/configPythiaEmpty.cfg +includePartonEvent = true diff --git a/MC/config/PWGEM/ini/GeneratorDYee_GapTriggered_Gap5_pp13600GeV.ini b/MC/config/PWGEM/ini/GeneratorDYee_GapTriggered_Gap5_pp13600GeV.ini new file mode 100644 index 000000000..7ecf763cf --- /dev/null +++ b/MC/config/PWGEM/ini/GeneratorDYee_GapTriggered_Gap5_pp13600GeV.ini @@ -0,0 +1,7 @@ +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/external/generator/Generator_pythia8_GapTriggered_DYll.C +funcName = GeneratorPythia8GapTriggeredDYll(5, 1, 11, -1.5, +1.5, 2212, 2212, 13600.0) + +[GeneratorPythia8] +config = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/generator/configPythiaEmpty.cfg +includePartonEvent = true diff --git a/MC/config/PWGEM/ini/GeneratorDYee_GapTriggered_Gap5_pp5360GeV.ini b/MC/config/PWGEM/ini/GeneratorDYee_GapTriggered_Gap5_pp5360GeV.ini new file mode 100644 index 000000000..70e802dec --- /dev/null +++ b/MC/config/PWGEM/ini/GeneratorDYee_GapTriggered_Gap5_pp5360GeV.ini @@ -0,0 +1,7 @@ +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/external/generator/Generator_pythia8_GapTriggered_DYll.C +funcName = GeneratorPythia8GapTriggeredDYll(5, 1, 11, -1.5, +1.5, 2212, 2212, 5360.0) + +[GeneratorPythia8] +config = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/generator/configPythiaEmpty.cfg +includePartonEvent = true diff --git a/MC/config/PWGEM/ini/GeneratorDYmumu_GapTriggered_Gap1_pp13600GeV.ini b/MC/config/PWGEM/ini/GeneratorDYmumu_GapTriggered_Gap1_pp13600GeV.ini new file mode 100644 index 000000000..101e8ce12 --- /dev/null +++ b/MC/config/PWGEM/ini/GeneratorDYmumu_GapTriggered_Gap1_pp13600GeV.ini @@ -0,0 +1,7 @@ +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/external/generator/Generator_pythia8_GapTriggered_DYll.C +funcName = GeneratorPythia8GapTriggeredDYll(1, 1, 13, -6, -1, 2212, 2212, 13600.0) + +[GeneratorPythia8] +config = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/generator/configPythiaEmpty.cfg +includePartonEvent = true diff --git a/MC/config/PWGEM/ini/GeneratorDYmumu_GapTriggered_Gap5_OO5360GeV.ini b/MC/config/PWGEM/ini/GeneratorDYmumu_GapTriggered_Gap5_OO5360GeV.ini new file mode 100644 index 000000000..67001defd --- /dev/null +++ b/MC/config/PWGEM/ini/GeneratorDYmumu_GapTriggered_Gap5_OO5360GeV.ini @@ -0,0 +1,7 @@ +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/external/generator/Generator_pythia8_GapTriggered_DYll.C +funcName = GeneratorPythia8GapTriggeredDYll(5, 1, 13, -6, -1, 1000080160, 1000080160, 5360.0) + +[GeneratorPythia8] +config = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/generator/configPythiaEmpty.cfg +includePartonEvent = true diff --git a/MC/config/PWGEM/ini/GeneratorDYmumu_GapTriggered_Gap5_pp5360GeV.ini b/MC/config/PWGEM/ini/GeneratorDYmumu_GapTriggered_Gap5_pp5360GeV.ini new file mode 100644 index 000000000..82c45f3fd --- /dev/null +++ b/MC/config/PWGEM/ini/GeneratorDYmumu_GapTriggered_Gap5_pp5360GeV.ini @@ -0,0 +1,7 @@ +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/external/generator/Generator_pythia8_GapTriggered_DYll.C +funcName = GeneratorPythia8GapTriggeredDYll(5, 1, 13, -6, -1, 2212, 2212, 5360.0) + +[GeneratorPythia8] +config = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/generator/configPythiaEmpty.cfg +includePartonEvent = true diff --git a/MC/config/PWGEM/ini/GeneratorEMCocktail.ini b/MC/config/PWGEM/ini/GeneratorEMCocktail.ini deleted file mode 100644 index 1959479f4..000000000 --- a/MC/config/PWGEM/ini/GeneratorEMCocktail.ini +++ /dev/null @@ -1,6 +0,0 @@ -### The setup uses an external event generator -### This part sets the path of the file and the function call to retrieve it - -[GeneratorExternal] -fileName = ${O2DPG_ROOT}/MC/config/PWGEM/external/generator/GeneratorEMCocktailV2.C -funcName=GenerateEMCocktail(400,0,3,63,"${O2DPG_ROOT}/MC/config/PWGEM/parametrizations/PbPb5TeV_central.json","5TeV_0005_wRatio_etatest",350,0.0,30.0,10000,1,1,0,0,"",0,1.1,"${O2DPG_ROOT}/MC/config/PWGEM/decaytables/decaytable_LMee.dat",0) \ No newline at end of file diff --git a/MC/config/PWGEM/ini/GeneratorEMCocktail_502PbPb_0005.ini b/MC/config/PWGEM/ini/GeneratorEMCocktail_502PbPb_0005.ini new file mode 100644 index 000000000..32a53e993 --- /dev/null +++ b/MC/config/PWGEM/ini/GeneratorEMCocktail_502PbPb_0005.ini @@ -0,0 +1,6 @@ +### The setup uses an external event generator +### This part sets the path of the file and the function call to retrieve it + +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/external/generator/GeneratorEMCocktailV2.C +funcName=GenerateEMCocktail(400,0,3,63,"${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/parametrizations/PbPb5TeV_central.json","5TeV_0005_wRatio_etatest",350,0.0,30.0,10000,1,1,0,0,"",0,1.1,"${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/decaytables/decaytable_LMee.dat",1) \ No newline at end of file diff --git a/MC/config/PWGEM/ini/GeneratorEMCocktail_502PbPb_0510.ini b/MC/config/PWGEM/ini/GeneratorEMCocktail_502PbPb_0510.ini new file mode 100644 index 000000000..2e26ba807 --- /dev/null +++ b/MC/config/PWGEM/ini/GeneratorEMCocktail_502PbPb_0510.ini @@ -0,0 +1,6 @@ +### The setup uses an external event generator +### This part sets the path of the file and the function call to retrieve it + +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/external/generator/GeneratorEMCocktailV2.C +funcName=GenerateEMCocktail(400,0,3,63,"${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/parametrizations/PbPb5TeV_central.json","5TeV_0510_wRatio_etatest",350,0.0,30.0,10000,1,1,0,0,"",0,1.1,"${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/decaytables/decaytable_LMee.dat",1) \ No newline at end of file diff --git a/MC/config/PWGEM/ini/GeneratorEMCocktail_502PbPb_1020.ini b/MC/config/PWGEM/ini/GeneratorEMCocktail_502PbPb_1020.ini new file mode 100644 index 000000000..a492a4a1e --- /dev/null +++ b/MC/config/PWGEM/ini/GeneratorEMCocktail_502PbPb_1020.ini @@ -0,0 +1,6 @@ +### The setup uses an external event generator +### This part sets the path of the file and the function call to retrieve it + +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/external/generator/GeneratorEMCocktailV2.C +funcName=GenerateEMCocktail(400,0,3,63,"${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/parametrizations/PbPb5TeV_central.json","5TeV_1020_wRatio_etatest",350,0.0,30.0,10000,1,1,0,0,"",0,1.1,"${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/decaytables/decaytable_LMee.dat",1) \ No newline at end of file diff --git a/MC/config/PWGEM/ini/GeneratorEMCocktail_502PbPb_2030.ini b/MC/config/PWGEM/ini/GeneratorEMCocktail_502PbPb_2030.ini new file mode 100644 index 000000000..0cc122f7b --- /dev/null +++ b/MC/config/PWGEM/ini/GeneratorEMCocktail_502PbPb_2030.ini @@ -0,0 +1,6 @@ +### The setup uses an external event generator +### This part sets the path of the file and the function call to retrieve it + +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/external/generator/GeneratorEMCocktailV2.C +funcName=GenerateEMCocktail(400,0,3,63,"${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/parametrizations/PbPb5TeV_central.json","5TeV_2030_wRatio_etatest",350,0.0,30.0,10000,1,1,0,0,"",0,1.1,"${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/decaytables/decaytable_LMee.dat",1) \ No newline at end of file diff --git a/MC/config/PWGEM/ini/GeneratorEMCocktail_502PbPb_3040.ini b/MC/config/PWGEM/ini/GeneratorEMCocktail_502PbPb_3040.ini new file mode 100644 index 000000000..73960d0e8 --- /dev/null +++ b/MC/config/PWGEM/ini/GeneratorEMCocktail_502PbPb_3040.ini @@ -0,0 +1,6 @@ +### The setup uses an external event generator +### This part sets the path of the file and the function call to retrieve it + +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/external/generator/GeneratorEMCocktailV2.C +funcName=GenerateEMCocktail(400,0,3,63,"${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/parametrizations/PbPb5TeV_central.json","5TeV_3040_wRatio_etatest",350,0.0,30.0,10000,1,1,0,0,"",0,1.1,"${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/decaytables/decaytable_LMee.dat",1) \ No newline at end of file diff --git a/MC/config/PWGEM/ini/GeneratorEMCocktail_502PbPb_4050.ini b/MC/config/PWGEM/ini/GeneratorEMCocktail_502PbPb_4050.ini new file mode 100644 index 000000000..25d78e2f1 --- /dev/null +++ b/MC/config/PWGEM/ini/GeneratorEMCocktail_502PbPb_4050.ini @@ -0,0 +1,6 @@ +### The setup uses an external event generator +### This part sets the path of the file and the function call to retrieve it + +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/external/generator/GeneratorEMCocktailV2.C +funcName=GenerateEMCocktail(400,0,3,63,"${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/parametrizations/PbPb5TeV_central.json","5TeV_4050_wRatio_etatest",350,0.0,30.0,10000,1,1,0,0,"",0,1.1,"${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/decaytables/decaytable_LMee.dat",1) \ No newline at end of file diff --git a/MC/config/PWGEM/ini/GeneratorEMCocktail_502PbPb_5060.ini b/MC/config/PWGEM/ini/GeneratorEMCocktail_502PbPb_5060.ini new file mode 100644 index 000000000..4e74682df --- /dev/null +++ b/MC/config/PWGEM/ini/GeneratorEMCocktail_502PbPb_5060.ini @@ -0,0 +1,6 @@ +### The setup uses an external event generator +### This part sets the path of the file and the function call to retrieve it + +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/external/generator/GeneratorEMCocktailV2.C +funcName=GenerateEMCocktail(400,0,3,63,"${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/parametrizations/PbPb5TeV_peripheral.json","5TeV_5060_wRatio_pi0corr",350,0.0,30.0,10000,1,1,0,0,"",0,1.1,"${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/decaytables/decaytable_LMee.dat",1) \ No newline at end of file diff --git a/MC/config/PWGEM/ini/GeneratorEMCocktail_502PbPb_6070.ini b/MC/config/PWGEM/ini/GeneratorEMCocktail_502PbPb_6070.ini new file mode 100644 index 000000000..0cc3b1065 --- /dev/null +++ b/MC/config/PWGEM/ini/GeneratorEMCocktail_502PbPb_6070.ini @@ -0,0 +1,6 @@ +### The setup uses an external event generator +### This part sets the path of the file and the function call to retrieve it + +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/external/generator/GeneratorEMCocktailV2.C +funcName=GenerateEMCocktail(400,0,3,63,"${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/parametrizations/PbPb5TeV_peripheral.json","5TeV_6070_wRatio_pi0corr",350,0.0,30.0,10000,1,1,0,0,"",0,1.1,"${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/decaytables/decaytable_LMee.dat",1) \ No newline at end of file diff --git a/MC/config/PWGEM/ini/GeneratorEMCocktail_502PbPb_7080.ini b/MC/config/PWGEM/ini/GeneratorEMCocktail_502PbPb_7080.ini new file mode 100644 index 000000000..524067a5e --- /dev/null +++ b/MC/config/PWGEM/ini/GeneratorEMCocktail_502PbPb_7080.ini @@ -0,0 +1,6 @@ +### The setup uses an external event generator +### This part sets the path of the file and the function call to retrieve it + +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/external/generator/GeneratorEMCocktailV2.C +funcName=GenerateEMCocktail(400,0,3,63,"${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/parametrizations/PbPb5TeV_peripheral.json","5TeV_7080_wRatio_pi0corr",350,0.0,30.0,10000,1,1,0,0,"",0,1.1,"${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/decaytables/decaytable_LMee.dat",1) \ No newline at end of file diff --git a/MC/config/PWGEM/ini/GeneratorEMCocktail_502PbPb_8090.ini b/MC/config/PWGEM/ini/GeneratorEMCocktail_502PbPb_8090.ini new file mode 100644 index 000000000..596653424 --- /dev/null +++ b/MC/config/PWGEM/ini/GeneratorEMCocktail_502PbPb_8090.ini @@ -0,0 +1,6 @@ +### The setup uses an external event generator +### This part sets the path of the file and the function call to retrieve it + +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/external/generator/GeneratorEMCocktailV2.C +funcName=GenerateEMCocktail(400,0,3,63,"${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/parametrizations/PbPb5TeV_peripheral.json","5TeV_8090_wRatio_pi0corr",350,0.0,30.0,10000,1,1,0,0,"",0,1.1,"${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/decaytables/decaytable_LMee.dat",1) \ No newline at end of file diff --git a/MC/config/PWGEM/ini/GeneratorEMCocktail_Run3_OO.ini b/MC/config/PWGEM/ini/GeneratorEMCocktail_Run3_OO.ini new file mode 100644 index 000000000..83898beae --- /dev/null +++ b/MC/config/PWGEM/ini/GeneratorEMCocktail_Run3_OO.ini @@ -0,0 +1,6 @@ +### The setup uses an external event generator +### This part sets the path of the file and the function call to retrieve it + +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/external/generator/GeneratorEMCocktailV2.C +funcName=GenerateEMCocktail(200,0,3,63,"${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/parametrizations/OO_536TeV_param.json","536TeV_MB_pythia",200,0.0,30.0,10000,1,1,0,0,"",0,1.1,"${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/decaytables/decaytable_LMee.dat",1) diff --git a/MC/config/PWGEM/ini/GeneratorEMCocktail_Run3pp.ini b/MC/config/PWGEM/ini/GeneratorEMCocktail_Run3pp.ini new file mode 100644 index 000000000..c3d533b4b --- /dev/null +++ b/MC/config/PWGEM/ini/GeneratorEMCocktail_Run3pp.ini @@ -0,0 +1,6 @@ +### The setup uses an external event generator +### This part sets the path of the file and the function call to retrieve it + +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/external/generator/GeneratorEMCocktailV2.C +funcName=GenerateEMCocktail(200,0,3,127,"${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/parametrizations/pp13TeV_run2.json","13TeV_MB_default",350,0.0,20.0,10000,1,1,0,0,"",0,1.1,"${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/decaytables/decaytable_LMee.dat",1) diff --git a/MC/config/PWGEM/ini/GeneratorEMCocktail_Run3pp_lowBeta.ini b/MC/config/PWGEM/ini/GeneratorEMCocktail_Run3pp_lowBeta.ini new file mode 100644 index 000000000..372a2e1a5 --- /dev/null +++ b/MC/config/PWGEM/ini/GeneratorEMCocktail_Run3pp_lowBeta.ini @@ -0,0 +1,6 @@ +### The setup uses an external event generator +### This part sets the path of the file and the function call to retrieve it + +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/external/generator/GeneratorEMCocktailV2.C +funcName=GenerateEMCocktail(200,0,3,63,"${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/parametrizations/pp136TeV_run3.json","13TeV_MB_default",200,0.0,30.0,10000,1,1,0,0,"",0,1.1,"${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/decaytables/decaytable_LMee.dat",1) diff --git a/MC/config/PWGEM/ini/GeneratorEMCocktail_Run3pp_v2.ini b/MC/config/PWGEM/ini/GeneratorEMCocktail_Run3pp_v2.ini new file mode 100644 index 000000000..09a5e36ab --- /dev/null +++ b/MC/config/PWGEM/ini/GeneratorEMCocktail_Run3pp_v2.ini @@ -0,0 +1,6 @@ +### The setup uses an external event generator +### This part sets the path of the file and the function call to retrieve it + +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/external/generator/GeneratorEMCocktailV2.C +funcName=GenerateEMCocktail(200,0,3,63,"${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/parametrizations/pp13TeV_run2.json","13TeV_MB_default",200,0.0,30.0,10000,1,1,0,0,"",0,1.1,"${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/decaytables/decaytable_LMee.dat",1) \ No newline at end of file diff --git a/MC/config/PWGEM/ini/GeneratorEMCocktailwithFlow3040.ini b/MC/config/PWGEM/ini/GeneratorEMCocktailwithFlow3040.ini new file mode 100644 index 000000000..d91e2e9ea --- /dev/null +++ b/MC/config/PWGEM/ini/GeneratorEMCocktailwithFlow3040.ini @@ -0,0 +1,6 @@ +### The setup uses an external event generator +### This part sets the path of the file and the function call to retrieve it + +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/external/generator/GeneratorEMCocktailV2.C +funcName=GenerateEMCocktail(400,0,3,63,"${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/parametrizations/PbPb5TeV_central.json","5TeV_3040_wRatio",350,0.0,30.0,10000,1,1,0,0,"5TeV_3040_wRatio",0,1.1,"${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/decaytables/decaytable_LMee.dat",1) diff --git a/MC/config/PWGEM/ini/GeneratorEMCocktailwithFlow4050.ini b/MC/config/PWGEM/ini/GeneratorEMCocktailwithFlow4050.ini new file mode 100644 index 000000000..f97517bab --- /dev/null +++ b/MC/config/PWGEM/ini/GeneratorEMCocktailwithFlow4050.ini @@ -0,0 +1,6 @@ +### The setup uses an external event generator +### This part sets the path of the file and the function call to retrieve it + +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/external/generator/GeneratorEMCocktailV2.C +funcName=GenerateEMCocktail(400,0,3,63,"${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/parametrizations/PbPb5TeV_central.json","5TeV_4050_wRatio",350,0.0,30.0,10000,1,1,0,0,"5TeV_4050_wRatio",0,1.1,"${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/decaytables/decaytable_LMee.dat",1) diff --git a/MC/config/PWGEM/ini/GeneratorHFFull_bbbarToDDbarToDielectrons.ini b/MC/config/PWGEM/ini/GeneratorHFFull_bbbarToDDbarToDielectrons.ini index 36a51b92c..35b214682 100644 --- a/MC/config/PWGEM/ini/GeneratorHFFull_bbbarToDDbarToDielectrons.ini +++ b/MC/config/PWGEM/ini/GeneratorHFFull_bbbarToDDbarToDielectrons.ini @@ -2,15 +2,15 @@ ### This part sets the path of the file and the function call to retrieve it [GeneratorExternal] -fileName = ${O2DPG_ROOT}/MC/config/PWGEM/external/generator/GeneratorHFToEleFull_EvtGen.C +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/external/generator/GeneratorHFToEleFull_EvtGen.C funcName = GeneratorHFToEleFull_EvtGen(false,true) ### The external generator derives from GeneratorPythia8. ### This part configures the bits of the interface: configuration and user hooks [GeneratorPythia8] -config = ${O2DPG_ROOT}/MC/config/common/pythia8/generator/pythia8_bbbar.cfg -hooksFileName = ${O2DPG_ROOT}/MC/config/PWGHF/pythia8/hooks/pythia8_userhooks_qqbar.C +config = ${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/generator/pythia8_bbbar.cfg +hooksFileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/hooks/pythia8_userhooks_qqbar.C hooksFuncName = pythia8_userhooks_bbbar(-1.5,1.5) ### The setup uses an external even generator trigger which is @@ -18,5 +18,5 @@ hooksFuncName = pythia8_userhooks_bbbar(-1.5,1.5) ### according to the specified function call [TriggerExternal] -fileName = ${O2DPG_ROOT}/MC/config/PWGDQ/trigger/selectDaughterFromHFwithinAcc.C +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/trigger/selectDaughterFromHFwithinAcc.C funcName = selectDaughterFromHFwithinAcc(11,kTRUE,-1.,1.,2) diff --git a/MC/config/PWGEM/ini/GeneratorHFFull_bbbarToDielectrons.ini b/MC/config/PWGEM/ini/GeneratorHFFull_bbbarToDielectrons.ini index 326185448..bdc8aa29d 100644 --- a/MC/config/PWGEM/ini/GeneratorHFFull_bbbarToDielectrons.ini +++ b/MC/config/PWGEM/ini/GeneratorHFFull_bbbarToDielectrons.ini @@ -2,15 +2,15 @@ ### This part sets the path of the file and the function call to retrieve it [GeneratorExternal] -fileName = ${O2DPG_ROOT}/MC/config/PWGEM/external/generator/GeneratorHFToEleFull_EvtGen.C +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/external/generator/GeneratorHFToEleFull_EvtGen.C funcName = GeneratorHFToEleFull_EvtGen(true,true) ### The external generator derives from GeneratorPythia8. ### This part configures the bits of the interface: configuration and user hooks [GeneratorPythia8] -config = ${O2DPG_ROOT}/MC/config/common/pythia8/generator/pythia8_bbbar.cfg -hooksFileName = ${O2DPG_ROOT}/MC/config/PWGHF/pythia8/hooks/pythia8_userhooks_qqbar.C +config = ${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/generator/pythia8_bbbar.cfg +hooksFileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/hooks/pythia8_userhooks_qqbar.C hooksFuncName = pythia8_userhooks_bbbar(-1.5,1.5) ### The setup uses an external even generator trigger which is @@ -18,5 +18,5 @@ hooksFuncName = pythia8_userhooks_bbbar(-1.5,1.5) ### according to the specified function call [TriggerExternal] -fileName = ${O2DPG_ROOT}/MC/config/PWGDQ/trigger/selectDaughterFromHFwithinAcc.C +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/trigger/selectDaughterFromHFwithinAcc.C funcName = selectDaughterFromHFwithinAcc(11,kTRUE,-1.,1.,2) diff --git a/MC/config/PWGEM/ini/GeneratorHFFull_ccbarToDielectrons.ini b/MC/config/PWGEM/ini/GeneratorHFFull_ccbarToDielectrons.ini index 4356972a4..7c33d925e 100644 --- a/MC/config/PWGEM/ini/GeneratorHFFull_ccbarToDielectrons.ini +++ b/MC/config/PWGEM/ini/GeneratorHFFull_ccbarToDielectrons.ini @@ -2,15 +2,15 @@ ### This part sets the path of the file and the function call to retrieve it [GeneratorExternal] -fileName = ${O2DPG_ROOT}/MC/config/PWGEM/external/generator/GeneratorHFToEleFull_EvtGen.C +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/external/generator/GeneratorHFToEleFull_EvtGen.C funcName = GeneratorHFToEleFull_EvtGen(true,false) ### The external generator derives from GeneratorPythia8 ### This part configures the bits of the interface: configuration and user hooks [GeneratorPythia8] -config = ${O2DPG_ROOT}/MC/config/common/pythia8/generator/pythia8_pp_cr2.cfg -hooksFileName = ${O2DPG_ROOT}/MC/config/PWGHF/pythia8/hooks/pythia8_userhooks_qqbar.C +config = ${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/generator/pythia8_pp_cr2.cfg +hooksFileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/hooks/pythia8_userhooks_qqbar.C hooksFuncName = pythia8_userhooks_ccbar(-1.5,1.5) ### The setup uses an external even generator trigger which is @@ -18,5 +18,5 @@ hooksFuncName = pythia8_userhooks_ccbar(-1.5,1.5) ### according to the specified function call [TriggerExternal] -fileName = ${O2DPG_ROOT}/MC/config/PWGDQ/trigger/selectDaughterFromHFwithinAcc.C +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/trigger/selectDaughterFromHFwithinAcc.C funcName = selectDaughterFromHFwithinAcc(11,kFALSE,-1.,1.) diff --git a/MC/config/PWGEM/ini/GeneratorHFGapTriggered_BeautyForcedDecay.ini b/MC/config/PWGEM/ini/GeneratorHFGapTriggered_BeautyForcedDecay.ini deleted file mode 100644 index 9ad5cc13c..000000000 --- a/MC/config/PWGEM/ini/GeneratorHFGapTriggered_BeautyForcedDecay.ini +++ /dev/null @@ -1,6 +0,0 @@ -[GeneratorExternal] -fileName = ${O2DPG_ROOT}/MC/config/PWGEM/external/generator/Generator_pythia8_GapTriggered_HFLepton.C -funcName = GeneratorPythia8GapTriggeredBeautyForcedDecays(3) - -[GeneratorPythia8] -config = ${O2DPG_ROOT}/MC/config/PWGEM/pythia8/generator/configPythiaEmpty.cfg diff --git a/MC/config/PWGEM/ini/GeneratorHFGapTriggered_BeautyForcedDecay_Gap2_OO_electron.ini b/MC/config/PWGEM/ini/GeneratorHFGapTriggered_BeautyForcedDecay_Gap2_OO_electron.ini new file mode 100644 index 000000000..f25401e61 --- /dev/null +++ b/MC/config/PWGEM/ini/GeneratorHFGapTriggered_BeautyForcedDecay_Gap2_OO_electron.ini @@ -0,0 +1,8 @@ +#NEV_TEST> 5 +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/external/generator/Generator_pythia8_GapTriggered_HFLepton_OO.C +funcName = GeneratorPythia8GapTriggeredBeautyForcedDecays(2, 2, 11, -1.5, +1.5, -1.2, +1.2) + +[GeneratorPythia8] +config = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/generator/configPythiaEmpty.cfg +includePartonEvent = true diff --git a/MC/config/PWGEM/ini/GeneratorHFGapTriggered_BeautyForcedDecay_Gap2_OO_muon.ini b/MC/config/PWGEM/ini/GeneratorHFGapTriggered_BeautyForcedDecay_Gap2_OO_muon.ini new file mode 100644 index 000000000..b129f9fe8 --- /dev/null +++ b/MC/config/PWGEM/ini/GeneratorHFGapTriggered_BeautyForcedDecay_Gap2_OO_muon.ini @@ -0,0 +1,8 @@ +#NEV_TEST> 5 +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/external/generator/Generator_pythia8_GapTriggered_HFLepton_OO.C +funcName = GeneratorPythia8GapTriggeredBeautyForcedDecays(2, 2, 13, -6, 0, -5, -1) + +[GeneratorPythia8] +config = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/generator/configPythiaEmpty.cfg +includePartonEvent = true diff --git a/MC/config/PWGEM/ini/GeneratorHFGapTriggered_BeautyForcedDecay_Gap3.ini b/MC/config/PWGEM/ini/GeneratorHFGapTriggered_BeautyForcedDecay_Gap3.ini new file mode 100644 index 000000000..16065af1c --- /dev/null +++ b/MC/config/PWGEM/ini/GeneratorHFGapTriggered_BeautyForcedDecay_Gap3.ini @@ -0,0 +1,7 @@ +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/external/generator/Generator_pythia8_GapTriggered_HFLepton.C +funcName = GeneratorPythia8GapTriggeredBeautyForcedDecays(3,2) + +[GeneratorPythia8] +config = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/generator/configPythiaEmpty.cfg +includePartonEvent = true diff --git a/MC/config/PWGEM/ini/GeneratorHFGapTriggered_BeautyForcedDecay_Gap4.ini b/MC/config/PWGEM/ini/GeneratorHFGapTriggered_BeautyForcedDecay_Gap4.ini new file mode 100644 index 000000000..3fd7026f7 --- /dev/null +++ b/MC/config/PWGEM/ini/GeneratorHFGapTriggered_BeautyForcedDecay_Gap4.ini @@ -0,0 +1,7 @@ +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/external/generator/Generator_pythia8_GapTriggered_HFLepton.C +funcName = GeneratorPythia8GapTriggeredBeautyForcedDecays(4,2) + +[GeneratorPythia8] +config = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/generator/configPythiaEmpty.cfg +includePartonEvent = true diff --git a/MC/config/PWGEM/ini/GeneratorHFGapTriggered_BeautyForcedDecay_Gap5.ini b/MC/config/PWGEM/ini/GeneratorHFGapTriggered_BeautyForcedDecay_Gap5.ini new file mode 100644 index 000000000..753959025 --- /dev/null +++ b/MC/config/PWGEM/ini/GeneratorHFGapTriggered_BeautyForcedDecay_Gap5.ini @@ -0,0 +1,7 @@ +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/external/generator/Generator_pythia8_GapTriggered_HFLepton.C +funcName = GeneratorPythia8GapTriggeredBeautyForcedDecays(5,2) + +[GeneratorPythia8] +config = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/generator/configPythiaEmpty.cfg +includePartonEvent = true diff --git a/MC/config/PWGEM/ini/GeneratorHFGapTriggered_BeautyForcedDecay_Gap5_pp5360GeV.ini b/MC/config/PWGEM/ini/GeneratorHFGapTriggered_BeautyForcedDecay_Gap5_pp5360GeV.ini new file mode 100644 index 000000000..54de7afa1 --- /dev/null +++ b/MC/config/PWGEM/ini/GeneratorHFGapTriggered_BeautyForcedDecay_Gap5_pp5360GeV.ini @@ -0,0 +1,7 @@ +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/external/generator/Generator_pythia8_GapTriggered_HFLepton_pp5360.C +funcName = GeneratorPythia8GapTriggeredBeautyForcedDecays(5,2) + +[GeneratorPythia8] +config = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/generator/configPythiaEmpty.cfg +includePartonEvent = true diff --git a/MC/config/PWGEM/ini/GeneratorHFGapTriggered_BeautyForcedDecay_Gap6.ini b/MC/config/PWGEM/ini/GeneratorHFGapTriggered_BeautyForcedDecay_Gap6.ini new file mode 100644 index 000000000..954243adb --- /dev/null +++ b/MC/config/PWGEM/ini/GeneratorHFGapTriggered_BeautyForcedDecay_Gap6.ini @@ -0,0 +1,7 @@ +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/external/generator/Generator_pythia8_GapTriggered_HFLepton.C +funcName = GeneratorPythia8GapTriggeredBeautyForcedDecays(6,2) + +[GeneratorPythia8] +config = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/generator/configPythiaEmpty.cfg +includePartonEvent = true diff --git a/MC/config/PWGEM/ini/GeneratorHFGapTriggered_BeautyForcedDecay_Gap7.ini b/MC/config/PWGEM/ini/GeneratorHFGapTriggered_BeautyForcedDecay_Gap7.ini new file mode 100644 index 000000000..5fb7f2a95 --- /dev/null +++ b/MC/config/PWGEM/ini/GeneratorHFGapTriggered_BeautyForcedDecay_Gap7.ini @@ -0,0 +1,7 @@ +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/external/generator/Generator_pythia8_GapTriggered_HFLepton.C +funcName = GeneratorPythia8GapTriggeredBeautyForcedDecays(7,2) + +[GeneratorPythia8] +config = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/generator/configPythiaEmpty.cfg +includePartonEvent = true diff --git a/MC/config/PWGEM/ini/GeneratorHFGapTriggered_BeautyNoForcedDecay.ini b/MC/config/PWGEM/ini/GeneratorHFGapTriggered_BeautyNoForcedDecay.ini deleted file mode 100644 index 115eba3d6..000000000 --- a/MC/config/PWGEM/ini/GeneratorHFGapTriggered_BeautyNoForcedDecay.ini +++ /dev/null @@ -1,6 +0,0 @@ -[GeneratorExternal] -fileName = ${O2DPG_ROOT}/MC/config/PWGEM/external/generator/Generator_pythia8_GapTriggered_HFLepton.C -funcName = GeneratorPythia8GapTriggeredBeautyNoForcedDecays(3) - -[GeneratorPythia8] -config = ${O2DPG_ROOT}/MC/config/PWGEM/pythia8/generator/configPythiaEmpty.cfg diff --git a/MC/config/PWGEM/ini/GeneratorHFGapTriggered_BeautyNoForcedDecay_Gap2_OO_electron.ini b/MC/config/PWGEM/ini/GeneratorHFGapTriggered_BeautyNoForcedDecay_Gap2_OO_electron.ini new file mode 100644 index 000000000..8cd7e5ab6 --- /dev/null +++ b/MC/config/PWGEM/ini/GeneratorHFGapTriggered_BeautyNoForcedDecay_Gap2_OO_electron.ini @@ -0,0 +1,8 @@ +#NEV_TEST> 5 +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/external/generator/Generator_pythia8_GapTriggered_HFLepton_OO.C +funcName = GeneratorPythia8GapTriggeredBeautyNoForcedDecays(2, 3, 11, -1.5, +1.5, -1.2, +1.2) + +[GeneratorPythia8] +config = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/generator/configPythiaEmpty.cfg +includePartonEvent = true diff --git a/MC/config/PWGEM/ini/GeneratorHFGapTriggered_BeautyNoForcedDecay_Gap2_OO_muon.ini b/MC/config/PWGEM/ini/GeneratorHFGapTriggered_BeautyNoForcedDecay_Gap2_OO_muon.ini new file mode 100644 index 000000000..97741b31b --- /dev/null +++ b/MC/config/PWGEM/ini/GeneratorHFGapTriggered_BeautyNoForcedDecay_Gap2_OO_muon.ini @@ -0,0 +1,8 @@ +#NEV_TEST> 5 +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/external/generator/Generator_pythia8_GapTriggered_HFLepton_OO.C +funcName = GeneratorPythia8GapTriggeredBeautyNoForcedDecays(2, 3, 13, -6, 0, -5, -1) + +[GeneratorPythia8] +config = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/generator/configPythiaEmpty.cfg +includePartonEvent = true diff --git a/MC/config/PWGEM/ini/GeneratorHFGapTriggered_BeautyNoForcedDecay_Gap3.ini b/MC/config/PWGEM/ini/GeneratorHFGapTriggered_BeautyNoForcedDecay_Gap3.ini new file mode 100644 index 000000000..e8eeaac6f --- /dev/null +++ b/MC/config/PWGEM/ini/GeneratorHFGapTriggered_BeautyNoForcedDecay_Gap3.ini @@ -0,0 +1,7 @@ +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/external/generator/Generator_pythia8_GapTriggered_HFLepton.C +funcName = GeneratorPythia8GapTriggeredBeautyNoForcedDecays(3,3) + +[GeneratorPythia8] +config = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/generator/configPythiaEmpty.cfg +includePartonEvent = true diff --git a/MC/config/PWGEM/ini/GeneratorHFGapTriggered_BeautyNoForcedDecay_Gap4.ini b/MC/config/PWGEM/ini/GeneratorHFGapTriggered_BeautyNoForcedDecay_Gap4.ini new file mode 100644 index 000000000..5d1aac509 --- /dev/null +++ b/MC/config/PWGEM/ini/GeneratorHFGapTriggered_BeautyNoForcedDecay_Gap4.ini @@ -0,0 +1,7 @@ +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/external/generator/Generator_pythia8_GapTriggered_HFLepton.C +funcName = GeneratorPythia8GapTriggeredBeautyNoForcedDecays(4,3) + +[GeneratorPythia8] +config = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/generator/configPythiaEmpty.cfg +includePartonEvent = true diff --git a/MC/config/PWGEM/ini/GeneratorHFGapTriggered_BeautyNoForcedDecay_Gap5.ini b/MC/config/PWGEM/ini/GeneratorHFGapTriggered_BeautyNoForcedDecay_Gap5.ini new file mode 100644 index 000000000..afdbe1e5b --- /dev/null +++ b/MC/config/PWGEM/ini/GeneratorHFGapTriggered_BeautyNoForcedDecay_Gap5.ini @@ -0,0 +1,7 @@ +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/external/generator/Generator_pythia8_GapTriggered_HFLepton.C +funcName = GeneratorPythia8GapTriggeredBeautyNoForcedDecays(5,3) + +[GeneratorPythia8] +config = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/generator/configPythiaEmpty.cfg +includePartonEvent = true diff --git a/MC/config/PWGEM/ini/GeneratorHFGapTriggered_BeautyNoForcedDecay_Gap5_pp5360GeV.ini b/MC/config/PWGEM/ini/GeneratorHFGapTriggered_BeautyNoForcedDecay_Gap5_pp5360GeV.ini new file mode 100644 index 000000000..0cc38a529 --- /dev/null +++ b/MC/config/PWGEM/ini/GeneratorHFGapTriggered_BeautyNoForcedDecay_Gap5_pp5360GeV.ini @@ -0,0 +1,7 @@ +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/external/generator/Generator_pythia8_GapTriggered_HFLepton_pp5360.C +funcName = GeneratorPythia8GapTriggeredBeautyNoForcedDecays(5,3) + +[GeneratorPythia8] +config = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/generator/configPythiaEmpty.cfg +includePartonEvent = true diff --git a/MC/config/PWGEM/ini/GeneratorHFGapTriggered_BeautyNoForcedDecay_Gap6.ini b/MC/config/PWGEM/ini/GeneratorHFGapTriggered_BeautyNoForcedDecay_Gap6.ini new file mode 100644 index 000000000..09ab425e3 --- /dev/null +++ b/MC/config/PWGEM/ini/GeneratorHFGapTriggered_BeautyNoForcedDecay_Gap6.ini @@ -0,0 +1,7 @@ +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/external/generator/Generator_pythia8_GapTriggered_HFLepton.C +funcName = GeneratorPythia8GapTriggeredBeautyNoForcedDecays(6,3) + +[GeneratorPythia8] +config = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/generator/configPythiaEmpty.cfg +includePartonEvent = true diff --git a/MC/config/PWGEM/ini/GeneratorHFGapTriggered_BeautyNoForcedDecay_Gap7.ini b/MC/config/PWGEM/ini/GeneratorHFGapTriggered_BeautyNoForcedDecay_Gap7.ini new file mode 100644 index 000000000..9ec19f1d5 --- /dev/null +++ b/MC/config/PWGEM/ini/GeneratorHFGapTriggered_BeautyNoForcedDecay_Gap7.ini @@ -0,0 +1,7 @@ +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/external/generator/Generator_pythia8_GapTriggered_HFLepton.C +funcName = GeneratorPythia8GapTriggeredBeautyNoForcedDecays(7,3) + +[GeneratorPythia8] +config = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/generator/configPythiaEmpty.cfg +includePartonEvent = true diff --git a/MC/config/PWGEM/ini/GeneratorHFGapTriggered_BeautyNoForcedDecay_Muon_pp13600GeV_Gap1.ini b/MC/config/PWGEM/ini/GeneratorHFGapTriggered_BeautyNoForcedDecay_Muon_pp13600GeV_Gap1.ini new file mode 100644 index 000000000..0641b5724 --- /dev/null +++ b/MC/config/PWGEM/ini/GeneratorHFGapTriggered_BeautyNoForcedDecay_Muon_pp13600GeV_Gap1.ini @@ -0,0 +1,7 @@ +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/external/generator/Generator_pythia8_GapTriggered_HFMuon.C +funcName = GeneratorPythia8GapTriggeredBeautyNoForcedDecays(1, 3, -6, -1) + +[GeneratorPythia8] +config = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/generator/configPythiaEmpty.cfg +includePartonEvent = true diff --git a/MC/config/PWGEM/ini/GeneratorHFGapTriggered_Charm.ini b/MC/config/PWGEM/ini/GeneratorHFGapTriggered_Charm.ini deleted file mode 100644 index bd925eb39..000000000 --- a/MC/config/PWGEM/ini/GeneratorHFGapTriggered_Charm.ini +++ /dev/null @@ -1,6 +0,0 @@ -[GeneratorExternal] -fileName = ${O2DPG_ROOT}/MC/config/PWGEM/external/generator/Generator_pythia8_GapTriggered_HFLepton.C -funcName = GeneratorPythia8GapTriggeredCharmLepton(3) - -[GeneratorPythia8] -config = ${O2DPG_ROOT}/MC/config/PWGEM/pythia8/generator/configPythiaEmpty.cfg diff --git a/MC/config/PWGEM/ini/GeneratorHFGapTriggered_Charm_Gap2_OO_electron.ini b/MC/config/PWGEM/ini/GeneratorHFGapTriggered_Charm_Gap2_OO_electron.ini new file mode 100644 index 000000000..9691afc04 --- /dev/null +++ b/MC/config/PWGEM/ini/GeneratorHFGapTriggered_Charm_Gap2_OO_electron.ini @@ -0,0 +1,8 @@ +#NEV_TEST> 5 +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/external/generator/Generator_pythia8_GapTriggered_HFLepton_OO.C +funcName = GeneratorPythia8GapTriggeredCharmLepton(2, 1, 11, -1.5, +1.5, -1.2, +1.2) + +[GeneratorPythia8] +config = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/generator/configPythiaEmpty.cfg +includePartonEvent = true diff --git a/MC/config/PWGEM/ini/GeneratorHFGapTriggered_Charm_Gap2_OO_muon.ini b/MC/config/PWGEM/ini/GeneratorHFGapTriggered_Charm_Gap2_OO_muon.ini new file mode 100644 index 000000000..59d5babf4 --- /dev/null +++ b/MC/config/PWGEM/ini/GeneratorHFGapTriggered_Charm_Gap2_OO_muon.ini @@ -0,0 +1,8 @@ +#NEV_TEST> 5 +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/external/generator/Generator_pythia8_GapTriggered_HFLepton_OO.C +funcName = GeneratorPythia8GapTriggeredCharmLepton(2, 1, 13, -6, 0, -5, -1) + +[GeneratorPythia8] +config = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/generator/configPythiaEmpty.cfg +includePartonEvent = true diff --git a/MC/config/PWGEM/ini/GeneratorHFGapTriggered_Charm_Gap3.ini b/MC/config/PWGEM/ini/GeneratorHFGapTriggered_Charm_Gap3.ini new file mode 100644 index 000000000..adc5f678a --- /dev/null +++ b/MC/config/PWGEM/ini/GeneratorHFGapTriggered_Charm_Gap3.ini @@ -0,0 +1,7 @@ +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/external/generator/Generator_pythia8_GapTriggered_HFLepton.C +funcName = GeneratorPythia8GapTriggeredCharmLepton(3,1) + +[GeneratorPythia8] +config = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/generator/configPythiaEmpty.cfg +includePartonEvent = true diff --git a/MC/config/PWGEM/ini/GeneratorHFGapTriggered_Charm_Gap4.ini b/MC/config/PWGEM/ini/GeneratorHFGapTriggered_Charm_Gap4.ini new file mode 100644 index 000000000..98a4c1d95 --- /dev/null +++ b/MC/config/PWGEM/ini/GeneratorHFGapTriggered_Charm_Gap4.ini @@ -0,0 +1,7 @@ +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/external/generator/Generator_pythia8_GapTriggered_HFLepton.C +funcName = GeneratorPythia8GapTriggeredCharmLepton(4,1) + +[GeneratorPythia8] +config = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/generator/configPythiaEmpty.cfg +includePartonEvent = true diff --git a/MC/config/PWGEM/ini/GeneratorHFGapTriggered_Charm_Gap5.ini b/MC/config/PWGEM/ini/GeneratorHFGapTriggered_Charm_Gap5.ini new file mode 100644 index 000000000..47b332df2 --- /dev/null +++ b/MC/config/PWGEM/ini/GeneratorHFGapTriggered_Charm_Gap5.ini @@ -0,0 +1,7 @@ +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/external/generator/Generator_pythia8_GapTriggered_HFLepton.C +funcName = GeneratorPythia8GapTriggeredCharmLepton(5,1) + +[GeneratorPythia8] +config = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/generator/configPythiaEmpty.cfg +includePartonEvent = true diff --git a/MC/config/PWGEM/ini/GeneratorHFGapTriggered_Charm_Gap5_pp5360GeV.ini b/MC/config/PWGEM/ini/GeneratorHFGapTriggered_Charm_Gap5_pp5360GeV.ini new file mode 100644 index 000000000..e653a993b --- /dev/null +++ b/MC/config/PWGEM/ini/GeneratorHFGapTriggered_Charm_Gap5_pp5360GeV.ini @@ -0,0 +1,7 @@ +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/external/generator/Generator_pythia8_GapTriggered_HFLepton_pp5360.C +funcName = GeneratorPythia8GapTriggeredCharmLepton(5,1) + +[GeneratorPythia8] +config = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/generator/configPythiaEmpty.cfg +includePartonEvent = true diff --git a/MC/config/PWGEM/ini/GeneratorHFGapTriggered_Charm_Gap6.ini b/MC/config/PWGEM/ini/GeneratorHFGapTriggered_Charm_Gap6.ini new file mode 100644 index 000000000..5865134d0 --- /dev/null +++ b/MC/config/PWGEM/ini/GeneratorHFGapTriggered_Charm_Gap6.ini @@ -0,0 +1,7 @@ +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/external/generator/Generator_pythia8_GapTriggered_HFLepton.C +funcName = GeneratorPythia8GapTriggeredCharmLepton(6,1) + +[GeneratorPythia8] +config = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/generator/configPythiaEmpty.cfg +includePartonEvent = true diff --git a/MC/config/PWGEM/ini/GeneratorHFGapTriggered_Charm_Gap7.ini b/MC/config/PWGEM/ini/GeneratorHFGapTriggered_Charm_Gap7.ini new file mode 100644 index 000000000..8a141ad4f --- /dev/null +++ b/MC/config/PWGEM/ini/GeneratorHFGapTriggered_Charm_Gap7.ini @@ -0,0 +1,7 @@ +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/external/generator/Generator_pythia8_GapTriggered_HFLepton.C +funcName = GeneratorPythia8GapTriggeredCharmLepton(7,1) + +[GeneratorPythia8] +config = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/generator/configPythiaEmpty.cfg +includePartonEvent = true diff --git a/MC/config/PWGEM/ini/GeneratorHFGapTriggered_Charm_Muon_pp13600GeV_Gap1.ini b/MC/config/PWGEM/ini/GeneratorHFGapTriggered_Charm_Muon_pp13600GeV_Gap1.ini new file mode 100644 index 000000000..302abe087 --- /dev/null +++ b/MC/config/PWGEM/ini/GeneratorHFGapTriggered_Charm_Muon_pp13600GeV_Gap1.ini @@ -0,0 +1,7 @@ +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/external/generator/Generator_pythia8_GapTriggered_HFMuon.C +funcName = GeneratorPythia8GapTriggeredCharmLepton(1, 1, -6, -1) + +[GeneratorPythia8] +config = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/generator/configPythiaEmpty.cfg +includePartonEvent = true diff --git a/MC/config/PWGEM/ini/GeneratorHF_BeautyNoForcedDecay_PbPb_electron.ini b/MC/config/PWGEM/ini/GeneratorHF_BeautyNoForcedDecay_PbPb_electron.ini new file mode 100644 index 000000000..f24c66d3e --- /dev/null +++ b/MC/config/PWGEM/ini/GeneratorHF_BeautyNoForcedDecay_PbPb_electron.ini @@ -0,0 +1,9 @@ +#NEV_TEST> 10 +### The external generator derives from GeneratorPythia8 +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/external/generator/Generator_pythia8_embed_HFLepton.C +funcName=GeneratorPythia8EmbedHFLeptonBeautyNoForcedDecays(3, 11) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/generator/configPythiaEmpty.cfg +includePartonEvent=true diff --git a/MC/config/PWGEM/ini/GeneratorHF_Charm_PbPb_electron.ini b/MC/config/PWGEM/ini/GeneratorHF_Charm_PbPb_electron.ini new file mode 100644 index 000000000..b72cfb523 --- /dev/null +++ b/MC/config/PWGEM/ini/GeneratorHF_Charm_PbPb_electron.ini @@ -0,0 +1,9 @@ +#NEV_TEST> 10 +### The external generator derives from GeneratorPythia8 +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/external/generator/Generator_pythia8_embed_HFLepton.C +funcName=GeneratorPythia8EmbedHFLeptonCharm(1, 11) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/generator/configPythiaEmpty.cfg +includePartonEvent=true diff --git a/MC/config/PWGEM/ini/GeneratorHF_bbbarToDDbarToDielectrons.ini b/MC/config/PWGEM/ini/GeneratorHF_bbbarToDDbarToDielectrons.ini deleted file mode 100644 index b1059bc84..000000000 --- a/MC/config/PWGEM/ini/GeneratorHF_bbbarToDDbarToDielectrons.ini +++ /dev/null @@ -1,22 +0,0 @@ -### The setup uses an external event generator -### This part sets the path of the file and the function call to retrieve it - -[GeneratorExternal] -fileName = ${O2DPG_ROOT}/MC/config/PWGEM/external/generator/GeneratorBeautyToEle_EvtGen.C -funcName = GeneratorBeautyToEle_EvtGen(-1.5,1.5,true,false) - -### The external generator derives from GeneratorPythia8. -### This part configures the bits of the interface: configuration and user hooks - -[GeneratorPythia8] -config = ${O2DPG_ROOT}/MC/config/common/pythia8/generator/pythia8_bbbar.cfg -hooksFileName = ${O2DPG_ROOT}/MC/config/PWGHF/pythia8/hooks/pythia8_userhooks_qqbar.C -hooksFuncName = pythia8_userhooks_bbbar(-1.5,1.5) - -### The setup uses an external even generator trigger which is -### defined in the following file and it is retrieved and configured -### according to the specified function call - -[TriggerExternal] -fileName = ${O2DPG_ROOT}/MC/config/PWGDQ/trigger/selectDaughterFromHFwithinAcc.C -funcName = selectDaughterFromHFwithinAcc(11,kTRUE,-1.,1.,2) diff --git a/MC/config/PWGEM/ini/GeneratorHF_bbbarToDielectrons.ini b/MC/config/PWGEM/ini/GeneratorHF_bbbarToDielectrons.ini deleted file mode 100644 index 750de923f..000000000 --- a/MC/config/PWGEM/ini/GeneratorHF_bbbarToDielectrons.ini +++ /dev/null @@ -1,22 +0,0 @@ -### The setup uses an external event generator -### This part sets the path of the file and the function call to retrieve it - -[GeneratorExternal] -fileName = ${O2DPG_ROOT}/MC/config/PWGEM/external/generator/GeneratorBeautyToEle_EvtGen.C -funcName = GeneratorBeautyToEle_EvtGen(-1.5,1.5,true) - -### The external generator derives from GeneratorPythia8. -### This part configures the bits of the interface: configuration and user hooks - -[GeneratorPythia8] -config = ${O2DPG_ROOT}/MC/config/common/pythia8/generator/pythia8_bbbar.cfg -hooksFileName = ${O2DPG_ROOT}/MC/config/PWGHF/pythia8/hooks/pythia8_userhooks_qqbar.C -hooksFuncName = pythia8_userhooks_bbbar(-1.5,1.5) - -### The setup uses an external even generator trigger which is -### defined in the following file and it is retrieved and configured -### according to the specified function call - -[TriggerExternal] -fileName = ${O2DPG_ROOT}/MC/config/PWGDQ/trigger/selectDaughterFromHFwithinAcc.C -funcName = selectDaughterFromHFwithinAcc(11,kFALSE,-1.,1.) diff --git a/MC/config/PWGEM/ini/GeneratorHF_ccbarToDielectrons.ini b/MC/config/PWGEM/ini/GeneratorHF_ccbarToDielectrons.ini deleted file mode 100644 index 02f8378a3..000000000 --- a/MC/config/PWGEM/ini/GeneratorHF_ccbarToDielectrons.ini +++ /dev/null @@ -1,22 +0,0 @@ -### The setup uses an external event generator -### This part sets the path of the file and the function call to retrieve it - -[GeneratorExternal] -fileName = ${O2DPG_ROOT}/MC/config/PWGEM/external/generator/GeneratorCharmToEle_EvtGen.C -funcName = GeneratorCharmToEle_EvtGen(-1.5,1.5) - -### The external generator derives from GeneratorPythia8. -### This part configures the bits of the interface: configuration and user hooks - -[GeneratorPythia8] -config = ${O2DPG_ROOT}/MC/config/common/pythia8/generator/pythia8_pp_cr2.cfg -hooksFileName = ${O2DPG_ROOT}/MC/config/PWGHF/pythia8/hooks/pythia8_userhooks_qqbar.C -hooksFuncName = pythia8_userhooks_ccbar(-1.5,1.5) - -### The setup uses an external even generator trigger which is -### defined in the following file and it is retrieved and configured -### according to the specified function call - -[TriggerExternal] -fileName = ${O2DPG_ROOT}/MC/config/PWGDQ/trigger/selectDaughterFromHFwithinAcc.C -funcName = selectDaughterFromHFwithinAcc(11,kFALSE,-1.,1.) diff --git a/MC/config/PWGEM/ini/GeneratorLFCocktailPbPb.ini b/MC/config/PWGEM/ini/GeneratorLFCocktailPbPb.ini index afed741e1..8be528fcf 100644 --- a/MC/config/PWGEM/ini/GeneratorLFCocktailPbPb.ini +++ b/MC/config/PWGEM/ini/GeneratorLFCocktailPbPb.ini @@ -2,6 +2,6 @@ ### This part sets the path of the file and the function call to retrieve it [GeneratorExternal] -fileName = ${O2DPG_ROOT}/MC/config/PWGEM/external/generator/GeneratorCocktailLF.C +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/external/generator/GeneratorCocktailLF.C funcName = GeneratorCocktailLF(1,false) diff --git a/MC/config/PWGEM/ini/GeneratorLFCocktailpp.ini b/MC/config/PWGEM/ini/GeneratorLFCocktailpp.ini index cab1066e0..a16d5e73b 100644 --- a/MC/config/PWGEM/ini/GeneratorLFCocktailpp.ini +++ b/MC/config/PWGEM/ini/GeneratorLFCocktailpp.ini @@ -2,6 +2,6 @@ ### This part sets the path of the file and the function call to retrieve it [GeneratorExternal] -fileName = ${O2DPG_ROOT}/MC/config/PWGEM/external/generator/GeneratorCocktailLF.C +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/external/generator/GeneratorCocktailLF.C funcName = GeneratorCocktailLF(1,true) diff --git a/MC/config/PWGEM/ini/Generator_GapTriggered_LFgamma.ini b/MC/config/PWGEM/ini/Generator_GapTriggered_LFgamma.ini new file mode 100644 index 000000000..64eca1631 --- /dev/null +++ b/MC/config/PWGEM/ini/Generator_GapTriggered_LFgamma.ini @@ -0,0 +1,10 @@ +### The setup uses an external event generator +### This part sets the path of the file and the function call to retrieve it + +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/external/generator/Generator_pythia8_GapTriggered_LFgamma.C +funcName = GeneratorPythia8GapTriggeredLFgamma_ForEM(5, -1.2, +1.2, 1) + +[GeneratorPythia8] +config = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/generator/pythia8_MB_gapevent.cfg +#config = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/generator/configPythiaEmpty.cfg diff --git a/MC/config/PWGEM/ini/Generator_GapTriggered_LFmumu.ini b/MC/config/PWGEM/ini/Generator_GapTriggered_LFmumu.ini new file mode 100644 index 000000000..f49c0b6d0 --- /dev/null +++ b/MC/config/PWGEM/ini/Generator_GapTriggered_LFmumu.ini @@ -0,0 +1,9 @@ +### The setup uses an external event generator +### This part sets the path of the file and the function call to retrieve it + +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/external/generator/Generator_pythia8_GapTriggered_LFmumu.C +funcName = GeneratorPythia8GapTriggeredLFmumu_ForEM(4+1, -4.3, -2.2, 1, 1) + +[GeneratorPythia8] +config = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/generator/pythia8_MB_gapevent.cfg diff --git a/MC/config/PWGEM/ini/Pythia8_Beauty_Cocktail.ini b/MC/config/PWGEM/ini/Pythia8_Beauty_Cocktail.ini new file mode 100644 index 000000000..c40d93ef6 --- /dev/null +++ b/MC/config/PWGEM/ini/Pythia8_Beauty_Cocktail.ini @@ -0,0 +1,13 @@ +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/external/generator/Generator_pythia8_forcedDecays.C +funcName=GeneratePythia8ForcedDecays("411;421;431;4122;4232;4132;4332") + +[GeneratorPythia8] +config = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/generator/pythia8_hf_cocktail.cfg +hooksFileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/hooks/pythia8_userhooks_qqbar.C +hooksFuncName = pythia8_userhooks_bbbar(-12.,12.) +includePartonEvent=true + +[DecayerPythia8] +config[0] = ${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/decayer/base.cfg +config[1] = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/decayer/force_semileptonic_charm.cfg diff --git a/MC/config/PWGEM/ini/Pythia8_Beauty_Cocktail_pp13.ini b/MC/config/PWGEM/ini/Pythia8_Beauty_Cocktail_pp13.ini new file mode 100644 index 000000000..4a0f54f13 --- /dev/null +++ b/MC/config/PWGEM/ini/Pythia8_Beauty_Cocktail_pp13.ini @@ -0,0 +1,13 @@ +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/external/generator/Generator_pythia8_forcedDecays.C +funcName=GeneratePythia8ForcedDecays("411;421;431;4122;4232;4132;4332") + +[GeneratorPythia8] +config = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/generator/pythia8_hf_cocktail_pp13.cfg +hooksFileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/hooks/pythia8_userhooks_qqbar.C +hooksFuncName = pythia8_userhooks_bbbar(-12.,12.) +includePartonEvent=true + +[DecayerPythia8] +config[0] = ${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/decayer/base.cfg +config[1] = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/decayer/force_semileptonic_charm.cfg diff --git a/MC/config/PWGEM/ini/Pythia8_Beauty_Cocktail_pp502.ini b/MC/config/PWGEM/ini/Pythia8_Beauty_Cocktail_pp502.ini new file mode 100644 index 000000000..a9fcaf789 --- /dev/null +++ b/MC/config/PWGEM/ini/Pythia8_Beauty_Cocktail_pp502.ini @@ -0,0 +1,13 @@ +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/external/generator/Generator_pythia8_forcedDecays.C +funcName=GeneratePythia8ForcedDecays("411;421;431;4122;4232;4132;4332") + +[GeneratorPythia8] +config = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/generator/pythia8_hf_cocktail_pp502.cfg +hooksFileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/hooks/pythia8_userhooks_qqbar.C +hooksFuncName = pythia8_userhooks_bbbar(-12.,12.) +includePartonEvent=true + +[DecayerPythia8] +config[0] = ${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/decayer/base.cfg +config[1] = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/decayer/force_semileptonic_charm.cfg diff --git a/MC/config/PWGEM/ini/Pythia8_Beauty_Cocktail_pp536.ini b/MC/config/PWGEM/ini/Pythia8_Beauty_Cocktail_pp536.ini new file mode 100644 index 000000000..1de490707 --- /dev/null +++ b/MC/config/PWGEM/ini/Pythia8_Beauty_Cocktail_pp536.ini @@ -0,0 +1,13 @@ +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/external/generator/Generator_pythia8_forcedDecays.C +funcName=GeneratePythia8ForcedDecays("411;421;431;4122;4232;4132;4332") + +[GeneratorPythia8] +config = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/generator/pythia8_hf_cocktail_pp536.cfg +hooksFileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/hooks/pythia8_userhooks_qqbar.C +hooksFuncName = pythia8_userhooks_bbbar(-12.,12.) +includePartonEvent=true + +[DecayerPythia8] +config[0] = ${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/decayer/base.cfg +config[1] = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/decayer/force_semileptonic_charm.cfg diff --git a/MC/config/PWGEM/ini/Pythia8_Charm_Cocktail.ini b/MC/config/PWGEM/ini/Pythia8_Charm_Cocktail.ini new file mode 100644 index 000000000..ad7392644 --- /dev/null +++ b/MC/config/PWGEM/ini/Pythia8_Charm_Cocktail.ini @@ -0,0 +1,13 @@ +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/external/generator/Generator_pythia8_forcedDecays.C +funcName=GeneratePythia8ForcedDecays("411;421;431;4122;4232;4132;4332") + +[GeneratorPythia8] +config = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/generator/pythia8_hf_cocktail.cfg +hooksFileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/hooks/pythia8_userhooks_qqbar.C +hooksFuncName = pythia8_userhooks_ccbar(-12.,12.) +includePartonEvent=true + +[DecayerPythia8] +config[0] = ${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/decayer/base.cfg +config[1] = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/decayer/force_semileptonic_charm.cfg diff --git a/MC/config/PWGEM/ini/Pythia8_Charm_Cocktail_pp13.ini b/MC/config/PWGEM/ini/Pythia8_Charm_Cocktail_pp13.ini new file mode 100644 index 000000000..cad2afe89 --- /dev/null +++ b/MC/config/PWGEM/ini/Pythia8_Charm_Cocktail_pp13.ini @@ -0,0 +1,13 @@ +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/external/generator/Generator_pythia8_forcedDecays.C +funcName=GeneratePythia8ForcedDecays("411;421;431;4122;4232;4132;4332") + +[GeneratorPythia8] +config = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/generator/pythia8_hf_cocktail_pp13.cfg +hooksFileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/hooks/pythia8_userhooks_qqbar.C +hooksFuncName = pythia8_userhooks_ccbar(-12.,12.) +includePartonEvent=true + +[DecayerPythia8] +config[0] = ${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/decayer/base.cfg +config[1] = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/decayer/force_semileptonic_charm.cfg diff --git a/MC/config/PWGEM/ini/Pythia8_Charm_Cocktail_pp502.ini b/MC/config/PWGEM/ini/Pythia8_Charm_Cocktail_pp502.ini new file mode 100644 index 000000000..42f30b441 --- /dev/null +++ b/MC/config/PWGEM/ini/Pythia8_Charm_Cocktail_pp502.ini @@ -0,0 +1,13 @@ +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/external/generator/Generator_pythia8_forcedDecays.C +funcName=GeneratePythia8ForcedDecays("411;421;431;4122;4232;4132;4332") + +[GeneratorPythia8] +config = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/generator/pythia8_hf_cocktail_pp502.cfg +hooksFileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/hooks/pythia8_userhooks_qqbar.C +hooksFuncName = pythia8_userhooks_ccbar(-12.,12.) +includePartonEvent=true + +[DecayerPythia8] +config[0] = ${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/decayer/base.cfg +config[1] = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/decayer/force_semileptonic_charm.cfg diff --git a/MC/config/PWGEM/ini/Pythia8_Charm_Cocktail_pp536.ini b/MC/config/PWGEM/ini/Pythia8_Charm_Cocktail_pp536.ini new file mode 100644 index 000000000..1b5d0e378 --- /dev/null +++ b/MC/config/PWGEM/ini/Pythia8_Charm_Cocktail_pp536.ini @@ -0,0 +1,13 @@ +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/external/generator/Generator_pythia8_forcedDecays.C +funcName=GeneratePythia8ForcedDecays("411;421;431;4122;4232;4132;4332") + +[GeneratorPythia8] +config = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/generator/pythia8_hf_cocktail_pp536.cfg +hooksFileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/hooks/pythia8_userhooks_qqbar.C +hooksFuncName = pythia8_userhooks_ccbar(-12.,12.) +includePartonEvent=true + +[DecayerPythia8] +config[0] = ${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/decayer/base.cfg +config[1] = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/decayer/force_semileptonic_charm.cfg diff --git a/MC/config/PWGEM/ini/pythia8_OO_536_VM2ll.ini b/MC/config/PWGEM/ini/pythia8_OO_536_VM2ll.ini new file mode 100644 index 000000000..953d909ce --- /dev/null +++ b/MC/config/PWGEM/ini/pythia8_OO_536_VM2ll.ini @@ -0,0 +1,9 @@ +[Diamond] +width[2]=6.0 + +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator_pythia8_ALICE3.C +funcName=generator_pythia8_ALICE3() + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/generator/pythia8_OO_536_VM2ll.cfg diff --git a/MC/config/PWGEM/ini/pythia8_PbPb_5360_VM2ll.ini b/MC/config/PWGEM/ini/pythia8_PbPb_5360_VM2ll.ini new file mode 100644 index 000000000..503e77188 --- /dev/null +++ b/MC/config/PWGEM/ini/pythia8_PbPb_5360_VM2ll.ini @@ -0,0 +1,10 @@ +#NEV_TEST> 5 +[Diamond] +width[2]=6.0 + +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator_pythia8_ALICE3.C +funcName=generator_pythia8_ALICE3() + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/generator/pythia8_PbPb_5360_VM2ll.cfg diff --git a/MC/config/PWGEM/ini/pythia8_pp_13600_ForcedDalitz.ini b/MC/config/PWGEM/ini/pythia8_pp_13600_ForcedDalitz.ini new file mode 100644 index 000000000..1cc9c5b5b --- /dev/null +++ b/MC/config/PWGEM/ini/pythia8_pp_13600_ForcedDalitz.ini @@ -0,0 +1,8 @@ +[Diamond] +width[2]=6.0 + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/generator/configPythia_ForcedDalitz.cfg + +[DecayerPythia8] +config[0]=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/decayer/force_dummy.cfg diff --git a/MC/config/PWGEM/ini/pythia8_pp_13600_VM2ll.ini b/MC/config/PWGEM/ini/pythia8_pp_13600_VM2ll.ini new file mode 100644 index 000000000..e261498c4 --- /dev/null +++ b/MC/config/PWGEM/ini/pythia8_pp_13600_VM2ll.ini @@ -0,0 +1,10 @@ +#NEV_TEST> 5 +[Diamond] +width[2]=6.0 + +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator_pythia8_ALICE3.C +funcName=generator_pythia8_ALICE3() + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/generator/pythia8_pp_13600_VM2ll.cfg diff --git a/MC/config/PWGEM/ini/pythia8_pp_5360_VM2ee.ini b/MC/config/PWGEM/ini/pythia8_pp_5360_VM2ee.ini new file mode 100644 index 000000000..5b17885ed --- /dev/null +++ b/MC/config/PWGEM/ini/pythia8_pp_5360_VM2ee.ini @@ -0,0 +1,10 @@ +#NEV_TEST> 5 +[Diamond] +width[2]=6.0 + +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator_pythia8_ALICE3.C +funcName=generator_pythia8_ALICE3() + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/generator/pythia8_pp_5360_VM2ee.cfg diff --git a/MC/config/PWGEM/ini/pythia8_pp_5360_VM2ll.ini b/MC/config/PWGEM/ini/pythia8_pp_5360_VM2ll.ini new file mode 100644 index 000000000..49be058cf --- /dev/null +++ b/MC/config/PWGEM/ini/pythia8_pp_5360_VM2ll.ini @@ -0,0 +1,10 @@ +#NEV_TEST> 5 +[Diamond] +width[2]=6.0 + +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator_pythia8_ALICE3.C +funcName=generator_pythia8_ALICE3() + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/pythia8/generator/pythia8_pp_5360_VM2ll.cfg diff --git a/MC/config/PWGEM/ini/tests/GeneratorDYee_GapTriggered_Gap2_OO5360GeV.C b/MC/config/PWGEM/ini/tests/GeneratorDYee_GapTriggered_Gap2_OO5360GeV.C new file mode 100644 index 000000000..aca7bb427 --- /dev/null +++ b/MC/config/PWGEM/ini/tests/GeneratorDYee_GapTriggered_Gap2_OO5360GeV.C @@ -0,0 +1,51 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + // Check that file exists, can be opened and has the correct tree + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) + { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) + { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + // Check if all events are filled + auto nEvents = tree->GetEntries(); + for (Long64_t i = 0; i < nEvents; ++i) + { + tree->GetEntry(i); + if (tracks->empty()) + { + std::cerr << "Empty entry found at event " << i << "\n"; + return 1; + } + } + // check if each event has at least two oxygen ions + for (int i = 0; i < nEvents; i++) + { + auto check = tree->GetEntry(i); + int count = 0; + for (int idxMCTrack = 0; idxMCTrack < tracks->size(); ++idxMCTrack) + { + auto track = tracks->at(idxMCTrack); + if (track.GetPdgCode() == 1000080160) + { + count++; + } + } + if (count < 2) + { + std::cerr << "Event " << i << " has less than 2 oxygen ions\n"; + return 1; + } + } + + return 0; +} diff --git a/MC/config/PWGEM/ini/tests/GeneratorDYee_GapTriggered_Gap5_OO5360GeV.C b/MC/config/PWGEM/ini/tests/GeneratorDYee_GapTriggered_Gap5_OO5360GeV.C new file mode 100644 index 000000000..aca7bb427 --- /dev/null +++ b/MC/config/PWGEM/ini/tests/GeneratorDYee_GapTriggered_Gap5_OO5360GeV.C @@ -0,0 +1,51 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + // Check that file exists, can be opened and has the correct tree + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) + { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) + { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + // Check if all events are filled + auto nEvents = tree->GetEntries(); + for (Long64_t i = 0; i < nEvents; ++i) + { + tree->GetEntry(i); + if (tracks->empty()) + { + std::cerr << "Empty entry found at event " << i << "\n"; + return 1; + } + } + // check if each event has at least two oxygen ions + for (int i = 0; i < nEvents; i++) + { + auto check = tree->GetEntry(i); + int count = 0; + for (int idxMCTrack = 0; idxMCTrack < tracks->size(); ++idxMCTrack) + { + auto track = tracks->at(idxMCTrack); + if (track.GetPdgCode() == 1000080160) + { + count++; + } + } + if (count < 2) + { + std::cerr << "Event " << i << " has less than 2 oxygen ions\n"; + return 1; + } + } + + return 0; +} diff --git a/MC/config/PWGEM/ini/tests/GeneratorDYee_GapTriggered_Gap5_pp13600GeV.C b/MC/config/PWGEM/ini/tests/GeneratorDYee_GapTriggered_Gap5_pp13600GeV.C new file mode 100644 index 000000000..0332e54fc --- /dev/null +++ b/MC/config/PWGEM/ini/tests/GeneratorDYee_GapTriggered_Gap5_pp13600GeV.C @@ -0,0 +1,51 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + // Check that file exists, can be opened and has the correct tree + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) + { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) + { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + // Check if all events are filled + auto nEvents = tree->GetEntries(); + for (Long64_t i = 0; i < nEvents; ++i) + { + tree->GetEntry(i); + if (tracks->empty()) + { + std::cerr << "Empty entry found at event " << i << "\n"; + return 1; + } + } + // check if each event has at least two oxygen ions + for (int i = 0; i < nEvents; i++) + { + auto check = tree->GetEntry(i); + int count = 0; + for (int idxMCTrack = 0; idxMCTrack < tracks->size(); ++idxMCTrack) + { + auto track = tracks->at(idxMCTrack); + if (track.GetPdgCode() == 2212) + { + count++; + } + } + if (count < 2) + { + std::cerr << "Event " << i << " has less than 2 oxygen ions\n"; + return 1; + } + } + + return 0; +} diff --git a/MC/config/PWGEM/ini/tests/GeneratorDYee_GapTriggered_Gap5_pp5360GeV.C b/MC/config/PWGEM/ini/tests/GeneratorDYee_GapTriggered_Gap5_pp5360GeV.C new file mode 100644 index 000000000..0332e54fc --- /dev/null +++ b/MC/config/PWGEM/ini/tests/GeneratorDYee_GapTriggered_Gap5_pp5360GeV.C @@ -0,0 +1,51 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + // Check that file exists, can be opened and has the correct tree + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) + { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) + { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + // Check if all events are filled + auto nEvents = tree->GetEntries(); + for (Long64_t i = 0; i < nEvents; ++i) + { + tree->GetEntry(i); + if (tracks->empty()) + { + std::cerr << "Empty entry found at event " << i << "\n"; + return 1; + } + } + // check if each event has at least two oxygen ions + for (int i = 0; i < nEvents; i++) + { + auto check = tree->GetEntry(i); + int count = 0; + for (int idxMCTrack = 0; idxMCTrack < tracks->size(); ++idxMCTrack) + { + auto track = tracks->at(idxMCTrack); + if (track.GetPdgCode() == 2212) + { + count++; + } + } + if (count < 2) + { + std::cerr << "Event " << i << " has less than 2 oxygen ions\n"; + return 1; + } + } + + return 0; +} diff --git a/MC/config/PWGEM/ini/tests/GeneratorDYmumu_GapTriggered_Gap1_pp13600GeV.C b/MC/config/PWGEM/ini/tests/GeneratorDYmumu_GapTriggered_Gap1_pp13600GeV.C new file mode 100644 index 000000000..0332e54fc --- /dev/null +++ b/MC/config/PWGEM/ini/tests/GeneratorDYmumu_GapTriggered_Gap1_pp13600GeV.C @@ -0,0 +1,51 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + // Check that file exists, can be opened and has the correct tree + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) + { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) + { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + // Check if all events are filled + auto nEvents = tree->GetEntries(); + for (Long64_t i = 0; i < nEvents; ++i) + { + tree->GetEntry(i); + if (tracks->empty()) + { + std::cerr << "Empty entry found at event " << i << "\n"; + return 1; + } + } + // check if each event has at least two oxygen ions + for (int i = 0; i < nEvents; i++) + { + auto check = tree->GetEntry(i); + int count = 0; + for (int idxMCTrack = 0; idxMCTrack < tracks->size(); ++idxMCTrack) + { + auto track = tracks->at(idxMCTrack); + if (track.GetPdgCode() == 2212) + { + count++; + } + } + if (count < 2) + { + std::cerr << "Event " << i << " has less than 2 oxygen ions\n"; + return 1; + } + } + + return 0; +} diff --git a/MC/config/PWGEM/ini/tests/GeneratorDYmumu_GapTriggered_Gap5_OO5360GeV.C b/MC/config/PWGEM/ini/tests/GeneratorDYmumu_GapTriggered_Gap5_OO5360GeV.C new file mode 100644 index 000000000..aca7bb427 --- /dev/null +++ b/MC/config/PWGEM/ini/tests/GeneratorDYmumu_GapTriggered_Gap5_OO5360GeV.C @@ -0,0 +1,51 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + // Check that file exists, can be opened and has the correct tree + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) + { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) + { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + // Check if all events are filled + auto nEvents = tree->GetEntries(); + for (Long64_t i = 0; i < nEvents; ++i) + { + tree->GetEntry(i); + if (tracks->empty()) + { + std::cerr << "Empty entry found at event " << i << "\n"; + return 1; + } + } + // check if each event has at least two oxygen ions + for (int i = 0; i < nEvents; i++) + { + auto check = tree->GetEntry(i); + int count = 0; + for (int idxMCTrack = 0; idxMCTrack < tracks->size(); ++idxMCTrack) + { + auto track = tracks->at(idxMCTrack); + if (track.GetPdgCode() == 1000080160) + { + count++; + } + } + if (count < 2) + { + std::cerr << "Event " << i << " has less than 2 oxygen ions\n"; + return 1; + } + } + + return 0; +} diff --git a/MC/config/PWGEM/ini/tests/GeneratorDYmumu_GapTriggered_Gap5_pp5360GeV.C b/MC/config/PWGEM/ini/tests/GeneratorDYmumu_GapTriggered_Gap5_pp5360GeV.C new file mode 100644 index 000000000..0332e54fc --- /dev/null +++ b/MC/config/PWGEM/ini/tests/GeneratorDYmumu_GapTriggered_Gap5_pp5360GeV.C @@ -0,0 +1,51 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + // Check that file exists, can be opened and has the correct tree + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) + { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) + { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + // Check if all events are filled + auto nEvents = tree->GetEntries(); + for (Long64_t i = 0; i < nEvents; ++i) + { + tree->GetEntry(i); + if (tracks->empty()) + { + std::cerr << "Empty entry found at event " << i << "\n"; + return 1; + } + } + // check if each event has at least two oxygen ions + for (int i = 0; i < nEvents; i++) + { + auto check = tree->GetEntry(i); + int count = 0; + for (int idxMCTrack = 0; idxMCTrack < tracks->size(); ++idxMCTrack) + { + auto track = tracks->at(idxMCTrack); + if (track.GetPdgCode() == 2212) + { + count++; + } + } + if (count < 2) + { + std::cerr << "Event " << i << " has less than 2 oxygen ions\n"; + return 1; + } + } + + return 0; +} diff --git a/MC/config/PWGEM/ini/tests/GeneratorEMCocktail.C b/MC/config/PWGEM/ini/tests/GeneratorEMCocktail.C deleted file mode 100644 index 600908d2e..000000000 --- a/MC/config/PWGEM/ini/tests/GeneratorEMCocktail.C +++ /dev/null @@ -1,179 +0,0 @@ -int External() -{ - - std::string path{"o2sim_Kine.root"}; - TFile file(path.c_str(), "READ"); - if (file.IsZombie()) { - std::cerr << "Cannot open ROOT file " << path << "\n"; - return 1; - } - - auto tree = (TTree*)file.Get("o2sim"); - std::vector* tracks{}; - tree->SetBranchAddress("MCTrack", &tracks); - - int nElectrons = 0; - int nPositrons = 0; - int nPions = 0; - int nEtas = 0; - int nEtaPrimes = 0; - int nRhos = 0; - int nPhis = 0; - int nOmegas = 0; - int nJPsis = 0; - int nPhotons = 0; - int nElectronsFromPion = 0; - int nElectronsFromEta = 0; - int nElectronsFromEtaPrime = 0; - int nElectronsFromOmega = 0; - int nElectronsFromRho = 0; - int nElectronsFromPhi = 0; - int nElectronsFromJPsi = 0; - int nLeptonsToBeDone = 0; - auto nEvents = tree->GetEntries(); - for (int i = 0; i < nEvents; i++) { - tree->GetEntry(i); - for (auto& track : *tracks) { - bool hasMother = track.getMotherTrackId()>-1; - auto pdg = track.GetPdgCode(); - switch (pdg){ - case 11: - nElectrons++; - if (track.getToBeDone()){ - nLeptonsToBeDone++; - } - break; - case -11: - nPositrons++; - if (track.getToBeDone()){ - nLeptonsToBeDone++; - } - break; - case 111: - if (!hasMother) - nPions++; - break; - case 221: - if (!hasMother) - nEtas++; - break; - case 331: - if (!hasMother) - nEtaPrimes++; - break; - case 113: - if (!hasMother) - nRhos++; - break; - case 223: - if (!hasMother) - nOmegas++; - break; - case 333: - if (!hasMother) - nPhis++; - break; - case 443: - if (!hasMother) - nJPsis++; - break; - case 22: - nPhotons++; - } - if (pdg == 11){ - int imother = track.getMotherTrackId(); - if (imother > -1) { - auto mother = (*tracks)[imother]; - int mpdg = mother.GetPdgCode(); - switch (mpdg){ - case 111: - nElectronsFromPion++; - break; - case 221: - nElectronsFromEta++; - break; - case 331: - nElectronsFromEtaPrime++; - break; - case 113: - nElectronsFromRho++; - break; - case 223: - nElectronsFromOmega++; - break; - case 333: - nElectronsFromPhi++; - break; - case 443: - nElectronsFromJPsi++; - break; - default: - std::cout << "Found electron with mother pdg " << mpdg << "\n"; - } - } else { - std::cerr << "Found electron with no mother" << "\n"; - return 1; - } - } - } - } - int nMothers = nPions+nEtas+nEtaPrimes+nRhos+nOmegas+nPhis+nJPsis; - std::cout << "#Events: " << nEvents << "\n" - << "#Electrons: " << nElectrons << "\n" - << "#Positrons: " << nPositrons << "\n" - << "#Leptons: " << nElectrons+nPositrons << ", #LeptonsToDone: " << nLeptonsToBeDone << "\n" - << "#Photons: " << nPhotons << "\n" - << "#Pions: " << nPions << ", #ElectronsFromPion: " << nElectronsFromPion << "\n" - << "#Etas: " << nEtas << ", #ElectronsFromEta: " << nElectronsFromEta << "\n" - << "#EtaPrimes: " << nEtaPrimes << ", #ElectronsFromEtaPrime: " << nElectronsFromEtaPrime << "\n" - << "#Rhos: " << nRhos << ", #ElectronsFromRho: " << nElectronsFromRho << "\n" - << "#Omegas: " << nOmegas << ", #ElectronsFromOmega: " << nElectronsFromOmega << "\n" - << "#Phis: " << nPhis << ", #ElectronsFromPhi: " << nElectronsFromPhi << "\n" - << "#JPsis: " << nJPsis << ", #ElectronsFromJPsi: " << nElectronsFromJPsi << "\n"; - if (nElectrons == 0) { - std::cerr << "No electrons found\n"; - return 1; - } - if (nElectrons != nPositrons) { - std::cerr << "Number of electrons should match number of positrons\n"; - return 1; - } - if (nLeptonsToBeDone != nElectrons+nPositrons) { - std::cerr << "The number of leptons should be the same as the number of leptons which should be transported.\n"; - return 1; - } - if (nMothers < nEvents) { - std::cerr << "The number of mother particles (pi0, eta, etaprime, rho, omega, phi, JPsi) must be at least the number of events\n"; - return 1; - } - if (nElectronsFromPion < nPions) { - std::cerr << "Number of of electrons from pions has to be at least the number of pions\n"; - return 1; - } - if (nElectronsFromEta < nEtas) { - std::cerr << "Number of of electrons from etas has to be at least the number of etas\n"; - return 1; - } - if (nElectronsFromEtaPrime < nEtaPrimes) { - std::cerr << "Number of of electrons from etaprimes has to be at least the number of etaprimes\n"; - return 1; - } - if (nElectronsFromRho < nRhos) { - std::cerr << "Number of of electrons from rhos has to be at least the number of rhos\n"; - return 1; - } - if (nElectronsFromOmega < nOmegas) { - std::cerr << "Number of of electrons from omegas has to be at least the number of omegas\n"; - return 1; - } - if (nElectronsFromPhi < nPhis) { - std::cerr << "Number of of electrons from phis has to be at least the number of phis\n"; - return 1; - } - if (nElectronsFromJPsi < nJPsis) { - std::cerr << "Number of of electrons from JPsis has to be at least the number of JPsis\n"; - return 1; - } - - return 0; -} diff --git a/MC/config/PWGEM/ini/tests/GeneratorEMCocktail_502PbPb_0005.C b/MC/config/PWGEM/ini/tests/GeneratorEMCocktail_502PbPb_0005.C new file mode 100644 index 000000000..a78a0cc8e --- /dev/null +++ b/MC/config/PWGEM/ini/tests/GeneratorEMCocktail_502PbPb_0005.C @@ -0,0 +1,64 @@ +int External() +{ + + int checkPdgDecay = -11; + std::string path{"o2sim_Kine.root"}; + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree*)file.Get("o2sim"); + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int nMesons{}; + int nMesonsDiElectronDecay{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + for (auto& track : *tracks) { + auto pdg = track.GetPdgCode(); + auto y = track.GetRapidity(); + if ((pdg == 111) || (pdg == 221) || (pdg == 331) || (pdg == 223) || (pdg == 113) || (pdg == 333)) { + if ((y>-1.2) && (y<1.2)) { + nMesons++; + Int_t counterel = 0; + Int_t counterpos = 0; + int k1 = track.getFirstDaughterTrackId(); + int k2 = track.getLastDaughterTrackId(); + // k1 < k2 and no -1 for k2 + for (int d=k1; d <= k2; d++) { + if (d>0) { + auto decay = (*tracks)[d]; + int pdgdecay = decay.GetPdgCode(); + if (pdgdecay == 11) { + counterel++; + } + if (pdgdecay == -11) { + counterpos++; + } + } + } + if ((counterel>0) && (counterpos>0)) nMesonsDiElectronDecay++; + } + } + } + } + + std::cout << "#events: " << nEvents << "\n" + << "#mesons: " << nMesons << "\n" + << "#mesons which decay semi-electronicly: " << nMesonsDiElectronDecay << "\n"; + if (nMesonsDiElectronDecay < nEvents) { + std::cerr << "One should have at least one meson that decays into dielectrons per event.\n"; + return 1; + } + if (nMesons < nEvents) { + std::cerr << "One meson per event should be produced.\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGEM/ini/tests/GeneratorEMCocktail_502PbPb_0510.C b/MC/config/PWGEM/ini/tests/GeneratorEMCocktail_502PbPb_0510.C new file mode 100644 index 000000000..a78a0cc8e --- /dev/null +++ b/MC/config/PWGEM/ini/tests/GeneratorEMCocktail_502PbPb_0510.C @@ -0,0 +1,64 @@ +int External() +{ + + int checkPdgDecay = -11; + std::string path{"o2sim_Kine.root"}; + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree*)file.Get("o2sim"); + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int nMesons{}; + int nMesonsDiElectronDecay{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + for (auto& track : *tracks) { + auto pdg = track.GetPdgCode(); + auto y = track.GetRapidity(); + if ((pdg == 111) || (pdg == 221) || (pdg == 331) || (pdg == 223) || (pdg == 113) || (pdg == 333)) { + if ((y>-1.2) && (y<1.2)) { + nMesons++; + Int_t counterel = 0; + Int_t counterpos = 0; + int k1 = track.getFirstDaughterTrackId(); + int k2 = track.getLastDaughterTrackId(); + // k1 < k2 and no -1 for k2 + for (int d=k1; d <= k2; d++) { + if (d>0) { + auto decay = (*tracks)[d]; + int pdgdecay = decay.GetPdgCode(); + if (pdgdecay == 11) { + counterel++; + } + if (pdgdecay == -11) { + counterpos++; + } + } + } + if ((counterel>0) && (counterpos>0)) nMesonsDiElectronDecay++; + } + } + } + } + + std::cout << "#events: " << nEvents << "\n" + << "#mesons: " << nMesons << "\n" + << "#mesons which decay semi-electronicly: " << nMesonsDiElectronDecay << "\n"; + if (nMesonsDiElectronDecay < nEvents) { + std::cerr << "One should have at least one meson that decays into dielectrons per event.\n"; + return 1; + } + if (nMesons < nEvents) { + std::cerr << "One meson per event should be produced.\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGEM/ini/tests/GeneratorEMCocktail_502PbPb_1020.C b/MC/config/PWGEM/ini/tests/GeneratorEMCocktail_502PbPb_1020.C new file mode 100644 index 000000000..a78a0cc8e --- /dev/null +++ b/MC/config/PWGEM/ini/tests/GeneratorEMCocktail_502PbPb_1020.C @@ -0,0 +1,64 @@ +int External() +{ + + int checkPdgDecay = -11; + std::string path{"o2sim_Kine.root"}; + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree*)file.Get("o2sim"); + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int nMesons{}; + int nMesonsDiElectronDecay{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + for (auto& track : *tracks) { + auto pdg = track.GetPdgCode(); + auto y = track.GetRapidity(); + if ((pdg == 111) || (pdg == 221) || (pdg == 331) || (pdg == 223) || (pdg == 113) || (pdg == 333)) { + if ((y>-1.2) && (y<1.2)) { + nMesons++; + Int_t counterel = 0; + Int_t counterpos = 0; + int k1 = track.getFirstDaughterTrackId(); + int k2 = track.getLastDaughterTrackId(); + // k1 < k2 and no -1 for k2 + for (int d=k1; d <= k2; d++) { + if (d>0) { + auto decay = (*tracks)[d]; + int pdgdecay = decay.GetPdgCode(); + if (pdgdecay == 11) { + counterel++; + } + if (pdgdecay == -11) { + counterpos++; + } + } + } + if ((counterel>0) && (counterpos>0)) nMesonsDiElectronDecay++; + } + } + } + } + + std::cout << "#events: " << nEvents << "\n" + << "#mesons: " << nMesons << "\n" + << "#mesons which decay semi-electronicly: " << nMesonsDiElectronDecay << "\n"; + if (nMesonsDiElectronDecay < nEvents) { + std::cerr << "One should have at least one meson that decays into dielectrons per event.\n"; + return 1; + } + if (nMesons < nEvents) { + std::cerr << "One meson per event should be produced.\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGEM/ini/tests/GeneratorEMCocktail_502PbPb_2030.C b/MC/config/PWGEM/ini/tests/GeneratorEMCocktail_502PbPb_2030.C new file mode 100644 index 000000000..a78a0cc8e --- /dev/null +++ b/MC/config/PWGEM/ini/tests/GeneratorEMCocktail_502PbPb_2030.C @@ -0,0 +1,64 @@ +int External() +{ + + int checkPdgDecay = -11; + std::string path{"o2sim_Kine.root"}; + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree*)file.Get("o2sim"); + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int nMesons{}; + int nMesonsDiElectronDecay{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + for (auto& track : *tracks) { + auto pdg = track.GetPdgCode(); + auto y = track.GetRapidity(); + if ((pdg == 111) || (pdg == 221) || (pdg == 331) || (pdg == 223) || (pdg == 113) || (pdg == 333)) { + if ((y>-1.2) && (y<1.2)) { + nMesons++; + Int_t counterel = 0; + Int_t counterpos = 0; + int k1 = track.getFirstDaughterTrackId(); + int k2 = track.getLastDaughterTrackId(); + // k1 < k2 and no -1 for k2 + for (int d=k1; d <= k2; d++) { + if (d>0) { + auto decay = (*tracks)[d]; + int pdgdecay = decay.GetPdgCode(); + if (pdgdecay == 11) { + counterel++; + } + if (pdgdecay == -11) { + counterpos++; + } + } + } + if ((counterel>0) && (counterpos>0)) nMesonsDiElectronDecay++; + } + } + } + } + + std::cout << "#events: " << nEvents << "\n" + << "#mesons: " << nMesons << "\n" + << "#mesons which decay semi-electronicly: " << nMesonsDiElectronDecay << "\n"; + if (nMesonsDiElectronDecay < nEvents) { + std::cerr << "One should have at least one meson that decays into dielectrons per event.\n"; + return 1; + } + if (nMesons < nEvents) { + std::cerr << "One meson per event should be produced.\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGEM/ini/tests/GeneratorEMCocktail_502PbPb_3040.C b/MC/config/PWGEM/ini/tests/GeneratorEMCocktail_502PbPb_3040.C new file mode 100644 index 000000000..a78a0cc8e --- /dev/null +++ b/MC/config/PWGEM/ini/tests/GeneratorEMCocktail_502PbPb_3040.C @@ -0,0 +1,64 @@ +int External() +{ + + int checkPdgDecay = -11; + std::string path{"o2sim_Kine.root"}; + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree*)file.Get("o2sim"); + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int nMesons{}; + int nMesonsDiElectronDecay{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + for (auto& track : *tracks) { + auto pdg = track.GetPdgCode(); + auto y = track.GetRapidity(); + if ((pdg == 111) || (pdg == 221) || (pdg == 331) || (pdg == 223) || (pdg == 113) || (pdg == 333)) { + if ((y>-1.2) && (y<1.2)) { + nMesons++; + Int_t counterel = 0; + Int_t counterpos = 0; + int k1 = track.getFirstDaughterTrackId(); + int k2 = track.getLastDaughterTrackId(); + // k1 < k2 and no -1 for k2 + for (int d=k1; d <= k2; d++) { + if (d>0) { + auto decay = (*tracks)[d]; + int pdgdecay = decay.GetPdgCode(); + if (pdgdecay == 11) { + counterel++; + } + if (pdgdecay == -11) { + counterpos++; + } + } + } + if ((counterel>0) && (counterpos>0)) nMesonsDiElectronDecay++; + } + } + } + } + + std::cout << "#events: " << nEvents << "\n" + << "#mesons: " << nMesons << "\n" + << "#mesons which decay semi-electronicly: " << nMesonsDiElectronDecay << "\n"; + if (nMesonsDiElectronDecay < nEvents) { + std::cerr << "One should have at least one meson that decays into dielectrons per event.\n"; + return 1; + } + if (nMesons < nEvents) { + std::cerr << "One meson per event should be produced.\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGEM/ini/tests/GeneratorEMCocktail_502PbPb_4050.C b/MC/config/PWGEM/ini/tests/GeneratorEMCocktail_502PbPb_4050.C new file mode 100644 index 000000000..a78a0cc8e --- /dev/null +++ b/MC/config/PWGEM/ini/tests/GeneratorEMCocktail_502PbPb_4050.C @@ -0,0 +1,64 @@ +int External() +{ + + int checkPdgDecay = -11; + std::string path{"o2sim_Kine.root"}; + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree*)file.Get("o2sim"); + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int nMesons{}; + int nMesonsDiElectronDecay{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + for (auto& track : *tracks) { + auto pdg = track.GetPdgCode(); + auto y = track.GetRapidity(); + if ((pdg == 111) || (pdg == 221) || (pdg == 331) || (pdg == 223) || (pdg == 113) || (pdg == 333)) { + if ((y>-1.2) && (y<1.2)) { + nMesons++; + Int_t counterel = 0; + Int_t counterpos = 0; + int k1 = track.getFirstDaughterTrackId(); + int k2 = track.getLastDaughterTrackId(); + // k1 < k2 and no -1 for k2 + for (int d=k1; d <= k2; d++) { + if (d>0) { + auto decay = (*tracks)[d]; + int pdgdecay = decay.GetPdgCode(); + if (pdgdecay == 11) { + counterel++; + } + if (pdgdecay == -11) { + counterpos++; + } + } + } + if ((counterel>0) && (counterpos>0)) nMesonsDiElectronDecay++; + } + } + } + } + + std::cout << "#events: " << nEvents << "\n" + << "#mesons: " << nMesons << "\n" + << "#mesons which decay semi-electronicly: " << nMesonsDiElectronDecay << "\n"; + if (nMesonsDiElectronDecay < nEvents) { + std::cerr << "One should have at least one meson that decays into dielectrons per event.\n"; + return 1; + } + if (nMesons < nEvents) { + std::cerr << "One meson per event should be produced.\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGEM/ini/tests/GeneratorEMCocktail_502PbPb_5060.C b/MC/config/PWGEM/ini/tests/GeneratorEMCocktail_502PbPb_5060.C new file mode 100644 index 000000000..a78a0cc8e --- /dev/null +++ b/MC/config/PWGEM/ini/tests/GeneratorEMCocktail_502PbPb_5060.C @@ -0,0 +1,64 @@ +int External() +{ + + int checkPdgDecay = -11; + std::string path{"o2sim_Kine.root"}; + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree*)file.Get("o2sim"); + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int nMesons{}; + int nMesonsDiElectronDecay{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + for (auto& track : *tracks) { + auto pdg = track.GetPdgCode(); + auto y = track.GetRapidity(); + if ((pdg == 111) || (pdg == 221) || (pdg == 331) || (pdg == 223) || (pdg == 113) || (pdg == 333)) { + if ((y>-1.2) && (y<1.2)) { + nMesons++; + Int_t counterel = 0; + Int_t counterpos = 0; + int k1 = track.getFirstDaughterTrackId(); + int k2 = track.getLastDaughterTrackId(); + // k1 < k2 and no -1 for k2 + for (int d=k1; d <= k2; d++) { + if (d>0) { + auto decay = (*tracks)[d]; + int pdgdecay = decay.GetPdgCode(); + if (pdgdecay == 11) { + counterel++; + } + if (pdgdecay == -11) { + counterpos++; + } + } + } + if ((counterel>0) && (counterpos>0)) nMesonsDiElectronDecay++; + } + } + } + } + + std::cout << "#events: " << nEvents << "\n" + << "#mesons: " << nMesons << "\n" + << "#mesons which decay semi-electronicly: " << nMesonsDiElectronDecay << "\n"; + if (nMesonsDiElectronDecay < nEvents) { + std::cerr << "One should have at least one meson that decays into dielectrons per event.\n"; + return 1; + } + if (nMesons < nEvents) { + std::cerr << "One meson per event should be produced.\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGEM/ini/tests/GeneratorEMCocktail_502PbPb_6070.C b/MC/config/PWGEM/ini/tests/GeneratorEMCocktail_502PbPb_6070.C new file mode 100644 index 000000000..a78a0cc8e --- /dev/null +++ b/MC/config/PWGEM/ini/tests/GeneratorEMCocktail_502PbPb_6070.C @@ -0,0 +1,64 @@ +int External() +{ + + int checkPdgDecay = -11; + std::string path{"o2sim_Kine.root"}; + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree*)file.Get("o2sim"); + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int nMesons{}; + int nMesonsDiElectronDecay{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + for (auto& track : *tracks) { + auto pdg = track.GetPdgCode(); + auto y = track.GetRapidity(); + if ((pdg == 111) || (pdg == 221) || (pdg == 331) || (pdg == 223) || (pdg == 113) || (pdg == 333)) { + if ((y>-1.2) && (y<1.2)) { + nMesons++; + Int_t counterel = 0; + Int_t counterpos = 0; + int k1 = track.getFirstDaughterTrackId(); + int k2 = track.getLastDaughterTrackId(); + // k1 < k2 and no -1 for k2 + for (int d=k1; d <= k2; d++) { + if (d>0) { + auto decay = (*tracks)[d]; + int pdgdecay = decay.GetPdgCode(); + if (pdgdecay == 11) { + counterel++; + } + if (pdgdecay == -11) { + counterpos++; + } + } + } + if ((counterel>0) && (counterpos>0)) nMesonsDiElectronDecay++; + } + } + } + } + + std::cout << "#events: " << nEvents << "\n" + << "#mesons: " << nMesons << "\n" + << "#mesons which decay semi-electronicly: " << nMesonsDiElectronDecay << "\n"; + if (nMesonsDiElectronDecay < nEvents) { + std::cerr << "One should have at least one meson that decays into dielectrons per event.\n"; + return 1; + } + if (nMesons < nEvents) { + std::cerr << "One meson per event should be produced.\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGEM/ini/tests/GeneratorEMCocktail_502PbPb_7080.C b/MC/config/PWGEM/ini/tests/GeneratorEMCocktail_502PbPb_7080.C new file mode 100644 index 000000000..a78a0cc8e --- /dev/null +++ b/MC/config/PWGEM/ini/tests/GeneratorEMCocktail_502PbPb_7080.C @@ -0,0 +1,64 @@ +int External() +{ + + int checkPdgDecay = -11; + std::string path{"o2sim_Kine.root"}; + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree*)file.Get("o2sim"); + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int nMesons{}; + int nMesonsDiElectronDecay{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + for (auto& track : *tracks) { + auto pdg = track.GetPdgCode(); + auto y = track.GetRapidity(); + if ((pdg == 111) || (pdg == 221) || (pdg == 331) || (pdg == 223) || (pdg == 113) || (pdg == 333)) { + if ((y>-1.2) && (y<1.2)) { + nMesons++; + Int_t counterel = 0; + Int_t counterpos = 0; + int k1 = track.getFirstDaughterTrackId(); + int k2 = track.getLastDaughterTrackId(); + // k1 < k2 and no -1 for k2 + for (int d=k1; d <= k2; d++) { + if (d>0) { + auto decay = (*tracks)[d]; + int pdgdecay = decay.GetPdgCode(); + if (pdgdecay == 11) { + counterel++; + } + if (pdgdecay == -11) { + counterpos++; + } + } + } + if ((counterel>0) && (counterpos>0)) nMesonsDiElectronDecay++; + } + } + } + } + + std::cout << "#events: " << nEvents << "\n" + << "#mesons: " << nMesons << "\n" + << "#mesons which decay semi-electronicly: " << nMesonsDiElectronDecay << "\n"; + if (nMesonsDiElectronDecay < nEvents) { + std::cerr << "One should have at least one meson that decays into dielectrons per event.\n"; + return 1; + } + if (nMesons < nEvents) { + std::cerr << "One meson per event should be produced.\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGEM/ini/tests/GeneratorEMCocktail_502PbPb_8090.C b/MC/config/PWGEM/ini/tests/GeneratorEMCocktail_502PbPb_8090.C new file mode 100644 index 000000000..a78a0cc8e --- /dev/null +++ b/MC/config/PWGEM/ini/tests/GeneratorEMCocktail_502PbPb_8090.C @@ -0,0 +1,64 @@ +int External() +{ + + int checkPdgDecay = -11; + std::string path{"o2sim_Kine.root"}; + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree*)file.Get("o2sim"); + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int nMesons{}; + int nMesonsDiElectronDecay{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + for (auto& track : *tracks) { + auto pdg = track.GetPdgCode(); + auto y = track.GetRapidity(); + if ((pdg == 111) || (pdg == 221) || (pdg == 331) || (pdg == 223) || (pdg == 113) || (pdg == 333)) { + if ((y>-1.2) && (y<1.2)) { + nMesons++; + Int_t counterel = 0; + Int_t counterpos = 0; + int k1 = track.getFirstDaughterTrackId(); + int k2 = track.getLastDaughterTrackId(); + // k1 < k2 and no -1 for k2 + for (int d=k1; d <= k2; d++) { + if (d>0) { + auto decay = (*tracks)[d]; + int pdgdecay = decay.GetPdgCode(); + if (pdgdecay == 11) { + counterel++; + } + if (pdgdecay == -11) { + counterpos++; + } + } + } + if ((counterel>0) && (counterpos>0)) nMesonsDiElectronDecay++; + } + } + } + } + + std::cout << "#events: " << nEvents << "\n" + << "#mesons: " << nMesons << "\n" + << "#mesons which decay semi-electronicly: " << nMesonsDiElectronDecay << "\n"; + if (nMesonsDiElectronDecay < nEvents) { + std::cerr << "One should have at least one meson that decays into dielectrons per event.\n"; + return 1; + } + if (nMesons < nEvents) { + std::cerr << "One meson per event should be produced.\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGEM/ini/tests/GeneratorEMCocktail_Run3_OO.C b/MC/config/PWGEM/ini/tests/GeneratorEMCocktail_Run3_OO.C new file mode 100644 index 000000000..a78a0cc8e --- /dev/null +++ b/MC/config/PWGEM/ini/tests/GeneratorEMCocktail_Run3_OO.C @@ -0,0 +1,64 @@ +int External() +{ + + int checkPdgDecay = -11; + std::string path{"o2sim_Kine.root"}; + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree*)file.Get("o2sim"); + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int nMesons{}; + int nMesonsDiElectronDecay{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + for (auto& track : *tracks) { + auto pdg = track.GetPdgCode(); + auto y = track.GetRapidity(); + if ((pdg == 111) || (pdg == 221) || (pdg == 331) || (pdg == 223) || (pdg == 113) || (pdg == 333)) { + if ((y>-1.2) && (y<1.2)) { + nMesons++; + Int_t counterel = 0; + Int_t counterpos = 0; + int k1 = track.getFirstDaughterTrackId(); + int k2 = track.getLastDaughterTrackId(); + // k1 < k2 and no -1 for k2 + for (int d=k1; d <= k2; d++) { + if (d>0) { + auto decay = (*tracks)[d]; + int pdgdecay = decay.GetPdgCode(); + if (pdgdecay == 11) { + counterel++; + } + if (pdgdecay == -11) { + counterpos++; + } + } + } + if ((counterel>0) && (counterpos>0)) nMesonsDiElectronDecay++; + } + } + } + } + + std::cout << "#events: " << nEvents << "\n" + << "#mesons: " << nMesons << "\n" + << "#mesons which decay semi-electronicly: " << nMesonsDiElectronDecay << "\n"; + if (nMesonsDiElectronDecay < nEvents) { + std::cerr << "One should have at least one meson that decays into dielectrons per event.\n"; + return 1; + } + if (nMesons < nEvents) { + std::cerr << "One meson per event should be produced.\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGEM/ini/tests/GeneratorHFGapTriggered_BeautyForcedDecay_Gap2_OO_electron.C b/MC/config/PWGEM/ini/tests/GeneratorHFGapTriggered_BeautyForcedDecay_Gap2_OO_electron.C new file mode 100644 index 000000000..d4c7056e4 --- /dev/null +++ b/MC/config/PWGEM/ini/tests/GeneratorHFGapTriggered_BeautyForcedDecay_Gap2_OO_electron.C @@ -0,0 +1,117 @@ +int External() +{ + int checkPdgDecay = 11; + std::string path{"o2sim_Kine.root"}; + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + float ratioTrigger = 1./3; // one event triggered out of 3 + auto tree = (TTree*)file.Get("o2sim"); + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int nLeptonsInAcceptance{}; + int nLeptons{}; + int nAntileptons{}; + int nLeptonsToBeDone{}; + int nAntileptonsToBeDone{}; + int nSignalPairs{}; + int nLeptonPairs{}; + int nLeptonPairsToBeDone{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + printf("Event %d\n",i); + tree->GetEntry(i); + int nElectrons = 0; + int nPositrons = 0; + int nElectronsToBeDone = 0; + int nPositronsToBeDone = 0; + int nOpenBeautyPos = 0; + int nOpenBeautyNeg = 0; + int nPositronsElectronsInAcceptance = 0; + for (auto& track : *tracks) { + auto pdg = track.GetPdgCode(); + auto y = track.GetRapidity(); + if (pdg == checkPdgDecay) { + int igmother = track.getMotherTrackId(); + if (igmother > 0) { + auto gmTrack = (*tracks)[igmother]; + int gmpdg = gmTrack.GetPdgCode(); + if (int(std::abs(gmpdg)/100.) == 5 || int(std::abs(gmpdg)/1000.) == 5 || int(std::abs(gmpdg)/100.) == 4 || int(std::abs(gmpdg)/1000.) == 4) { + nLeptons++; + nElectrons++; + if (-1 < y && y < 1) nPositronsElectronsInAcceptance++; + if (track.getToBeDone()) { + nLeptonsToBeDone++; + nElectronsToBeDone++; + } + } + } + } else if (pdg == -checkPdgDecay) { + int igmother = track.getMotherTrackId(); + if (igmother > 0) { + auto gmTrack = (*tracks)[igmother]; + int gmpdg = gmTrack.GetPdgCode(); + if (int(TMath::Abs(gmpdg)/100.) == 4 || int(TMath::Abs(gmpdg)/1000.) == 4 || int(std::abs(gmpdg)/100.) == 5 || int(std::abs(gmpdg)/1000.) == 5) { + nAntileptons++; + nPositrons++; + if (-1 < y && y < 1) nPositronsElectronsInAcceptance++; + if (track.getToBeDone()) { + nAntileptonsToBeDone++; + nPositronsToBeDone++; + } + } + } + } else if (pdg == 511 || pdg == 521 || pdg == 531 || pdg == 5122 || pdg == 5132 || pdg == 5232 || pdg == 5332) { + nOpenBeautyPos++; + } else if (pdg == -511 || pdg == -521 || pdg == -531 || pdg == -5122 || pdg == -5132 || pdg == -5232 || pdg == -5332) { + nOpenBeautyNeg++; + } + } + if (nOpenBeautyPos > 0 && nOpenBeautyNeg > 0) { + nSignalPairs++; + } + if (nPositronsElectronsInAcceptance > 1) { + nLeptonsInAcceptance++; + } + if (nElectrons > 0 && nPositrons > 0) { + nLeptonPairs++; + } + if (nElectronsToBeDone > 0 && nPositronsToBeDone > 0) nLeptonPairsToBeDone++; + } + std::cout << "#events: " << nEvents << "\n" + << "#leptons: " << nLeptons << "\n" + << "#antileptons: " << nAntileptons << "\n" + << "#leptons to be done: " << nLeptonsToBeDone << "\n" + << "#antileptons to be done: " << nAntileptonsToBeDone << "\n" + << "#Open-beauty hadron pairs: " << nSignalPairs << "\n" + << "#leptons in acceptance: " << nLeptonsInAcceptance << "\n" + << "#Electron-positron pairs " << nLeptonPairs << "\n" + << "#Electron-positron pairs to be done: " << nLeptonPairsToBeDone << "\n"; + if (nLeptons == 0 && nAntileptons == 0) { + std::cerr << "Number of leptons, number of anti-leptons should all be greater than 1.\n"; + return 1; + } + if (nLeptonPairs != nLeptonPairsToBeDone) { + std::cerr << "The number of electron-positron pairs should be the same as the number of electron-positron pairs which should be transported.\n"; + return 1; + } + if (nLeptons != nLeptonsToBeDone) { + std::cerr << "The number of leptons should be the same as the number of leptons which should be transported.\n"; + return 1; + } + if (nLeptonsInAcceptance == (nEvents/ratioTrigger)) { + std::cerr << "The number of leptons in acceptance should be at least equaled to the number of events.\n"; + return 1; + } + if (nLeptonPairs < nLeptonsInAcceptance) { + std::cerr << "The number of positron-electron pairs should be at least equaled to the number of leptons in acceptance.\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGEM/ini/tests/GeneratorHFGapTriggered_BeautyForcedDecay_Gap2_OO_muon.C b/MC/config/PWGEM/ini/tests/GeneratorHFGapTriggered_BeautyForcedDecay_Gap2_OO_muon.C new file mode 100644 index 000000000..dea67792e --- /dev/null +++ b/MC/config/PWGEM/ini/tests/GeneratorHFGapTriggered_BeautyForcedDecay_Gap2_OO_muon.C @@ -0,0 +1,117 @@ +int External() +{ + int checkPdgDecay = 13; + std::string path{"o2sim_Kine.root"}; + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + float ratioTrigger = 1./3; // one event triggered out of 3 + auto tree = (TTree*)file.Get("o2sim"); + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int nLeptonsInAcceptance{}; + int nLeptons{}; + int nAntileptons{}; + int nLeptonsToBeDone{}; + int nAntileptonsToBeDone{}; + int nSignalPairs{}; + int nLeptonPairs{}; + int nLeptonPairsToBeDone{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + printf("Event %d\n",i); + tree->GetEntry(i); + int nElectrons = 0; + int nPositrons = 0; + int nElectronsToBeDone = 0; + int nPositronsToBeDone = 0; + int nOpenBeautyPos = 0; + int nOpenBeautyNeg = 0; + int nPositronsElectronsInAcceptance = 0; + for (auto& track : *tracks) { + auto pdg = track.GetPdgCode(); + auto y = track.GetRapidity(); + if (pdg == checkPdgDecay) { + int igmother = track.getMotherTrackId(); + if (igmother > 0) { + auto gmTrack = (*tracks)[igmother]; + int gmpdg = gmTrack.GetPdgCode(); + if (int(std::abs(gmpdg)/100.) == 5 || int(std::abs(gmpdg)/1000.) == 5 || int(std::abs(gmpdg)/100.) == 4 || int(std::abs(gmpdg)/1000.) == 4) { + nLeptons++; + nElectrons++; + if (-1 < y && y < 1) nPositronsElectronsInAcceptance++; + if (track.getToBeDone()) { + nLeptonsToBeDone++; + nElectronsToBeDone++; + } + } + } + } else if (pdg == -checkPdgDecay) { + int igmother = track.getMotherTrackId(); + if (igmother > 0) { + auto gmTrack = (*tracks)[igmother]; + int gmpdg = gmTrack.GetPdgCode(); + if (int(TMath::Abs(gmpdg)/100.) == 4 || int(TMath::Abs(gmpdg)/1000.) == 4 || int(std::abs(gmpdg)/100.) == 5 || int(std::abs(gmpdg)/1000.) == 5) { + nAntileptons++; + nPositrons++; + if (-1 < y && y < 1) nPositronsElectronsInAcceptance++; + if (track.getToBeDone()) { + nAntileptonsToBeDone++; + nPositronsToBeDone++; + } + } + } + } else if (pdg == 511 || pdg == 521 || pdg == 531 || pdg == 5122 || pdg == 5132 || pdg == 5232 || pdg == 5332) { + nOpenBeautyPos++; + } else if (pdg == -511 || pdg == -521 || pdg == -531 || pdg == -5122 || pdg == -5132 || pdg == -5232 || pdg == -5332) { + nOpenBeautyNeg++; + } + } + if (nOpenBeautyPos > 0 && nOpenBeautyNeg > 0) { + nSignalPairs++; + } + if (nPositronsElectronsInAcceptance > 1) { + nLeptonsInAcceptance++; + } + if (nElectrons > 0 && nPositrons > 0) { + nLeptonPairs++; + } + if (nElectronsToBeDone > 0 && nPositronsToBeDone > 0) nLeptonPairsToBeDone++; + } + std::cout << "#events: " << nEvents << "\n" + << "#leptons: " << nLeptons << "\n" + << "#antileptons: " << nAntileptons << "\n" + << "#leptons to be done: " << nLeptonsToBeDone << "\n" + << "#antileptons to be done: " << nAntileptonsToBeDone << "\n" + << "#Open-beauty hadron pairs: " << nSignalPairs << "\n" + << "#leptons in acceptance: " << nLeptonsInAcceptance << "\n" + << "#Electron-positron pairs " << nLeptonPairs << "\n" + << "#Electron-positron pairs to be done: " << nLeptonPairsToBeDone << "\n"; + if (nLeptons == 0 && nAntileptons == 0) { + std::cerr << "Number of leptons, number of anti-leptons should all be greater than 1.\n"; + return 1; + } + if (nLeptonPairs != nLeptonPairsToBeDone) { + std::cerr << "The number of electron-positron pairs should be the same as the number of electron-positron pairs which should be transported.\n"; + return 1; + } + if (nLeptons != nLeptonsToBeDone) { + std::cerr << "The number of leptons should be the same as the number of leptons which should be transported.\n"; + return 1; + } + if (nLeptonsInAcceptance == (nEvents/ratioTrigger)) { + std::cerr << "The number of leptons in acceptance should be at least equaled to the number of events.\n"; + return 1; + } + if (nLeptonPairs < nLeptonsInAcceptance) { + std::cerr << "The number of positron-electron pairs should be at least equaled to the number of leptons in acceptance.\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGEM/ini/tests/GeneratorHFGapTriggered_BeautyForcedDecay_Gap5_pp5360GeV.C b/MC/config/PWGEM/ini/tests/GeneratorHFGapTriggered_BeautyForcedDecay_Gap5_pp5360GeV.C new file mode 100644 index 000000000..5bee390cc --- /dev/null +++ b/MC/config/PWGEM/ini/tests/GeneratorHFGapTriggered_BeautyForcedDecay_Gap5_pp5360GeV.C @@ -0,0 +1,118 @@ +int External() +{ + + int checkPdgDecay = -11; + std::string path{"o2sim_Kine.root"}; + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + float ratioTrigger = 1./3; // one event triggered out of 3 + auto tree = (TTree*)file.Get("o2sim"); + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int nLeptonsInAcceptance{}; + int nLeptons{}; + int nAntileptons{}; + int nLeptonsToBeDone{}; + int nAntileptonsToBeDone{}; + int nSignalPairs{}; + int nLeptonPairs{}; + int nLeptonPairsToBeDone{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + printf("Event %d\n",i); + tree->GetEntry(i); + int nElectrons = 0; + int nPositrons = 0; + int nElectronsToBeDone = 0; + int nPositronsToBeDone = 0; + int nOpenBeautyPos = 0; + int nOpenBeautyNeg = 0; + int nPositronsElectronsInAcceptance = 0; + for (auto& track : *tracks) { + auto pdg = track.GetPdgCode(); + auto y = track.GetRapidity(); + if (pdg == checkPdgDecay) { + int igmother = track.getMotherTrackId(); + if (igmother > 0) { + auto gmTrack = (*tracks)[igmother]; + int gmpdg = gmTrack.GetPdgCode(); + if (int(std::abs(gmpdg)/100.) == 5 || int(std::abs(gmpdg)/1000.) == 5 || int(std::abs(gmpdg)/100.) == 4 || int(std::abs(gmpdg)/1000.) == 4) { + nLeptons++; + nElectrons++; + if (-1 < y && y < 1) nPositronsElectronsInAcceptance++; + if (track.getToBeDone()) { + nLeptonsToBeDone++; + nElectronsToBeDone++; + } + } + } + } else if (pdg == -checkPdgDecay) { + int igmother = track.getMotherTrackId(); + if (igmother > 0) { + auto gmTrack = (*tracks)[igmother]; + int gmpdg = gmTrack.GetPdgCode(); + if (int(TMath::Abs(gmpdg)/100.) == 4 || int(TMath::Abs(gmpdg)/1000.) == 4 || int(std::abs(gmpdg)/100.) == 5 || int(std::abs(gmpdg)/1000.) == 5) { + nAntileptons++; + nPositrons++; + if (-1 < y && y < 1) nPositronsElectronsInAcceptance++; + if (track.getToBeDone()) { + nAntileptonsToBeDone++; + nPositronsToBeDone++; + } + } + } + } else if (pdg == 511 || pdg == 521 || pdg == 531 || pdg == 5122 || pdg == 5132 || pdg == 5232 || pdg == 5332) { + nOpenBeautyPos++; + } else if (pdg == -511 || pdg == -521 || pdg == -531 || pdg == -5122 || pdg == -5132 || pdg == -5232 || pdg == -5332) { + nOpenBeautyNeg++; + } + } + if (nOpenBeautyPos > 0 && nOpenBeautyNeg > 0) { + nSignalPairs++; + } + if (nPositronsElectronsInAcceptance > 1) { + nLeptonsInAcceptance++; + } + if (nElectrons > 0 && nPositrons > 0) { + nLeptonPairs++; + } + if (nElectronsToBeDone > 0 && nPositronsToBeDone > 0) nLeptonPairsToBeDone++; + } + std::cout << "#events: " << nEvents << "\n" + << "#leptons: " << nLeptons << "\n" + << "#antileptons: " << nAntileptons << "\n" + << "#leptons to be done: " << nLeptonsToBeDone << "\n" + << "#antileptons to be done: " << nAntileptonsToBeDone << "\n" + << "#Open-beauty hadron pairs: " << nSignalPairs << "\n" + << "#leptons in acceptance: " << nLeptonsInAcceptance << "\n" + << "#Electron-positron pairs " << nLeptonPairs << "\n" + << "#Electron-positron pairs to be done: " << nLeptonPairsToBeDone << "\n"; + if (nLeptons == 0 && nAntileptons == 0) { + std::cerr << "Number of leptons, number of anti-leptons should all be greater than 1.\n"; + return 1; + } + if (nLeptonPairs != nLeptonPairsToBeDone) { + std::cerr << "The number of electron-positron pairs should be the same as the number of electron-positron pairs which should be transported.\n"; + return 1; + } + if (nLeptons != nLeptonsToBeDone) { + std::cerr << "The number of leptons should be the same as the number of leptons which should be transported.\n"; + return 1; + } + if (nLeptonsInAcceptance == (nEvents/ratioTrigger)) { + std::cerr << "The number of leptons in acceptance should be at least equaled to the number of events.\n"; + return 1; + } + if (nLeptonPairs < nLeptonsInAcceptance) { + std::cerr << "The number of positron-electron pairs should be at least equaled to the number of leptons in acceptance.\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGEM/ini/tests/GeneratorHFGapTriggered_BeautyNoForcedDecay_Gap2_OO_electron.C b/MC/config/PWGEM/ini/tests/GeneratorHFGapTriggered_BeautyNoForcedDecay_Gap2_OO_electron.C new file mode 100644 index 000000000..ac2a627cf --- /dev/null +++ b/MC/config/PWGEM/ini/tests/GeneratorHFGapTriggered_BeautyNoForcedDecay_Gap2_OO_electron.C @@ -0,0 +1,112 @@ +int External() +{ + int checkPdgDecay = 11; + std::string path{"o2sim_Kine.root"}; + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + float ratioTrigger = 1./3; // one event triggered out of 3 + auto tree = (TTree*)file.Get("o2sim"); + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int nLeptonsInAcceptance{}; + int nLeptons{}; + int nAntileptons{}; + int nLeptonsToBeDone{}; + int nAntileptonsToBeDone{}; + int nSignalPairs{}; + int nLeptonPairs{}; + int nLeptonPairsToBeDone{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + int nElectrons = 0; + int nPositrons = 0; + int nElectronsToBeDone = 0; + int nPositronsToBeDone = 0; + int nOpenBeautyPos = 0; + int nOpenBeautyNeg = 0; + int nPositronsElectronsInAcceptance = 0; + for (auto& track : *tracks) { + auto pdg = track.GetPdgCode(); + auto y = track.GetRapidity(); + if (pdg == checkPdgDecay) { + int igmother = track.getMotherTrackId(); + if (igmother > 0) { + auto gmTrack = (*tracks)[igmother]; + int gmpdg = gmTrack.GetPdgCode(); + if (int(std::abs(gmpdg)/100.) == 5 || int(std::abs(gmpdg)/1000.) == 5 || int(std::abs(gmpdg)/100.) == 4 || int(std::abs(gmpdg)/1000.) == 4) { + nLeptons++; + nElectrons++; + if (-1 < y && y < 1) nPositronsElectronsInAcceptance++; + if (track.getToBeDone()) { + nLeptonsToBeDone++; + nElectronsToBeDone++; + } + } + } + } else if (pdg == -checkPdgDecay) { + int igmother = track.getMotherTrackId(); + if (igmother > 0) { + auto gmTrack = (*tracks)[igmother]; + int gmpdg = gmTrack.GetPdgCode(); + if (int(TMath::Abs(gmpdg)/100.) == 4 || int(TMath::Abs(gmpdg)/1000.) == 4 || int(std::abs(gmpdg)/100.) == 5 || int(std::abs(gmpdg)/1000.) == 5) { + nAntileptons++; + nPositrons++; + if (-1 < y && y < 1) nPositronsElectronsInAcceptance++; + if (track.getToBeDone()) { + nAntileptonsToBeDone++; + nPositronsToBeDone++; + } + } + } + } else if (pdg == 511 || pdg == 521 || pdg == 531 || pdg == 5122 || pdg == 5132 || pdg == 5232 || pdg == 5332) { + nOpenBeautyPos++; + } else if (pdg == -511 || pdg == -521 || pdg == -531 || pdg == -5122 || pdg == -5132 || pdg == -5232 || pdg == -5332) { + nOpenBeautyNeg++; + } + } + if (nOpenBeautyPos > 0 && nOpenBeautyNeg > 0) { + nSignalPairs++; + } + if (nPositronsElectronsInAcceptance > 1) { + nLeptonsInAcceptance++; + } + if (nElectrons > 0 && nPositrons > 0) { + nLeptonPairs++; + } + if (nElectronsToBeDone > 0 && nPositronsToBeDone > 0) nLeptonPairsToBeDone++; + } + std::cout << "#events: " << nEvents << "\n" + << "#leptons: " << nLeptons << "\n" + << "#antileptons: " << nAntileptons << "\n" + << "#leptons to be done: " << nLeptonsToBeDone << "\n" + << "#antileptons to be done: " << nAntileptonsToBeDone << "\n" + << "#Open-beauty hadron pairs: " << nSignalPairs << "\n" + << "#leptons in acceptance: " << nLeptonsInAcceptance << "\n" + << "#Electron-positron pairs: " << nLeptonPairs << "\n" + << "#Electron-positron pairs to be done: " << nLeptonPairsToBeDone << "\n"; + if (nLeptons == 0 && nAntileptons == 0) { + std::cerr << "Number of leptons, number of anti-leptons should all be greater than 1.\n"; + return 1; + } + if (nLeptonPairs != nLeptonPairsToBeDone) { + std::cerr << "The number of lepton pairs should be the same as the number of lepton pairs which should be transported.\n"; + return 1; + } + if (nLeptons != nLeptonsToBeDone) { + std::cerr << "The number of leptons should be the same as the number of leptons which should be transported.\n"; + return 1; + } + if (nLeptonsInAcceptance == (nEvents/ratioTrigger)) { + std::cerr << "The number of leptons in acceptance should be at least equaled to the number of events.\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGEM/ini/tests/GeneratorHFGapTriggered_BeautyNoForcedDecay_Gap2_OO_muon.C b/MC/config/PWGEM/ini/tests/GeneratorHFGapTriggered_BeautyNoForcedDecay_Gap2_OO_muon.C new file mode 100644 index 000000000..ac2a627cf --- /dev/null +++ b/MC/config/PWGEM/ini/tests/GeneratorHFGapTriggered_BeautyNoForcedDecay_Gap2_OO_muon.C @@ -0,0 +1,112 @@ +int External() +{ + int checkPdgDecay = 11; + std::string path{"o2sim_Kine.root"}; + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + float ratioTrigger = 1./3; // one event triggered out of 3 + auto tree = (TTree*)file.Get("o2sim"); + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int nLeptonsInAcceptance{}; + int nLeptons{}; + int nAntileptons{}; + int nLeptonsToBeDone{}; + int nAntileptonsToBeDone{}; + int nSignalPairs{}; + int nLeptonPairs{}; + int nLeptonPairsToBeDone{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + int nElectrons = 0; + int nPositrons = 0; + int nElectronsToBeDone = 0; + int nPositronsToBeDone = 0; + int nOpenBeautyPos = 0; + int nOpenBeautyNeg = 0; + int nPositronsElectronsInAcceptance = 0; + for (auto& track : *tracks) { + auto pdg = track.GetPdgCode(); + auto y = track.GetRapidity(); + if (pdg == checkPdgDecay) { + int igmother = track.getMotherTrackId(); + if (igmother > 0) { + auto gmTrack = (*tracks)[igmother]; + int gmpdg = gmTrack.GetPdgCode(); + if (int(std::abs(gmpdg)/100.) == 5 || int(std::abs(gmpdg)/1000.) == 5 || int(std::abs(gmpdg)/100.) == 4 || int(std::abs(gmpdg)/1000.) == 4) { + nLeptons++; + nElectrons++; + if (-1 < y && y < 1) nPositronsElectronsInAcceptance++; + if (track.getToBeDone()) { + nLeptonsToBeDone++; + nElectronsToBeDone++; + } + } + } + } else if (pdg == -checkPdgDecay) { + int igmother = track.getMotherTrackId(); + if (igmother > 0) { + auto gmTrack = (*tracks)[igmother]; + int gmpdg = gmTrack.GetPdgCode(); + if (int(TMath::Abs(gmpdg)/100.) == 4 || int(TMath::Abs(gmpdg)/1000.) == 4 || int(std::abs(gmpdg)/100.) == 5 || int(std::abs(gmpdg)/1000.) == 5) { + nAntileptons++; + nPositrons++; + if (-1 < y && y < 1) nPositronsElectronsInAcceptance++; + if (track.getToBeDone()) { + nAntileptonsToBeDone++; + nPositronsToBeDone++; + } + } + } + } else if (pdg == 511 || pdg == 521 || pdg == 531 || pdg == 5122 || pdg == 5132 || pdg == 5232 || pdg == 5332) { + nOpenBeautyPos++; + } else if (pdg == -511 || pdg == -521 || pdg == -531 || pdg == -5122 || pdg == -5132 || pdg == -5232 || pdg == -5332) { + nOpenBeautyNeg++; + } + } + if (nOpenBeautyPos > 0 && nOpenBeautyNeg > 0) { + nSignalPairs++; + } + if (nPositronsElectronsInAcceptance > 1) { + nLeptonsInAcceptance++; + } + if (nElectrons > 0 && nPositrons > 0) { + nLeptonPairs++; + } + if (nElectronsToBeDone > 0 && nPositronsToBeDone > 0) nLeptonPairsToBeDone++; + } + std::cout << "#events: " << nEvents << "\n" + << "#leptons: " << nLeptons << "\n" + << "#antileptons: " << nAntileptons << "\n" + << "#leptons to be done: " << nLeptonsToBeDone << "\n" + << "#antileptons to be done: " << nAntileptonsToBeDone << "\n" + << "#Open-beauty hadron pairs: " << nSignalPairs << "\n" + << "#leptons in acceptance: " << nLeptonsInAcceptance << "\n" + << "#Electron-positron pairs: " << nLeptonPairs << "\n" + << "#Electron-positron pairs to be done: " << nLeptonPairsToBeDone << "\n"; + if (nLeptons == 0 && nAntileptons == 0) { + std::cerr << "Number of leptons, number of anti-leptons should all be greater than 1.\n"; + return 1; + } + if (nLeptonPairs != nLeptonPairsToBeDone) { + std::cerr << "The number of lepton pairs should be the same as the number of lepton pairs which should be transported.\n"; + return 1; + } + if (nLeptons != nLeptonsToBeDone) { + std::cerr << "The number of leptons should be the same as the number of leptons which should be transported.\n"; + return 1; + } + if (nLeptonsInAcceptance == (nEvents/ratioTrigger)) { + std::cerr << "The number of leptons in acceptance should be at least equaled to the number of events.\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGEM/ini/tests/GeneratorHFGapTriggered_BeautyNoForcedDecay_Gap5_pp5360GeV.C b/MC/config/PWGEM/ini/tests/GeneratorHFGapTriggered_BeautyNoForcedDecay_Gap5_pp5360GeV.C new file mode 100644 index 000000000..10907110b --- /dev/null +++ b/MC/config/PWGEM/ini/tests/GeneratorHFGapTriggered_BeautyNoForcedDecay_Gap5_pp5360GeV.C @@ -0,0 +1,113 @@ +int External() +{ + + int checkPdgDecay = -11; + std::string path{"o2sim_Kine.root"}; + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + float ratioTrigger = 1./3; // one event triggered out of 3 + auto tree = (TTree*)file.Get("o2sim"); + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int nLeptonsInAcceptance{}; + int nLeptons{}; + int nAntileptons{}; + int nLeptonsToBeDone{}; + int nAntileptonsToBeDone{}; + int nSignalPairs{}; + int nLeptonPairs{}; + int nLeptonPairsToBeDone{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + int nElectrons = 0; + int nPositrons = 0; + int nElectronsToBeDone = 0; + int nPositronsToBeDone = 0; + int nOpenBeautyPos = 0; + int nOpenBeautyNeg = 0; + int nPositronsElectronsInAcceptance = 0; + for (auto& track : *tracks) { + auto pdg = track.GetPdgCode(); + auto y = track.GetRapidity(); + if (pdg == checkPdgDecay) { + int igmother = track.getMotherTrackId(); + if (igmother > 0) { + auto gmTrack = (*tracks)[igmother]; + int gmpdg = gmTrack.GetPdgCode(); + if (int(std::abs(gmpdg)/100.) == 5 || int(std::abs(gmpdg)/1000.) == 5 || int(std::abs(gmpdg)/100.) == 4 || int(std::abs(gmpdg)/1000.) == 4) { + nLeptons++; + nElectrons++; + if (-1 < y && y < 1) nPositronsElectronsInAcceptance++; + if (track.getToBeDone()) { + nLeptonsToBeDone++; + nElectronsToBeDone++; + } + } + } + } else if (pdg == -checkPdgDecay) { + int igmother = track.getMotherTrackId(); + if (igmother > 0) { + auto gmTrack = (*tracks)[igmother]; + int gmpdg = gmTrack.GetPdgCode(); + if (int(TMath::Abs(gmpdg)/100.) == 4 || int(TMath::Abs(gmpdg)/1000.) == 4 || int(std::abs(gmpdg)/100.) == 5 || int(std::abs(gmpdg)/1000.) == 5) { + nAntileptons++; + nPositrons++; + if (-1 < y && y < 1) nPositronsElectronsInAcceptance++; + if (track.getToBeDone()) { + nAntileptonsToBeDone++; + nPositronsToBeDone++; + } + } + } + } else if (pdg == 511 || pdg == 521 || pdg == 531 || pdg == 5122 || pdg == 5132 || pdg == 5232 || pdg == 5332) { + nOpenBeautyPos++; + } else if (pdg == -511 || pdg == -521 || pdg == -531 || pdg == -5122 || pdg == -5132 || pdg == -5232 || pdg == -5332) { + nOpenBeautyNeg++; + } + } + if (nOpenBeautyPos > 0 && nOpenBeautyNeg > 0) { + nSignalPairs++; + } + if (nPositronsElectronsInAcceptance > 1) { + nLeptonsInAcceptance++; + } + if (nElectrons > 0 && nPositrons > 0) { + nLeptonPairs++; + } + if (nElectronsToBeDone > 0 && nPositronsToBeDone > 0) nLeptonPairsToBeDone++; + } + std::cout << "#events: " << nEvents << "\n" + << "#leptons: " << nLeptons << "\n" + << "#antileptons: " << nAntileptons << "\n" + << "#leptons to be done: " << nLeptonsToBeDone << "\n" + << "#antileptons to be done: " << nAntileptonsToBeDone << "\n" + << "#Open-beauty hadron pairs: " << nSignalPairs << "\n" + << "#leptons in acceptance: " << nLeptonsInAcceptance << "\n" + << "#Electron-positron pairs: " << nLeptonPairs << "\n" + << "#Electron-positron pairs to be done: " << nLeptonPairsToBeDone << "\n"; + if (nLeptons == 0 && nAntileptons == 0) { + std::cerr << "Number of leptons, number of anti-leptons should all be greater than 1.\n"; + return 1; + } + if (nLeptonPairs != nLeptonPairsToBeDone) { + std::cerr << "The number of lepton pairs should be the same as the number of lepton pairs which should be transported.\n"; + return 1; + } + if (nLeptons != nLeptonsToBeDone) { + std::cerr << "The number of leptons should be the same as the number of leptons which should be transported.\n"; + return 1; + } + if (nLeptonsInAcceptance == (nEvents/ratioTrigger)) { + std::cerr << "The number of leptons in acceptance should be at least equaled to the number of events.\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGEM/ini/tests/GeneratorHFGapTriggered_BeautyNoForcedDecay_Muon_pp13600GeV_Gap1.C b/MC/config/PWGEM/ini/tests/GeneratorHFGapTriggered_BeautyNoForcedDecay_Muon_pp13600GeV_Gap1.C new file mode 100644 index 000000000..99e697a46 --- /dev/null +++ b/MC/config/PWGEM/ini/tests/GeneratorHFGapTriggered_BeautyNoForcedDecay_Muon_pp13600GeV_Gap1.C @@ -0,0 +1,113 @@ +int External() +{ + + int checkPdgDecay = -13; + std::string path{"o2sim_Kine.root"}; + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + float ratioTrigger = 1./1; // one event triggered out of 1 + auto tree = (TTree*)file.Get("o2sim"); + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int nLeptonsInAcceptance{}; + int nLeptons{}; + int nAntileptons{}; + int nLeptonsToBeDone{}; + int nAntileptonsToBeDone{}; + int nSignalPairs{}; + int nLeptonPairs{}; + int nLeptonPairsToBeDone{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + int nElectrons = 0; + int nPositrons = 0; + int nElectronsToBeDone = 0; + int nPositronsToBeDone = 0; + int nOpenBeautyPos = 0; + int nOpenBeautyNeg = 0; + int nPositronsElectronsInAcceptance = 0; + for (auto& track : *tracks) { + auto pdg = track.GetPdgCode(); + auto y = track.GetRapidity(); + if (pdg == checkPdgDecay) { + int igmother = track.getMotherTrackId(); + if (igmother > 0) { + auto gmTrack = (*tracks)[igmother]; + int gmpdg = gmTrack.GetPdgCode(); + if (int(std::abs(gmpdg)/100.) == 5 || int(std::abs(gmpdg)/1000.) == 5 || int(std::abs(gmpdg)/100.) == 4 || int(std::abs(gmpdg)/1000.) == 4) { + nLeptons++; + nElectrons++; + if (-1 < y && y < 1) nPositronsElectronsInAcceptance++; + if (track.getToBeDone()) { + nLeptonsToBeDone++; + nElectronsToBeDone++; + } + } + } + } else if (pdg == -checkPdgDecay) { + int igmother = track.getMotherTrackId(); + if (igmother > 0) { + auto gmTrack = (*tracks)[igmother]; + int gmpdg = gmTrack.GetPdgCode(); + if (int(TMath::Abs(gmpdg)/100.) == 4 || int(TMath::Abs(gmpdg)/1000.) == 4 || int(std::abs(gmpdg)/100.) == 5 || int(std::abs(gmpdg)/1000.) == 5) { + nAntileptons++; + nPositrons++; + if (-1 < y && y < 1) nPositronsElectronsInAcceptance++; + if (track.getToBeDone()) { + nAntileptonsToBeDone++; + nPositronsToBeDone++; + } + } + } + } else if (pdg == 511 || pdg == 521 || pdg == 531 || pdg == 5122 || pdg == 5132 || pdg == 5232 || pdg == 5332) { + nOpenBeautyPos++; + } else if (pdg == -511 || pdg == -521 || pdg == -531 || pdg == -5122 || pdg == -5132 || pdg == -5232 || pdg == -5332) { + nOpenBeautyNeg++; + } + } + if (nOpenBeautyPos > 0 && nOpenBeautyNeg > 0) { + nSignalPairs++; + } + if (nPositronsElectronsInAcceptance > 1) { + nLeptonsInAcceptance++; + } + if (nElectrons > 0 && nPositrons > 0) { + nLeptonPairs++; + } + if (nElectronsToBeDone > 0 && nPositronsToBeDone > 0) nLeptonPairsToBeDone++; + } + std::cout << "#events: " << nEvents << "\n" + << "#leptons: " << nLeptons << "\n" + << "#antileptons: " << nAntileptons << "\n" + << "#leptons to be done: " << nLeptonsToBeDone << "\n" + << "#antileptons to be done: " << nAntileptonsToBeDone << "\n" + << "#Open-beauty hadron pairs: " << nSignalPairs << "\n" + << "#leptons in acceptance: " << nLeptonsInAcceptance << "\n" + << "#Electron-positron pairs: " << nLeptonPairs << "\n" + << "#Electron-positron pairs to be done: " << nLeptonPairsToBeDone << "\n"; + if (nLeptons == 0 && nAntileptons == 0) { + std::cerr << "Number of leptons, number of anti-leptons should all be greater than 1.\n"; + return 1; + } + if (nLeptonPairs != nLeptonPairsToBeDone) { + std::cerr << "The number of lepton pairs should be the same as the number of lepton pairs which should be transported.\n"; + return 1; + } + if (nLeptons != nLeptonsToBeDone) { + std::cerr << "The number of leptons should be the same as the number of leptons which should be transported.\n"; + return 1; + } + if (nLeptonsInAcceptance == (nEvents/ratioTrigger)) { + std::cerr << "The number of leptons in acceptance should be at least equaled to the number of events.\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGEM/ini/tests/GeneratorHFGapTriggered_Charm_Gap2_OO_electron.C b/MC/config/PWGEM/ini/tests/GeneratorHFGapTriggered_Charm_Gap2_OO_electron.C new file mode 100644 index 000000000..ed19c336e --- /dev/null +++ b/MC/config/PWGEM/ini/tests/GeneratorHFGapTriggered_Charm_Gap2_OO_electron.C @@ -0,0 +1,117 @@ +int External() +{ + + int checkPdgDecay = 11; + std::string path{"o2sim_Kine.root"}; + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + float ratioTrigger = 1./3.; // one event triggered out of 3 + auto tree = (TTree*)file.Get("o2sim"); + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int nLeptonsInAcceptance{}; + int nLeptons{}; + int nAntileptons{}; + int nLeptonsToBeDone{}; + int nAntileptonsToBeDone{}; + int nSignalPairs{}; + int nLeptonPairs{}; + int nLeptonPairsToBeDone{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + int nElectrons = 0; + int nPositrons = 0; + int nElectronsToBeDone = 0; + int nPositronsToBeDone = 0; + int nOpenCharmPos = 0; + int nOpenCharmNeg = 0; + int nPositronsElectronsInAcceptance = 0; + for (auto& track : *tracks) { + auto pdg = track.GetPdgCode(); + auto y = track.GetRapidity(); + if (pdg == checkPdgDecay) { + int igmother = track.getMotherTrackId(); + if (igmother > 0) { + auto gmTrack = (*tracks)[igmother]; + int gmpdg = gmTrack.GetPdgCode(); + if (int(std::abs(gmpdg)/100.) == 4 || int(std::abs(gmpdg)/1000.) == 4) { + nLeptons++; + nElectrons++; + if (-1 < y && y < 1) nPositronsElectronsInAcceptance++; + if (track.getToBeDone()) { + nLeptonsToBeDone++; + nElectronsToBeDone++; + } + } + } + } else if (pdg == -checkPdgDecay) { + int igmother = track.getMotherTrackId(); + if (igmother > 0) { + auto gmTrack = (*tracks)[igmother]; + int gmpdg = gmTrack.GetPdgCode(); + if (int(TMath::Abs(gmpdg)/100.) == 4 || int(TMath::Abs(gmpdg)/1000.) == 4) { + nAntileptons++; + nPositrons++; + if (-1 < y && y < 1) nPositronsElectronsInAcceptance++; + if (track.getToBeDone()) { + nAntileptonsToBeDone++; + nPositronsToBeDone++; + } + } + } + } else if (pdg == 411 || pdg == 421 || pdg == 431 || pdg == 4122 || pdg == 4132 || pdg == 4232 || pdg == 4332) { + nOpenCharmPos++; + } else if (pdg == -411 || pdg == -421 || pdg == -431 || pdg == -4122 || pdg == -4132 || pdg == -4232 || pdg == -4332) { + nOpenCharmNeg++; + } + } + if (nOpenCharmPos > 0 && nOpenCharmNeg > 0) { + nSignalPairs++; + } + if (nPositronsElectronsInAcceptance > 1) { + nLeptonsInAcceptance++; + } + if (nElectrons > 0 && nPositrons > 0) { + nLeptonPairs++; + } + if (nElectronsToBeDone > 0 && nPositronsToBeDone > 0) nLeptonPairsToBeDone++; + } + std::cout << "#events: " << nEvents << "\n" + << "#leptons: " << nLeptons << "\n" + << "#antileptons: " << nAntileptons << "\n" + << "#leptons to be done: " << nLeptonsToBeDone << "\n" + << "#antileptons to be done: " << nAntileptonsToBeDone << "\n" + << "#open-charm hadron pairs: " << nSignalPairs << "\n" + << "#leptons in acceptance: " << nLeptonsInAcceptance << "\n" + << "#Electron-positron pairs: " << nLeptonPairs << "\n" + << "#Electron-positron pairs to be done: " << nLeptonPairsToBeDone << "\n"; + if (nLeptons == 0 && nAntileptons == 0) { + std::cerr << "Number of leptons, number of anti-leptons should all be greater than 1.\n"; + return 1; + } + if (nLeptonPairs != nLeptonPairsToBeDone) { + std::cerr << "The number of electron-positron pairs should be the same as the number of electron-positron pairs which should be transported.\n"; + return 1; + } + if (nLeptons != nLeptonsToBeDone) { + std::cerr << "The number of leptons should be the same as the number of leptons which should be transported.\n"; + return 1; + } + if (nLeptonsInAcceptance == (nEvents/ratioTrigger)) { + std::cerr << "The number of leptons in acceptance should be at least equaled to the number of events.\n"; + return 1; + } + if (nLeptonPairs < nLeptonsInAcceptance) { + std::cerr << "The number of positron-electron pairs should be at least equaled to the number of leptons in acceptance.\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGEM/ini/tests/GeneratorHFGapTriggered_Charm_Gap2_OO_muon.C b/MC/config/PWGEM/ini/tests/GeneratorHFGapTriggered_Charm_Gap2_OO_muon.C new file mode 100644 index 000000000..0b837b413 --- /dev/null +++ b/MC/config/PWGEM/ini/tests/GeneratorHFGapTriggered_Charm_Gap2_OO_muon.C @@ -0,0 +1,116 @@ +int External() +{ + int checkPdgDecay = 13; + std::string path{"o2sim_Kine.root"}; + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + float ratioTrigger = 1./3.; // one event triggered out of 3 + auto tree = (TTree*)file.Get("o2sim"); + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int nLeptonsInAcceptance{}; + int nLeptons{}; + int nAntileptons{}; + int nLeptonsToBeDone{}; + int nAntileptonsToBeDone{}; + int nSignalPairs{}; + int nLeptonPairs{}; + int nLeptonPairsToBeDone{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + int nElectrons = 0; + int nPositrons = 0; + int nElectronsToBeDone = 0; + int nPositronsToBeDone = 0; + int nOpenCharmPos = 0; + int nOpenCharmNeg = 0; + int nPositronsElectronsInAcceptance = 0; + for (auto& track : *tracks) { + auto pdg = track.GetPdgCode(); + auto y = track.GetRapidity(); + if (pdg == checkPdgDecay) { + int igmother = track.getMotherTrackId(); + if (igmother > 0) { + auto gmTrack = (*tracks)[igmother]; + int gmpdg = gmTrack.GetPdgCode(); + if (int(std::abs(gmpdg)/100.) == 4 || int(std::abs(gmpdg)/1000.) == 4) { + nLeptons++; + nElectrons++; + if (-1 < y && y < 1) nPositronsElectronsInAcceptance++; + if (track.getToBeDone()) { + nLeptonsToBeDone++; + nElectronsToBeDone++; + } + } + } + } else if (pdg == -checkPdgDecay) { + int igmother = track.getMotherTrackId(); + if (igmother > 0) { + auto gmTrack = (*tracks)[igmother]; + int gmpdg = gmTrack.GetPdgCode(); + if (int(TMath::Abs(gmpdg)/100.) == 4 || int(TMath::Abs(gmpdg)/1000.) == 4) { + nAntileptons++; + nPositrons++; + if (-1 < y && y < 1) nPositronsElectronsInAcceptance++; + if (track.getToBeDone()) { + nAntileptonsToBeDone++; + nPositronsToBeDone++; + } + } + } + } else if (pdg == 411 || pdg == 421 || pdg == 431 || pdg == 4122 || pdg == 4132 || pdg == 4232 || pdg == 4332) { + nOpenCharmPos++; + } else if (pdg == -411 || pdg == -421 || pdg == -431 || pdg == -4122 || pdg == -4132 || pdg == -4232 || pdg == -4332) { + nOpenCharmNeg++; + } + } + if (nOpenCharmPos > 0 && nOpenCharmNeg > 0) { + nSignalPairs++; + } + if (nPositronsElectronsInAcceptance > 1) { + nLeptonsInAcceptance++; + } + if (nElectrons > 0 && nPositrons > 0) { + nLeptonPairs++; + } + if (nElectronsToBeDone > 0 && nPositronsToBeDone > 0) nLeptonPairsToBeDone++; + } + std::cout << "#events: " << nEvents << "\n" + << "#leptons: " << nLeptons << "\n" + << "#antileptons: " << nAntileptons << "\n" + << "#leptons to be done: " << nLeptonsToBeDone << "\n" + << "#antileptons to be done: " << nAntileptonsToBeDone << "\n" + << "#open-charm hadron pairs: " << nSignalPairs << "\n" + << "#leptons in acceptance: " << nLeptonsInAcceptance << "\n" + << "#Electron-positron pairs: " << nLeptonPairs << "\n" + << "#Electron-positron pairs to be done: " << nLeptonPairsToBeDone << "\n"; + if (nLeptons == 0 && nAntileptons == 0) { + std::cerr << "Number of leptons, number of anti-leptons should all be greater than 1.\n"; + return 1; + } + if (nLeptonPairs != nLeptonPairsToBeDone) { + std::cerr << "The number of electron-positron pairs should be the same as the number of electron-positron pairs which should be transported.\n"; + return 1; + } + if (nLeptons != nLeptonsToBeDone) { + std::cerr << "The number of leptons should be the same as the number of leptons which should be transported.\n"; + return 1; + } + if (nLeptonsInAcceptance == (nEvents/ratioTrigger)) { + std::cerr << "The number of leptons in acceptance should be at least equaled to the number of events.\n"; + return 1; + } + if (nLeptonPairs < nLeptonsInAcceptance) { + std::cerr << "The number of positron-electron pairs should be at least equaled to the number of leptons in acceptance.\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGEM/ini/tests/GeneratorHF_ccbarToDielectrons.C b/MC/config/PWGEM/ini/tests/GeneratorHFGapTriggered_Charm_Gap5_pp5360GeV.C similarity index 68% rename from MC/config/PWGEM/ini/tests/GeneratorHF_ccbarToDielectrons.C rename to MC/config/PWGEM/ini/tests/GeneratorHFGapTriggered_Charm_Gap5_pp5360GeV.C index efe6027a9..9081b8a81 100644 --- a/MC/config/PWGEM/ini/tests/GeneratorHF_ccbarToDielectrons.C +++ b/MC/config/PWGEM/ini/tests/GeneratorHFGapTriggered_Charm_Gap5_pp5360GeV.C @@ -9,6 +9,7 @@ int External() return 1; } + float ratioTrigger = 1./3.; // one event triggered out of 3 auto tree = (TTree*)file.Get("o2sim"); std::vector* tracks{}; tree->SetBranchAddress("MCTrack", &tracks); @@ -16,7 +17,6 @@ int External() int nLeptonsInAcceptance{}; int nLeptons{}; int nAntileptons{}; - int nAntileptonsInAcceptance{}; int nLeptonsToBeDone{}; int nAntileptonsToBeDone{}; int nSignalPairs{}; @@ -25,13 +25,14 @@ int External() auto nEvents = tree->GetEntries(); for (int i = 0; i < nEvents; i++) { - tree->GetEntry(i); + tree->GetEntry(i); int nElectrons = 0; int nPositrons = 0; int nElectronsToBeDone = 0; int nPositronsToBeDone = 0; int nOpenCharmPos = 0; int nOpenCharmNeg = 0; + int nPositronsElectronsInAcceptance = 0; for (auto& track : *tracks) { auto pdg = track.GetPdgCode(); auto y = track.GetRapidity(); @@ -41,9 +42,9 @@ int External() auto gmTrack = (*tracks)[igmother]; int gmpdg = gmTrack.GetPdgCode(); if (int(std::abs(gmpdg)/100.) == 4 || int(std::abs(gmpdg)/1000.) == 4) { - nLeptons++; + nLeptons++; nElectrons++; - if (-1 < y && y < 1) nLeptonsInAcceptance++; + if (-1 < y && y < 1) nPositronsElectronsInAcceptance++; if (track.getToBeDone()) { nLeptonsToBeDone++; nElectronsToBeDone++; @@ -56,9 +57,9 @@ int External() auto gmTrack = (*tracks)[igmother]; int gmpdg = gmTrack.GetPdgCode(); if (int(TMath::Abs(gmpdg)/100.) == 4 || int(TMath::Abs(gmpdg)/1000.) == 4) { - nAntileptons++; + nAntileptons++; nPositrons++; - if (-1 < y && y < 1) nAntileptonsInAcceptance++; + if (-1 < y && y < 1) nPositronsElectronsInAcceptance++; if (track.getToBeDone()) { nAntileptonsToBeDone++; nPositronsToBeDone++; @@ -71,47 +72,44 @@ int External() nOpenCharmNeg++; } } - if (nOpenCharmPos > 0 && nOpenCharmNeg > 0) nSignalPairs++; - if (nElectrons > 0 && nPositrons > 0) nLeptonPairs++; + if (nOpenCharmPos > 0 && nOpenCharmNeg > 0) { + nSignalPairs++; + } + if (nPositronsElectronsInAcceptance > 1) { + nLeptonsInAcceptance++; + } + if (nElectrons > 0 && nPositrons > 0) { + nLeptonPairs++; + } if (nElectronsToBeDone > 0 && nPositronsToBeDone > 0) nLeptonPairsToBeDone++; } std::cout << "#events: " << nEvents << "\n" << "#leptons: " << nLeptons << "\n" - << "#leptons in acceptance: " << nLeptonsInAcceptance << "\n" << "#antileptons: " << nAntileptons << "\n" - << "#antileptons in acceptance: " << nAntileptonsInAcceptance << "\n" << "#leptons to be done: " << nLeptonsToBeDone << "\n" << "#antileptons to be done: " << nAntileptonsToBeDone << "\n" - << "#signal pairs: " << nSignalPairs << "\n" - << "#lepton pairs: " << nLeptonPairs << "\n" - << "#lepton pairs to be done: " << nLeptonPairsToBeDone << "\n"; - int nTotalLeptonsInAcceptance = nLeptonsInAcceptance + nAntileptonsInAcceptance; + << "#open-charm hadron pairs: " << nSignalPairs << "\n" + << "#leptons in acceptance: " << nLeptonsInAcceptance << "\n" + << "#Electron-positron pairs: " << nLeptonPairs << "\n" + << "#Electron-positron pairs to be done: " << nLeptonPairsToBeDone << "\n"; if (nLeptons == 0 && nAntileptons == 0) { std::cerr << "Number of leptons, number of anti-leptons should all be greater than 1.\n"; return 1; } - if (nLeptonPairs < nSignalPairs) { - std::cerr << "Number of lepton pairs should be at least equaled to the number of open charm hadron pairs\n"; - return 1; - } if (nLeptonPairs != nLeptonPairsToBeDone) { - std::cerr << "The number of lepton pairs should be the same as the number of lepton pairs which should be transported.\n"; + std::cerr << "The number of electron-positron pairs should be the same as the number of electron-positron pairs which should be transported.\n"; return 1; } if (nLeptons != nLeptonsToBeDone) { std::cerr << "The number of leptons should be the same as the number of leptons which should be transported.\n"; return 1; } - if (nTotalLeptonsInAcceptance < nEvents) { + if (nLeptonsInAcceptance == (nEvents/ratioTrigger)) { std::cerr << "The number of leptons in acceptance should be at least equaled to the number of events.\n"; return 1; } - if (nLeptons != nLeptonsInAcceptance) { - std::cerr << "The number of leptons in acceptance should be the same as the number of leptons.\n"; - return 1; - } - if (nAntileptons != nAntileptonsInAcceptance) { - std::cerr << "The number of anti-leptons in acceptance should be the same as the number of anti-leptons.\n"; + if (nLeptonPairs < nLeptonsInAcceptance) { + std::cerr << "The number of positron-electron pairs should be at least equaled to the number of leptons in acceptance.\n"; return 1; } diff --git a/MC/config/PWGEM/ini/tests/GeneratorHFGapTriggered_Charm_Muon_pp13600GeV_Gap1.C b/MC/config/PWGEM/ini/tests/GeneratorHFGapTriggered_Charm_Muon_pp13600GeV_Gap1.C new file mode 100644 index 000000000..41ee27f54 --- /dev/null +++ b/MC/config/PWGEM/ini/tests/GeneratorHFGapTriggered_Charm_Muon_pp13600GeV_Gap1.C @@ -0,0 +1,117 @@ +int External() +{ + + int checkPdgDecay = -13; + std::string path{"o2sim_Kine.root"}; + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + float ratioTrigger = 1./1.; // one event triggered out of 1 + auto tree = (TTree*)file.Get("o2sim"); + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int nLeptonsInAcceptance{}; + int nLeptons{}; + int nAntileptons{}; + int nLeptonsToBeDone{}; + int nAntileptonsToBeDone{}; + int nSignalPairs{}; + int nLeptonPairs{}; + int nLeptonPairsToBeDone{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + int nElectrons = 0; + int nPositrons = 0; + int nElectronsToBeDone = 0; + int nPositronsToBeDone = 0; + int nOpenCharmPos = 0; + int nOpenCharmNeg = 0; + int nPositronsElectronsInAcceptance = 0; + for (auto& track : *tracks) { + auto pdg = track.GetPdgCode(); + auto y = track.GetRapidity(); + if (pdg == checkPdgDecay) { + int igmother = track.getMotherTrackId(); + if (igmother > 0) { + auto gmTrack = (*tracks)[igmother]; + int gmpdg = gmTrack.GetPdgCode(); + if (int(std::abs(gmpdg)/100.) == 4 || int(std::abs(gmpdg)/1000.) == 4) { + nLeptons++; + nElectrons++; + if (-1 < y && y < 1) nPositronsElectronsInAcceptance++; + if (track.getToBeDone()) { + nLeptonsToBeDone++; + nElectronsToBeDone++; + } + } + } + } else if (pdg == -checkPdgDecay) { + int igmother = track.getMotherTrackId(); + if (igmother > 0) { + auto gmTrack = (*tracks)[igmother]; + int gmpdg = gmTrack.GetPdgCode(); + if (int(TMath::Abs(gmpdg)/100.) == 4 || int(TMath::Abs(gmpdg)/1000.) == 4) { + nAntileptons++; + nPositrons++; + if (-1 < y && y < 1) nPositronsElectronsInAcceptance++; + if (track.getToBeDone()) { + nAntileptonsToBeDone++; + nPositronsToBeDone++; + } + } + } + } else if (pdg == 411 || pdg == 421 || pdg == 431 || pdg == 4122 || pdg == 4132 || pdg == 4232 || pdg == 4332) { + nOpenCharmPos++; + } else if (pdg == -411 || pdg == -421 || pdg == -431 || pdg == -4122 || pdg == -4132 || pdg == -4232 || pdg == -4332) { + nOpenCharmNeg++; + } + } + if (nOpenCharmPos > 0 && nOpenCharmNeg > 0) { + nSignalPairs++; + } + if (nPositronsElectronsInAcceptance > 1) { + nLeptonsInAcceptance++; + } + if (nElectrons > 0 && nPositrons > 0) { + nLeptonPairs++; + } + if (nElectronsToBeDone > 0 && nPositronsToBeDone > 0) nLeptonPairsToBeDone++; + } + std::cout << "#events: " << nEvents << "\n" + << "#leptons: " << nLeptons << "\n" + << "#antileptons: " << nAntileptons << "\n" + << "#leptons to be done: " << nLeptonsToBeDone << "\n" + << "#antileptons to be done: " << nAntileptonsToBeDone << "\n" + << "#open-charm hadron pairs: " << nSignalPairs << "\n" + << "#leptons in acceptance: " << nLeptonsInAcceptance << "\n" + << "#Electron-positron pairs: " << nLeptonPairs << "\n" + << "#Electron-positron pairs to be done: " << nLeptonPairsToBeDone << "\n"; + if (nLeptons == 0 && nAntileptons == 0) { + std::cerr << "Number of leptons, number of anti-leptons should all be greater than 1.\n"; + return 1; + } + if (nLeptonPairs != nLeptonPairsToBeDone) { + std::cerr << "The number of electron-positron pairs should be the same as the number of electron-positron pairs which should be transported.\n"; + return 1; + } + if (nLeptons != nLeptonsToBeDone) { + std::cerr << "The number of leptons should be the same as the number of leptons which should be transported.\n"; + return 1; + } + if (nLeptonsInAcceptance == (nEvents/ratioTrigger)) { + std::cerr << "The number of leptons in acceptance should be at least equaled to the number of events.\n"; + return 1; + } + if (nLeptonPairs < nLeptonsInAcceptance) { + std::cerr << "The number of positron-electron pairs should be at least equaled to the number of leptons in acceptance.\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGEM/ini/tests/GeneratorHF_BeautyNoForcedDecay_PbPb_electron.C b/MC/config/PWGEM/ini/tests/GeneratorHF_BeautyNoForcedDecay_PbPb_electron.C new file mode 100644 index 000000000..375e79b75 --- /dev/null +++ b/MC/config/PWGEM/ini/tests/GeneratorHF_BeautyNoForcedDecay_PbPb_electron.C @@ -0,0 +1,111 @@ +int External() +{ + int checkPdgDecay = 11; + std::string path{"o2sim_Kine.root"}; + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree*)file.Get("o2sim"); + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int nLeptonsInAcceptance{}; + int nLeptons{}; + int nAntileptons{}; + int nLeptonsToBeDone{}; + int nAntileptonsToBeDone{}; + int nSignalPairs{}; + int nLeptonPairs{}; + int nLeptonPairsToBeDone{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + int nElectrons = 0; + int nPositrons = 0; + int nElectronsToBeDone = 0; + int nPositronsToBeDone = 0; + int nOpenBeautyPos = 0; + int nOpenBeautyNeg = 0; + int nPositronsElectronsInAcceptance = 0; + for (auto& track : *tracks) { + auto pdg = track.GetPdgCode(); + auto y = track.GetRapidity(); + if (pdg == checkPdgDecay) { + int igmother = track.getMotherTrackId(); + if (igmother > 0) { + auto gmTrack = (*tracks)[igmother]; + int gmpdg = gmTrack.GetPdgCode(); + if (int(std::abs(gmpdg)/100.) == 5 || int(std::abs(gmpdg)/1000.) == 5 || int(std::abs(gmpdg)/100.) == 4 || int(std::abs(gmpdg)/1000.) == 4) { + nLeptons++; + nElectrons++; + if (-1 < y && y < 1) nPositronsElectronsInAcceptance++; + if (track.getToBeDone()) { + nLeptonsToBeDone++; + nElectronsToBeDone++; + } + } + } + } else if (pdg == -checkPdgDecay) { + int igmother = track.getMotherTrackId(); + if (igmother > 0) { + auto gmTrack = (*tracks)[igmother]; + int gmpdg = gmTrack.GetPdgCode(); + if (int(TMath::Abs(gmpdg)/100.) == 4 || int(TMath::Abs(gmpdg)/1000.) == 4 || int(std::abs(gmpdg)/100.) == 5 || int(std::abs(gmpdg)/1000.) == 5) { + nAntileptons++; + nPositrons++; + if (-1 < y && y < 1) nPositronsElectronsInAcceptance++; + if (track.getToBeDone()) { + nAntileptonsToBeDone++; + nPositronsToBeDone++; + } + } + } + } else if (pdg == 511 || pdg == 521 || pdg == 531 || pdg == 5122 || pdg == 5132 || pdg == 5232 || pdg == 5332) { + nOpenBeautyPos++; + } else if (pdg == -511 || pdg == -521 || pdg == -531 || pdg == -5122 || pdg == -5132 || pdg == -5232 || pdg == -5332) { + nOpenBeautyNeg++; + } + } + if (nOpenBeautyPos > 0 && nOpenBeautyNeg > 0) { + nSignalPairs++; + } + if (nPositronsElectronsInAcceptance > 1) { + nLeptonsInAcceptance++; + } + if (nElectrons > 0 && nPositrons > 0) { + nLeptonPairs++; + } + if (nElectronsToBeDone > 0 && nPositronsToBeDone > 0) nLeptonPairsToBeDone++; + } + std::cout << "#events: " << nEvents << "\n" + << "#leptons: " << nLeptons << "\n" + << "#antileptons: " << nAntileptons << "\n" + << "#leptons to be done: " << nLeptonsToBeDone << "\n" + << "#antileptons to be done: " << nAntileptonsToBeDone << "\n" + << "#Open-beauty hadron pairs: " << nSignalPairs << "\n" + << "#leptons in acceptance: " << nLeptonsInAcceptance << "\n" + << "#Electron-positron pairs: " << nLeptonPairs << "\n" + << "#Electron-positron pairs to be done: " << nLeptonPairsToBeDone << "\n"; + if (nLeptons == 0 && nAntileptons == 0) { + std::cerr << "Number of leptons, number of anti-leptons should all be greater than 1.\n"; + return 1; + } + if (nLeptonPairs != nLeptonPairsToBeDone) { + std::cerr << "The number of lepton pairs should be the same as the number of lepton pairs which should be transported.\n"; + return 1; + } + if (nLeptons != nLeptonsToBeDone) { + std::cerr << "The number of leptons should be the same as the number of leptons which should be transported.\n"; + return 1; + } + if (nLeptonsInAcceptance != nEvents) { + std::cerr << "The number of leptons in acceptance should be at least equaled to the number of events.\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGEM/ini/tests/GeneratorHF_Charm_PbPb_electron.C b/MC/config/PWGEM/ini/tests/GeneratorHF_Charm_PbPb_electron.C new file mode 100644 index 000000000..d55c16c11 --- /dev/null +++ b/MC/config/PWGEM/ini/tests/GeneratorHF_Charm_PbPb_electron.C @@ -0,0 +1,116 @@ +int External() +{ + + int checkPdgDecay = 11; + std::string path{"o2sim_Kine.root"}; + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree*)file.Get("o2sim"); + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int nLeptonsInAcceptance{}; + int nLeptons{}; + int nAntileptons{}; + int nLeptonsToBeDone{}; + int nAntileptonsToBeDone{}; + int nSignalPairs{}; + int nLeptonPairs{}; + int nLeptonPairsToBeDone{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + int nElectrons = 0; + int nPositrons = 0; + int nElectronsToBeDone = 0; + int nPositronsToBeDone = 0; + int nOpenCharmPos = 0; + int nOpenCharmNeg = 0; + int nPositronsElectronsInAcceptance = 0; + for (auto& track : *tracks) { + auto pdg = track.GetPdgCode(); + auto y = track.GetRapidity(); + if (pdg == checkPdgDecay) { + int igmother = track.getMotherTrackId(); + if (igmother > 0) { + auto gmTrack = (*tracks)[igmother]; + int gmpdg = gmTrack.GetPdgCode(); + if (int(std::abs(gmpdg)/100.) == 4 || int(std::abs(gmpdg)/1000.) == 4) { + nLeptons++; + nElectrons++; + if (-1 < y && y < 1) nPositronsElectronsInAcceptance++; + if (track.getToBeDone()) { + nLeptonsToBeDone++; + nElectronsToBeDone++; + } + } + } + } else if (pdg == -checkPdgDecay) { + int igmother = track.getMotherTrackId(); + if (igmother > 0) { + auto gmTrack = (*tracks)[igmother]; + int gmpdg = gmTrack.GetPdgCode(); + if (int(TMath::Abs(gmpdg)/100.) == 4 || int(TMath::Abs(gmpdg)/1000.) == 4) { + nAntileptons++; + nPositrons++; + if (-1 < y && y < 1) nPositronsElectronsInAcceptance++; + if (track.getToBeDone()) { + nAntileptonsToBeDone++; + nPositronsToBeDone++; + } + } + } + } else if (pdg == 411 || pdg == 421 || pdg == 431 || pdg == 4122 || pdg == 4132 || pdg == 4232 || pdg == 4332) { + nOpenCharmPos++; + } else if (pdg == -411 || pdg == -421 || pdg == -431 || pdg == -4122 || pdg == -4132 || pdg == -4232 || pdg == -4332) { + nOpenCharmNeg++; + } + } + if (nOpenCharmPos > 0 && nOpenCharmNeg > 0) { + nSignalPairs++; + } + if (nPositronsElectronsInAcceptance > 1) { + nLeptonsInAcceptance++; + } + if (nElectrons > 0 && nPositrons > 0) { + nLeptonPairs++; + } + if (nElectronsToBeDone > 0 && nPositronsToBeDone > 0) nLeptonPairsToBeDone++; + } + std::cout << "#events: " << nEvents << "\n" + << "#leptons: " << nLeptons << "\n" + << "#antileptons: " << nAntileptons << "\n" + << "#leptons to be done: " << nLeptonsToBeDone << "\n" + << "#antileptons to be done: " << nAntileptonsToBeDone << "\n" + << "#open-charm hadron pairs: " << nSignalPairs << "\n" + << "#leptons in acceptance: " << nLeptonsInAcceptance << "\n" + << "#Electron-positron pairs: " << nLeptonPairs << "\n" + << "#Electron-positron pairs to be done: " << nLeptonPairsToBeDone << "\n"; + if (nLeptons == 0 && nAntileptons == 0) { + std::cerr << "Number of leptons, number of anti-leptons should all be greater than 1.\n"; + return 1; + } + if (nLeptonPairs != nLeptonPairsToBeDone) { + std::cerr << "The number of electron-positron pairs should be the same as the number of electron-positron pairs which should be transported.\n"; + return 1; + } + if (nLeptons != nLeptonsToBeDone) { + std::cerr << "The number of leptons should be the same as the number of leptons which should be transported.\n"; + return 1; + } + if (nLeptonsInAcceptance != nEvents) { + std::cerr << "The number of leptons in acceptance should be at least equaled to the number of events.\n"; + return 1; + } + if (nLeptonPairs < nLeptonsInAcceptance) { + std::cerr << "The number of positron-electron pairs should be at least equaled to the number of leptons in acceptance.\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGEM/ini/tests/GeneratorHF_bbbarToDDbarToDielectrons.C b/MC/config/PWGEM/ini/tests/GeneratorHF_bbbarToDDbarToDielectrons.C deleted file mode 100644 index 21e68170b..000000000 --- a/MC/config/PWGEM/ini/tests/GeneratorHF_bbbarToDDbarToDielectrons.C +++ /dev/null @@ -1,90 +0,0 @@ -int External() -{ - - int checkPdgDecay = 11; - std::string path{"o2sim_Kine.root"}; - TFile file(path.c_str(), "READ"); - if (file.IsZombie()) { - std::cerr << "Cannot open ROOT file " << path << "\n"; - return 1; - } - auto tree = (TTree*)file.Get("o2sim"); - std::vector* tracks{}; - tree->SetBranchAddress("MCTrack", &tracks); - - int nLeptons{}; - int nLeptonsInAcceptance{}; - int nLeptonsToBeDone{}; - int nSignalPairs{}; - int nLeptonPairs{}; - int nLeptonPairsInAcceptance{}; - int nLeptonPairsToBeDone{}; - auto nEvents = tree->GetEntries(); - - for (int i = 0; i < nEvents; i++) { - tree->GetEntry(i); - int nleptonseinacc = 0; - int nleptonse = 0; - int nleptonseToBeDone = 0; - int nopenHeavy = 0; - for (auto& track : *tracks) { - auto pdg = track.GetPdgCode(); - auto y = track.GetRapidity(); - if (std::abs(pdg) == checkPdgDecay) { - int igmother = track.getMotherTrackId(); - if (igmother > 0) { - auto gmTrack = (*tracks)[igmother]; - int gmpdg = gmTrack.GetPdgCode(); - if ( int(std::abs(gmpdg)/100.) == 4 || int(std::abs(gmpdg)/1000.) == 4 || int(std::abs(gmpdg)/100.) == 5 || int(std::abs(gmpdg)/1000.) == 5 ) { - nLeptons++; - nleptonse++; - if (-1 < y && y < 1) { - nleptonseinacc++; - nLeptonsInAcceptance++; - } - if (track.getToBeDone()) { - nLeptonsToBeDone++; - nleptonseToBeDone++; - } - } - } - } else if (std::abs(pdg) == 411 || std::abs(pdg) == 421 || std::abs(pdg) == 431 || std::abs(pdg) == 4122 || std::abs(pdg) == 4132 || std::abs(pdg) == 4232 || std::abs(pdg) == 4332 || std::abs(pdg) == 511 || std::abs(pdg) == 521 || std::abs(pdg) == 531 || std::abs(pdg) == 541 || std::abs(pdg) == 5112 || std::abs(pdg) == 5122 || std::abs(pdg) == 5232 || std::abs(pdg) == 5132 || std::abs(pdg) == 5332) { - nopenHeavy++; - } - } - if (nopenHeavy > 1) nSignalPairs++; - if (nleptonse > 1) nLeptonPairs++; - if (nleptonseToBeDone > 1) nLeptonPairsToBeDone++; - if (nleptonseinacc > 1) nLeptonPairsInAcceptance++; - } - std::cout << "#events: " << nEvents << "\n" - << "#leptons in acceptance: " << nLeptonsInAcceptance << "\n" - << "#lepton pairs in acceptance: " << nLeptonPairsInAcceptance << "\n" - << "#leptons: " << nLeptons << "\n" - << "#leptons to be done: " << nLeptonsToBeDone << "\n" - << "#signal pairs: " << nSignalPairs << "\n" - << "#lepton pairs: " << nLeptonPairs << "\n" - << "#lepton pairs to be done: " << nLeptonPairsToBeDone << "\n"; - if (nLeptons == 0) { - std::cerr << "Number of leptons should be greater than 1.\n"; - return 1; - } - if (nLeptonPairs < nSignalPairs) { - std::cerr << "Number of lepton pairs should be at least equaled to the number of open charm hadron pairs\n"; - return 1; - } - if (nLeptonPairsInAcceptance < nEvents) { - std::cerr << "Number of lepton pairs should be at least equaled to the number of events\n"; - return 1; - } - if (nLeptonPairs != nLeptonPairsToBeDone) { - std::cerr << "The number of lepton pairs should be the same as the number of lepton pairs which should be transported.\n"; - return 1; - } - if (nLeptons != nLeptonsToBeDone) { - std::cerr << "The number of leptons should be the same as the number of leptons which should be transported.\n"; - return 1; - } - - return 0; -} diff --git a/MC/config/PWGEM/ini/tests/GeneratorHF_bbbarToDielectrons.C b/MC/config/PWGEM/ini/tests/GeneratorHF_bbbarToDielectrons.C deleted file mode 100644 index 9fd1cb188..000000000 --- a/MC/config/PWGEM/ini/tests/GeneratorHF_bbbarToDielectrons.C +++ /dev/null @@ -1,86 +0,0 @@ -int External() -{ - - int checkPdgDecay = 11; - std::string path{"o2sim_Kine.root"}; - TFile file(path.c_str(), "READ"); - if (file.IsZombie()) { - std::cerr << "Cannot open ROOT file " << path << "\n"; - return 1; - } - auto tree = (TTree*)file.Get("o2sim"); - std::vector* tracks{}; - tree->SetBranchAddress("MCTrack", &tracks); - - int nLeptonsInAcceptance{}; - int nLeptons{}; - int nLeptonsToBeDone{}; - int nSignalPairs{}; - int nLeptonPairs{}; - int nLeptonPairsToBeDone{}; - auto nEvents = tree->GetEntries(); - - for (int i = 0; i < nEvents; i++) { - tree->GetEntry(i); - int nleptonse = 0; - int nleptonseToBeDone = 0; - int nopenHeavy = 0; - for (auto& track : *tracks) { - auto pdg = track.GetPdgCode(); - auto y = track.GetRapidity(); - if (std::abs(pdg) == checkPdgDecay) { - int igmother = track.getMotherTrackId(); - if (igmother > 0) { - auto gmTrack = (*tracks)[igmother]; - int gmpdg = gmTrack.GetPdgCode(); - if (int(std::abs(gmpdg)/100.) == 4 || int(std::abs(gmpdg)/1000.) == 4 || int(std::abs(gmpdg)/100.) == 5 || int(std::abs(gmpdg)/1000.) == 5) { - nLeptons++; - nleptonse++; - if (-1 < y && y < 1) nLeptonsInAcceptance++; - if (track.getToBeDone()) { - nLeptonsToBeDone++; - nleptonseToBeDone++; - } - } - } - } else if (std::abs(pdg) == 411 || std::abs(pdg) == 421 || std::abs(pdg) == 431 || std::abs(pdg) == 4122 || std::abs(pdg) == 4132 || std::abs(pdg) == 4232 || std::abs(pdg) == 4332 || std::abs(pdg) == 511 || std::abs(pdg) == 521 || std::abs(pdg) == 531 || std::abs(pdg) == 541 || std::abs(pdg) == 5112 || std::abs(pdg) == 5122 || std::abs(pdg) == 5232 || std::abs(pdg) == 5132 || std::abs(pdg) == 5332) { - nopenHeavy++; - } - } - if (nopenHeavy > 1) nSignalPairs++; - if (nleptonse > 1) nLeptonPairs++; - if (nleptonseToBeDone > 1) nLeptonPairsToBeDone++; - } - std::cout << "#events: " << nEvents << "\n" - << "#leptons in acceptance: " << nLeptonsInAcceptance << "\n" - << "#leptons: " << nLeptons << "\n" - << "#leptons to be done: " << nLeptonsToBeDone << "\n" - << "#signal pairs: " << nSignalPairs << "\n" - << "#lepton pairs: " << nLeptonPairs << "\n" - << "#lepton pairs to be done: " << nLeptonPairsToBeDone << "\n"; - if (nLeptons == 0) { - std::cerr << "Number of leptons should be greater than 1.\n"; - return 1; - } - if (nLeptonPairs < nSignalPairs) { - std::cerr << "Number of lepton pairs should be at least equaled to the number of open charm hadron pairs\n"; - return 1; - } - if (nLeptonPairs != nLeptonPairsToBeDone) { - std::cerr << "The number of lepton pairs should be the same as the number of lepton pairs which should be transported.\n"; - return 1; - } - if (nLeptons != nLeptonsToBeDone) { - std::cerr << "The number of leptons should be the same as the number of leptons which should be transported.\n"; - return 1; - } - if (nLeptonsInAcceptance < nEvents) { - std::cerr << "The number of leptons in acceptance should be at least equaled to the number of events.\n"; - return 1; - } - if (nLeptonsInAcceptance != nLeptons) { - std::cerr << "The number of leptons in acceptance should be the same as the total number of leptons\n"; - return 1; - } - return 0; -} diff --git a/MC/config/PWGEM/ini/tests/Generator_GapTriggered_LFgamma.C b/MC/config/PWGEM/ini/tests/Generator_GapTriggered_LFgamma.C new file mode 100644 index 000000000..290e95f6b --- /dev/null +++ b/MC/config/PWGEM/ini/tests/Generator_GapTriggered_LFgamma.C @@ -0,0 +1,59 @@ +int External() +{ + + int checkPdgDecay = 22; + std::string path{"o2sim_Kine.root"}; + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree*)file.Get("o2sim"); + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int nMesons{}; + int nMesonsDiElectronDecay{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + for (auto& track : *tracks) { + auto pdg = track.GetPdgCode(); + auto y = track.GetRapidity(); + if ((pdg == 111) || (pdg == 221)) { + if ((y>-1.2) && (y<1.2)) { + nMesons++; + int counter_gamma = 0; + int k1 = track.getFirstDaughterTrackId(); + int k2 = track.getLastDaughterTrackId(); + // k1 < k2 and no -1 for k2 + for (int d=k1; d <= k2; d++) { + if (d>0) { + auto decay = (*tracks)[d]; + int pdgdecay = decay.GetPdgCode(); + if (pdgdecay == 22) { + counter_gamma++; + } + } + } + } + } + } + } + + std::cout << "#events: " << nEvents << "\n" + << "#mesons: " << nMesons << "\n"; + +// if (nMesonsDiElectronDecay < (2*nEvents)) { +// std::cerr << "One should have at least 2 mesons that decay into dielectrons per event.\n"; +// return 1; +// } +// if (nMesons < nEvents) { +// std::cerr << "One meson per event should be produced.\n"; +// return 1; +// } + + return 0; +} diff --git a/MC/config/PWGEM/ini/tests/Generator_GapTriggered_LFmumu.C b/MC/config/PWGEM/ini/tests/Generator_GapTriggered_LFmumu.C new file mode 100644 index 000000000..16f2d9118 --- /dev/null +++ b/MC/config/PWGEM/ini/tests/Generator_GapTriggered_LFmumu.C @@ -0,0 +1,62 @@ +int External() +{ + std::string path{"o2sim_Kine.root"}; + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree*)file.Get("o2sim"); + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int nMesons{}; + int nMesonsDiMuonDecay{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + for (auto& track : *tracks) { + auto pdg = track.GetPdgCode(); + auto y = track.GetRapidity(); + if ((pdg == 221) || (pdg == 331) || (pdg == 223) || (pdg == 113) || (pdg == 333)) { + if ((y>-4.3) && (y<-2.2)) { + nMesons++; + Int_t counterel = 0; + Int_t counterpos = 0; + int k1 = track.getFirstDaughterTrackId(); + int k2 = track.getLastDaughterTrackId(); + // k1 < k2 and no -1 for k2 + for (int d=k1; d <= k2; d++) { + if (d>0) { + auto decay = (*tracks)[d]; + int pdgdecay = decay.GetPdgCode(); + if (pdgdecay == 13) { + counterel++; + } + if (pdgdecay == -13) { + counterpos++; + } + } + } + if ((counterel>0) && (counterpos>0)) nMesonsDiMuonDecay++; + } + } + } + } + + std::cout << "#events: " << nEvents << "\n" + << "#mesons: " << nMesons << "\n"; + + if (nMesons < (nEvents/5)) { + std::cerr << "One should have at least one mesons in forward region per 5 events.\n"; + return 1; + } + //if (nMesonsDiMuonDecay < nEvents) { + //std::cerr << "One meson to dimuon decay per event should be produced.\n"; + //return 1; + //} + + return 0; +} diff --git a/MC/config/PWGEM/ini/tests/Pythia8_Beauty_Cocktail.C b/MC/config/PWGEM/ini/tests/Pythia8_Beauty_Cocktail.C new file mode 100644 index 000000000..ef3c3929e --- /dev/null +++ b/MC/config/PWGEM/ini/tests/Pythia8_Beauty_Cocktail.C @@ -0,0 +1,61 @@ +int External() +{ + + std::string path{"o2sim_Kine.root"}; + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree*)file.Get("o2sim"); + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + auto nEvents = tree->GetEntries(); + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + bool found_b = false; + bool found_bbar = false; + for (auto& track : *tracks) { + int pdg = track.GetPdgCode(); + if (pdg == 5){ + found_b = true; + } + if (pdg == -5){ + found_bbar = true; + } + if ( abs(pdg) == 411 || abs(pdg) == 421 || abs(pdg) == 431 || abs(pdg) == 4122 || abs(pdg) == 4232 || abs(pdg) == 4132 || abs(pdg) == 4332){ + int ifirstdaughter = track.getFirstDaughterTrackId(); + int ilastdaughter = track.getLastDaughterTrackId(); + if (ifirstdaughter == -1 || ilastdaughter == -1){ + std::cerr << "Found charm hadron that does not have daughters" << "\n"; + return 1; + } + bool found_electron = false; + for (int j = ifirstdaughter; j<= ilastdaughter; j++){ + auto track2 = (*tracks)[j]; + if ( abs(track2.GetPdgCode())==11){ + found_electron = true; + if (!(track2.getWeight() < 0.999)){ + std::cerr << "Found electron from forced decay with weight 1" << "\n"; + return 1; + } + if (!track2.getToBeDone()){ + std::cerr << "Found electron from forced decay that is not transported" << "\n"; + return 1; + } + } + } + if (!found_electron){ + std::cerr << "Found charm hadron that does not decay to electron" << "\n"; + return 1; + } + } + } + if ((!found_b) || (!found_bbar)){ + std::cerr << "Found event without b-bbar pair" << "\n"; + return 1; + } + } + return 0; +} diff --git a/MC/config/PWGEM/ini/tests/Pythia8_Charm_Cocktail.C b/MC/config/PWGEM/ini/tests/Pythia8_Charm_Cocktail.C new file mode 100644 index 000000000..1d4b785e3 --- /dev/null +++ b/MC/config/PWGEM/ini/tests/Pythia8_Charm_Cocktail.C @@ -0,0 +1,61 @@ +int External() +{ + + std::string path{"o2sim_Kine.root"}; + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree*)file.Get("o2sim"); + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + auto nEvents = tree->GetEntries(); + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + bool found_c = false; + bool found_cbar = false; + for (auto& track : *tracks) { + int pdg = track.GetPdgCode(); + if (pdg == 4){ + found_c = true; + } + if (pdg == -4){ + found_cbar = true; + } + if ( abs(pdg) == 411 || abs(pdg) == 421 || abs(pdg) == 431 || abs(pdg) == 4122 || abs(pdg) == 4232 || abs(pdg) == 4132 || abs(pdg) == 4332){ + int ifirstdaughter = track.getFirstDaughterTrackId(); + int ilastdaughter = track.getLastDaughterTrackId(); + if (ifirstdaughter == -1 || ilastdaughter == -1){ + std::cerr << "Found charm hadron that does not have daughters" << "\n"; + return 1; + } + bool found_electron = false; + for (int j = ifirstdaughter; j<= ilastdaughter; j++){ + auto track2 = (*tracks)[j]; + if ( abs(track2.GetPdgCode())==11){ + found_electron = true; + if (!(track2.getWeight() < 0.999)){ + std::cerr << "Found electron from forced decay with weight 1" << "\n"; + return 1; + } + if (!track2.getToBeDone()){ + std::cerr << "Found electron from forced decay that is not transported" << "\n"; + return 1; + } + } + } + if (!found_electron){ + std::cerr << "Found charm hadron that does not decay to electron" << "\n"; + return 1; + } + } + } + if ((!found_c) || (!found_cbar)){ + std::cerr << "Found event without c-cbar pair" << "\n"; + return 1; + } + } + return 0; +} diff --git a/MC/config/PWGEM/ini/tests/pythia8_OO_536_VM2ll.C b/MC/config/PWGEM/ini/tests/pythia8_OO_536_VM2ll.C new file mode 100644 index 000000000..ac9df9f68 --- /dev/null +++ b/MC/config/PWGEM/ini/tests/pythia8_OO_536_VM2ll.C @@ -0,0 +1,56 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + // Check that file exists, can be opened and has the correct tree + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) + { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) + { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + // Check if all events are filled + auto nEvents = tree->GetEntries(); + for (Long64_t i = 0; i < nEvents; ++i) + { + tree->GetEntry(i); + if (tracks->empty()) + { + std::cerr << "Empty entry found at event " << i << "\n"; + return 1; + } + } + // check if each event has at least two oxygen ions + for (int i = 0; i < nEvents; i++) + { + auto check = tree->GetEntry(i); + int count = 0; + for (int idxMCTrack = 0; idxMCTrack < tracks->size(); ++idxMCTrack) + { + auto track = tracks->at(idxMCTrack); + if (track.GetPdgCode() == 1000080160) + { + count++; + } + } + if (count < 2) + { + std::cerr << "Event " << i << " has less than 2 oxygen ions\n"; + return 1; + } + } + + return 0; +} + +int pythia8() +{ + return External(); +} diff --git a/MC/config/PWGEM/ini/tests/pythia8_PbPb_5360_VM2ll.C b/MC/config/PWGEM/ini/tests/pythia8_PbPb_5360_VM2ll.C new file mode 100644 index 000000000..b00728e80 --- /dev/null +++ b/MC/config/PWGEM/ini/tests/pythia8_PbPb_5360_VM2ll.C @@ -0,0 +1,56 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + // Check that file exists, can be opened and has the correct tree + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) + { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) + { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + // Check if all events are filled + auto nEvents = tree->GetEntries(); + for (Long64_t i = 0; i < nEvents; ++i) + { + tree->GetEntry(i); + if (tracks->empty()) + { + std::cerr << "Empty entry found at event " << i << "\n"; + return 1; + } + } + // check if each event has at least two oxygen ions + for (int i = 0; i < nEvents; i++) + { + auto check = tree->GetEntry(i); + int count = 0; + for (int idxMCTrack = 0; idxMCTrack < tracks->size(); ++idxMCTrack) + { + auto track = tracks->at(idxMCTrack); + if (track.GetPdgCode() == 1000822080) + { + count++; + } + } + if (count < 2) + { + std::cerr << "Event " << i << " has less than 2 lead ions\n"; + return 1; + } + } + + return 0; +} + +int pythia8() +{ + return External(); +} diff --git a/MC/config/PWGEM/ini/tests/pythia8_pp_13600_ForcedDalitz.C b/MC/config/PWGEM/ini/tests/pythia8_pp_13600_ForcedDalitz.C new file mode 100644 index 000000000..4829e48d5 --- /dev/null +++ b/MC/config/PWGEM/ini/tests/pythia8_pp_13600_ForcedDalitz.C @@ -0,0 +1,39 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + // Check that file exists, can be opened and has the correct tree + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) + { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) + { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + // Check if all events are filled + auto nEvents = tree->GetEntries(); + for (Long64_t i = 0; i < nEvents; ++i) + { + tree->GetEntry(i); + if (tracks->empty()) + { + std::cerr << "Empty entry found at event " << i << "\n"; + return 1; + } + } + + + return 0; + } + + int pythia8() + { + return External(); + } + \ No newline at end of file diff --git a/MC/config/PWGEM/ini/tests/pythia8_pp_13600_VM2ll.C b/MC/config/PWGEM/ini/tests/pythia8_pp_13600_VM2ll.C new file mode 100644 index 000000000..aaee11ffc --- /dev/null +++ b/MC/config/PWGEM/ini/tests/pythia8_pp_13600_VM2ll.C @@ -0,0 +1,56 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + // Check that file exists, can be opened and has the correct tree + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) + { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) + { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + // Check if all events are filled + auto nEvents = tree->GetEntries(); + for (Long64_t i = 0; i < nEvents; ++i) + { + tree->GetEntry(i); + if (tracks->empty()) + { + std::cerr << "Empty entry found at event " << i << "\n"; + return 1; + } + } + // // check if each event has at least two oxygen ions + // for (int i = 0; i < nEvents; i++) + // { + // auto check = tree->GetEntry(i); + // int count = 0; + // for (int idxMCTrack = 0; idxMCTrack < tracks->size(); ++idxMCTrack) + // { + // auto track = tracks->at(idxMCTrack); + // if (track.GetPdgCode() == 1000080160) + // { + // count++; + // } + // } + // if (count < 2) + // { + // std::cerr << "Event " << i << " has less than 2 oxygen ions\n"; + // return 1; + // } + // } + + return 0; +} + +int pythia8() +{ + return External(); +} diff --git a/MC/config/PWGEM/ini/tests/pythia8_pp_5360_VM2ee.C b/MC/config/PWGEM/ini/tests/pythia8_pp_5360_VM2ee.C new file mode 100644 index 000000000..aaee11ffc --- /dev/null +++ b/MC/config/PWGEM/ini/tests/pythia8_pp_5360_VM2ee.C @@ -0,0 +1,56 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + // Check that file exists, can be opened and has the correct tree + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) + { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) + { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + // Check if all events are filled + auto nEvents = tree->GetEntries(); + for (Long64_t i = 0; i < nEvents; ++i) + { + tree->GetEntry(i); + if (tracks->empty()) + { + std::cerr << "Empty entry found at event " << i << "\n"; + return 1; + } + } + // // check if each event has at least two oxygen ions + // for (int i = 0; i < nEvents; i++) + // { + // auto check = tree->GetEntry(i); + // int count = 0; + // for (int idxMCTrack = 0; idxMCTrack < tracks->size(); ++idxMCTrack) + // { + // auto track = tracks->at(idxMCTrack); + // if (track.GetPdgCode() == 1000080160) + // { + // count++; + // } + // } + // if (count < 2) + // { + // std::cerr << "Event " << i << " has less than 2 oxygen ions\n"; + // return 1; + // } + // } + + return 0; +} + +int pythia8() +{ + return External(); +} diff --git a/MC/config/PWGEM/ini/tests/pythia8_pp_5360_VM2ll.C b/MC/config/PWGEM/ini/tests/pythia8_pp_5360_VM2ll.C new file mode 100644 index 000000000..aaee11ffc --- /dev/null +++ b/MC/config/PWGEM/ini/tests/pythia8_pp_5360_VM2ll.C @@ -0,0 +1,56 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + // Check that file exists, can be opened and has the correct tree + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) + { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) + { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + // Check if all events are filled + auto nEvents = tree->GetEntries(); + for (Long64_t i = 0; i < nEvents; ++i) + { + tree->GetEntry(i); + if (tracks->empty()) + { + std::cerr << "Empty entry found at event " << i << "\n"; + return 1; + } + } + // // check if each event has at least two oxygen ions + // for (int i = 0; i < nEvents; i++) + // { + // auto check = tree->GetEntry(i); + // int count = 0; + // for (int idxMCTrack = 0; idxMCTrack < tracks->size(); ++idxMCTrack) + // { + // auto track = tracks->at(idxMCTrack); + // if (track.GetPdgCode() == 1000080160) + // { + // count++; + // } + // } + // if (count < 2) + // { + // std::cerr << "Event " << i << " has less than 2 oxygen ions\n"; + // return 1; + // } + // } + + return 0; +} + +int pythia8() +{ + return External(); +} diff --git a/MC/config/PWGEM/parametrizations/OO_536TeV_param.json b/MC/config/PWGEM/parametrizations/OO_536TeV_param.json new file mode 100644 index 000000000..bad021d49 --- /dev/null +++ b/MC/config/PWGEM/parametrizations/OO_536TeV_param.json @@ -0,0 +1,10 @@ +{ + "536TeV_MB_pythia": { + "111_pt": "56.6336*x*pow(1+(sqrt(x*x+0.138918*0.138918)-0.138918)/(6.8304*0.139501),-6.8304)", + "221_pt": "1939.44*x*pow(1+(sqrt(x*x+-0.424653*-0.424653)--0.424653)/(6.84504*0.0745758),-6.84504)", + "331_pt": "2.89402*x*pow(1+(sqrt(x*x+-0.322689*-0.322689)--0.322689)/(8.32826*0.211736),-8.32826)", + "113_pt": "75.4035*x*pow(1+(sqrt(x*x+-0.360389*-0.360389)--0.360389)/(7.74615*0.16301),-7.74615)", + "223_pt": "2.29*x*pow(1+(sqrt(x*x+0.405007*0.405007)-0.405007)/(7.61366*0.243978),-7.61366)", + "333_pt": "0.646087*x*pow(1+(sqrt(x*x+-0.257169*-0.257169)--0.257169)/(8.17199*0.261382),-8.17199)" + } +} \ No newline at end of file diff --git a/MC/config/PWGEM/parametrizations/PbPb5TeV_central.json b/MC/config/PWGEM/parametrizations/PbPb5TeV_central.json index 85e0fec17..546802e7f 100644 --- a/MC/config/PWGEM/parametrizations/PbPb5TeV_central.json +++ b/MC/config/PWGEM/parametrizations/PbPb5TeV_central.json @@ -1,253 +1,333 @@ { - "5TeV_0005_wRatio": { + "5TeV_0005_wRatio_etatest": { "histoMtScaleFactor": { "113": 0.85, "223": 0.7, "331": 0.4 }, "111_pt": "TMath::TwoPi()*x*(219.382*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.384303)+2471.6*pow(exp(-1.12373*x-0.752507*x*x)+x/0.438343,-5.45943))", - "221_pt": "(TMath::TwoPi()*x*(219.382*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.384303)+2471.6*pow(exp(-1.12373*x-0.752507*x*x)+x/0.438343,-5.45943)))*(((x<=4.303300)*(0.385689/(1+exp(-(x-0.442495)/0.303977))+-0.324645*TMath::Gaus(x,0.364119,1.01338)+-0.268261*TMath::Gaus(x,8.32791,4.74697)+0.318614)+(x>4.303300)*(0.0836242/(1+exp(-(x-6.88914)/2.15587))+0.757533*TMath::Gaus(x,1.19329,1.35263)+40382.9*TMath::Gaus(x,-92.3117,-16.5516)+0.442)+((0.550454*exp((((0.995785*x)-sqrt((x*x)+(0.5478530000*0.5478530000)))/sqrt(1-(0.995785*0.995785)))/1.71099))+(0.0106936*(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/(exp((((0.995785*x)-sqrt((x*x)+(0.1349770000*0.1349770000)))/sqrt(1-(0.995785*0.995785)))/1.71099)+(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/2.)", + "221_pt": "(TMath::TwoPi()*x*(219.382*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.384303)+2471.6*pow(exp(-1.12373*x-0.752507*x*x)+x/0.438343,-5.45943)))*((9.36242e-05*TMath::Power((exp(- -2.56458*sqrt(x*x+0.547*0.547-0.139*0.139)-0.742625*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.457041/9.36242e-05,-1./8.15756)*sqrt(x*x+0.547*0.547-0.139*0.139)/0.0235913),-8.15756)/TMath::Power((exp(- -2.56458*x-0.742625*x*x)+x/0.0235913),-8.15756)+1.50683*TMath::Landau(x,2.1159,0.843387)))", "333_pt": "(TMath::TwoPi()*x*(219.382*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.384303)+2471.6*pow(exp(-1.12373*x-0.752507*x*x)+x/0.438343,-5.45943)))*(0.1191/(1+exp(-(x- -0.316437)/0.554431))+-0.507618*TMath::Gaus(x,1.69654,1.62649)+0.494136*TMath::Gaus(x,2.56805,1.6193)+0.0789659)" }, - "5TeV_0510_wRatio": { + "5TeV_0005_wRatio_etatest_ratiosup": { "histoMtScaleFactor": { - "113": 0.85, - "223": 0.7, - "331": 0.4 + "113": 1.02, + "223": 0.84, + "331": 0.48 }, - "111_pt": "TMath::TwoPi()*x*(156.897*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.390093)+1835.17*pow(exp(-1.1618*x-0.514011*x*x)+x/0.465575,-5.50332))", - "221_pt": "(TMath::TwoPi()*x*(156.897*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.390093)+1835.17*pow(exp(-1.1618*x-0.514011*x*x)+x/0.465575,-5.50332)))*(((x<=4.125494)*(0.462257/(1+exp(-(x-0.489168)/0.259816))+-0.338875*TMath::Gaus(x,0.655039,0.915494)+-0.277601*TMath::Gaus(x,5.26982,2.67864)+0.291403)+(x>4.125494)*(0.197492/(1+exp(-(x-5.11825)/1.30203))+-596719*TMath::Gaus(x,0.710032,0.569486)+1.82757*TMath::Gaus(x,-2.55348,2.83425)+0.33265)+((0.550454*exp((((0.995785*x)-sqrt((x*x)+(0.5478530000*0.5478530000)))/sqrt(1-(0.995785*0.995785)))/1.71099))+(0.0106936*(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/(exp((((0.995785*x)-sqrt((x*x)+(0.1349770000*0.1349770000)))/sqrt(1-(0.995785*0.995785)))/1.71099)+(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/2.)", - "333_pt": "(TMath::TwoPi()*x*(156.897*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.390093)+1835.17*pow(exp(-1.1618*x-0.514011*x*x)+x/0.465575,-5.50332)))*(0.1191/(1+exp(-(x- -0.316437)/0.554431))+-0.507618*TMath::Gaus(x,1.69654,1.62649)+0.494136*TMath::Gaus(x,2.56805,1.6193)+0.0789659)" + "111_pt": "TMath::TwoPi()*x*(219.382*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.384303)+2471.6*pow(exp(-1.12373*x-0.752507*x*x)+x/0.438343,-5.45943))", + "221_pt": "(TMath::TwoPi()*x*(219.382*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.384303)+2471.6*pow(exp(-1.12373*x-0.752507*x*x)+x/0.438343,-5.45943)))*((((x<9.168)*(TMath::Max(TMath::Max(0.00147786*TMath::Power((exp(- -0.297384*sqrt(x*x+0.547*0.547-0.139*0.139)-0.0282388*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.464407/0.00147786,-1./1.14468)*sqrt(x*x+0.547*0.547-0.139*0.139)/0.0102013),-1.14468)/TMath::Power((exp(- -0.297384*x-0.0282388*x*x)+x/0.0102013),-1.14468)+2.16163*TMath::Landau(x,3.97083,1.44648),0.00489879*TMath::Power((exp(- -2.36164*sqrt(x*x+0.547*0.547-0.139*0.139)-0.72334*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.478277/0.00489879,-1./1.05253)*sqrt(x*x+0.547*0.547-0.139*0.139)/0.0139668),-1.05253)/TMath::Power((exp(- -2.36164*x-0.72334*x*x)+x/0.0139668),-1.05253)+1.73053*TMath::Landau(x,2.39399,0.879267)),TMath::Max(0.000175259*TMath::Power((exp(- -2.1325*sqrt(x*x+0.547*0.547-0.139*0.139)-0.591882*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.482288/0.000175259,-1./15.7215)*sqrt(x*x+0.547*0.547-0.139*0.139)/0.027171),-15.7215)/TMath::Power((exp(- -2.1325*x-0.591882*x*x)+x/0.027171),-15.7215)+1.7109*TMath::Landau(x,2.36532,0.973179),0.00489879*TMath::Power((exp(- -2.36164*sqrt(x*x+0.547*0.547-0.139*0.139)-0.72334*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.478277/0.00489879,-1./1.05253)*sqrt(x*x+0.547*0.547-0.139*0.139)/0.0139668),-1.05253)/TMath::Power((exp(- -2.36164*x-0.72334*x*x)+x/0.0139668),-1.05253)+1.73053*TMath::Landau(x,2.39399,0.879267))))+(x>=9.168)*1.08*(((0.499605*1.)+(155.836*(-0.00300369*TMath::Power(1.+((x/1.09373)*(x/1.09373)),-(0.672088)))))/(1.+(-0.00300369*TMath::Power(1.+((x/1.09373)*(x/1.09373)),-(0.672088))))))))", + "333_pt": "(TMath::TwoPi()*x*(219.382*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.384303)+2471.6*pow(exp(-1.12373*x-0.752507*x*x)+x/0.438343,-5.45943)))*((x<=0.5)*(-0.3/0.5*x+1.5)*(0.1191/(1+exp(-(x- -0.316437)/0.554431))+-0.507618*TMath::Gaus(x,1.69654,1.62649)+0.494136*TMath::Gaus(x,2.56805,1.6193)+0.0789659)+(x>0.5)*1.2*(0.1191/(1+exp(-(x- -0.316437)/0.554431))+-0.507618*TMath::Gaus(x,1.69654,1.62649)+0.494136*TMath::Gaus(x,2.56805,1.6193)+0.0789659))" }, - "5TeV_3040_wRatio": { + "5TeV_0005_wRatio_etatest_ratiosdown": { "histoMtScaleFactor": { - "113": 0.85, - "223": 0.7, - "331": 0.4 + "113": 0.68, + "223": 0.56, + "331": 0.32 }, - "111_pt": "TMath::TwoPi()*x*(24.3786*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.412015)+908.266*pow(exp(-0.735883*x-0.425837*x*x)+x/0.505017,-5.68841))", - "221_pt": "(TMath::TwoPi()*x*(24.3786*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.412015)+908.266*pow(exp(-0.735883*x-0.425837*x*x)+x/0.505017,-5.68841)))*((0.317347/(1+exp(-(x-0.475129)/0.245111))+-0.265641*TMath::Gaus(x,0.276828,1.17016)+-0.0289536*TMath::Gaus(x,5.40691,2.58911)+0.249861+((0.550454*exp((((0.995785*x)-sqrt((x*x)+(0.5478530000*0.5478530000)))/sqrt(1-(0.995785*0.995785)))/1.71099))+(0.0106936*(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/(exp((((0.995785*x)-sqrt((x*x)+(0.1349770000*0.1349770000)))/sqrt(1-(0.995785*0.995785)))/1.71099)+(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/2.)", - "333_pt": "(TMath::TwoPi()*x*(24.3786*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.412015)+908.266*pow(exp(-0.735883*x-0.425837*x*x)+x/0.505017,-5.68841)))*(0.106572/(1+exp(-(x-1.71328)/1.68645))+-0.54012*TMath::Gaus(x,0.754082,1.88026)+0.452996*TMath::Gaus(x,1.49283,2.4567)+0.0943071)" + "111_pt": "TMath::TwoPi()*x*(219.382*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.384303)+2471.6*pow(exp(-1.12373*x-0.752507*x*x)+x/0.438343,-5.45943))", + "221_pt": "(TMath::TwoPi()*x*(219.382*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.384303)+2471.6*pow(exp(-1.12373*x-0.752507*x*x)+x/0.438343,-5.45943)))*((((x<11.6581)*(TMath::Min(TMath::Min(0.00302888*TMath::Power((exp(-0.246982*sqrt(x*x+0.547*0.547-0.139*0.139)-0.204843*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.485848/0.00302888,-1./335.069)*sqrt(x*x+0.547*0.547-0.139*0.139)/2.46929),-335.069)/TMath::Power((exp(-0.246982*x-0.204843*x*x)+x/2.46929),-335.069)+2.62546*TMath::Landau(x,3.61147,1.38592),0.926*(9.36242e-05*TMath::Power((exp(- -2.56458*sqrt(x*x+0.547*0.547-0.139*0.139)-0.742625*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.457041/9.36242e-05,-1./8.15756)*sqrt(x*x+0.547*0.547-0.139*0.139)/0.0235913),-8.15756)/TMath::Power((exp(- -2.56458*x-0.742625*x*x)+x/0.0235913),-8.15756)+1.50683*TMath::Landau(x,2.1159,0.843387))),TMath::Min(0.00624935*TMath::Power((exp(- -2.56471*sqrt(x*x+0.547*0.547-0.139*0.139)-2.49586*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.45/0.00624935,-1./44.8766)*sqrt(x*x+0.547*0.547-0.139*0.139)/0.202909),-44.8766)/TMath::Power((exp(- -2.56471*x-2.49586*x*x)+x/0.202909),-44.8766)+1.58602*TMath::Landau(x,2.23166,0.861224),0.926*(9.36242e-05*TMath::Power((exp(- -2.56458*sqrt(x*x+0.547*0.547-0.139*0.139)-0.742625*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.457041/9.36242e-05,-1./8.15756)*sqrt(x*x+0.547*0.547-0.139*0.139)/0.0235913),-8.15756)/TMath::Power((exp(- -2.56458*x-0.742625*x*x)+x/0.0235913),-8.15756)+1.50683*TMath::Landau(x,2.1159,0.843387)))))+(x>=11.6581)*0.426)))", + "333_pt": "(TMath::TwoPi()*x*(219.382*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.384303)+2471.6*pow(exp(-1.12373*x-0.752507*x*x)+x/0.438343,-5.45943)))*((x<=0.5)*(0.3/0.5*x+0.5)*(0.1191/(1+exp(-(x- -0.316437)/0.554431))+-0.507618*TMath::Gaus(x,1.69654,1.62649)+0.494136*TMath::Gaus(x,2.56805,1.6193)+0.0789659)+(x>0.5)*0.8*(0.1191/(1+exp(-(x- -0.316437)/0.554431))+-0.507618*TMath::Gaus(x,1.69654,1.62649)+0.494136*TMath::Gaus(x,2.56805,1.6193)+0.0789659))" }, - "5TeV_4050_wRatio": { + "5TeV_0005_wRatio_etatest_pi0up": { "histoMtScaleFactor": { "113": 0.85, "223": 0.7, "331": 0.4 }, - "111_pt": "TMath::TwoPi()*x*(9.00063*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.428379)+636.492*pow(exp(-0.659016*x-0.397367*x*x)+x/0.512428,-5.72313))", - "221_pt": "(TMath::TwoPi()*x*(9.00063*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.428379)+636.492*pow(exp(-0.659016*x-0.397367*x*x)+x/0.512428,-5.72313)))*((0.298649/(1+exp(-(x-0.494749)/0.23628))+-0.255064*TMath::Gaus(x,0.170043,1.31558)+-0.024999*TMath::Gaus(x,8.22033,2.71373)+0.251236+((0.550454*exp((((0.995785*x)-sqrt((x*x)+(0.5478530000*0.5478530000)))/sqrt(1-(0.995785*0.995785)))/1.71099))+(0.0106936*(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/(exp((((0.995785*x)-sqrt((x*x)+(0.1349770000*0.1349770000)))/sqrt(1-(0.995785*0.995785)))/1.71099)+(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/2.)", - "333_pt": "(TMath::TwoPi()*x*(9.00063*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.428379)+636.492*pow(exp(-0.659016*x-0.397367*x*x)+x/0.512428,-5.72313)))*(0.0979137/(1+exp(-(x-1.53259)/0.370136))+-0.537275*TMath::Gaus(x,0.706186,2.03543)+0.45201*TMath::Gaus(x,1.18876,2.40911)+0.104985)" + "111_pt": "TMath::TwoPi()*x*(236.555*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.382365)+2787.27*pow(exp(-1.05343*x-0.780376*x*x)+x/0.43017,-5.45613))", + "221_pt": "(TMath::TwoPi()*x*(236.555*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.382365)+2787.27*pow(exp(-1.05343*x-0.780376*x*x)+x/0.43017,-5.45613)))*((9.36242e-05*TMath::Power((exp(- -2.56458*sqrt(x*x+0.547*0.547-0.139*0.139)-0.742625*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.457041/9.36242e-05,-1./8.15756)*sqrt(x*x+0.547*0.547-0.139*0.139)/0.0235913),-8.15756)/TMath::Power((exp(- -2.56458*x-0.742625*x*x)+x/0.0235913),-8.15756)+1.50683*TMath::Landau(x,2.1159,0.843387)))", + "333_pt": "(TMath::TwoPi()*x*(219.382*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.384303)+2471.6*pow(exp(-1.12373*x-0.752507*x*x)+x/0.438343,-5.45943)))*(0.1191/(1+exp(-(x- -0.316437)/0.554431))+-0.507618*TMath::Gaus(x,1.69654,1.62649)+0.494136*TMath::Gaus(x,2.56805,1.6193)+0.0789659)" }, - "5TeV_4050_wRatio_pi0up": { + "5TeV_0005_wRatio_etatest_pi0down": { "histoMtScaleFactor": { "113": 0.85, "223": 0.7, "331": 0.4 }, - "111_pt": "TMath::TwoPi()*x*(10.1537*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.423934)+708.793*pow(exp(-0.595237*x-0.468542*x*x)+x/0.502946,-5.71377))", - "221_pt": "(TMath::TwoPi()*x*(10.1537*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.423934)+708.793*pow(exp(-0.595237*x-0.468542*x*x)+x/0.502946,-5.71377)))*((0.298649/(1+exp(-(x-0.494749)/0.23628))+-0.255064*TMath::Gaus(x,0.170043,1.31558)+-0.024999*TMath::Gaus(x,8.22033,2.71373)+0.251236+((0.550454*exp((((0.995785*x)-sqrt((x*x)+(0.5478530000*0.5478530000)))/sqrt(1-(0.995785*0.995785)))/1.71099))+(0.0106936*(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/(exp((((0.995785*x)-sqrt((x*x)+(0.1349770000*0.1349770000)))/sqrt(1-(0.995785*0.995785)))/1.71099)+(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/2.)", - "333_pt": "(TMath::TwoPi()*x*(9.00063*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.428379)+636.492*pow(exp(-0.659016*x-0.397367*x*x)+x/0.512428,-5.72313)))*(0.0979137/(1+exp(-(x-1.53259)/0.370136))+-0.537275*TMath::Gaus(x,0.706186,2.03543)+0.45201*TMath::Gaus(x,1.18876,2.40911)+0.104985)" + "111_pt": "TMath::TwoPi()*x*(202.506*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.386428)+2172.76*pow(exp(-1.20019*x-0.719277*x*x)+x/0.447319,-5.46279))", + "221_pt": "(TMath::TwoPi()*x*(202.506*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.386428)+2172.76*pow(exp(-1.20019*x-0.719277*x*x)+x/0.447319,-5.46279)))*((9.36242e-05*TMath::Power((exp(- -2.56458*sqrt(x*x+0.547*0.547-0.139*0.139)-0.742625*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.457041/9.36242e-05,-1./8.15756)*sqrt(x*x+0.547*0.547-0.139*0.139)/0.0235913),-8.15756)/TMath::Power((exp(- -2.56458*x-0.742625*x*x)+x/0.0235913),-8.15756)+1.50683*TMath::Landau(x,2.1159,0.843387)))", + "333_pt": "(TMath::TwoPi()*x*(219.382*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.384303)+2471.6*pow(exp(-1.12373*x-0.752507*x*x)+x/0.438343,-5.45943)))*(0.1191/(1+exp(-(x- -0.316437)/0.554431))+-0.507618*TMath::Gaus(x,1.69654,1.62649)+0.494136*TMath::Gaus(x,2.56805,1.6193)+0.0789659)" }, - "5TeV_3040_wRatio_pi0up": { + "5TeV_0510_wRatio_etatest": { "histoMtScaleFactor": { "113": 0.85, "223": 0.7, "331": 0.4 }, - "111_pt": "TMath::TwoPi()*x*(26.8208*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.408437)+1019.42*pow(exp(-0.662498*x-0.498822*x*x)+x/0.495404,-5.68121))", - "221_pt": "(TMath::TwoPi()*x*(26.8208*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.408437)+1019.42*pow(exp(-0.662498*x-0.498822*x*x)+x/0.495404,-5.68121)))*((0.317347/(1+exp(-(x-0.475129)/0.245111))+-0.265641*TMath::Gaus(x,0.276828,1.17016)+-0.0289536*TMath::Gaus(x,5.40691,2.58911)+0.249861+((0.550454*exp((((0.995785*x)-sqrt((x*x)+(0.5478530000*0.5478530000)))/sqrt(1-(0.995785*0.995785)))/1.71099))+(0.0106936*(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/(exp((((0.995785*x)-sqrt((x*x)+(0.1349770000*0.1349770000)))/sqrt(1-(0.995785*0.995785)))/1.71099)+(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/2.)", - "333_pt": "(TMath::TwoPi()*x*(24.3786*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.412015)+908.266*pow(exp(-0.735883*x-0.425837*x*x)+x/0.505017,-5.68841)))*(0.106572/(1+exp(-(x-1.71328)/1.68645))+-0.54012*TMath::Gaus(x,0.754082,1.88026)+0.452996*TMath::Gaus(x,1.49283,2.4567)+0.0943071)" + "111_pt": "TMath::TwoPi()*x*(156.897*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.390093)+1835.17*pow(exp(-1.1618*x-0.514011*x*x)+x/0.465575,-5.50332))", + "221_pt": "(TMath::TwoPi()*x*(156.897*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.390093)+1835.17*pow(exp(-1.1618*x-0.514011*x*x)+x/0.465575,-5.50332)))*((0.000246618*TMath::Power((exp(- -3.44417*sqrt(x*x+0.547*0.547-0.139*0.139)-0.952884*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.476466/0.000246618,-1./8.8133)*sqrt(x*x+0.547*0.547-0.139*0.139)/0.0060628),-8.8133)/TMath::Power((exp(- -3.44417*x-0.952884*x*x)+x/0.0060628),-8.8133)+1.09231*TMath::Landau(x,2.04517,0.864605)))", + "333_pt": "(TMath::TwoPi()*x*(156.897*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.390093)+1835.17*pow(exp(-1.1618*x-0.514011*x*x)+x/0.465575,-5.50332)))*(0.1191/(1+exp(-(x- -0.316437)/0.554431))+-0.507618*TMath::Gaus(x,1.69654,1.62649)+0.494136*TMath::Gaus(x,2.56805,1.6193)+0.0789659)" + }, + "5TeV_0510_wRatio_etatest_ratiosup": { + "histoMtScaleFactor": { + "113": 1.02, + "223": 0.84, + "331": 0.48 + }, + "111_pt": "TMath::TwoPi()*x*(156.897*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.390093)+1835.17*pow(exp(-1.1618*x-0.514011*x*x)+x/0.465575,-5.50332))", + "221_pt": "(TMath::TwoPi()*x*(156.897*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.390093)+1835.17*pow(exp(-1.1618*x-0.514011*x*x)+x/0.465575,-5.50332)))*((((x<10.278)*(TMath::Max(TMath::Max(0.000295533*TMath::Power((exp(- -1.60043*sqrt(x*x+0.547*0.547-0.139*0.139)-0.655985*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.500931/0.000295533,-1./16.3545)*sqrt(x*x+0.547*0.547-0.139*0.139)/0.130844),-16.3545)/TMath::Power((exp(- -1.60043*x-0.655985*x*x)+x/0.130844),-16.3545)+1.5704*TMath::Landau(x,2.17776,0.921819),0.00540987*TMath::Power((exp(-0.202531*sqrt(x*x+0.547*0.547-0.139*0.139)-0.404918*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.496965/0.00540987,-1./0.457227)*sqrt(x*x+0.547*0.547-0.139*0.139)/0.010344),-0.457227)/TMath::Power((exp(-0.202531*x-0.404918*x*x)+x/0.010344),-0.457227)+1.48331*TMath::Landau(x,2.17198,0.823192)),TMath::Max(3.85365e-05*TMath::Power((exp(- -0.50908*sqrt(x*x+0.547*0.547-0.139*0.139)-0.0438035*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.463429/3.85365e-05,-1./6.0698)*sqrt(x*x+0.547*0.547-0.139*0.139)/0.0472676),-6.0698)/TMath::Power((exp(- -0.50908*x-0.0438035*x*x)+x/0.0472676),-6.0698)+2.34475*TMath::Landau(x,3.86817,1.59145),0.00540987*TMath::Power((exp(-0.202531*sqrt(x*x+0.547*0.547-0.139*0.139)-0.404918*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.496965/0.00540987,-1./0.457227)*sqrt(x*x+0.547*0.547-0.139*0.139)/0.010344),-0.457227)/TMath::Power((exp(-0.202531*x-0.404918*x*x)+x/0.010344),-0.457227)+1.48331*TMath::Landau(x,2.17198,0.823192))))+(x>=10.278)*1.08*(((0.499605*1.)+(155.836*(-0.00300369*TMath::Power(1.+((x/1.09373)*(x/1.09373)),-(0.672088)))))/(1.+(-0.00300369*TMath::Power(1.+((x/1.09373)*(x/1.09373)),-(0.672088))))))))", + "333_pt": "(TMath::TwoPi()*x*(156.897*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.390093)+1835.17*pow(exp(-1.1618*x-0.514011*x*x)+x/0.465575,-5.50332)))*((x<=0.5)*(-0.3/0.5*x+1.5)*(0.1191/(1+exp(-(x- -0.316437)/0.554431))+-0.507618*TMath::Gaus(x,1.69654,1.62649)+0.494136*TMath::Gaus(x,2.56805,1.6193)+0.0789659)+(x>0.5)*1.2*(0.1191/(1+exp(-(x- -0.316437)/0.554431))+-0.507618*TMath::Gaus(x,1.69654,1.62649)+0.494136*TMath::Gaus(x,2.56805,1.6193)+0.0789659))" + }, + "5TeV_0510_wRatio_etatest_ratiosdown": { + "histoMtScaleFactor": { + "113": 0.68, + "223": 0.56, + "331": 0.32 + }, + "111_pt": "TMath::TwoPi()*x*(156.897*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.390093)+1835.17*pow(exp(-1.1618*x-0.514011*x*x)+x/0.465575,-5.50332))", + "221_pt": "(TMath::TwoPi()*x*(156.897*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.390093)+1835.17*pow(exp(-1.1618*x-0.514011*x*x)+x/0.465575,-5.50332)))*((((x<11.6581)*(TMath::Min(TMath::Min(0.00302888*TMath::Power((exp(-0.246982*sqrt(x*x+0.547*0.547-0.139*0.139)-0.204843*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.485848/0.00302888,-1./335.069)*sqrt(x*x+0.547*0.547-0.139*0.139)/2.46929),-335.069)/TMath::Power((exp(-0.246982*x-0.204843*x*x)+x/2.46929),-335.069)+2.62546*TMath::Landau(x,3.61147,1.38592),0.926*(9.36242e-05*TMath::Power((exp(- -2.56458*sqrt(x*x+0.547*0.547-0.139*0.139)-0.742625*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.457041/9.36242e-05,-1./8.15756)*sqrt(x*x+0.547*0.547-0.139*0.139)/0.0235913),-8.15756)/TMath::Power((exp(- -2.56458*x-0.742625*x*x)+x/0.0235913),-8.15756)+1.50683*TMath::Landau(x,2.1159,0.843387))),TMath::Min(0.00624935*TMath::Power((exp(- -2.56471*sqrt(x*x+0.547*0.547-0.139*0.139)-2.49586*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.45/0.00624935,-1./44.8766)*sqrt(x*x+0.547*0.547-0.139*0.139)/0.202909),-44.8766)/TMath::Power((exp(- -2.56471*x-2.49586*x*x)+x/0.202909),-44.8766)+1.58602*TMath::Landau(x,2.23166,0.861224),0.926*(9.36242e-05*TMath::Power((exp(- -2.56458*sqrt(x*x+0.547*0.547-0.139*0.139)-0.742625*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.457041/9.36242e-05,-1./8.15756)*sqrt(x*x+0.547*0.547-0.139*0.139)/0.0235913),-8.15756)/TMath::Power((exp(- -2.56458*x-0.742625*x*x)+x/0.0235913),-8.15756)+1.50683*TMath::Landau(x,2.1159,0.843387)))))+(x>=11.6581)*0.426)))", + "333_pt": "(TMath::TwoPi()*x*(156.897*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.390093)+1835.17*pow(exp(-1.1618*x-0.514011*x*x)+x/0.465575,-5.50332)))*((x<=0.5)*(0.3/0.5*x+0.5)*(0.1191/(1+exp(-(x- -0.316437)/0.554431))+-0.507618*TMath::Gaus(x,1.69654,1.62649)+0.494136*TMath::Gaus(x,2.56805,1.6193)+0.0789659)+(x>0.5)*0.8*(0.1191/(1+exp(-(x- -0.316437)/0.554431))+-0.507618*TMath::Gaus(x,1.69654,1.62649)+0.494136*TMath::Gaus(x,2.56805,1.6193)+0.0789659))" }, - "5TeV_0510_wRatio_pi0up": { + "5TeV_0510_wRatio_etatest_pi0up": { "histoMtScaleFactor": { "113": 0.85, "223": 0.7, "331": 0.4 }, "111_pt": "TMath::TwoPi()*x*(170.514*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.38767)+2102.92*pow(exp(-1.0722*x-0.567406*x*x)+x/0.45525,-5.49867))", - "221_pt": "(TMath::TwoPi()*x*(170.514*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.38767)+2102.92*pow(exp(-1.0722*x-0.567406*x*x)+x/0.45525,-5.49867)))*(((x<=4.125494)*(0.462257/(1+exp(-(x-0.489168)/0.259816))+-0.338875*TMath::Gaus(x,0.655039,0.915494)+-0.277601*TMath::Gaus(x,5.26982,2.67864)+0.291403)+(x>4.125494)*(0.197492/(1+exp(-(x-5.11825)/1.30203))+-596719*TMath::Gaus(x,0.710032,0.569486)+1.82757*TMath::Gaus(x,-2.55348,2.83425)+0.33265)+((0.550454*exp((((0.995785*x)-sqrt((x*x)+(0.5478530000*0.5478530000)))/sqrt(1-(0.995785*0.995785)))/1.71099))+(0.0106936*(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/(exp((((0.995785*x)-sqrt((x*x)+(0.1349770000*0.1349770000)))/sqrt(1-(0.995785*0.995785)))/1.71099)+(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/2.)", + "221_pt": "(TMath::TwoPi()*x*(170.514*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.38767)+2102.92*pow(exp(-1.0722*x-0.567406*x*x)+x/0.45525,-5.49867)))*((0.000246618*TMath::Power((exp(- -3.44417*sqrt(x*x+0.547*0.547-0.139*0.139)-0.952884*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.476466/0.000246618,-1./8.8133)*sqrt(x*x+0.547*0.547-0.139*0.139)/0.0060628),-8.8133)/TMath::Power((exp(- -3.44417*x-0.952884*x*x)+x/0.0060628),-8.8133)+1.09231*TMath::Landau(x,2.04517,0.864605)))", "333_pt": "(TMath::TwoPi()*x*(156.897*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.390093)+1835.17*pow(exp(-1.1618*x-0.514011*x*x)+x/0.465575,-5.50332)))*(0.1191/(1+exp(-(x- -0.316437)/0.554431))+-0.507618*TMath::Gaus(x,1.69654,1.62649)+0.494136*TMath::Gaus(x,2.56805,1.6193)+0.0789659)" }, - "5TeV_0005_wRatio_pi0up": { + "5TeV_0510_wRatio_etatest_pi0down": { "histoMtScaleFactor": { "113": 0.85, "223": 0.7, "331": 0.4 }, - "111_pt": "TMath::TwoPi()*x*(236.555*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.382365)+2787.27*pow(exp(-1.05343*x-0.780376*x*x)+x/0.43017,-5.45613))", - "221_pt": "(TMath::TwoPi()*x*(236.555*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.382365)+2787.27*pow(exp(-1.05343*x-0.780376*x*x)+x/0.43017,-5.45613)))*(((x<=4.303300)*(0.385689/(1+exp(-(x-0.442495)/0.303977))+-0.324645*TMath::Gaus(x,0.364119,1.01338)+-0.268261*TMath::Gaus(x,8.32791,4.74697)+0.318614)+(x>4.303300)*(0.0836242/(1+exp(-(x-6.88914)/2.15587))+0.757533*TMath::Gaus(x,1.19329,1.35263)+40382.9*TMath::Gaus(x,-92.3117,-16.5516)+0.442)+((0.550454*exp((((0.995785*x)-sqrt((x*x)+(0.5478530000*0.5478530000)))/sqrt(1-(0.995785*0.995785)))/1.71099))+(0.0106936*(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/(exp((((0.995785*x)-sqrt((x*x)+(0.1349770000*0.1349770000)))/sqrt(1-(0.995785*0.995785)))/1.71099)+(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/2.)", - "333_pt": "(TMath::TwoPi()*x*(219.382*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.384303)+2471.6*pow(exp(-1.12373*x-0.752507*x*x)+x/0.438343,-5.45943)))*(0.1191/(1+exp(-(x- -0.316437)/0.554431))+-0.507618*TMath::Gaus(x,1.69654,1.62649)+0.494136*TMath::Gaus(x,2.56805,1.6193)+0.0789659)" + "111_pt": "TMath::TwoPi()*x*(143.633*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.392747)+1585.64*pow(exp(-1.25879*x-0.453071*x*x)+x/0.476927,-5.50804))", + "221_pt": "(TMath::TwoPi()*x*(143.633*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.392747)+1585.64*pow(exp(-1.25879*x-0.453071*x*x)+x/0.476927,-5.50804)))*((0.000246618*TMath::Power((exp(- -3.44417*sqrt(x*x+0.547*0.547-0.139*0.139)-0.952884*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.476466/0.000246618,-1./8.8133)*sqrt(x*x+0.547*0.547-0.139*0.139)/0.0060628),-8.8133)/TMath::Power((exp(- -3.44417*x-0.952884*x*x)+x/0.0060628),-8.8133)+1.09231*TMath::Landau(x,2.04517,0.864605)))", + "333_pt": "(TMath::TwoPi()*x*(156.897*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.390093)+1835.17*pow(exp(-1.1618*x-0.514011*x*x)+x/0.465575,-5.50332)))*(0.1191/(1+exp(-(x- -0.316437)/0.554431))+-0.507618*TMath::Gaus(x,1.69654,1.62649)+0.494136*TMath::Gaus(x,2.56805,1.6193)+0.0789659)" }, - "5TeV_0005_wRatio_pi0down": { + "5TeV_1020_wRatio_etatest": { "histoMtScaleFactor": { "113": 0.85, "223": 0.7, "331": 0.4 }, - "111_pt": "TMath::TwoPi()*x*(202.506*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.386428)+2172.76*pow(exp(-1.20019*x-0.719277*x*x)+x/0.447319,-5.46279))", - "221_pt": "(TMath::TwoPi()*x*(202.506*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.386428)+2172.76*pow(exp(-1.20019*x-0.719277*x*x)+x/0.447319,-5.46279)))*(((x<=4.303300)*(0.385689/(1+exp(-(x-0.442495)/0.303977))+-0.324645*TMath::Gaus(x,0.364119,1.01338)+-0.268261*TMath::Gaus(x,8.32791,4.74697)+0.318614)+(x>4.303300)*(0.0836242/(1+exp(-(x-6.88914)/2.15587))+0.757533*TMath::Gaus(x,1.19329,1.35263)+40382.9*TMath::Gaus(x,-92.3117,-16.5516)+0.442)+((0.550454*exp((((0.995785*x)-sqrt((x*x)+(0.5478530000*0.5478530000)))/sqrt(1-(0.995785*0.995785)))/1.71099))+(0.0106936*(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/(exp((((0.995785*x)-sqrt((x*x)+(0.1349770000*0.1349770000)))/sqrt(1-(0.995785*0.995785)))/1.71099)+(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/2.)", - "333_pt": "(TMath::TwoPi()*x*(219.382*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.384303)+2471.6*pow(exp(-1.12373*x-0.752507*x*x)+x/0.438343,-5.45943)))*(0.1191/(1+exp(-(x- -0.316437)/0.554431))+-0.507618*TMath::Gaus(x,1.69654,1.62649)+0.494136*TMath::Gaus(x,2.56805,1.6193)+0.0789659)" + "111_pt": "TMath::TwoPi()*x*(102.279*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.395141)+1545.4*pow(exp(-1.0113*x-0.476997*x*x)+x/0.47739,-5.54937))", + "221_pt": "(2.25035e-05*TMath::Power((exp(--2.8764*sqrt(x*x+0.547*0.547-0.139*0.139)-0.734747*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.477784/2.25035e-05,-1./8.26791)*sqrt(x*x+0.547*0.547-0.139*0.139)/0.00603254),-8.26791)/TMath::Power((exp(--2.8764*x-0.734747*x*x)+x/0.00603254),-8.26791)+1.14358*TMath::Landau(x,2.10694,0.888101))*(TMath::TwoPi()*x*(102.279*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.395141)+1545.4*pow(exp(-1.0113*x-0.476997*x*x)+x/0.47739,-5.54937)))", + "333_pt": "(TMath::TwoPi()*x*(102.279*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.395141)+1545.4*pow(exp(-1.0113*x-0.476997*x*x)+x/0.47739,-5.54937)))*(0.161108/(1+exp(-(x-1.093)/0.625883))+-0.463285*TMath::Gaus(x,1.78683,1.67212)+0.486619*TMath::Gaus(x,2.62071,2.10312)+0.0149748)" }, - "5TeV_0510_wRatio_pi0down": { + "5TeV_1020_wRatio_etatest_ratiosup": { "histoMtScaleFactor": { - "113": 0.85, - "223": 0.7, - "331": 0.4 + "113": 1.02, + "223": 0.84, + "331": 0.48 }, - "111_pt": "TMath::TwoPi()*x*(143.633*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.392747)+1585.64*pow(exp(-1.25879*x-0.453071*x*x)+x/0.476927,-5.50804))", - "221_pt": "(TMath::TwoPi()*x*(143.633*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.392747)+1585.64*pow(exp(-1.25879*x-0.453071*x*x)+x/0.476927,-5.50804)))*(((x<=4.125494)*(0.462257/(1+exp(-(x-0.489168)/0.259816))+-0.338875*TMath::Gaus(x,0.655039,0.915494)+-0.277601*TMath::Gaus(x,5.26982,2.67864)+0.291403)+(x>4.125494)*(0.197492/(1+exp(-(x-5.11825)/1.30203))+-596719*TMath::Gaus(x,0.710032,0.569486)+1.82757*TMath::Gaus(x,-2.55348,2.83425)+0.33265)+((0.550454*exp((((0.995785*x)-sqrt((x*x)+(0.5478530000*0.5478530000)))/sqrt(1-(0.995785*0.995785)))/1.71099))+(0.0106936*(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/(exp((((0.995785*x)-sqrt((x*x)+(0.1349770000*0.1349770000)))/sqrt(1-(0.995785*0.995785)))/1.71099)+(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/2.)", - "333_pt": "(TMath::TwoPi()*x*(156.897*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.390093)+1835.17*pow(exp(-1.1618*x-0.514011*x*x)+x/0.465575,-5.50332)))*(0.1191/(1+exp(-(x- -0.316437)/0.554431))+-0.507618*TMath::Gaus(x,1.69654,1.62649)+0.494136*TMath::Gaus(x,2.56805,1.6193)+0.0789659)" + "111_pt": "TMath::TwoPi()*x*(102.279*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.395141)+1545.4*pow(exp(-1.0113*x-0.476997*x*x)+x/0.47739,-5.54937))", + "221_pt": "(TMath::TwoPi()*x*(102.279*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.395141)+1545.4*pow(exp(-1.0113*x-0.476997*x*x)+x/0.47739,-5.54937)))*((TMath::TwoPi()*x*(102.279*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.395141)+1545.4*pow(exp(-1.0113*x-0.476997*x*x)+x/0.47739,-5.54937)))*((x<=0.5)*(-0.3/0.5*x+1.5)*((0.161108/(1+exp(-(x-1.093)/0.625883))+-0.463285*TMath::Gaus(x,1.78683,1.67212)+0.486619*TMath::Gaus(x,2.62071,2.10312)+0.0149748)) +(x>0.5)*1.2*((0.161108/(1+exp(-(x-1.093)/0.625883))+-0.463285*TMath::Gaus(x,1.78683,1.67212)+0.486619*TMath::Gaus(x,2.62071,2.10312)+0.0149748))))", + "333_pt": "(TMath::TwoPi()*x*(102.279*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.395141)+1545.4*pow(exp(-1.0113*x-0.476997*x*x)+x/0.47739,-5.54937))) *((x<=0.5)*(-0.3/0.5*x+1.5)*((0.161108/(1+exp(-(x-1.093)/0.625883))+-0.463285*TMath::Gaus(x,1.78683,1.67212)+0.486619*TMath::Gaus(x,2.62071,2.10312)+0.0149748)) +(x>0.5)*1.2*((0.161108/(1+exp(-(x-1.093)/0.625883))+-0.463285*TMath::Gaus(x,1.78683,1.67212)+0.486619*TMath::Gaus(x,2.62071,2.10312)+0.0149748)))" }, - "5TeV_3040_wRatio_pi0down": { + "5TeV_1020_wRatio_etatest_ratiosdown": { + "histoMtScaleFactor": { + "113": 0.68, + "223": 0.56, + "331": 0.32 + }, + "111_pt": "TMath::TwoPi()*x*(102.279*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.395141)+1545.4*pow(exp(-1.0113*x-0.476997*x*x)+x/0.47739,-5.54937))", + "221_pt": "(TMath::TwoPi()*x*(102.279*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.395141)+1545.4*pow(exp(-1.0113*x-0.476997*x*x)+x/0.47739,-5.54937)))*((x<=0.5)*(0.3/0.5*x+0.5)*((0.161108/(1+exp(-(x-1.093)/0.625883))+-0.463285*TMath::Gaus(x,1.78683,1.67212)+0.486619*TMath::Gaus(x,2.62071,2.10312)+0.0149748)) +(x>0.5)*0.8*((0.161108/(1+exp(-(x-1.093)/0.625883))+-0.463285*TMath::Gaus(x,1.78683,1.67212)+0.486619*TMath::Gaus(x,2.62071,2.10312)+0.0149748)))", + "333_pt": "(TMath::TwoPi()*x*(102.279*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.395141)+1545.4*pow(exp(-1.0113*x-0.476997*x*x)+x/0.47739,-5.54937))) *((x<=0.5)*(0.3/0.5*x+0.5)*((0.161108/(1+exp(-(x-1.093)/0.625883))+-0.463285*TMath::Gaus(x,1.78683,1.67212)+0.486619*TMath::Gaus(x,2.62071,2.10312)+0.0149748)) +(x>0.5)*0.8*((0.161108/(1+exp(-(x-1.093)/0.625883))+-0.463285*TMath::Gaus(x,1.78683,1.67212)+0.486619*TMath::Gaus(x,2.62071,2.10312)+0.0149748)))" + }, + "5TeV_1020_wRatio_etatest_pi0up": { "histoMtScaleFactor": { "113": 0.85, "223": 0.7, "331": 0.4 }, - "111_pt": "TMath::TwoPi()*x*(22.2048*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.41549)+802.356*pow(exp(-0.814916*x-0.343908*x*x)+x/0.515668,-5.69624))", - "221_pt": "(TMath::TwoPi()*x*(22.2048*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.41549)+802.356*pow(exp(-0.814916*x-0.343908*x*x)+x/0.515668,-5.69624)))*((0.317347/(1+exp(-(x-0.475129)/0.245111))+-0.265641*TMath::Gaus(x,0.276828,1.17016)+-0.0289536*TMath::Gaus(x,5.40691,2.58911)+0.249861+((0.550454*exp((((0.995785*x)-sqrt((x*x)+(0.5478530000*0.5478530000)))/sqrt(1-(0.995785*0.995785)))/1.71099))+(0.0106936*(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/(exp((((0.995785*x)-sqrt((x*x)+(0.1349770000*0.1349770000)))/sqrt(1-(0.995785*0.995785)))/1.71099)+(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/2.)", - "333_pt": "(TMath::TwoPi()*x*(24.3786*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.412015)+908.266*pow(exp(-0.735883*x-0.425837*x*x)+x/0.505017,-5.68841)))*(0.106572/(1+exp(-(x-1.71328)/1.68645))+-0.54012*TMath::Gaus(x,0.754082,1.88026)+0.452996*TMath::Gaus(x,1.49283,2.4567)+0.0943071)" + "111_pt": "TMath::TwoPi()*x*(111.543*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.392547)+1748.52*pow(exp(-0.930464*x-0.532639*x*x)+x/0.467782,-5.54415))", + "221_pt": "(TMath::TwoPi()*x*(111.543*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.392547)+1748.52*pow(exp(-0.930464*x-0.532639*x*x)+x/0.467782,-5.54415)))*(2.25035e-05*TMath::Power((exp(--2.8764*sqrt(x*x+0.547*0.547-0.139*0.139)-0.734747*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.477784/2.25035e-05,-1./8.26791)*sqrt(x*x+0.547*0.547-0.139*0.139)/0.00603254),-8.26791)/TMath::Power((exp(--2.8764*x-0.734747*x*x)+x/0.00603254),-8.26791)+1.14358*TMath::Landau(x,2.10694,0.888101))", + "333_pt": "(TMath::TwoPi()*x*(102.279*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.395141)+1545.4*pow(exp(-1.0113*x-0.476997*x*x)+x/0.47739,-5.54937)))*(0.161108/(1+exp(-(x-1.093)/0.625883))+-0.463285*TMath::Gaus(x,1.78683,1.67212)+0.486619*TMath::Gaus(x,2.62071,2.10312)+0.0149748)" }, - "5TeV_4050_wRatio_pi0down": { + "5TeV_1020_wRatio_etatest_pi0down": { "histoMtScaleFactor": { "113": 0.85, "223": 0.7, "331": 0.4 }, - "111_pt": "TMath::TwoPi()*x*(8.04609*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.432276)+566.92*pow(exp(-0.727741*x-0.316795*x*x)+x/0.523032,-5.73361))", - "221_pt": "(TMath::TwoPi()*x*(8.04609*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.432276)+566.92*pow(exp(-0.727741*x-0.316795*x*x)+x/0.523032,-5.73361)))*((0.298649/(1+exp(-(x-0.494749)/0.23628))+-0.255064*TMath::Gaus(x,0.170043,1.31558)+-0.024999*TMath::Gaus(x,8.22033,2.71373)+0.251236+((0.550454*exp((((0.995785*x)-sqrt((x*x)+(0.5478530000*0.5478530000)))/sqrt(1-(0.995785*0.995785)))/1.71099))+(0.0106936*(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/(exp((((0.995785*x)-sqrt((x*x)+(0.1349770000*0.1349770000)))/sqrt(1-(0.995785*0.995785)))/1.71099)+(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/2.)", - "333_pt": "(TMath::TwoPi()*x*(9.00063*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.428379)+636.492*pow(exp(-0.659016*x-0.397367*x*x)+x/0.512428,-5.72313)))*(0.0979137/(1+exp(-(x-1.53259)/0.370136))+-0.537275*TMath::Gaus(x,0.706186,2.03543)+0.45201*TMath::Gaus(x,1.18876,2.40911)+0.104985)" + "111_pt": "TMath::TwoPi()*x*(93.2981*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.397978)+1354.67*pow(exp(-1.09806*x-0.414866*x*x)+x/0.487866,-5.55472))", + "221_pt": "(TMath::TwoPi()*x*(93.2981*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.397978)+1354.67*pow(exp(-1.09806*x-0.414866*x*x)+x/0.487866,-5.55472)))*(2.25035e-05*TMath::Power((exp(--2.8764*sqrt(x*x+0.547*0.547-0.139*0.139)-0.734747*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.477784/2.25035e-05,-1./8.26791)*sqrt(x*x+0.547*0.547-0.139*0.139)/0.00603254),-8.26791)/TMath::Power((exp(--2.8764*x-0.734747*x*x)+x/0.00603254),-8.26791)+1.14358*TMath::Landau(x,2.10694,0.888101))", + "333_pt": "(TMath::TwoPi()*x*(102.279*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.395141)+1545.4*pow(exp(-1.0113*x-0.476997*x*x)+x/0.47739,-5.54937)))*(0.161108/(1+exp(-(x-1.093)/0.625883))+-0.463285*TMath::Gaus(x,1.78683,1.67212)+0.486619*TMath::Gaus(x,2.62071,2.10312)+0.0149748)" }, - "5TeV_4050_wRatio_ratiosdown": { + "5TeV_2030_wRatio_etatest": { "histoMtScaleFactor": { - "113": 0.68, - "223": 0.56, - "331": 0.32 + "113": 0.85, + "223": 0.7, + "331": 0.4 }, - "111_pt": "TMath::TwoPi()*x*(9.00063*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.428379)+636.492*pow(exp(-0.659016*x-0.397367*x*x)+x/0.512428,-5.72313))", - "221_pt": "(TMath::TwoPi()*x*(9.00063*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.428379)+636.492*pow(exp(-0.659016*x-0.397367*x*x)+x/0.512428,-5.72313)))*(((0.550454*exp((((0.995785*x)-sqrt((x*x)+(0.5478530000*0.5478530000)))/sqrt(1-(0.995785*0.995785)))/1.71099))+(0.0106936*(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/(exp((((0.995785*x)-sqrt((x*x)+(0.1349770000*0.1349770000)))/sqrt(1-(0.995785*0.995785)))/1.71099)+(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))", - "333_pt": "(TMath::TwoPi()*x*(9.00063*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.428379)+636.492*pow(exp(-0.659016*x-0.397367*x*x)+x/0.512428,-5.72313)))*((x<=0.5)*(0.3/0.5*x+0.5)*(0.0979137/(1+exp(-(x-1.53259)/0.370136))+-0.537275*TMath::Gaus(x,0.706186,2.03543)+0.45201*TMath::Gaus(x,1.18876,2.40911)+0.104985)+(x>0.5)*0.8*(0.0979137/(1+exp(-(x-1.53259)/0.370136))+-0.537275*TMath::Gaus(x,0.706186,2.03543)+0.45201*TMath::Gaus(x,1.18876,2.40911)+0.104985))" + "111_pt": "TMath::TwoPi()*x*(49.8609*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.407854)+1273.88*pow(exp(-0.824661*x-0.498002*x*x)+x/0.487977,-5.62108))", + "221_pt": "(TMath::TwoPi()*x*(49.8609*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.407854)+1273.88*pow(exp(-0.824661*x-0.498002*x*x)+x/0.487977,-5.62108)))*(0.00038991*TMath::Power((exp(--2.8689*sqrt(x*x+0.547*0.547-0.139*0.139)-0.716403*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.478705/0.00038991,-1./7.39763)*sqrt(x*x+0.547*0.547-0.139*0.139)/0.00709108),-7.39763)/TMath::Power((exp(--2.8689*x-0.716403*x*x)+x/0.00709108),-7.39763)+0.917471*TMath::Landau(x,2.17426,0.976011))", + "333_pt": "(TMath::TwoPi()*x*(49.8609*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.407854)+1273.88*pow(exp(-0.824661*x-0.498002*x*x)+x/0.487977,-5.62108)))*(0.120134/(1+exp(-(x--0.905858)/0.386723))+-0.558917*TMath::Gaus(x,1.59687,1.87228)+0.524197*TMath::Gaus(x,2.55436,1.78153)+0.0923629)" }, - "5TeV_3040_wRatio_ratiosdown": { + "5TeV_2030_wRatio_etatest_ratiosup": { "histoMtScaleFactor": { - "113": 0.68, - "223": 0.56, - "331": 0.32 + "113": 1.02, + "223": 0.84, + "331": 0.48 }, - "111_pt": "TMath::TwoPi()*x*(24.3786*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.412015)+908.266*pow(exp(-0.735883*x-0.425837*x*x)+x/0.505017,-5.68841))", - "221_pt": "(TMath::TwoPi()*x*(24.3786*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.412015)+908.266*pow(exp(-0.735883*x-0.425837*x*x)+x/0.505017,-5.68841)))*(((0.550454*exp((((0.995785*x)-sqrt((x*x)+(0.5478530000*0.5478530000)))/sqrt(1-(0.995785*0.995785)))/1.71099))+(0.0106936*(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/(exp((((0.995785*x)-sqrt((x*x)+(0.1349770000*0.1349770000)))/sqrt(1-(0.995785*0.995785)))/1.71099)+(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))", - "333_pt": "(TMath::TwoPi()*x*(24.3786*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.412015)+908.266*pow(exp(-0.735883*x-0.425837*x*x)+x/0.505017,-5.68841)))*((x<=0.5)*(0.3/0.5*x+0.5)*(0.106572/(1+exp(-(x-1.71328)/1.68645))+-0.54012*TMath::Gaus(x,0.754082,1.88026)+0.452996*TMath::Gaus(x,1.49283,2.4567)+0.0943071)+(x>0.5)*0.8*(0.106572/(1+exp(-(x-1.71328)/1.68645))+-0.54012*TMath::Gaus(x,0.754082,1.88026)+0.452996*TMath::Gaus(x,1.49283,2.4567)+0.0943071))" + "111_pt": "TMath::TwoPi()*x*(49.8609*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.407854)+1273.88*pow(exp(-0.824661*x-0.498002*x*x)+x/0.487977,-5.62108))", + "221_pt": "(TMath::TwoPi()*x*(49.8609*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.407854)+1273.88*pow(exp(-0.824661*x-0.498002*x*x)+x/0.487977,-5.62108)))*((x<11.3582)*(TMath::Max(TMath::Max(0.0622931*TMath::Power((exp(--0.430138*sqrt(x*x+0.547*0.547-0.139*0.139)-0.308378*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.497447/0.0622931,-1./7.93993)*sqrt(x*x+0.547*0.547-0.139*0.139)/1.24166),-7.93993)/TMath::Power((exp(--0.430138*x-0.308378*x*x)+x/1.24166),-7.93993)+0.909031*TMath::Landau(x,2.74364,1.23978),0.00172495*TMath::Power((exp(-1.13225*sqrt(x*x+0.547*0.547-0.139*0.139)-0.126955*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.496991/0.00172495,-1./0.657116)*sqrt(x*x+0.547*0.547-0.139*0.139)/0.064674),-0.657116)/TMath::Power((exp(-1.13225*x-0.126955*x*x)+x/0.064674),-0.657116)+1.64346*TMath::Landau(x,2.12011,0.856277)),TMath::Max(0.000832695*TMath::Power((exp(-0.26446*sqrt(x*x+0.547*0.547-0.139*0.139)-0.0253786*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.48268/0.000832695,-1./4.34962)*sqrt(x*x+0.547*0.547-0.139*0.139)/0.110892),-4.34962)/TMath::Power((exp(-0.26446*x-0.0253786*x*x)+x/0.110892),-4.34962)+0.846008*TMath::Landau(x,3.42622,1.68176),0.00172495*TMath::Power((exp(-1.13225*sqrt(x*x+0.547*0.547-0.139*0.139)-0.126955*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.496991/0.00172495,-1./0.657116)*sqrt(x*x+0.547*0.547-0.139*0.139)/0.064674),-0.657116)/TMath::Power((exp(-1.13225*x-0.126955*x*x)+x/0.064674),-0.657116)+1.64346*TMath::Landau(x,2.12011,0.856277))))+(x>=11.3582)*1.08*(((0.499605*1.)+(155.836*(-0.00300369*TMath::Power(1.+((x/1.09373)*(x/1.09373)),-(0.672088)))))/(1.+(-0.00300369*TMath::Power(1.+((x/1.09373)*(x/1.09373)),-(0.672088))))))", + "333_pt": "(TMath::TwoPi()*x*(49.8609*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.407854)+1273.88*pow(exp(-0.824661*x-0.498002*x*x)+x/0.487977,-5.62108)))*((x<=0.5)*(-0.3/0.5*x+1.5)*((0.120134/(1+exp(-(x--0.905858)/0.386723))+-0.558917*TMath::Gaus(x,1.59687,1.87228)+0.524197*TMath::Gaus(x,2.55436,1.78153)+0.0923629))+(x>0.5)*1.2*((0.120134/(1+exp(-(x--0.905858)/0.386723))+-0.558917*TMath::Gaus(x,1.59687,1.87228)+0.524197*TMath::Gaus(x,2.55436,1.78153)+0.0923629)))" }, - "5TeV_0510_wRatio_ratiosdown": { + "5TeV_2030_wRatio_etatest_ratiosdown": { "histoMtScaleFactor": { "113": 0.68, "223": 0.56, "331": 0.32 }, - "111_pt": "TMath::TwoPi()*x*(156.897*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.390093)+1835.17*pow(exp(-1.1618*x-0.514011*x*x)+x/0.465575,-5.50332))", - "221_pt": "(TMath::TwoPi()*x*(156.897*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.390093)+1835.17*pow(exp(-1.1618*x-0.514011*x*x)+x/0.465575,-5.50332)))*(((0.550454*exp((((0.995785*x)-sqrt((x*x)+(0.5478530000*0.5478530000)))/sqrt(1-(0.995785*0.995785)))/1.71099))+(0.0106936*(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/(exp((((0.995785*x)-sqrt((x*x)+(0.1349770000*0.1349770000)))/sqrt(1-(0.995785*0.995785)))/1.71099)+(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))", - "333_pt": "(TMath::TwoPi()*x*(156.897*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.390093)+1835.17*pow(exp(-1.1618*x-0.514011*x*x)+x/0.465575,-5.50332)))*((x<=0.5)*(0.3/0.5*x+0.5)*(0.1191/(1+exp(-(x- -0.316437)/0.554431))+-0.507618*TMath::Gaus(x,1.69654,1.62649)+0.494136*TMath::Gaus(x,2.56805,1.6193)+0.0789659)+(x>0.5)*0.8*(0.1191/(1+exp(-(x- -0.316437)/0.554431))+-0.507618*TMath::Gaus(x,1.69654,1.62649)+0.494136*TMath::Gaus(x,2.56805,1.6193)+0.0789659))" + "111_pt": "TMath::TwoPi()*x*(49.8609*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.407854)+1273.88*pow(exp(-0.824661*x-0.498002*x*x)+x/0.487977,-5.62108))", + "221_pt": "(TMath::TwoPi()*x*(49.8609*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.407854)+1273.88*pow(exp(-0.824661*x-0.498002*x*x)+x/0.487977,-5.62108)))*((x<6.000000)*(0.940000*(TMath::Min(TMath::Min(0.00779577*TMath::Power((exp(--0.684847*sqrt(x*x+0.547*0.547-0.139*0.139)-1.53786*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.45/0.00779577,-1./60.1223)*sqrt(x*x+0.547*0.547-0.139*0.139)/0.685219),-60.1223)/TMath::Power((exp(--0.684847*x-1.53786*x*x)+x/0.685219),-60.1223)+1.63771*TMath::Landau(x,2.42852,0.955009),1.92417*TMath::Power((exp(-0.0810564*sqrt(x*x+0.547*0.547-0.139*0.139)-0.616729*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.45/1.92417,-1./68.3973)*sqrt(x*x+0.547*0.547-0.139*0.139)/0.973421),-68.3973)/TMath::Power((exp(-0.0810564*x-0.616729*x*x)+x/0.973421),-68.3973)+1.5307*TMath::Landau(x,2.72093,1.07219)),TMath::Min(0.000514864*TMath::Power((exp(-1.38498*sqrt(x*x+0.547*0.547-0.139*0.139)-2.57506*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.466/0.000514864,-1./7.53845)*sqrt(x*x+0.547*0.547-0.139*0.139)/1e-09),-7.53845)/TMath::Power((exp(-1.38498*x-2.57506*x*x)+x/1e-09),-7.53845)+0.22292*TMath::Landau(x,0.338702,0.226932),1.92417*TMath::Power((exp(-0.0810564*sqrt(x*x+0.547*0.547-0.139*0.139)-0.616729*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.45/1.92417,-1./68.3973)*sqrt(x*x+0.547*0.547-0.139*0.139)/0.973421),-68.3973)/TMath::Power((exp(-0.0810564*x-0.616729*x*x)+x/0.973421),-68.3973)+1.5307*TMath::Landau(x,2.72093,1.07219)))))+(x>=6.000000)*0.426)", + "333_pt": "(TMath::TwoPi()*x*(49.8609*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.407854)+1273.88*pow(exp(-0.824661*x-0.498002*x*x)+x/0.487977,-5.62108)))*((x<=0.5)*(0.3/0.5*x+0.5)*((0.120134/(1+exp(-(x--0.905858)/0.386723))+-0.558917*TMath::Gaus(x,1.59687,1.87228)+0.524197*TMath::Gaus(x,2.55436,1.78153)+0.0923629)) +(x>0.5)*0.8*((0.120134/(1+exp(-(x--0.905858)/0.386723))+-0.558917*TMath::Gaus(x,1.59687,1.87228)+0.524197*TMath::Gaus(x,2.55436,1.78153)+0.0923629)))" }, - "5TeV_0005_wRatio_ratiosdown": { + "5TeV_2030_wRatio_etatest_pi0up": { "histoMtScaleFactor": { - "113": 0.68, - "223": 0.56, - "331": 0.32 + "113": 0.85, + "223": 0.7, + "331": 0.4 }, - "111_pt": "TMath::TwoPi()*x*(219.382*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.384303)+2471.6*pow(exp(-1.12373*x-0.752507*x*x)+x/0.438343,-5.45943))", - "221_pt": "(TMath::TwoPi()*x*(219.382*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.384303)+2471.6*pow(exp(-1.12373*x-0.752507*x*x)+x/0.438343,-5.45943)))*(((0.550454*exp((((0.995785*x)-sqrt((x*x)+(0.5478530000*0.5478530000)))/sqrt(1-(0.995785*0.995785)))/1.71099))+(0.0106936*(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/(exp((((0.995785*x)-sqrt((x*x)+(0.1349770000*0.1349770000)))/sqrt(1-(0.995785*0.995785)))/1.71099)+(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))", - "333_pt": "(TMath::TwoPi()*x*(219.382*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.384303)+2471.6*pow(exp(-1.12373*x-0.752507*x*x)+x/0.438343,-5.45943)))*((x<=0.5)*(0.3/0.5*x+0.5)*(0.1191/(1+exp(-(x- -0.316437)/0.554431))+-0.507618*TMath::Gaus(x,1.69654,1.62649)+0.494136*TMath::Gaus(x,2.56805,1.6193)+0.0789659)+(x>0.5)*0.8*(0.1191/(1+exp(-(x- -0.316437)/0.554431))+-0.507618*TMath::Gaus(x,1.69654,1.62649)+0.494136*TMath::Gaus(x,2.56805,1.6193)+0.0789659))" + "111_pt": "TMath::TwoPi()*x*(54.9157*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.404309)+1436.73*pow(exp(-0.74937*x-0.556775*x*x)+x/0.478695,-5.61584))", + "221_pt": "(TMath::TwoPi()*x*(54.9157*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.404309)+1436.73*pow(exp(-0.74937*x-0.556775*x*x)+x/0.478695,-5.61584)))*(0.00038991*TMath::Power((exp(--2.8689*sqrt(x*x+0.547*0.547-0.139*0.139)-0.716403*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.478705/0.00038991,-1./7.39763)*sqrt(x*x+0.547*0.547-0.139*0.139)/0.00709108),-7.39763)/TMath::Power((exp(--2.8689*x-0.716403*x*x)+x/0.00709108),-7.39763)+0.917471*TMath::Landau(x,2.17426,0.976011))", + "333_pt": "(TMath::TwoPi()*x*(49.8609*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.407854)+1273.88*pow(exp(-0.824661*x-0.498002*x*x)+x/0.487977,-5.62108)))*(0.120134/(1+exp(-(x--0.905858)/0.386723))+-0.558917*TMath::Gaus(x,1.59687,1.87228)+0.524197*TMath::Gaus(x,2.55436,1.78153)+0.0923629)" }, - "5TeV_0005_wRatio_ratiosup": { + "5TeV_2030_wRatio_etatest_pi0down": { "histoMtScaleFactor": { - "113": 1.02, - "223": 0.84, - "331": 0.48 + "113": 0.85, + "223": 0.7, + "331": 0.4 }, - "111_pt": "TMath::TwoPi()*x*(219.382*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.384303)+2471.6*pow(exp(-1.12373*x-0.752507*x*x)+x/0.438343,-5.45943))", - "221_pt": "(TMath::TwoPi()*x*(219.382*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.384303)+2471.6*pow(exp(-1.12373*x-0.752507*x*x)+x/0.438343,-5.45943)))*((x<=4.303300)*(0.385689/(1+exp(-(x-0.442495)/0.303977))+-0.324645*TMath::Gaus(x,0.364119,1.01338)+-0.268261*TMath::Gaus(x,8.32791,4.74697)+0.318614)+(x>4.303300)*(0.0836242/(1+exp(-(x-6.88914)/2.15587))+0.757533*TMath::Gaus(x,1.19329,1.35263)+40382.9*TMath::Gaus(x,-92.3117,-16.5516)+0.442))", - "333_pt": "(TMath::TwoPi()*x*(219.382*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.384303)+2471.6*pow(exp(-1.12373*x-0.752507*x*x)+x/0.438343,-5.45943)))*((x<=0.5)*(-0.3/0.5*x+1.5)*(0.1191/(1+exp(-(x- -0.316437)/0.554431))+-0.507618*TMath::Gaus(x,1.69654,1.62649)+0.494136*TMath::Gaus(x,2.56805,1.6193)+0.0789659)+(x>0.5)*1.2*(0.1191/(1+exp(-(x- -0.316437)/0.554431))+-0.507618*TMath::Gaus(x,1.69654,1.62649)+0.494136*TMath::Gaus(x,2.56805,1.6193)+0.0789659))" + "111_pt": "TMath::TwoPi()*x*(45.066*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.41167)+1119.91*pow(exp(-0.905844*x-0.432426*x*x)+x/0.498136,-5.62645))", + "221_pt": "(TMath::TwoPi()*x*(45.066*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.41167)+1119.91*pow(exp(-0.905844*x-0.432426*x*x)+x/0.498136,-5.62645)))*(0.00038991*TMath::Power((exp(--2.8689*sqrt(x*x+0.547*0.547-0.139*0.139)-0.716403*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.478705/0.00038991,-1./7.39763)*sqrt(x*x+0.547*0.547-0.139*0.139)/0.00709108),-7.39763)/TMath::Power((exp(--2.8689*x-0.716403*x*x)+x/0.00709108),-7.39763)+0.917471*TMath::Landau(x,2.17426,0.976011))", + "333_pt": "(TMath::TwoPi()*x*(49.8609*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.407854)+1273.88*pow(exp(-0.824661*x-0.498002*x*x)+x/0.487977,-5.62108)))*(0.120134/(1+exp(-(x--0.905858)/0.386723))+-0.558917*TMath::Gaus(x,1.59687,1.87228)+0.524197*TMath::Gaus(x,2.55436,1.78153)+0.0923629)" }, - "5TeV_0510_wRatio_ratiosup": { + "5TeV_3040_wRatio_etatest": { "histoMtScaleFactor": { - "113": 1.02, - "223": 0.84, - "331": 0.48 + "113": 0.85, + "223": 0.7, + "331": 0.4 }, - "111_pt": "TMath::TwoPi()*x*(156.897*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.390093)+1835.17*pow(exp(-1.1618*x-0.514011*x*x)+x/0.465575,-5.50332))", - "221_pt": "(TMath::TwoPi()*x*(156.897*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.390093)+1835.17*pow(exp(-1.1618*x-0.514011*x*x)+x/0.465575,-5.50332)))*((x<=4.125494)*(0.462257/(1+exp(-(x-0.489168)/0.259816))+-0.338875*TMath::Gaus(x,0.655039,0.915494)+-0.277601*TMath::Gaus(x,5.26982,2.67864)+0.291403)+(x>4.125494)*(0.197492/(1+exp(-(x-5.11825)/1.30203))+-596719*TMath::Gaus(x,0.710032,0.569486)+1.82757*TMath::Gaus(x,-2.55348,2.83425)+0.33265))", - "333_pt": "(TMath::TwoPi()*x*(156.897*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.390093)+1835.17*pow(exp(-1.1618*x-0.514011*x*x)+x/0.465575,-5.50332)))*((x<=0.5)*(-0.3/0.5*x+1.5)*(0.1191/(1+exp(-(x- -0.316437)/0.554431))+-0.507618*TMath::Gaus(x,1.69654,1.62649)+0.494136*TMath::Gaus(x,2.56805,1.6193)+0.0789659)+(x>0.5)*1.2*(0.1191/(1+exp(-(x- -0.316437)/0.554431))+-0.507618*TMath::Gaus(x,1.69654,1.62649)+0.494136*TMath::Gaus(x,2.56805,1.6193)+0.0789659))" + "111_pt": "TMath::TwoPi()*x*(24.3786*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.412015)+908.266*pow(exp(-0.735883*x-0.425837*x*x)+x/0.505017,-5.68841))", + "221_pt": "(TMath::TwoPi()*x*(24.3786*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.412015)+908.266*pow(exp(-0.735883*x-0.425837*x*x)+x/0.505017,-5.68841)))*(0.00028072*TMath::Power((exp(- -3.25466*sqrt(x*x+0.547*0.547-0.139*0.139)-0.761446*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.471621/0.00028072,-1./6.66634)*sqrt(x*x+0.547*0.547-0.139*0.139)/0.00480919),-6.66634)/TMath::Power((exp(- -3.25466*x-0.761446*x*x)+x/0.00480919),-6.66634)+1.12405*TMath::Landau(x,2.5931,1.14043))", + "333_pt": "(TMath::TwoPi()*x*(24.3786*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.412015)+908.266*pow(exp(-0.735883*x-0.425837*x*x)+x/0.505017,-5.68841)))*(0.106572/(1+exp(-(x-1.71328)/1.68645))+-0.54012*TMath::Gaus(x,0.754082,1.88026)+0.452996*TMath::Gaus(x,1.49283,2.4567)+0.0943071)", + "111_v2_def": "x*x*(0.29722*exp(-x/0.318227)+0.560172*pow(1+x/0.401715,-2.04434)+0.193576*exp(-x/1.2557))", + "310_v2_def": "x*x*(0.0280321*exp(-x/0.49621)+0.328197*pow(1+x/0.0128527,-0.853155)+0.214485*exp(-x/1.29581))", + "333_v2_def": "x*x*(1.2367*exp(-x/0.629473)+-3.9919*pow(1+x/0.886275,-3.11899)+0.274776*exp(-x/1.82705))" }, - "5TeV_3040_wRatio_ratiosup": { + "5TeV_3040_wRatio_etatest_ratiosup": { "histoMtScaleFactor": { "113": 1.02, "223": 0.84, "331": 0.48 }, "111_pt": "TMath::TwoPi()*x*(24.3786*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.412015)+908.266*pow(exp(-0.735883*x-0.425837*x*x)+x/0.505017,-5.68841))", - "221_pt": "(TMath::TwoPi()*x*(24.3786*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.412015)+908.266*pow(exp(-0.735883*x-0.425837*x*x)+x/0.505017,-5.68841)))*(0.317347/(1+exp(-(x-0.475129)/0.245111))+-0.265641*TMath::Gaus(x,0.276828,1.17016)+-0.0289536*TMath::Gaus(x,5.40691,2.58911)+0.249861)", - "333_pt": "(TMath::TwoPi()*x*(24.3786*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.412015)+908.266*pow(exp(-0.735883*x-0.425837*x*x)+x/0.505017,-5.68841)))*((x<=0.5)*(-0.3/0.5*x+1.5)*(0.106572/(1+exp(-(x-1.71328)/1.68645))+-0.54012*TMath::Gaus(x,0.754082,1.88026)+0.452996*TMath::Gaus(x,1.49283,2.4567)+0.0943071)+(x>0.5)*1.2*(0.106572/(1+exp(-(x-1.71328)/1.68645))+-0.54012*TMath::Gaus(x,0.754082,1.88026)+0.452996*TMath::Gaus(x,1.49283,2.4567)+0.0943071))" + "221_pt": "(TMath::TwoPi()*x*(24.3786*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.412015)+908.266*pow(exp(-0.735883*x-0.425837*x*x)+x/0.505017,-5.68841)))*(((x<11.3582)*(TMath::Max(TMath::Max(3.62635e-06*TMath::Power((exp(-1.04848*sqrt(x*x+0.547*0.547-0.139*0.139)-0.00760898*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.494194/3.62635e-06,-1./6.52441)*sqrt(x*x+0.547*0.547-0.139*0.139)/1.37753),-6.52441)/TMath::Power((exp(-1.04848*x-0.00760898*x*x)+x/1.37753),-6.52441)+1.85024*TMath::Landau(x,2.3343,0.982346),2.67119e-07*TMath::Power((exp(-0.504012*sqrt(x*x+0.547*0.547-0.139*0.139)-0.192368*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.495159/2.67119e-07,-1./2.55351)*sqrt(x*x+0.547*0.547-0.139*0.139)/0.0853583),-2.55351)/TMath::Power((exp(-0.504012*x-0.192368*x*x)+x/0.0853583),-2.55351)+1.96831*TMath::Landau(x,2.25187,0.92394)),TMath::Max(0.00152416*TMath::Power((exp(-1.50417*sqrt(x*x+0.547*0.547-0.139*0.139)- -0.0524029*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.496824/0.00152416,-1./3.48956)*sqrt(x*x+0.547*0.547-0.139*0.139)/8.34972),-3.48956)/TMath::Power((exp(-1.50417*x- -0.0524029*x*x)+x/8.34972),-3.48956)+1.90397*TMath::Landau(x,2.2369,0.884653),2.67119e-07*TMath::Power((exp(-0.504012*sqrt(x*x+0.547*0.547-0.139*0.139)-0.192368*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.495159/2.67119e-07,-1./2.55351)*sqrt(x*x+0.547*0.547-0.139*0.139)/0.0853583),-2.55351)/TMath::Power((exp(-0.504012*x-0.192368*x*x)+x/0.0853583),-2.55351)+1.96831*TMath::Landau(x,2.25187,0.92394))))+(x>=11.3582)*1.08*(((0.499605*1.)+(155.836*(-0.00300369*TMath::Power(1.+((x/1.09373)*(x/1.09373)),-(0.672088)))))/(1.+(-0.00300369*TMath::Power(1.+((x/1.09373)*(x/1.09373)),-(0.672088)))))))", + "333_pt": "(TMath::TwoPi()*x*(24.3786*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.412015)+908.266*pow(exp(-0.735883*x-0.425837*x*x)+x/0.505017,-5.68841)))*((x<=0.5)*(-0.3/0.5*x+1.5)*(0.106572/(1+exp(-(x-1.71328)/1.68645))+-0.54012*TMath::Gaus(x,0.754082,1.88026)+0.452996*TMath::Gaus(x,1.49283,2.4567)+0.0943071)+(x>0.5)*1.2*(0.106572/(1+exp(-(x-1.71328)/1.68645))+-0.54012*TMath::Gaus(x,0.754082,1.88026)+0.452996*TMath::Gaus(x,1.49283,2.4567)+0.0943071))", + "111_v2_def": "x*x*(0.29722*exp(-x/0.318227)+0.560172*pow(1+x/0.401715,-2.04434)+0.193576*exp(-x/1.2557))", + "310_v2_def": "x*x*(0.0280321*exp(-x/0.49621)+0.328197*pow(1+x/0.0128527,-0.853155)+0.214485*exp(-x/1.29581))", + "333_v2_def": "x*x*(1.2367*exp(-x/0.629473)+-3.9919*pow(1+x/0.886275,-3.11899)+0.274776*exp(-x/1.82705))" }, - "5TeV_4050_wRatio_ratiosup": { + "5TeV_3040_wRatio_etatest_ratiosdown": { "histoMtScaleFactor": { - "113": 1.02, - "223": 0.84, - "331": 0.48 + "113": 0.68, + "223": 0.56, + "331": 0.32 }, - "111_pt": "TMath::TwoPi()*x*(9.00063*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.428379)+636.492*pow(exp(-0.659016*x-0.397367*x*x)+x/0.512428,-5.72313))", - "221_pt": "(TMath::TwoPi()*x*(9.00063*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.428379)+636.492*pow(exp(-0.659016*x-0.397367*x*x)+x/0.512428,-5.72313)))*(0.298649/(1+exp(-(x-0.494749)/0.23628))+-0.255064*TMath::Gaus(x,0.170043,1.31558)+-0.024999*TMath::Gaus(x,8.22033,2.71373)+0.251236)", - "333_pt": "(TMath::TwoPi()*x*(9.00063*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.428379)+636.492*pow(exp(-0.659016*x-0.397367*x*x)+x/0.512428,-5.72313)))*((x<=0.5)*(-0.3/0.5*x+1.5)*(0.0979137/(1+exp(-(x-1.53259)/0.370136))+-0.537275*TMath::Gaus(x,0.706186,2.03543)+0.45201*TMath::Gaus(x,1.18876,2.40911)+0.104985)+(x>0.5)*1.2*(0.0979137/(1+exp(-(x-1.53259)/0.370136))+-0.537275*TMath::Gaus(x,0.706186,2.03543)+0.45201*TMath::Gaus(x,1.18876,2.40911)+0.104985))" + "111_pt": "TMath::TwoPi()*x*(24.3786*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.412015)+908.266*pow(exp(-0.735883*x-0.425837*x*x)+x/0.505017,-5.68841))", + "221_pt": "(TMath::TwoPi()*x*(24.3786*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.412015)+908.266*pow(exp(-0.735883*x-0.425837*x*x)+x/0.505017,-5.68841)))*(((x<6.7544)*(0.9*(TMath::Min(TMath::Min(0.000624633*TMath::Power((exp(- -0.139403*sqrt(x*x+0.547*0.547-0.139*0.139)-0.883882*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.45/0.000624633,-1./91.7456)*sqrt(x*x+0.547*0.547-0.139*0.139)/1.27557),-91.7456)/TMath::Power((exp(- -0.139403*x-0.883882*x*x)+x/1.27557),-91.7456)+1.93275*TMath::Landau(x,2.96282,1.17967),0.806467*TMath::Power((exp(-0.123521*sqrt(x*x+0.547*0.547-0.139*0.139)-0.533763*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.45/0.806467,-1./80.3123)*sqrt(x*x+0.547*0.547-0.139*0.139)/1.16637),-80.3123)/TMath::Power((exp(-0.123521*x-0.533763*x*x)+x/1.16637),-80.3123)+1.6774*TMath::Landau(x,3.13342,1.2489)),TMath::Min(0.0105235*TMath::Power((exp(- -0.843626*sqrt(x*x+0.547*0.547-0.139*0.139)-1.26105*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.450001/0.0105235,-1./22.4747)*sqrt(x*x+0.547*0.547-0.139*0.139)/0.776751),-22.4747)/TMath::Power((exp(- -0.843626*x-1.26105*x*x)+x/0.776751),-22.4747)+0.774392*TMath::Landau(x,2.71895,1.26325),0.806467*TMath::Power((exp(-0.123521*sqrt(x*x+0.547*0.547-0.139*0.139)-0.533763*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.45/0.806467,-1./80.3123)*sqrt(x*x+0.547*0.547-0.139*0.139)/1.16637),-80.3123)/TMath::Power((exp(-0.123521*x-0.533763*x*x)+x/1.16637),-80.3123)+1.6774*TMath::Landau(x,3.13342,1.2489)))))+(x>=6.7544)*0.426))", + "333_pt": "(TMath::TwoPi()*x*(24.3786*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.412015)+908.266*pow(exp(-0.735883*x-0.425837*x*x)+x/0.505017,-5.68841)))*((x<=0.5)*(0.3/0.5*x+0.5)*(0.106572/(1+exp(-(x-1.71328)/1.68645))+-0.54012*TMath::Gaus(x,0.754082,1.88026)+0.452996*TMath::Gaus(x,1.49283,2.4567)+0.0943071)+(x>0.5)*0.8*(0.106572/(1+exp(-(x-1.71328)/1.68645))+-0.54012*TMath::Gaus(x,0.754082,1.88026)+0.452996*TMath::Gaus(x,1.49283,2.4567)+0.0943071))", + "111_v2_def": "x*x*(0.29722*exp(-x/0.318227)+0.560172*pow(1+x/0.401715,-2.04434)+0.193576*exp(-x/1.2557))", + "310_v2_def": "x*x*(0.0280321*exp(-x/0.49621)+0.328197*pow(1+x/0.0128527,-0.853155)+0.214485*exp(-x/1.29581))", + "333_v2_def": "x*x*(1.2367*exp(-x/0.629473)+-3.9919*pow(1+x/0.886275,-3.11899)+0.274776*exp(-x/1.82705))" }, - "5TeV_0005_wRatio_etatest": { + "5TeV_3040_wRatio_etatest_pi0up": { "histoMtScaleFactor": { "113": 0.85, "223": 0.7, "331": 0.4 }, - "111_pt": "TMath::TwoPi()*x*(219.382*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.384303)+2471.6*pow(exp(-1.12373*x-0.752507*x*x)+x/0.438343,-5.45943))", - "221_pt": "(TMath::TwoPi()*x*(219.382*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.384303)+2471.6*pow(exp(-1.12373*x-0.752507*x*x)+x/0.438343,-5.45943)))*((9.36242e-05*TMath::Power((exp(- -2.56458*sqrt(x*x+0.547*0.547-0.139*0.139)-0.742625*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.457041/9.36242e-05,-1./8.15756)*sqrt(x*x+0.547*0.547-0.139*0.139)/0.0235913),-8.15756)/TMath::Power((exp(- -2.56458*x-0.742625*x*x)+x/0.0235913),-8.15756)+1.50683*TMath::Landau(x,2.1159,0.843387)))", - "333_pt": "(TMath::TwoPi()*x*(219.382*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.384303)+2471.6*pow(exp(-1.12373*x-0.752507*x*x)+x/0.438343,-5.45943)))*(0.1191/(1+exp(-(x- -0.316437)/0.554431))+-0.507618*TMath::Gaus(x,1.69654,1.62649)+0.494136*TMath::Gaus(x,2.56805,1.6193)+0.0789659)" + "111_pt": "TMath::TwoPi()*x*(26.8208*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.408437)+1019.42*pow(exp(-0.662498*x-0.498822*x*x)+x/0.495404,-5.68121))", + "221_pt": "(TMath::TwoPi()*x*(26.8208*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.408437)+1019.42*pow(exp(-0.662498*x-0.498822*x*x)+x/0.495404,-5.68121)))*(0.00028072*TMath::Power((exp(- -3.25466*sqrt(x*x+0.547*0.547-0.139*0.139)-0.761446*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.471621/0.00028072,-1./6.66634)*sqrt(x*x+0.547*0.547-0.139*0.139)/0.00480919),-6.66634)/TMath::Power((exp(- -3.25466*x-0.761446*x*x)+x/0.00480919),-6.66634)+1.12405*TMath::Landau(x,2.5931,1.14043))", + "333_pt": "(TMath::TwoPi()*x*(24.3786*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.412015)+908.266*pow(exp(-0.735883*x-0.425837*x*x)+x/0.505017,-5.68841)))*(0.106572/(1+exp(-(x-1.71328)/1.68645))+-0.54012*TMath::Gaus(x,0.754082,1.88026)+0.452996*TMath::Gaus(x,1.49283,2.4567)+0.0943071)", + "111_v2_def": "x*x*(0.29722*exp(-x/0.318227)+0.560172*pow(1+x/0.401715,-2.04434)+0.193576*exp(-x/1.2557))", + "310_v2_def": "x*x*(0.0280321*exp(-x/0.49621)+0.328197*pow(1+x/0.0128527,-0.853155)+0.214485*exp(-x/1.29581))", + "333_v2_def": "x*x*(1.2367*exp(-x/0.629473)+-3.9919*pow(1+x/0.886275,-3.11899)+0.274776*exp(-x/1.82705))" }, - "5TeV_0005_wRatio_etatestup": { + "5TeV_3040_wRatio_etatest_pi0down": { "histoMtScaleFactor": { "113": 0.85, "223": 0.7, "331": 0.4 }, - "111_pt": "TMath::TwoPi()*x*(219.382*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.384303)+2471.6*pow(exp(-1.12373*x-0.752507*x*x)+x/0.438343,-5.45943))", - "221_pt": "(TMath::TwoPi()*x*(219.382*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.384303)+2471.6*pow(exp(-1.12373*x-0.752507*x*x)+x/0.438343,-5.45943)))*((((x<9.168)*(TMath::Max(TMath::Max(0.00147786*TMath::Power((exp(- -0.297384*sqrt(x*x+0.547*0.547-0.139*0.139)-0.0282388*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.464407/0.00147786,-1./1.14468)*sqrt(x*x+0.547*0.547-0.139*0.139)/0.0102013),-1.14468)/TMath::Power((exp(- -0.297384*x-0.0282388*x*x)+x/0.0102013),-1.14468)+2.16163*TMath::Landau(x,3.97083,1.44648),0.00489879*TMath::Power((exp(- -2.36164*sqrt(x*x+0.547*0.547-0.139*0.139)-0.72334*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.478277/0.00489879,-1./1.05253)*sqrt(x*x+0.547*0.547-0.139*0.139)/0.0139668),-1.05253)/TMath::Power((exp(- -2.36164*x-0.72334*x*x)+x/0.0139668),-1.05253)+1.73053*TMath::Landau(x,2.39399,0.879267)),TMath::Max(0.000175259*TMath::Power((exp(- -2.1325*sqrt(x*x+0.547*0.547-0.139*0.139)-0.591882*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.482288/0.000175259,-1./15.7215)*sqrt(x*x+0.547*0.547-0.139*0.139)/0.027171),-15.7215)/TMath::Power((exp(- -2.1325*x-0.591882*x*x)+x/0.027171),-15.7215)+1.7109*TMath::Landau(x,2.36532,0.973179),0.00489879*TMath::Power((exp(- -2.36164*sqrt(x*x+0.547*0.547-0.139*0.139)-0.72334*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.478277/0.00489879,-1./1.05253)*sqrt(x*x+0.547*0.547-0.139*0.139)/0.0139668),-1.05253)/TMath::Power((exp(- -2.36164*x-0.72334*x*x)+x/0.0139668),-1.05253)+1.73053*TMath::Landau(x,2.39399,0.879267))))+(x>=9.168)*1.08*(((0.499605*1.)+(155.836*(-0.00300369*TMath::Power(1.+((x/1.09373)*(x/1.09373)),-(0.672088)))))/(1.+(-0.00300369*TMath::Power(1.+((x/1.09373)*(x/1.09373)),-(0.672088))))))))", - "333_pt": "(TMath::TwoPi()*x*(219.382*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.384303)+2471.6*pow(exp(-1.12373*x-0.752507*x*x)+x/0.438343,-5.45943)))*(0.1191/(1+exp(-(x- -0.316437)/0.554431))+-0.507618*TMath::Gaus(x,1.69654,1.62649)+0.494136*TMath::Gaus(x,2.56805,1.6193)+0.0789659)" + "111_pt": "TMath::TwoPi()*x*(22.2048*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.41549)+802.356*pow(exp(-0.814916*x-0.343908*x*x)+x/0.515668,-5.69624))", + "221_pt": "(TMath::TwoPi()*x*(22.2048*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.41549)+802.356*pow(exp(-0.814916*x-0.343908*x*x)+x/0.515668,-5.69624)))*(0.00028072*TMath::Power((exp(- -3.25466*sqrt(x*x+0.547*0.547-0.139*0.139)-0.761446*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.471621/0.00028072,-1./6.66634)*sqrt(x*x+0.547*0.547-0.139*0.139)/0.00480919),-6.66634)/TMath::Power((exp(- -3.25466*x-0.761446*x*x)+x/0.00480919),-6.66634)+1.12405*TMath::Landau(x,2.5931,1.14043))", + "333_pt": "(TMath::TwoPi()*x*(24.3786*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.412015)+908.266*pow(exp(-0.735883*x-0.425837*x*x)+x/0.505017,-5.68841)))*(0.106572/(1+exp(-(x-1.71328)/1.68645))+-0.54012*TMath::Gaus(x,0.754082,1.88026)+0.452996*TMath::Gaus(x,1.49283,2.4567)+0.0943071)", + "111_v2_def": "x*x*(0.29722*exp(-x/0.318227)+0.560172*pow(1+x/0.401715,-2.04434)+0.193576*exp(-x/1.2557))", + "310_v2_def": "x*x*(0.0280321*exp(-x/0.49621)+0.328197*pow(1+x/0.0128527,-0.853155)+0.214485*exp(-x/1.29581))", + "333_v2_def": "x*x*(1.2367*exp(-x/0.629473)+-3.9919*pow(1+x/0.886275,-3.11899)+0.274776*exp(-x/1.82705))" }, - "5TeV_0005_wRatio_etatestdown": { + "5TeV_4050_wRatio_etatest": { "histoMtScaleFactor": { "113": 0.85, "223": 0.7, "331": 0.4 }, - "111_pt": "TMath::TwoPi()*x*(219.382*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.384303)+2471.6*pow(exp(-1.12373*x-0.752507*x*x)+x/0.438343,-5.45943))", - "221_pt": "(TMath::TwoPi()*x*(219.382*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.384303)+2471.6*pow(exp(-1.12373*x-0.752507*x*x)+x/0.438343,-5.45943)))*((((x<11.6581)*(TMath::Min(TMath::Min(0.00302888*TMath::Power((exp(-0.246982*sqrt(x*x+0.547*0.547-0.139*0.139)-0.204843*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.485848/0.00302888,-1./335.069)*sqrt(x*x+0.547*0.547-0.139*0.139)/2.46929),-335.069)/TMath::Power((exp(-0.246982*x-0.204843*x*x)+x/2.46929),-335.069)+2.62546*TMath::Landau(x,3.61147,1.38592),0.926*(9.36242e-05*TMath::Power((exp(- -2.56458*sqrt(x*x+0.547*0.547-0.139*0.139)-0.742625*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.457041/9.36242e-05,-1./8.15756)*sqrt(x*x+0.547*0.547-0.139*0.139)/0.0235913),-8.15756)/TMath::Power((exp(- -2.56458*x-0.742625*x*x)+x/0.0235913),-8.15756)+1.50683*TMath::Landau(x,2.1159,0.843387))),TMath::Min(0.00624935*TMath::Power((exp(- -2.56471*sqrt(x*x+0.547*0.547-0.139*0.139)-2.49586*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.45/0.00624935,-1./44.8766)*sqrt(x*x+0.547*0.547-0.139*0.139)/0.202909),-44.8766)/TMath::Power((exp(- -2.56471*x-2.49586*x*x)+x/0.202909),-44.8766)+1.58602*TMath::Landau(x,2.23166,0.861224),0.926*(9.36242e-05*TMath::Power((exp(- -2.56458*sqrt(x*x+0.547*0.547-0.139*0.139)-0.742625*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.457041/9.36242e-05,-1./8.15756)*sqrt(x*x+0.547*0.547-0.139*0.139)/0.0235913),-8.15756)/TMath::Power((exp(- -2.56458*x-0.742625*x*x)+x/0.0235913),-8.15756)+1.50683*TMath::Landau(x,2.1159,0.843387)))))+(x>=11.6581)*0.426)))", - "333_pt": "(TMath::TwoPi()*x*(219.382*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.384303)+2471.6*pow(exp(-1.12373*x-0.752507*x*x)+x/0.438343,-5.45943)))*(0.1191/(1+exp(-(x- -0.316437)/0.554431))+-0.507618*TMath::Gaus(x,1.69654,1.62649)+0.494136*TMath::Gaus(x,2.56805,1.6193)+0.0789659)" + "111_pt": "TMath::TwoPi()*x*(9.00063*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.428379)+636.492*pow(exp(-0.659016*x-0.397367*x*x)+x/0.512428,-5.72313))", + "221_pt": "(TMath::TwoPi()*x*(9.00063*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.428379)+636.492*pow(exp(-0.659016*x-0.397367*x*x)+x/0.512428,-5.72313)))*(0.00028072*TMath::Power((exp(- -3.25466*sqrt(x*x+0.547*0.547-0.139*0.139)-0.761446*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.471621/0.00028072,-1./6.66634)*sqrt(x*x+0.547*0.547-0.139*0.139)/0.00480919),-6.66634)/TMath::Power((exp(- -3.25466*x-0.761446*x*x)+x/0.00480919),-6.66634)+1.12405*TMath::Landau(x,2.5931,1.14043))", + "333_pt": "(TMath::TwoPi()*x*(9.00063*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.428379)+636.492*pow(exp(-0.659016*x-0.397367*x*x)+x/0.512428,-5.72313)))*(0.0979137/(1+exp(-(x-1.53259)/0.370136))+-0.537275*TMath::Gaus(x,0.706186,2.03543)+0.45201*TMath::Gaus(x,1.18876,2.40911)+0.104985)", + "111_v2_def": "x*x*(0.171282*exp(-x/0.353318)+1.13749*pow(1+x/0.290699,-2.02699)+0.20604*exp(-x/1.19178))", + "310_v2_def": "x*x*(-0.524086*exp(-x/0.402378)+0.891134*pow(1+x/1.15413,-3.07832)+0.0812177*exp(-x/1.22691))", + "333_v2_def": "x*x*(-2.78876*exp(-x/0.581864)+3.6669*pow(1+x/3.01301,-6.60505)+0.0436791*exp(-x/1.23285))" }, - "5TeV_0510_wRatio_etatest": { + "5TeV_4050_wRatio_etatest_ratiosup": { + "histoMtScaleFactor": { + "113": 1.02, + "223": 0.84, + "331": 0.48 + }, + "111_pt": "TMath::TwoPi()*x*(9.00063*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.428379)+636.492*pow(exp(-0.659016*x-0.397367*x*x)+x/0.512428,-5.72313))", + "221_pt": "(TMath::TwoPi()*x*(9.00063*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.428379)+636.492*pow(exp(-0.659016*x-0.397367*x*x)+x/0.512428,-5.72313)))*(((x<11.3582)*(TMath::Max(TMath::Max(3.62635e-06*TMath::Power((exp(-1.04848*sqrt(x*x+0.547*0.547-0.139*0.139)-0.00760898*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.494194/3.62635e-06,-1./6.52441)*sqrt(x*x+0.547*0.547-0.139*0.139)/1.37753),-6.52441)/TMath::Power((exp(-1.04848*x-0.00760898*x*x)+x/1.37753),-6.52441)+1.85024*TMath::Landau(x,2.3343,0.982346),2.67119e-07*TMath::Power((exp(-0.504012*sqrt(x*x+0.547*0.547-0.139*0.139)-0.192368*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.495159/2.67119e-07,-1./2.55351)*sqrt(x*x+0.547*0.547-0.139*0.139)/0.0853583),-2.55351)/TMath::Power((exp(-0.504012*x-0.192368*x*x)+x/0.0853583),-2.55351)+1.96831*TMath::Landau(x,2.25187,0.92394)),TMath::Max(0.00152416*TMath::Power((exp(-1.50417*sqrt(x*x+0.547*0.547-0.139*0.139)- -0.0524029*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.496824/0.00152416,-1./3.48956)*sqrt(x*x+0.547*0.547-0.139*0.139)/8.34972),-3.48956)/TMath::Power((exp(-1.50417*x- -0.0524029*x*x)+x/8.34972),-3.48956)+1.90397*TMath::Landau(x,2.2369,0.884653),2.67119e-07*TMath::Power((exp(-0.504012*sqrt(x*x+0.547*0.547-0.139*0.139)-0.192368*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.495159/2.67119e-07,-1./2.55351)*sqrt(x*x+0.547*0.547-0.139*0.139)/0.0853583),-2.55351)/TMath::Power((exp(-0.504012*x-0.192368*x*x)+x/0.0853583),-2.55351)+1.96831*TMath::Landau(x,2.25187,0.92394))))+(x>=11.3582)*1.08*(((0.499605*1.)+(155.836*(-0.00300369*TMath::Power(1.+((x/1.09373)*(x/1.09373)),-(0.672088)))))/(1.+(-0.00300369*TMath::Power(1.+((x/1.09373)*(x/1.09373)),-(0.672088)))))))", + "333_pt": "(TMath::TwoPi()*x*(9.00063*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.428379)+636.492*pow(exp(-0.659016*x-0.397367*x*x)+x/0.512428,-5.72313)))*((x<=0.5)*(-0.3/0.5*x+1.5)*(0.0979137/(1+exp(-(x-1.53259)/0.370136))+-0.537275*TMath::Gaus(x,0.706186,2.03543)+0.45201*TMath::Gaus(x,1.18876,2.40911)+0.104985)+(x>0.5)*1.2*(0.0979137/(1+exp(-(x-1.53259)/0.370136))+-0.537275*TMath::Gaus(x,0.706186,2.03543)+0.45201*TMath::Gaus(x,1.18876,2.40911)+0.104985))", + "111_v2_def": "x*x*(0.171282*exp(-x/0.353318)+1.13749*pow(1+x/0.290699,-2.02699)+0.20604*exp(-x/1.19178))", + "310_v2_def": "x*x*(-0.524086*exp(-x/0.402378)+0.891134*pow(1+x/1.15413,-3.07832)+0.0812177*exp(-x/1.22691))", + "333_v2_def": "x*x*(-2.78876*exp(-x/0.581864)+3.6669*pow(1+x/3.01301,-6.60505)+0.0436791*exp(-x/1.23285))" + }, + "5TeV_4050_wRatio_etatest_ratiosdown": { + "histoMtScaleFactor": { + "113": 0.68, + "223": 0.56, + "331": 0.32 + }, + "111_pt": "TMath::TwoPi()*x*(9.00063*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.428379)+636.492*pow(exp(-0.659016*x-0.397367*x*x)+x/0.512428,-5.72313))", + "221_pt": "(TMath::TwoPi()*x*(9.00063*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.428379)+636.492*pow(exp(-0.659016*x-0.397367*x*x)+x/0.512428,-5.72313)))*(((x<6.7544)*(0.9*(TMath::Min(TMath::Min(0.000624633*TMath::Power((exp(- -0.139403*sqrt(x*x+0.547*0.547-0.139*0.139)-0.883882*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.45/0.000624633,-1./91.7456)*sqrt(x*x+0.547*0.547-0.139*0.139)/1.27557),-91.7456)/TMath::Power((exp(- -0.139403*x-0.883882*x*x)+x/1.27557),-91.7456)+1.93275*TMath::Landau(x,2.96282,1.17967),0.806467*TMath::Power((exp(-0.123521*sqrt(x*x+0.547*0.547-0.139*0.139)-0.533763*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.45/0.806467,-1./80.3123)*sqrt(x*x+0.547*0.547-0.139*0.139)/1.16637),-80.3123)/TMath::Power((exp(-0.123521*x-0.533763*x*x)+x/1.16637),-80.3123)+1.6774*TMath::Landau(x,3.13342,1.2489)),TMath::Min(0.0105235*TMath::Power((exp(- -0.843626*sqrt(x*x+0.547*0.547-0.139*0.139)-1.26105*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.450001/0.0105235,-1./22.4747)*sqrt(x*x+0.547*0.547-0.139*0.139)/0.776751),-22.4747)/TMath::Power((exp(- -0.843626*x-1.26105*x*x)+x/0.776751),-22.4747)+0.774392*TMath::Landau(x,2.71895,1.26325),0.806467*TMath::Power((exp(-0.123521*sqrt(x*x+0.547*0.547-0.139*0.139)-0.533763*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.45/0.806467,-1./80.3123)*sqrt(x*x+0.547*0.547-0.139*0.139)/1.16637),-80.3123)/TMath::Power((exp(-0.123521*x-0.533763*x*x)+x/1.16637),-80.3123)+1.6774*TMath::Landau(x,3.13342,1.2489)))))+(x>=6.7544)*0.426))", + "333_pt": "(TMath::TwoPi()*x*(9.00063*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.428379)+636.492*pow(exp(-0.659016*x-0.397367*x*x)+x/0.512428,-5.72313)))*((x<=0.5)*(0.3/0.5*x+0.5)*(0.0979137/(1+exp(-(x-1.53259)/0.370136))+-0.537275*TMath::Gaus(x,0.706186,2.03543)+0.45201*TMath::Gaus(x,1.18876,2.40911)+0.104985)+(x>0.5)*0.8*(0.0979137/(1+exp(-(x-1.53259)/0.370136))+-0.537275*TMath::Gaus(x,0.706186,2.03543)+0.45201*TMath::Gaus(x,1.18876,2.40911)+0.104985))", + "111_v2_def": "x*x*(0.171282*exp(-x/0.353318)+1.13749*pow(1+x/0.290699,-2.02699)+0.20604*exp(-x/1.19178))", + "310_v2_def": "x*x*(-0.524086*exp(-x/0.402378)+0.891134*pow(1+x/1.15413,-3.07832)+0.0812177*exp(-x/1.22691))", + "333_v2_def": "x*x*(-2.78876*exp(-x/0.581864)+3.6669*pow(1+x/3.01301,-6.60505)+0.0436791*exp(-x/1.23285))" + }, + "5TeV_4050_wRatio_etatest_pi0up": { "histoMtScaleFactor": { "113": 0.85, "223": 0.7, "331": 0.4 }, - "111_pt": "TMath::TwoPi()*x*(156.897*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.390093)+1835.17*pow(exp(-1.1618*x-0.514011*x*x)+x/0.465575,-5.50332))", - "221_pt": "(TMath::TwoPi()*x*(156.897*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.390093)+1835.17*pow(exp(-1.1618*x-0.514011*x*x)+x/0.465575,-5.50332)))*((0.000246618*TMath::Power((exp(- -3.44417*sqrt(x*x+0.547*0.547-0.139*0.139)-0.952884*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.476466/0.000246618,-1./8.8133)*sqrt(x*x+0.547*0.547-0.139*0.139)/0.0060628),-8.8133)/TMath::Power((exp(- -3.44417*x-0.952884*x*x)+x/0.0060628),-8.8133)+1.09231*TMath::Landau(x,2.04517,0.864605)))", - "333_pt": "(TMath::TwoPi()*x*(156.897*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.390093)+1835.17*pow(exp(-1.1618*x-0.514011*x*x)+x/0.465575,-5.50332)))*(0.1191/(1+exp(-(x- -0.316437)/0.554431))+-0.507618*TMath::Gaus(x,1.69654,1.62649)+0.494136*TMath::Gaus(x,2.56805,1.6193)+0.0789659)" + "111_pt": "TMath::TwoPi()*x*(10.1537*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.423934)+708.793*pow(exp(-0.595237*x-0.468542*x*x)+x/0.502946,-5.71377))", + "221_pt": "(TMath::TwoPi()*x*(10.1537*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.423934)+708.793*pow(exp(-0.595237*x-0.468542*x*x)+x/0.502946,-5.71377)))*(0.00028072*TMath::Power((exp(- -3.25466*sqrt(x*x+0.547*0.547-0.139*0.139)-0.761446*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.471621/0.00028072,-1./6.66634)*sqrt(x*x+0.547*0.547-0.139*0.139)/0.00480919),-6.66634)/TMath::Power((exp(- -3.25466*x-0.761446*x*x)+x/0.00480919),-6.66634)+1.12405*TMath::Landau(x,2.5931,1.14043))", + "333_pt": "(TMath::TwoPi()*x*(9.00063*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.428379)+636.492*pow(exp(-0.659016*x-0.397367*x*x)+x/0.512428,-5.72313)))*(0.0979137/(1+exp(-(x-1.53259)/0.370136))+-0.537275*TMath::Gaus(x,0.706186,2.03543)+0.45201*TMath::Gaus(x,1.18876,2.40911)+0.104985)", + "111_v2_def": "x*x*(0.171282*exp(-x/0.353318)+1.13749*pow(1+x/0.290699,-2.02699)+0.20604*exp(-x/1.19178))", + "310_v2_def": "x*x*(-0.524086*exp(-x/0.402378)+0.891134*pow(1+x/1.15413,-3.07832)+0.0812177*exp(-x/1.22691))", + "333_v2_def": "x*x*(-2.78876*exp(-x/0.581864)+3.6669*pow(1+x/3.01301,-6.60505)+0.0436791*exp(-x/1.23285))" }, - "5TeV_0510_wRatio_etatestup": { + "5TeV_4050_wRatio_etatest_pi0down": { "histoMtScaleFactor": { "113": 0.85, "223": 0.7, "331": 0.4 }, - "111_pt": "TMath::TwoPi()*x*(156.897*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.390093)+1835.17*pow(exp(-1.1618*x-0.514011*x*x)+x/0.465575,-5.50332))", - "221_pt": "(TMath::TwoPi()*x*(156.897*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.390093)+1835.17*pow(exp(-1.1618*x-0.514011*x*x)+x/0.465575,-5.50332)))*((((x<10.278)*(TMath::Max(TMath::Max(0.000295533*TMath::Power((exp(- -1.60043*sqrt(x*x+0.547*0.547-0.139*0.139)-0.655985*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.500931/0.000295533,-1./16.3545)*sqrt(x*x+0.547*0.547-0.139*0.139)/0.130844),-16.3545)/TMath::Power((exp(- -1.60043*x-0.655985*x*x)+x/0.130844),-16.3545)+1.5704*TMath::Landau(x,2.17776,0.921819),0.00540987*TMath::Power((exp(-0.202531*sqrt(x*x+0.547*0.547-0.139*0.139)-0.404918*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.496965/0.00540987,-1./0.457227)*sqrt(x*x+0.547*0.547-0.139*0.139)/0.010344),-0.457227)/TMath::Power((exp(-0.202531*x-0.404918*x*x)+x/0.010344),-0.457227)+1.48331*TMath::Landau(x,2.17198,0.823192)),TMath::Max(3.85365e-05*TMath::Power((exp(- -0.50908*sqrt(x*x+0.547*0.547-0.139*0.139)-0.0438035*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.463429/3.85365e-05,-1./6.0698)*sqrt(x*x+0.547*0.547-0.139*0.139)/0.0472676),-6.0698)/TMath::Power((exp(- -0.50908*x-0.0438035*x*x)+x/0.0472676),-6.0698)+2.34475*TMath::Landau(x,3.86817,1.59145),0.00540987*TMath::Power((exp(-0.202531*sqrt(x*x+0.547*0.547-0.139*0.139)-0.404918*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.496965/0.00540987,-1./0.457227)*sqrt(x*x+0.547*0.547-0.139*0.139)/0.010344),-0.457227)/TMath::Power((exp(-0.202531*x-0.404918*x*x)+x/0.010344),-0.457227)+1.48331*TMath::Landau(x,2.17198,0.823192))))+(x>=10.278)*1.08*(((0.499605*1.)+(155.836*(-0.00300369*TMath::Power(1.+((x/1.09373)*(x/1.09373)),-(0.672088)))))/(1.+(-0.00300369*TMath::Power(1.+((x/1.09373)*(x/1.09373)),-(0.672088))))))))", - "333_pt": "(TMath::TwoPi()*x*(156.897*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.390093)+1835.17*pow(exp(-1.1618*x-0.514011*x*x)+x/0.465575,-5.50332)))*(0.1191/(1+exp(-(x- -0.316437)/0.554431))+-0.507618*TMath::Gaus(x,1.69654,1.62649)+0.494136*TMath::Gaus(x,2.56805,1.6193)+0.0789659)" + "111_pt": "TMath::TwoPi()*x*(8.04609*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.432276)+566.92*pow(exp(-0.727741*x-0.316795*x*x)+x/0.523032,-5.73361))", + "221_pt": "(TMath::TwoPi()*x*(8.04609*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.432276)+566.92*pow(exp(-0.727741*x-0.316795*x*x)+x/0.523032,-5.73361)))*(0.00028072*TMath::Power((exp(- -3.25466*sqrt(x*x+0.547*0.547-0.139*0.139)-0.761446*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.471621/0.00028072,-1./6.66634)*sqrt(x*x+0.547*0.547-0.139*0.139)/0.00480919),-6.66634)/TMath::Power((exp(- -3.25466*x-0.761446*x*x)+x/0.00480919),-6.66634)+1.12405*TMath::Landau(x,2.5931,1.14043))", + "333_pt": "(TMath::TwoPi()*x*(9.00063*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.428379)+636.492*pow(exp(-0.659016*x-0.397367*x*x)+x/0.512428,-5.72313)))*(0.0979137/(1+exp(-(x-1.53259)/0.370136))+-0.537275*TMath::Gaus(x,0.706186,2.03543)+0.45201*TMath::Gaus(x,1.18876,2.40911)+0.104985)", + "111_v2_def": "x*x*(0.171282*exp(-x/0.353318)+1.13749*pow(1+x/0.290699,-2.02699)+0.20604*exp(-x/1.19178))", + "310_v2_def": "x*x*(-0.524086*exp(-x/0.402378)+0.891134*pow(1+x/1.15413,-3.07832)+0.0812177*exp(-x/1.22691))", + "333_v2_def": "x*x*(-2.78876*exp(-x/0.581864)+3.6669*pow(1+x/3.01301,-6.60505)+0.0436791*exp(-x/1.23285))" }, "5TeV_0005_wRatio_etatestcenter_pp13": { "histoMtScaleFactor": { @@ -279,26 +359,6 @@ "221_pt": "(TMath::TwoPi()*x*(156.897*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.390093)+1835.17*pow(exp(-1.1618*x-0.514011*x*x)+x/0.465575,-5.50332)))*((0.000946307*TMath::Power((exp(- -0.678673*sqrt(x*x+0.547*0.547-0.139*0.139)-1.46197*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.475252/0.000946307,-1./42.0453)*sqrt(x*x+0.547*0.547-0.139*0.139)/0.718978),-42.0453)/TMath::Power((exp(- -0.678673*x-1.46197*x*x)+x/0.718978),-42.0453)+1.5622*TMath::Landau(x,2.61642,1.08512)))", "333_pt": "(TMath::TwoPi()*x*(156.897*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.390093)+1835.17*pow(exp(-1.1618*x-0.514011*x*x)+x/0.465575,-5.50332)))*(0.1191/(1+exp(-(x- -0.316437)/0.554431))+-0.507618*TMath::Gaus(x,1.69654,1.62649)+0.494136*TMath::Gaus(x,2.56805,1.6193)+0.0789659)" }, - "5TeV_0005_wRatio_etatestpi0down": { - "histoMtScaleFactor": { - "113": 0.85, - "223": 0.7, - "331": 0.4 - }, - "111_pt": "TMath::TwoPi()*x*(202.506*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.386428)+2172.76*pow(exp(-1.20019*x-0.719277*x*x)+x/0.447319,-5.46279))", - "221_pt": "(TMath::TwoPi()*x*(202.506*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.386428)+2172.76*pow(exp(-1.20019*x-0.719277*x*x)+x/0.447319,-5.46279)))*((9.36242e-05*TMath::Power((exp(- -2.56458*sqrt(x*x+0.547*0.547-0.139*0.139)-0.742625*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.457041/9.36242e-05,-1./8.15756)*sqrt(x*x+0.547*0.547-0.139*0.139)/0.0235913),-8.15756)/TMath::Power((exp(- -2.56458*x-0.742625*x*x)+x/0.0235913),-8.15756)+1.50683*TMath::Landau(x,2.1159,0.843387)))", - "333_pt": "(TMath::TwoPi()*x*(219.382*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.384303)+2471.6*pow(exp(-1.12373*x-0.752507*x*x)+x/0.438343,-5.45943)))*(0.1191/(1+exp(-(x- -0.316437)/0.554431))+-0.507618*TMath::Gaus(x,1.69654,1.62649)+0.494136*TMath::Gaus(x,2.56805,1.6193)+0.0789659)" - }, - "5TeV_0005_wRatio_etatestpi0up": { - "histoMtScaleFactor": { - "113": 0.85, - "223": 0.7, - "331": 0.4 - }, - "111_pt": "TMath::TwoPi()*x*(236.555*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.382365)+2787.27*pow(exp(-1.05343*x-0.780376*x*x)+x/0.43017,-5.45613))", - "221_pt": "(TMath::TwoPi()*x*(236.555*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.382365)+2787.27*pow(exp(-1.05343*x-0.780376*x*x)+x/0.43017,-5.45613)))*((9.36242e-05*TMath::Power((exp(- -2.56458*sqrt(x*x+0.547*0.547-0.139*0.139)-0.742625*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.457041/9.36242e-05,-1./8.15756)*sqrt(x*x+0.547*0.547-0.139*0.139)/0.0235913),-8.15756)/TMath::Power((exp(- -2.56458*x-0.742625*x*x)+x/0.0235913),-8.15756)+1.50683*TMath::Landau(x,2.1159,0.843387)))", - "333_pt": "(TMath::TwoPi()*x*(219.382*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.384303)+2471.6*pow(exp(-1.12373*x-0.752507*x*x)+x/0.438343,-5.45943)))*(0.1191/(1+exp(-(x- -0.316437)/0.554431))+-0.507618*TMath::Gaus(x,1.69654,1.62649)+0.494136*TMath::Gaus(x,2.56805,1.6193)+0.0789659)" - }, "5TeV_0005_wRatio_etatestpi0up_pp13": { "histoMtScaleFactor": { "113": 0.85, @@ -319,26 +379,6 @@ "221_pt": "(TMath::TwoPi()*x*(202.506*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.386428)+2172.76*pow(exp(-1.20019*x-0.719277*x*x)+x/0.447319,-5.46279)))*((0.000376932*TMath::Power((exp(-0.821512*sqrt(x*x+0.547*0.547-0.139*0.139)-0.143837*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.4615/0.000376932,-1./13.0537)*sqrt(x*x+0.547*0.547-0.139*0.139)/4.58538),-13.0537)/TMath::Power((exp(-0.821512*x-0.143837*x*x)+x/4.58538),-13.0537)+1.65536*TMath::Landau(x,2.12278,0.816391)))", "333_pt": "(TMath::TwoPi()*x*(219.382*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.384303)+2471.6*pow(exp(-1.12373*x-0.752507*x*x)+x/0.438343,-5.45943)))*(0.1191/(1+exp(-(x- -0.316437)/0.554431))+-0.507618*TMath::Gaus(x,1.69654,1.62649)+0.494136*TMath::Gaus(x,2.56805,1.6193)+0.0789659)" }, - "5TeV_0510_wRatio_etatestpi0down": { - "histoMtScaleFactor": { - "113": 0.85, - "223": 0.7, - "331": 0.4 - }, - "111_pt": "TMath::TwoPi()*x*(143.633*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.392747)+1585.64*pow(exp(-1.25879*x-0.453071*x*x)+x/0.476927,-5.50804))", - "221_pt": "(TMath::TwoPi()*x*(143.633*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.392747)+1585.64*pow(exp(-1.25879*x-0.453071*x*x)+x/0.476927,-5.50804)))*((0.000246618*TMath::Power((exp(- -3.44417*sqrt(x*x+0.547*0.547-0.139*0.139)-0.952884*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.476466/0.000246618,-1./8.8133)*sqrt(x*x+0.547*0.547-0.139*0.139)/0.0060628),-8.8133)/TMath::Power((exp(- -3.44417*x-0.952884*x*x)+x/0.0060628),-8.8133)+1.09231*TMath::Landau(x,2.04517,0.864605)))", - "333_pt": "(TMath::TwoPi()*x*(156.897*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.390093)+1835.17*pow(exp(-1.1618*x-0.514011*x*x)+x/0.465575,-5.50332)))*(0.1191/(1+exp(-(x- -0.316437)/0.554431))+-0.507618*TMath::Gaus(x,1.69654,1.62649)+0.494136*TMath::Gaus(x,2.56805,1.6193)+0.0789659)" - }, - "5TeV_0510_wRatio_etatestpi0up": { - "histoMtScaleFactor": { - "113": 0.85, - "223": 0.7, - "331": 0.4 - }, - "111_pt": "TMath::TwoPi()*x*(170.514*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.38767)+2102.92*pow(exp(-1.0722*x-0.567406*x*x)+x/0.45525,-5.49867))", - "221_pt": "(TMath::TwoPi()*x*(170.514*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.38767)+2102.92*pow(exp(-1.0722*x-0.567406*x*x)+x/0.45525,-5.49867)))*((0.000246618*TMath::Power((exp(- -3.44417*sqrt(x*x+0.547*0.547-0.139*0.139)-0.952884*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.476466/0.000246618,-1./8.8133)*sqrt(x*x+0.547*0.547-0.139*0.139)/0.0060628),-8.8133)/TMath::Power((exp(- -3.44417*x-0.952884*x*x)+x/0.0060628),-8.8133)+1.09231*TMath::Landau(x,2.04517,0.864605)))", - "333_pt": "(TMath::TwoPi()*x*(156.897*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.390093)+1835.17*pow(exp(-1.1618*x-0.514011*x*x)+x/0.465575,-5.50332)))*(0.1191/(1+exp(-(x- -0.316437)/0.554431))+-0.507618*TMath::Gaus(x,1.69654,1.62649)+0.494136*TMath::Gaus(x,2.56805,1.6193)+0.0789659)" - }, "5TeV_0510_wRatio_etatestpi0up_pp13": { "histoMtScaleFactor": { "113": 0.85, @@ -359,16 +399,6 @@ "221_pt": "(TMath::TwoPi()*x*(143.633*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.392747)+1585.64*pow(exp(-1.25879*x-0.453071*x*x)+x/0.476927,-5.50804)))*((0.000946307*TMath::Power((exp(- -0.678673*sqrt(x*x+0.547*0.547-0.139*0.139)-1.46197*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.475252/0.000946307,-1./42.0453)*sqrt(x*x+0.547*0.547-0.139*0.139)/0.718978),-42.0453)/TMath::Power((exp(- -0.678673*x-1.46197*x*x)+x/0.718978),-42.0453)+1.5622*TMath::Landau(x,2.61642,1.08512)))", "333_pt": "(TMath::TwoPi()*x*(156.897*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.390093)+1835.17*pow(exp(-1.1618*x-0.514011*x*x)+x/0.465575,-5.50332)))*(0.1191/(1+exp(-(x- -0.316437)/0.554431))+-0.507618*TMath::Gaus(x,1.69654,1.62649)+0.494136*TMath::Gaus(x,2.56805,1.6193)+0.0789659)" }, - "5TeV_0510_wRatio_etatestdown": { - "histoMtScaleFactor": { - "113": 0.85, - "223": 0.7, - "331": 0.4 - }, - "111_pt": "TMath::TwoPi()*x*(156.897*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.390093)+1835.17*pow(exp(-1.1618*x-0.514011*x*x)+x/0.465575,-5.50332))", - "221_pt": "(TMath::TwoPi()*x*(156.897*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.390093)+1835.17*pow(exp(-1.1618*x-0.514011*x*x)+x/0.465575,-5.50332)))*((((x<11.6581)*(TMath::Min(TMath::Min(0.00302888*TMath::Power((exp(-0.246982*sqrt(x*x+0.547*0.547-0.139*0.139)-0.204843*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.485848/0.00302888,-1./335.069)*sqrt(x*x+0.547*0.547-0.139*0.139)/2.46929),-335.069)/TMath::Power((exp(-0.246982*x-0.204843*x*x)+x/2.46929),-335.069)+2.62546*TMath::Landau(x,3.61147,1.38592),0.926*(9.36242e-05*TMath::Power((exp(- -2.56458*sqrt(x*x+0.547*0.547-0.139*0.139)-0.742625*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.457041/9.36242e-05,-1./8.15756)*sqrt(x*x+0.547*0.547-0.139*0.139)/0.0235913),-8.15756)/TMath::Power((exp(- -2.56458*x-0.742625*x*x)+x/0.0235913),-8.15756)+1.50683*TMath::Landau(x,2.1159,0.843387))),TMath::Min(0.00624935*TMath::Power((exp(- -2.56471*sqrt(x*x+0.547*0.547-0.139*0.139)-2.49586*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.45/0.00624935,-1./44.8766)*sqrt(x*x+0.547*0.547-0.139*0.139)/0.202909),-44.8766)/TMath::Power((exp(- -2.56471*x-2.49586*x*x)+x/0.202909),-44.8766)+1.58602*TMath::Landau(x,2.23166,0.861224),0.926*(9.36242e-05*TMath::Power((exp(- -2.56458*sqrt(x*x+0.547*0.547-0.139*0.139)-0.742625*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.457041/9.36242e-05,-1./8.15756)*sqrt(x*x+0.547*0.547-0.139*0.139)/0.0235913),-8.15756)/TMath::Power((exp(- -2.56458*x-0.742625*x*x)+x/0.0235913),-8.15756)+1.50683*TMath::Landau(x,2.1159,0.843387)))))+(x>=11.6581)*0.426)))", - "333_pt": "(TMath::TwoPi()*x*(156.897*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.390093)+1835.17*pow(exp(-1.1618*x-0.514011*x*x)+x/0.465575,-5.50332)))*(0.1191/(1+exp(-(x- -0.316437)/0.554431))+-0.507618*TMath::Gaus(x,1.69654,1.62649)+0.494136*TMath::Gaus(x,2.56805,1.6193)+0.0789659)" - }, "5TeV_0510_wRatio_etatestdown_pp13": { "histoMtScaleFactor": { "113": 0.85, @@ -399,4 +429,4 @@ "221_pt": "(TMath::TwoPi()*x*(219.382*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.384303)+2471.6*pow(exp(-1.12373*x-0.752507*x*x)+x/0.438343,-5.45943)))*((((x<9.445400)*(TMath::Max(TMath::Max(1.68883e-09*TMath::Power((exp(- -1.16032*sqrt(x*x+0.547*0.547-0.139*0.139)-0.435153*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.480215/1.68883e-09,-1./16.7077)*sqrt(x*x+0.547*0.547-0.139*0.139)/0.0356546),-16.7077)/TMath::Power((exp(- -1.16032*x-0.435153*x*x)+x/0.0356546),-16.7077)+1.75797*TMath::Landau(x,2.41484,1.01421),0.00469052*TMath::Power((exp(- -0.124633*sqrt(x*x+0.547*0.547-0.139*0.139)-0.310906*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.479677/0.00469052,-1./0.657178)*sqrt(x*x+0.547*0.547-0.139*0.139)/0.0210313),-0.657178)/TMath::Power((exp(- -0.124633*x-0.310906*x*x)+x/0.0210313),-0.657178)+1.42452*TMath::Landau(x,2.46537,0.969902)),TMath::Max(4.88363e-10*TMath::Power((exp(- -2.82354*sqrt(x*x+0.547*0.547-0.139*0.139)-0.728315*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.483053/4.88363e-10,-1./16.2073)*sqrt(x*x+0.547*0.547-0.139*0.139)/0.0021492),-16.2073)/TMath::Power((exp(- -2.82354*x-0.728315*x*x)+x/0.0021492),-16.2073)+1.57731*TMath::Landau(x,2.40466,1.00957),0.00469052*TMath::Power((exp(- -0.124633*sqrt(x*x+0.547*0.547-0.139*0.139)-0.310906*(x*x+0.547*0.547-0.139*0.139))+TMath::Power(0.479677/0.00469052,-1./0.657178)*sqrt(x*x+0.547*0.547-0.139*0.139)/0.0210313),-0.657178)/TMath::Power((exp(- -0.124633*x-0.310906*x*x)+x/0.0210313),-0.657178)+1.42452*TMath::Landau(x,2.46537,0.969902))))+(x>=9.445400)*1.06*(((0.52091*1.)+(159.301*(-0.00307058*TMath::Power(1.+((x/1.03888)*(x/1.03888)),-(0.567478)))))/(1.+(-0.00307058*TMath::Power(1.+((x/1.03888)*(x/1.03888)),-(0.567478))))))))", "333_pt": "(TMath::TwoPi()*x*(219.382*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.384303)+2471.6*pow(exp(-1.12373*x-0.752507*x*x)+x/0.438343,-5.45943)))*(0.1191/(1+exp(-(x- -0.316437)/0.554431))+-0.507618*TMath::Gaus(x,1.69654,1.62649)+0.494136*TMath::Gaus(x,2.56805,1.6193)+0.0789659)" } -} \ No newline at end of file +} diff --git a/MC/config/PWGEM/parametrizations/PbPb5TeV_peripheral.json b/MC/config/PWGEM/parametrizations/PbPb5TeV_peripheral.json new file mode 100644 index 000000000..937481744 --- /dev/null +++ b/MC/config/PWGEM/parametrizations/PbPb5TeV_peripheral.json @@ -0,0 +1,282 @@ +{ + "5TeV_5060_wRatio_pi0corr": { + "histoMtScaleFactor": { + "113": 0.84, + "331": 0.4 + }, + "111_pt": "(TMath::TwoPi()*x*(2.37689*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.448898)+416.404*pow(exp(-0.582249*x-0.314888*x*x)+x/0.528081,-5.81419)))/(7.50435e-01+(9.92566e-01-7.50435e-01)*(1-exp(-x/3.90154e-01)))", + "221_pt": "((TMath::TwoPi()*x*(2.37689*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.448898)+416.404*pow(exp(-0.582249*x-0.314888*x*x)+x/0.528081,-5.81419)))/(7.50435e-01+(9.92566e-01-7.50435e-01)*(1-exp(-x/3.90154e-01))))*(((TMath::TwoPi()*x*(2.37689*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.448898)+416.404*pow(exp(-0.582249*x-0.314888*x*x)+x/0.528081,-5.81419)))*((0.406052/(1+exp(-(x-0.411746)/0.334347))+-0.248337*TMath::Gaus(x,-0.64255,2.29301)+-0.0467237*TMath::Gaus(x,6.03639,1.43975)+0.150899+((0.550454*exp((((0.995785*x)-sqrt((x*x)+(0.5478530000*0.5478530000)))/sqrt(1-(0.995785*0.995785)))/1.71099))+(0.0106936*(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/(exp((((0.995785*x)-sqrt((x*x)+(0.1349770000*0.1349770000)))/sqrt(1-(0.995785*0.995785)))/1.71099)+(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/2.))/(TMath::TwoPi()*x*(2.37689*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.448898)+416.404*pow(exp(-0.582249*x-0.314888*x*x)+x/0.528081,-5.81419))))", + "223_pt": "(TMath::TwoPi()*x*(2.37689*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.448898)+416.404*pow(exp(-0.582249*x-0.314888*x*x)+x/0.528081,-5.81419)))*(0.324086/(1+exp(-(x-0.573304)/0.143352))+-0.106092*TMath::Gaus(x,0.857984,0.27756)+-0.35343*TMath::Gaus(x,-0.266188,-1.4987)+0.358613)", + "333_pt": "(TMath::TwoPi()*x*(2.37689*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.448898)+416.404*pow(exp(-0.582249*x-0.314888*x*x)+x/0.528081,-5.81419)))*(0.107098/(1+exp(-(x-1.38974)/1.10537))+-0.544628*TMath::Gaus(x,0.660805,1.77065)+0.445236*TMath::Gaus(x,1.03665,2.09659)+0.0915189)" + }, + "5TeV_5060_wRatio_pi0corrup": { + "histoMtScaleFactor": { + "113": 0.84, + "331": 0.4 + }, + "111_pt": "(TMath::TwoPi()*x*(2.37689*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.448898)+416.404*pow(exp(-0.582249*x-0.314888*x*x)+x/0.528081,-5.81419)))/(7.50435e-01*0.95+(9.92566e-01-7.50435e-01*0.95)*(1-exp(-x/3.90154e-01)))", + "221_pt": "((TMath::TwoPi()*x*(2.37689*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.448898)+416.404*pow(exp(-0.582249*x-0.314888*x*x)+x/0.528081,-5.81419)))/(7.50435e-01*0.95+(9.92566e-01-7.50435e-01*0.95)*(1-exp(-x/3.90154e-01))))*(((TMath::TwoPi()*x*(2.37689*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.448898)+416.404*pow(exp(-0.582249*x-0.314888*x*x)+x/0.528081,-5.81419)))*((0.406052/(1+exp(-(x-0.411746)/0.334347))+-0.248337*TMath::Gaus(x,-0.64255,2.29301)+-0.0467237*TMath::Gaus(x,6.03639,1.43975)+0.150899+((0.550454*exp((((0.995785*x)-sqrt((x*x)+(0.5478530000*0.5478530000)))/sqrt(1-(0.995785*0.995785)))/1.71099))+(0.0106936*(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/(exp((((0.995785*x)-sqrt((x*x)+(0.1349770000*0.1349770000)))/sqrt(1-(0.995785*0.995785)))/1.71099)+(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/2.))/(TMath::TwoPi()*x*(2.37689*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.448898)+416.404*pow(exp(-0.582249*x-0.314888*x*x)+x/0.528081,-5.81419))))", + "223_pt": "(TMath::TwoPi()*x*(2.37689*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.448898)+416.404*pow(exp(-0.582249*x-0.314888*x*x)+x/0.528081,-5.81419)))*(0.324086/(1+exp(-(x-0.573304)/0.143352))+-0.106092*TMath::Gaus(x,0.857984,0.27756)+-0.35343*TMath::Gaus(x,-0.266188,-1.4987)+0.358613)", + "333_pt": "(TMath::TwoPi()*x*(2.37689*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.448898)+416.404*pow(exp(-0.582249*x-0.314888*x*x)+x/0.528081,-5.81419)))*(0.107098/(1+exp(-(x-1.38974)/1.10537))+-0.544628*TMath::Gaus(x,0.660805,1.77065)+0.445236*TMath::Gaus(x,1.03665,2.09659)+0.0915189)" + }, + "5TeV_5060_wRatio_pi0corrdown": { + "histoMtScaleFactor": { + "113": 0.84, + "331": 0.4 + }, + "111_pt": "(TMath::TwoPi()*x*(2.37689*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.448898)+416.404*pow(exp(-0.582249*x-0.314888*x*x)+x/0.528081,-5.81419)))/(7.50435e-01*1.05+(9.92566e-01-7.50435e-01*1.05)*(1-exp(-x/3.90154e-01)))", + "221_pt": "((TMath::TwoPi()*x*(2.37689*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.448898)+416.404*pow(exp(-0.582249*x-0.314888*x*x)+x/0.528081,-5.81419)))/(7.50435e-01*1.05+(9.92566e-01-7.50435e-01*1.05)*(1-exp(-x/3.90154e-01))))*(((TMath::TwoPi()*x*(2.37689*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.448898)+416.404*pow(exp(-0.582249*x-0.314888*x*x)+x/0.528081,-5.81419)))*((0.406052/(1+exp(-(x-0.411746)/0.334347))+-0.248337*TMath::Gaus(x,-0.64255,2.29301)+-0.0467237*TMath::Gaus(x,6.03639,1.43975)+0.150899+((0.550454*exp((((0.995785*x)-sqrt((x*x)+(0.5478530000*0.5478530000)))/sqrt(1-(0.995785*0.995785)))/1.71099))+(0.0106936*(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/(exp((((0.995785*x)-sqrt((x*x)+(0.1349770000*0.1349770000)))/sqrt(1-(0.995785*0.995785)))/1.71099)+(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/2.))/(TMath::TwoPi()*x*(2.37689*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.448898)+416.404*pow(exp(-0.582249*x-0.314888*x*x)+x/0.528081,-5.81419))))", + "223_pt": "(TMath::TwoPi()*x*(2.37689*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.448898)+416.404*pow(exp(-0.582249*x-0.314888*x*x)+x/0.528081,-5.81419)))*(0.324086/(1+exp(-(x-0.573304)/0.143352))+-0.106092*TMath::Gaus(x,0.857984,0.27756)+-0.35343*TMath::Gaus(x,-0.266188,-1.4987)+0.358613)", + "333_pt": "(TMath::TwoPi()*x*(2.37689*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.448898)+416.404*pow(exp(-0.582249*x-0.314888*x*x)+x/0.528081,-5.81419)))*(0.107098/(1+exp(-(x-1.38974)/1.10537))+-0.544628*TMath::Gaus(x,0.660805,1.77065)+0.445236*TMath::Gaus(x,1.03665,2.09659)+0.0915189)" + }, + "5TeV_5060_wRatio_pi0corr_up": { + "histoMtScaleFactor": { + "113": 0.84, + "331": 0.4 + }, + "111_pt": "(TMath::TwoPi()*x*(2.60114*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.447208)+465.994*pow(exp(-0.521709*x-0.39326*x*x)+x/0.516902,-5.79931)))/(7.50435e-01+(9.92566e-01-7.50435e-01)*(1-exp(-x/3.90154e-01)))", + "221_pt": "((TMath::TwoPi()*x*(2.60114*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.447208)+465.994*pow(exp(-0.521709*x-0.39326*x*x)+x/0.516902,-5.79931)))/(7.50435e-01+(9.92566e-01-7.50435e-01)*(1-exp(-x/3.90154e-01))))*(((TMath::TwoPi()*x*(2.60114*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.447208)+465.994*pow(exp(-0.521709*x-0.39326*x*x)+x/0.516902,-5.79931)))*((0.406052/(1+exp(-(x-0.411746)/0.334347))+-0.248337*TMath::Gaus(x,-0.64255,2.29301)+-0.0467237*TMath::Gaus(x,6.03639,1.43975)+0.150899+((0.550454*exp((((0.995785*x)-sqrt((x*x)+(0.5478530000*0.5478530000)))/sqrt(1-(0.995785*0.995785)))/1.71099))+(0.0106936*(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/(exp((((0.995785*x)-sqrt((x*x)+(0.1349770000*0.1349770000)))/sqrt(1-(0.995785*0.995785)))/1.71099)+(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/2.))/(TMath::TwoPi()*x*(2.60114*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.447208)+465.994*pow(exp(-0.521709*x-0.39326*x*x)+x/0.516902,-5.79931))))", + "223_pt": "(TMath::TwoPi()*x*(2.60114*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.447208)+465.994*pow(exp(-0.521709*x-0.39326*x*x)+x/0.516902,-5.79931)))*(0.324086/(1+exp(-(x-0.573304)/0.143352))+-0.106092*TMath::Gaus(x,0.857984,0.27756)+-0.35343*TMath::Gaus(x,-0.266188,-1.4987)+0.358613)", + "333_pt": "(TMath::TwoPi()*x*(2.60114*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.447208)+465.994*pow(exp(-0.521709*x-0.39326*x*x)+x/0.516902,-5.79931)))*(0.107098/(1+exp(-(x-1.38974)/1.10537))+-0.544628*TMath::Gaus(x,0.660805,1.77065)+0.445236*TMath::Gaus(x,1.03665,2.09659)+0.0915189)" + }, + "5TeV_5060_wRatio_pi0corr_down": { + "histoMtScaleFactor": { + "113": 0.84, + "331": 0.4 + }, + "111_pt": "(TMath::TwoPi()*x*(2.38584*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.444595)+367.741*pow(exp(-0.649054*x-0.220079*x*x)+x/0.541328,-5.83225)))/(7.50435e-01+(9.92566e-01-7.50435e-01)*(1-exp(-x/3.90154e-01)))", + "221_pt": "((TMath::TwoPi()*x*(2.38584*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.444595)+367.741*pow(exp(-0.649054*x-0.220079*x*x)+x/0.541328,-5.83225)))/(7.50435e-01+(9.92566e-01-7.50435e-01)*(1-exp(-x/3.90154e-01))))*(((TMath::TwoPi()*x*(2.38584*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.444595)+367.741*pow(exp(-0.649054*x-0.220079*x*x)+x/0.541328,-5.83225)))*((0.406052/(1+exp(-(x-0.411746)/0.334347))+-0.248337*TMath::Gaus(x,-0.64255,2.29301)+-0.0467237*TMath::Gaus(x,6.03639,1.43975)+0.150899+((0.550454*exp((((0.995785*x)-sqrt((x*x)+(0.5478530000*0.5478530000)))/sqrt(1-(0.995785*0.995785)))/1.71099))+(0.0106936*(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/(exp((((0.995785*x)-sqrt((x*x)+(0.1349770000*0.1349770000)))/sqrt(1-(0.995785*0.995785)))/1.71099)+(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/2.))/(TMath::TwoPi()*x*(2.38584*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.444595)+367.741*pow(exp(-0.649054*x-0.220079*x*x)+x/0.541328,-5.83225))))", + "223_pt": "(TMath::TwoPi()*x*(2.38584*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.444595)+367.741*pow(exp(-0.649054*x-0.220079*x*x)+x/0.541328,-5.83225)))*(0.324086/(1+exp(-(x-0.573304)/0.143352))+-0.106092*TMath::Gaus(x,0.857984,0.27756)+-0.35343*TMath::Gaus(x,-0.266188,-1.4987)+0.358613)", + "333_pt": "(TMath::TwoPi()*x*(2.38584*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.444595)+367.741*pow(exp(-0.649054*x-0.220079*x*x)+x/0.541328,-5.83225)))*(0.107098/(1+exp(-(x-1.38974)/1.10537))+-0.544628*TMath::Gaus(x,0.660805,1.77065)+0.445236*TMath::Gaus(x,1.03665,2.09659)+0.0915189)" + }, + "5TeV_5060_wRatio_pi0corr_ratiosup": { + "histoMtScaleFactor": { + "113": 1.01, + "331": 0.48 + }, + "111_pt": "(TMath::TwoPi()*x*(2.37689*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.448898)+416.404*pow(exp(-0.582249*x-0.314888*x*x)+x/0.528081,-5.81419)))/(7.50435e-01+(9.92566e-01-7.50435e-01)*(1-exp(-x/3.90154e-01)))", + "221_pt": "((TMath::TwoPi()*x*(2.37689*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.448898)+416.404*pow(exp(-0.582249*x-0.314888*x*x)+x/0.528081,-5.81419)))/(7.50435e-01+(9.92566e-01-7.50435e-01)*(1-exp(-x/3.90154e-01))))*(((x<0.25)*(1.5*(1-0.8*x)))*(((TMath::TwoPi()*x*(2.37689*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.448898)+416.404*pow(exp(-0.582249*x-0.314888*x*x)+x/0.528081,-5.81419)))*((0.406052/(1+exp(-(x-0.411746)/0.334347))+-0.248337*TMath::Gaus(x,-0.64255,2.29301)+-0.0467237*TMath::Gaus(x,6.03639,1.43975)+0.150899+((0.550454*exp((((0.995785*x)-sqrt((x*x)+(0.5478530000*0.5478530000)))/sqrt(1-(0.995785*0.995785)))/1.71099))+(0.0106936*(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/(exp((((0.995785*x)-sqrt((x*x)+(0.1349770000*0.1349770000)))/sqrt(1-(0.995785*0.995785)))/1.71099)+(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/2.))/(TMath::TwoPi()*x*(2.37689*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.448898)+416.404*pow(exp(-0.582249*x-0.314888*x*x)+x/0.528081,-5.81419))))+(x>=0.25)*1.2*(((TMath::TwoPi()*x*(2.37689*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.448898)+416.404*pow(exp(-0.582249*x-0.314888*x*x)+x/0.528081,-5.81419)))*((0.406052/(1+exp(-(x-0.411746)/0.334347))+-0.248337*TMath::Gaus(x,-0.64255,2.29301)+-0.0467237*TMath::Gaus(x,6.03639,1.43975)+0.150899+((0.550454*exp((((0.995785*x)-sqrt((x*x)+(0.5478530000*0.5478530000)))/sqrt(1-(0.995785*0.995785)))/1.71099))+(0.0106936*(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/(exp((((0.995785*x)-sqrt((x*x)+(0.1349770000*0.1349770000)))/sqrt(1-(0.995785*0.995785)))/1.71099)+(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/2.))/(TMath::TwoPi()*x*(2.37689*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.448898)+416.404*pow(exp(-0.582249*x-0.314888*x*x)+x/0.528081,-5.81419)))))", + "223_pt": "(TMath::TwoPi()*x*(2.37689*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.448898)+416.404*pow(exp(-0.582249*x-0.314888*x*x)+x/0.528081,-5.81419)))*(((x<0.25)*(1.5*(1-0.8*x)))*(0.324086/(1+exp(-(x-0.573304)/0.143352))+-0.106092*TMath::Gaus(x,0.857984,0.27756)+-0.35343*TMath::Gaus(x,-0.266188,-1.4987)+0.358613)+((x>=0.25)*1.2*(0.324086/(1+exp(-(x-0.573304)/0.143352))+-0.106092*TMath::Gaus(x,0.857984,0.27756)+-0.35343*TMath::Gaus(x,-0.266188,-1.4987)+0.358613)))", + "333_pt": "(TMath::TwoPi()*x*(2.37689*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.448898)+416.404*pow(exp(-0.582249*x-0.314888*x*x)+x/0.528081,-5.81419)))*(1.2*(0.107098/(1+exp(-(x-1.38974)/1.10537))+-0.544628*TMath::Gaus(x,0.660805,1.77065)+0.445236*TMath::Gaus(x,1.03665,2.09659)+0.0915189))" + }, + "5TeV_5060_wRatio_pi0corr_ratiosdown": { + "histoMtScaleFactor": { + "223": 0.62, + "331": 0.32 + }, + "111_pt": "(TMath::TwoPi()*x*(2.37689*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.448898)+416.404*pow(exp(-0.582249*x-0.314888*x*x)+x/0.528081,-5.81419)))/(7.50435e-01+(9.92566e-01-7.50435e-01)*(1-exp(-x/3.90154e-01)))", + "221_pt": "((TMath::TwoPi()*x*(2.37689*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.448898)+416.404*pow(exp(-0.582249*x-0.314888*x*x)+x/0.528081,-5.81419)))/(7.50435e-01+(9.92566e-01-7.50435e-01)*(1-exp(-x/3.90154e-01))))*(((x<0.4)*(0.4*(1+2.5*x)))*(((TMath::TwoPi()*x*(2.37689*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.448898)+416.404*pow(exp(-0.582249*x-0.314888*x*x)+x/0.528081,-5.81419)))*((0.406052/(1+exp(-(x-0.411746)/0.334347))+-0.248337*TMath::Gaus(x,-0.64255,2.29301)+-0.0467237*TMath::Gaus(x,6.03639,1.43975)+0.150899+((0.550454*exp((((0.995785*x)-sqrt((x*x)+(0.5478530000*0.5478530000)))/sqrt(1-(0.995785*0.995785)))/1.71099))+(0.0106936*(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/(exp((((0.995785*x)-sqrt((x*x)+(0.1349770000*0.1349770000)))/sqrt(1-(0.995785*0.995785)))/1.71099)+(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/2.))/(TMath::TwoPi()*x*(2.37689*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.448898)+416.404*pow(exp(-0.582249*x-0.314888*x*x)+x/0.528081,-5.81419))))+(x>=0.4)*0.8*(((TMath::TwoPi()*x*(2.37689*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.448898)+416.404*pow(exp(-0.582249*x-0.314888*x*x)+x/0.528081,-5.81419)))*((0.406052/(1+exp(-(x-0.411746)/0.334347))+-0.248337*TMath::Gaus(x,-0.64255,2.29301)+-0.0467237*TMath::Gaus(x,6.03639,1.43975)+0.150899+((0.550454*exp((((0.995785*x)-sqrt((x*x)+(0.5478530000*0.5478530000)))/sqrt(1-(0.995785*0.995785)))/1.71099))+(0.0106936*(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/(exp((((0.995785*x)-sqrt((x*x)+(0.1349770000*0.1349770000)))/sqrt(1-(0.995785*0.995785)))/1.71099)+(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/2.))/(TMath::TwoPi()*x*(2.37689*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.448898)+416.404*pow(exp(-0.582249*x-0.314888*x*x)+x/0.528081,-5.81419)))))", + "113_pt": "(TMath::TwoPi()*x*(2.37689*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.448898)+416.404*pow(exp(-0.582249*x-0.314888*x*x)+x/0.528081,-5.81419)))*((x>1.951)*(0.451114/(1+exp(-(x-0.707439)/0.374928))+-0.307087*TMath::Gaus(x,-0.577264,1.60957)+-0.20694*TMath::Gaus(x,-2.50098,4.31777)+0.337195)+(x<=1.951)*((((0.52988/(1+exp(-((x-0.303965)/0.287856))))+(-0.281848*TMath::Gaus(x,0.497102,0.688973)))+(-0.249068*TMath::Gaus(x,1.09157,1.16378)))+0.253347))", + "333_pt": "(TMath::TwoPi()*x*(2.37689*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.448898)+416.404*pow(exp(-0.582249*x-0.314888*x*x)+x/0.528081,-5.81419)))*(0.8*(0.107098/(1+exp(-(x-1.38974)/1.10537))+-0.544628*TMath::Gaus(x,0.660805,1.77065)+0.445236*TMath::Gaus(x,1.03665,2.09659)+0.0915189))" + }, + "5TeV_6070_wRatio_pi0corr": { + "histoMtScaleFactor": { + "113": 0.84, + "331": 0.4 + }, + "111_pt": "(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+237.396*pow(exp(-0.450001*x-0.267691*x*x)+x/0.561352,-5.96452)))/(7.50435e-01+(9.92566e-01-7.50435e-01)*(1-exp(-x/3.90154e-01)))", + "221_pt": "((TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+237.396*pow(exp(-0.450001*x-0.267691*x*x)+x/0.561352,-5.96452)))/(7.50435e-01+(9.92566e-01-7.50435e-01)*(1-exp(-x/3.90154e-01))))*(((TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+237.396*pow(exp(-0.450001*x-0.267691*x*x)+x/0.561352,-5.96452)))*((0.287493/(1+exp(-(x-0.381675)/0.227939))+-0.301666*TMath::Gaus(x,0.0639027,1.54263)+-0.0271521*TMath::Gaus(x,4.71408,1.51159)+0.274684+((0.550454*exp((((0.995785*x)-sqrt((x*x)+(0.5478530000*0.5478530000)))/sqrt(1-(0.995785*0.995785)))/1.71099))+(0.0106936*(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/(exp((((0.995785*x)-sqrt((x*x)+(0.1349770000*0.1349770000)))/sqrt(1-(0.995785*0.995785)))/1.71099)+(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/2.))/(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+237.396*pow(exp(-0.450001*x-0.267691*x*x)+x/0.561352,-5.96452))))", + "223_pt": "(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+237.396*pow(exp(-0.450001*x-0.267691*x*x)+x/0.561352,-5.96452)))*(0.324086/(1+exp(-(x-0.573304)/0.143352))+-0.106092*TMath::Gaus(x,0.857984,0.27756)+-0.35343*TMath::Gaus(x,-0.266188,-1.4987)+0.358613)", + "333_pt": "(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+237.396*pow(exp(-0.450001*x-0.267691*x*x)+x/0.561352,-5.96452)))*(0.109761/(1+exp(-(x-1.3902)/1.35693))+-0.549036*TMath::Gaus(x,0.570177,1.69368)+0.441093*TMath::Gaus(x,0.836878,1.93285)+0.089258)" + }, + "5TeV_6070_wRatio_pi0corrup": { + "histoMtScaleFactor": { + "113": 0.84, + "331": 0.4 + }, + "111_pt": "(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+237.396*pow(exp(-0.450001*x-0.267691*x*x)+x/0.561352,-5.96452)))/(7.50435e-01*0.95+(9.92566e-01-7.50435e-01*0.95)*(1-exp(-x/3.90154e-01)))", + "221_pt": "((TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+237.396*pow(exp(-0.450001*x-0.267691*x*x)+x/0.561352,-5.96452)))/(7.50435e-01*0.95+(9.92566e-01-7.50435e-01*0.95)*(1-exp(-x/3.90154e-01))))*(((TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+237.396*pow(exp(-0.450001*x-0.267691*x*x)+x/0.561352,-5.96452)))*((0.287493/(1+exp(-(x-0.381675)/0.227939))+-0.301666*TMath::Gaus(x,0.0639027,1.54263)+-0.0271521*TMath::Gaus(x,4.71408,1.51159)+0.274684+((0.550454*exp((((0.995785*x)-sqrt((x*x)+(0.5478530000*0.5478530000)))/sqrt(1-(0.995785*0.995785)))/1.71099))+(0.0106936*(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/(exp((((0.995785*x)-sqrt((x*x)+(0.1349770000*0.1349770000)))/sqrt(1-(0.995785*0.995785)))/1.71099)+(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/2.))/(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+237.396*pow(exp(-0.450001*x-0.267691*x*x)+x/0.561352,-5.96452))))", + "223_pt": "(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+237.396*pow(exp(-0.450001*x-0.267691*x*x)+x/0.561352,-5.96452)))*(0.324086/(1+exp(-(x-0.573304)/0.143352))+-0.106092*TMath::Gaus(x,0.857984,0.27756)+-0.35343*TMath::Gaus(x,-0.266188,-1.4987)+0.358613)", + "333_pt": "(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+237.396*pow(exp(-0.450001*x-0.267691*x*x)+x/0.561352,-5.96452)))*(0.109761/(1+exp(-(x-1.3902)/1.35693))+-0.549036*TMath::Gaus(x,0.570177,1.69368)+0.441093*TMath::Gaus(x,0.836878,1.93285)+0.089258)" + }, + "5TeV_6070_wRatio_pi0corrdown": { + "histoMtScaleFactor": { + "113": 0.84, + "331": 0.4 + }, + "111_pt": "(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+237.396*pow(exp(-0.450001*x-0.267691*x*x)+x/0.561352,-5.96452)))/(7.50435e-01*1.05+(9.92566e-01-7.50435e-01*1.05)*(1-exp(-x/3.90154e-01)))", + "221_pt": "((TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+237.396*pow(exp(-0.450001*x-0.267691*x*x)+x/0.561352,-5.96452)))/(7.50435e-01*1.05+(9.92566e-01-7.50435e-01*1.05)*(1-exp(-x/3.90154e-01))))*(((TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+237.396*pow(exp(-0.450001*x-0.267691*x*x)+x/0.561352,-5.96452)))*((0.287493/(1+exp(-(x-0.381675)/0.227939))+-0.301666*TMath::Gaus(x,0.0639027,1.54263)+-0.0271521*TMath::Gaus(x,4.71408,1.51159)+0.274684+((0.550454*exp((((0.995785*x)-sqrt((x*x)+(0.5478530000*0.5478530000)))/sqrt(1-(0.995785*0.995785)))/1.71099))+(0.0106936*(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/(exp((((0.995785*x)-sqrt((x*x)+(0.1349770000*0.1349770000)))/sqrt(1-(0.995785*0.995785)))/1.71099)+(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/2.))/(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+237.396*pow(exp(-0.450001*x-0.267691*x*x)+x/0.561352,-5.96452))))", + "223_pt": "(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+237.396*pow(exp(-0.450001*x-0.267691*x*x)+x/0.561352,-5.96452)))*(0.324086/(1+exp(-(x-0.573304)/0.143352))+-0.106092*TMath::Gaus(x,0.857984,0.27756)+-0.35343*TMath::Gaus(x,-0.266188,-1.4987)+0.358613)", + "333_pt": "(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+237.396*pow(exp(-0.450001*x-0.267691*x*x)+x/0.561352,-5.96452)))*(0.109761/(1+exp(-(x-1.3902)/1.35693))+-0.549036*TMath::Gaus(x,0.570177,1.69368)+0.441093*TMath::Gaus(x,0.836878,1.93285)+0.089258)" + }, + "5TeV_6070_wRatio_pi0corr_up": { + "histoMtScaleFactor": { + "113": 0.84, + "331": 0.4 + }, + "111_pt": "(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+258.434*pow(exp(-0.409765*x-0.32565*x*x)+x/0.554505,-5.9546)))/(7.50435e-01+(9.92566e-01-7.50435e-01)*(1-exp(-x/3.90154e-01)))", + "221_pt": "((TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+258.434*pow(exp(-0.409765*x-0.32565*x*x)+x/0.554505,-5.9546)))/(7.50435e-01+(9.92566e-01-7.50435e-01)*(1-exp(-x/3.90154e-01))))*(((TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+258.434*pow(exp(-0.409765*x-0.32565*x*x)+x/0.554505,-5.9546)))*((0.287493/(1+exp(-(x-0.381675)/0.227939))+-0.301666*TMath::Gaus(x,0.0639027,1.54263)+-0.0271521*TMath::Gaus(x,4.71408,1.51159)+0.274684+((0.550454*exp((((0.995785*x)-sqrt((x*x)+(0.5478530000*0.5478530000)))/sqrt(1-(0.995785*0.995785)))/1.71099))+(0.0106936*(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/(exp((((0.995785*x)-sqrt((x*x)+(0.1349770000*0.1349770000)))/sqrt(1-(0.995785*0.995785)))/1.71099)+(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/2.))/(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+258.434*pow(exp(-0.409765*x-0.32565*x*x)+x/0.554505,-5.9546))))", + "223_pt": "(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+258.434*pow(exp(-0.409765*x-0.32565*x*x)+x/0.554505,-5.9546)))*(0.324086/(1+exp(-(x-0.573304)/0.143352))+-0.106092*TMath::Gaus(x,0.857984,0.27756)+-0.35343*TMath::Gaus(x,-0.266188,-1.4987)+0.358613)", + "333_pt": "(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+258.434*pow(exp(-0.409765*x-0.32565*x*x)+x/0.554505,-5.9546)))*(0.109761/(1+exp(-(x-1.3902)/1.35693))+-0.549036*TMath::Gaus(x,0.570177,1.69368)+0.441093*TMath::Gaus(x,0.836878,1.93285)+0.089258)" + }, + "5TeV_6070_wRatio_pi0corr_down": { + "histoMtScaleFactor": { + "113": 0.84, + "331": 0.4 + }, + "111_pt": "(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+217.529*pow(exp(-0.488748*x-0.208088*x*x)+x/0.56927,-5.97798)))/(7.50435e-01+(9.92566e-01-7.50435e-01)*(1-exp(-x/3.90154e-01)))", + "221_pt": "((TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+217.529*pow(exp(-0.488748*x-0.208088*x*x)+x/0.56927,-5.97798)))/(7.50435e-01+(9.92566e-01-7.50435e-01)*(1-exp(-x/3.90154e-01))))*(((TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+217.529*pow(exp(-0.488748*x-0.208088*x*x)+x/0.56927,-5.97798)))*((0.287493/(1+exp(-(x-0.381675)/0.227939))+-0.301666*TMath::Gaus(x,0.0639027,1.54263)+-0.0271521*TMath::Gaus(x,4.71408,1.51159)+0.274684+((0.550454*exp((((0.995785*x)-sqrt((x*x)+(0.5478530000*0.5478530000)))/sqrt(1-(0.995785*0.995785)))/1.71099))+(0.0106936*(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/(exp((((0.995785*x)-sqrt((x*x)+(0.1349770000*0.1349770000)))/sqrt(1-(0.995785*0.995785)))/1.71099)+(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/2.))/(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+217.529*pow(exp(-0.488748*x-0.208088*x*x)+x/0.56927,-5.97798))))", + "223_pt": "(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+217.529*pow(exp(-0.488748*x-0.208088*x*x)+x/0.56927,-5.97798)))*(0.324086/(1+exp(-(x-0.573304)/0.143352))+-0.106092*TMath::Gaus(x,0.857984,0.27756)+-0.35343*TMath::Gaus(x,-0.266188,-1.4987)+0.358613)", + "333_pt": "(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+217.529*pow(exp(-0.488748*x-0.208088*x*x)+x/0.56927,-5.97798)))*(0.109761/(1+exp(-(x-1.3902)/1.35693))+-0.549036*TMath::Gaus(x,0.570177,1.69368)+0.441093*TMath::Gaus(x,0.836878,1.93285)+0.089258)" + }, + "5TeV_6070_wRatio_pi0corr_ratiosup": { + "histoMtScaleFactor": { + "113": 1.01, + "331": 0.48 + }, + "111_pt": "(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+237.396*pow(exp(-0.450001*x-0.267691*x*x)+x/0.561352,-5.96452)))/(7.50435e-01+(9.92566e-01-7.50435e-01)*(1-exp(-x/3.90154e-01)))", + "221_pt": "((TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+237.396*pow(exp(-0.450001*x-0.267691*x*x)+x/0.561352,-5.96452)))/(7.50435e-01+(9.92566e-01-7.50435e-01)*(1-exp(-x/3.90154e-01))))*(((x<0.25)*(1.5*(1-0.8*x)))*(((TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+237.396*pow(exp(-0.450001*x-0.267691*x*x)+x/0.561352,-5.96452)))*((0.287493/(1+exp(-(x-0.381675)/0.227939))+-0.301666*TMath::Gaus(x,0.0639027,1.54263)+-0.0271521*TMath::Gaus(x,4.71408,1.51159)+0.274684+((0.550454*exp((((0.995785*x)-sqrt((x*x)+(0.5478530000*0.5478530000)))/sqrt(1-(0.995785*0.995785)))/1.71099))+(0.0106936*(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/(exp((((0.995785*x)-sqrt((x*x)+(0.1349770000*0.1349770000)))/sqrt(1-(0.995785*0.995785)))/1.71099)+(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/2.))/(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+237.396*pow(exp(-0.450001*x-0.267691*x*x)+x/0.561352,-5.96452))))+(x>=0.25)*1.2*(((TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+237.396*pow(exp(-0.450001*x-0.267691*x*x)+x/0.561352,-5.96452)))*((0.287493/(1+exp(-(x-0.381675)/0.227939))+-0.301666*TMath::Gaus(x,0.0639027,1.54263)+-0.0271521*TMath::Gaus(x,4.71408,1.51159)+0.274684+((0.550454*exp((((0.995785*x)-sqrt((x*x)+(0.5478530000*0.5478530000)))/sqrt(1-(0.995785*0.995785)))/1.71099))+(0.0106936*(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/(exp((((0.995785*x)-sqrt((x*x)+(0.1349770000*0.1349770000)))/sqrt(1-(0.995785*0.995785)))/1.71099)+(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/2.))/(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+237.396*pow(exp(-0.450001*x-0.267691*x*x)+x/0.561352,-5.96452)))))", + "223_pt": "(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+237.396*pow(exp(-0.450001*x-0.267691*x*x)+x/0.561352,-5.96452)))*(((x<0.25)*(1.5*(1-0.8*x)))*(0.324086/(1+exp(-(x-0.573304)/0.143352))+-0.106092*TMath::Gaus(x,0.857984,0.27756)+-0.35343*TMath::Gaus(x,-0.266188,-1.4987)+0.358613)+((x>=0.25)*1.2*(0.324086/(1+exp(-(x-0.573304)/0.143352))+-0.106092*TMath::Gaus(x,0.857984,0.27756)+-0.35343*TMath::Gaus(x,-0.266188,-1.4987)+0.358613)))", + "333_pt": "(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+237.396*pow(exp(-0.450001*x-0.267691*x*x)+x/0.561352,-5.96452)))*(1.2*(0.109761/(1+exp(-(x-1.3902)/1.35693))+-0.549036*TMath::Gaus(x,0.570177,1.69368)+0.441093*TMath::Gaus(x,0.836878,1.93285)+0.089258))" + }, + "5TeV_6070_wRatio_pi0corr_ratiosdown": { + "histoMtScaleFactor": { + "223": 0.62, + "331": 0.32 + }, + "111_pt": "(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+237.396*pow(exp(-0.450001*x-0.267691*x*x)+x/0.561352,-5.96452)))/(7.50435e-01+(9.92566e-01-7.50435e-01)*(1-exp(-x/3.90154e-01)))", + "221_pt": "((TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+237.396*pow(exp(-0.450001*x-0.267691*x*x)+x/0.561352,-5.96452)))/(7.50435e-01+(9.92566e-01-7.50435e-01)*(1-exp(-x/3.90154e-01))))*(((x<0.4)*(0.4*(1+2.5*x)))*(((TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+237.396*pow(exp(-0.450001*x-0.267691*x*x)+x/0.561352,-5.96452)))*((0.287493/(1+exp(-(x-0.381675)/0.227939))+-0.301666*TMath::Gaus(x,0.0639027,1.54263)+-0.0271521*TMath::Gaus(x,4.71408,1.51159)+0.274684+((0.550454*exp((((0.995785*x)-sqrt((x*x)+(0.5478530000*0.5478530000)))/sqrt(1-(0.995785*0.995785)))/1.71099))+(0.0106936*(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/(exp((((0.995785*x)-sqrt((x*x)+(0.1349770000*0.1349770000)))/sqrt(1-(0.995785*0.995785)))/1.71099)+(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/2.))/(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+237.396*pow(exp(-0.450001*x-0.267691*x*x)+x/0.561352,-5.96452))))+(x>=0.4)*0.8*(((TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+237.396*pow(exp(-0.450001*x-0.267691*x*x)+x/0.561352,-5.96452)))*((0.287493/(1+exp(-(x-0.381675)/0.227939))+-0.301666*TMath::Gaus(x,0.0639027,1.54263)+-0.0271521*TMath::Gaus(x,4.71408,1.51159)+0.274684+((0.550454*exp((((0.995785*x)-sqrt((x*x)+(0.5478530000*0.5478530000)))/sqrt(1-(0.995785*0.995785)))/1.71099))+(0.0106936*(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/(exp((((0.995785*x)-sqrt((x*x)+(0.1349770000*0.1349770000)))/sqrt(1-(0.995785*0.995785)))/1.71099)+(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/2.))/(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+237.396*pow(exp(-0.450001*x-0.267691*x*x)+x/0.561352,-5.96452)))))", + "113_pt": "(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+237.396*pow(exp(-0.450001*x-0.267691*x*x)+x/0.561352,-5.96452)))*((x>1.951)*(0.451114/(1+exp(-(x-0.707439)/0.374928))+-0.307087*TMath::Gaus(x,-0.577264,1.60957)+-0.20694*TMath::Gaus(x,-2.50098,4.31777)+0.337195)+(x<=1.951)*((((0.52988/(1+exp(-((x-0.303965)/0.287856))))+(-0.281848*TMath::Gaus(x,0.497102,0.688973)))+(-0.249068*TMath::Gaus(x,1.09157,1.16378)))+0.253347))", + "333_pt": "(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+237.396*pow(exp(-0.450001*x-0.267691*x*x)+x/0.561352,-5.96452)))*(0.8*(0.109761/(1+exp(-(x-1.3902)/1.35693))+-0.549036*TMath::Gaus(x,0.570177,1.69368)+0.441093*TMath::Gaus(x,0.836878,1.93285)+0.089258))" + }, + "5TeV_7080_wRatio_pi0corr": { + "histoMtScaleFactor": { + "113": 0.84, + "331": 0.4 + }, + "111_pt": "(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+116.441*pow(exp(-0.453438*x-0.157659*x*x)+x/0.569422,-6.02001)))/(7.18342e-01+(9.86860e-01-7.18342e-01)*(1-exp(-x/3.07386e-01)))", + "221_pt": "((TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+116.441*pow(exp(-0.453438*x-0.157659*x*x)+x/0.569422,-6.02001)))/(7.18342e-01+(9.86860e-01-7.18342e-01)*(1-exp(-x/3.07386e-01))))*(((TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+116.441*pow(exp(-0.453438*x-0.157659*x*x)+x/0.569422,-6.02001)))*((0.509043/(1+exp(-(x-0.37759)/0.335237))+-0.159762*TMath::Gaus(x,0.948373,1.33321)+-0.0429426*TMath::Gaus(x,2.4898,2.67569)+0.0428236+((0.550454*exp((((0.995785*x)-sqrt((x*x)+(0.5478530000*0.5478530000)))/sqrt(1-(0.995785*0.995785)))/1.71099))+(0.0106936*(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/(exp((((0.995785*x)-sqrt((x*x)+(0.1349770000*0.1349770000)))/sqrt(1-(0.995785*0.995785)))/1.71099)+(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/2.))/(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+116.441*pow(exp(-0.453438*x-0.157659*x*x)+x/0.569422,-6.02001))))", + "223_pt": "(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+116.441*pow(exp(-0.453438*x-0.157659*x*x)+x/0.569422,-6.02001)))*(0.324086/(1+exp(-(x-0.573304)/0.143352))+-0.106092*TMath::Gaus(x,0.857984,0.27756)+-0.35343*TMath::Gaus(x,-0.266188,-1.4987)+0.358613)", + "333_pt": "(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+116.441*pow(exp(-0.453438*x-0.157659*x*x)+x/0.569422,-6.02001)))*(0.0822258/(1+exp(-(x-1.07925)/1.16509))+-0.551967*TMath::Gaus(x,0.203349,1.77693)+0.438271*TMath::Gaus(x,0.266864,2.0576)+0.0913634)" + }, + "5TeV_7080_wRatio_pi0corrup": { + "histoMtScaleFactor": { + "113": 0.84, + "331": 0.4 + }, + "111_pt": "(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+116.441*pow(exp(-0.453438*x-0.157659*x*x)+x/0.569422,-6.02001)))/(7.18342e-01*0.95+(9.86860e-01-7.18342e-01*0.95)*(1-exp(-x/3.07386e-01)))", + "221_pt": "((TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+116.441*pow(exp(-0.453438*x-0.157659*x*x)+x/0.569422,-6.02001)))/(7.18342e-01*0.95+(9.86860e-01-7.18342e-01*0.95)*(1-exp(-x/3.07386e-01))))*(((TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+116.441*pow(exp(-0.453438*x-0.157659*x*x)+x/0.569422,-6.02001)))*((0.509043/(1+exp(-(x-0.37759)/0.335237))+-0.159762*TMath::Gaus(x,0.948373,1.33321)+-0.0429426*TMath::Gaus(x,2.4898,2.67569)+0.0428236+((0.550454*exp((((0.995785*x)-sqrt((x*x)+(0.5478530000*0.5478530000)))/sqrt(1-(0.995785*0.995785)))/1.71099))+(0.0106936*(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/(exp((((0.995785*x)-sqrt((x*x)+(0.1349770000*0.1349770000)))/sqrt(1-(0.995785*0.995785)))/1.71099)+(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/2.))/(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+116.441*pow(exp(-0.453438*x-0.157659*x*x)+x/0.569422,-6.02001))))", + "223_pt": "(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+116.441*pow(exp(-0.453438*x-0.157659*x*x)+x/0.569422,-6.02001)))*(0.324086/(1+exp(-(x-0.573304)/0.143352))+-0.106092*TMath::Gaus(x,0.857984,0.27756)+-0.35343*TMath::Gaus(x,-0.266188,-1.4987)+0.358613)", + "333_pt": "(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+116.441*pow(exp(-0.453438*x-0.157659*x*x)+x/0.569422,-6.02001)))*(0.0822258/(1+exp(-(x-1.07925)/1.16509))+-0.551967*TMath::Gaus(x,0.203349,1.77693)+0.438271*TMath::Gaus(x,0.266864,2.0576)+0.0913634)" + }, + "5TeV_7080_wRatio_pi0corrdown": { + "histoMtScaleFactor": { + "113": 0.84, + "331": 0.4 + }, + "111_pt": "(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+116.441*pow(exp(-0.453438*x-0.157659*x*x)+x/0.569422,-6.02001)))/(7.18342e-01*1.05+(9.86860e-01-7.18342e-01*1.05)*(1-exp(-x/3.07386e-01)))", + "221_pt": "((TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+116.441*pow(exp(-0.453438*x-0.157659*x*x)+x/0.569422,-6.02001)))/(7.18342e-01*1.05+(9.86860e-01-7.18342e-01*1.05)*(1-exp(-x/3.07386e-01))))*(((TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+116.441*pow(exp(-0.453438*x-0.157659*x*x)+x/0.569422,-6.02001)))*((0.509043/(1+exp(-(x-0.37759)/0.335237))+-0.159762*TMath::Gaus(x,0.948373,1.33321)+-0.0429426*TMath::Gaus(x,2.4898,2.67569)+0.0428236+((0.550454*exp((((0.995785*x)-sqrt((x*x)+(0.5478530000*0.5478530000)))/sqrt(1-(0.995785*0.995785)))/1.71099))+(0.0106936*(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/(exp((((0.995785*x)-sqrt((x*x)+(0.1349770000*0.1349770000)))/sqrt(1-(0.995785*0.995785)))/1.71099)+(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/2.))/(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+116.441*pow(exp(-0.453438*x-0.157659*x*x)+x/0.569422,-6.02001))))", + "223_pt": "(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+116.441*pow(exp(-0.453438*x-0.157659*x*x)+x/0.569422,-6.02001)))*(0.324086/(1+exp(-(x-0.573304)/0.143352))+-0.106092*TMath::Gaus(x,0.857984,0.27756)+-0.35343*TMath::Gaus(x,-0.266188,-1.4987)+0.358613)", + "333_pt": "(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+116.441*pow(exp(-0.453438*x-0.157659*x*x)+x/0.569422,-6.02001)))*(0.0822258/(1+exp(-(x-1.07925)/1.16509))+-0.551967*TMath::Gaus(x,0.203349,1.77693)+0.438271*TMath::Gaus(x,0.266864,2.0576)+0.0913634)" + }, + "5TeV_7080_wRatio_pi0corr_up": { + "histoMtScaleFactor": { + "113": 0.84, + "331": 0.4 + }, + "111_pt": "(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+126.267*pow(exp(-0.426848*x-0.209316*x*x)+x/0.559286,-5.99183)))/(7.18342e-01+(9.86860e-01-7.18342e-01)*(1-exp(-x/3.07386e-01)))", + "221_pt": "((TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+126.267*pow(exp(-0.426848*x-0.209316*x*x)+x/0.559286,-5.99183)))/(7.18342e-01+(9.86860e-01-7.18342e-01)*(1-exp(-x/3.07386e-01))))*(((TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+126.267*pow(exp(-0.426848*x-0.209316*x*x)+x/0.559286,-5.99183)))*((0.509043/(1+exp(-(x-0.37759)/0.335237))+-0.159762*TMath::Gaus(x,0.948373,1.33321)+-0.0429426*TMath::Gaus(x,2.4898,2.67569)+0.0428236+((0.550454*exp((((0.995785*x)-sqrt((x*x)+(0.5478530000*0.5478530000)))/sqrt(1-(0.995785*0.995785)))/1.71099))+(0.0106936*(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/(exp((((0.995785*x)-sqrt((x*x)+(0.1349770000*0.1349770000)))/sqrt(1-(0.995785*0.995785)))/1.71099)+(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/2.))/(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+126.267*pow(exp(-0.426848*x-0.209316*x*x)+x/0.559286,-5.99183))))", + "223_pt": "(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+126.267*pow(exp(-0.426848*x-0.209316*x*x)+x/0.559286,-5.99183)))*(0.324086/(1+exp(-(x-0.573304)/0.143352))+-0.106092*TMath::Gaus(x,0.857984,0.27756)+-0.35343*TMath::Gaus(x,-0.266188,-1.4987)+0.358613)", + "333_pt": "(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+126.267*pow(exp(-0.426848*x-0.209316*x*x)+x/0.559286,-5.99183)))*(0.0822258/(1+exp(-(x-1.07925)/1.16509))+-0.551967*TMath::Gaus(x,0.203349,1.77693)+0.438271*TMath::Gaus(x,0.266864,2.0576)+0.0913634)" + }, + "5TeV_7080_wRatio_pi0corr_down": { + "histoMtScaleFactor": { + "113": 0.84, + "331": 0.4 + }, + "111_pt": "(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+107.412*pow(exp(-0.472618*x-0.107443*x*x)+x/0.581829,-6.05744)))/(7.18342e-01+(9.86860e-01-7.18342e-01)*(1-exp(-x/3.07386e-01)))", + "221_pt": "((TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+107.412*pow(exp(-0.472618*x-0.107443*x*x)+x/0.581829,-6.05744)))/(7.18342e-01+(9.86860e-01-7.18342e-01)*(1-exp(-x/3.07386e-01))))*(((TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+107.412*pow(exp(-0.472618*x-0.107443*x*x)+x/0.581829,-6.05744)))*((0.509043/(1+exp(-(x-0.37759)/0.335237))+-0.159762*TMath::Gaus(x,0.948373,1.33321)+-0.0429426*TMath::Gaus(x,2.4898,2.67569)+0.0428236+((0.550454*exp((((0.995785*x)-sqrt((x*x)+(0.5478530000*0.5478530000)))/sqrt(1-(0.995785*0.995785)))/1.71099))+(0.0106936*(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/(exp((((0.995785*x)-sqrt((x*x)+(0.1349770000*0.1349770000)))/sqrt(1-(0.995785*0.995785)))/1.71099)+(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/2.))/(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+107.412*pow(exp(-0.472618*x-0.107443*x*x)+x/0.581829,-6.05744))))", + "223_pt": "(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+107.412*pow(exp(-0.472618*x-0.107443*x*x)+x/0.581829,-6.05744)))*(0.324086/(1+exp(-(x-0.573304)/0.143352))+-0.106092*TMath::Gaus(x,0.857984,0.27756)+-0.35343*TMath::Gaus(x,-0.266188,-1.4987)+0.358613)", + "333_pt": "(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+107.412*pow(exp(-0.472618*x-0.107443*x*x)+x/0.581829,-6.05744)))*(0.0822258/(1+exp(-(x-1.07925)/1.16509))+-0.551967*TMath::Gaus(x,0.203349,1.77693)+0.438271*TMath::Gaus(x,0.266864,2.0576)+0.0913634)" + }, + "5TeV_7080_wRatio_pi0corr_ratiosup": { + "histoMtScaleFactor": { + "113": 1.01, + "331": 0.48 + }, + "111_pt": "(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+116.441*pow(exp(-0.453438*x-0.157659*x*x)+x/0.569422,-6.02001)))/(7.18342e-01+(9.86860e-01-7.18342e-01)*(1-exp(-x/3.07386e-01)))", + "221_pt": "((TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+116.441*pow(exp(-0.453438*x-0.157659*x*x)+x/0.569422,-6.02001)))/(7.18342e-01+(9.86860e-01-7.18342e-01)*(1-exp(-x/3.07386e-01))))*(((x<0.25)*(1.5*(1-0.8*x)))*(((TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+116.441*pow(exp(-0.453438*x-0.157659*x*x)+x/0.569422,-6.02001)))*((0.509043/(1+exp(-(x-0.37759)/0.335237))+-0.159762*TMath::Gaus(x,0.948373,1.33321)+-0.0429426*TMath::Gaus(x,2.4898,2.67569)+0.0428236+((0.550454*exp((((0.995785*x)-sqrt((x*x)+(0.5478530000*0.5478530000)))/sqrt(1-(0.995785*0.995785)))/1.71099))+(0.0106936*(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/(exp((((0.995785*x)-sqrt((x*x)+(0.1349770000*0.1349770000)))/sqrt(1-(0.995785*0.995785)))/1.71099)+(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/2.))/(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+116.441*pow(exp(-0.453438*x-0.157659*x*x)+x/0.569422,-6.02001))))+(x>=0.25)*1.2*(((TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+116.441*pow(exp(-0.453438*x-0.157659*x*x)+x/0.569422,-6.02001)))*((0.509043/(1+exp(-(x-0.37759)/0.335237))+-0.159762*TMath::Gaus(x,0.948373,1.33321)+-0.0429426*TMath::Gaus(x,2.4898,2.67569)+0.0428236+((0.550454*exp((((0.995785*x)-sqrt((x*x)+(0.5478530000*0.5478530000)))/sqrt(1-(0.995785*0.995785)))/1.71099))+(0.0106936*(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/(exp((((0.995785*x)-sqrt((x*x)+(0.1349770000*0.1349770000)))/sqrt(1-(0.995785*0.995785)))/1.71099)+(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/2.))/(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+116.441*pow(exp(-0.453438*x-0.157659*x*x)+x/0.569422,-6.02001)))))", + "223_pt": "(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+116.441*pow(exp(-0.453438*x-0.157659*x*x)+x/0.569422,-6.02001)))*(((x<0.25)*(1.5*(1-0.8*x)))*(0.324086/(1+exp(-(x-0.573304)/0.143352))+-0.106092*TMath::Gaus(x,0.857984,0.27756)+-0.35343*TMath::Gaus(x,-0.266188,-1.4987)+0.358613)+((x>=0.25)*1.2*(0.324086/(1+exp(-(x-0.573304)/0.143352))+-0.106092*TMath::Gaus(x,0.857984,0.27756)+-0.35343*TMath::Gaus(x,-0.266188,-1.4987)+0.358613)))", + "333_pt": "(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+116.441*pow(exp(-0.453438*x-0.157659*x*x)+x/0.569422,-6.02001)))*(1.2*(0.0822258/(1+exp(-(x-1.07925)/1.16509))+-0.551967*TMath::Gaus(x,0.203349,1.77693)+0.438271*TMath::Gaus(x,0.266864,2.0576)+0.0913634))" + }, + "5TeV_7080_wRatio_pi0corr_ratiosdown": { + "histoMtScaleFactor": { + "223": 0.62, + "331": 0.32 + }, + "111_pt": "(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+116.441*pow(exp(-0.453438*x-0.157659*x*x)+x/0.569422,-6.02001)))/(7.18342e-01+(9.86860e-01-7.18342e-01)*(1-exp(-x/3.07386e-01)))", + "221_pt": "((TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+116.441*pow(exp(-0.453438*x-0.157659*x*x)+x/0.569422,-6.02001)))/(7.18342e-01+(9.86860e-01-7.18342e-01)*(1-exp(-x/3.07386e-01))))*(((x<0.4)*(0.4*(1+2.5*x)))*(((TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+116.441*pow(exp(-0.453438*x-0.157659*x*x)+x/0.569422,-6.02001)))*((0.509043/(1+exp(-(x-0.37759)/0.335237))+-0.159762*TMath::Gaus(x,0.948373,1.33321)+-0.0429426*TMath::Gaus(x,2.4898,2.67569)+0.0428236+((0.550454*exp((((0.995785*x)-sqrt((x*x)+(0.5478530000*0.5478530000)))/sqrt(1-(0.995785*0.995785)))/1.71099))+(0.0106936*(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/(exp((((0.995785*x)-sqrt((x*x)+(0.1349770000*0.1349770000)))/sqrt(1-(0.995785*0.995785)))/1.71099)+(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/2.))/(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+116.441*pow(exp(-0.453438*x-0.157659*x*x)+x/0.569422,-6.02001))))+(x>=0.4)*0.8*(((TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+116.441*pow(exp(-0.453438*x-0.157659*x*x)+x/0.569422,-6.02001)))*((0.509043/(1+exp(-(x-0.37759)/0.335237))+-0.159762*TMath::Gaus(x,0.948373,1.33321)+-0.0429426*TMath::Gaus(x,2.4898,2.67569)+0.0428236+((0.550454*exp((((0.995785*x)-sqrt((x*x)+(0.5478530000*0.5478530000)))/sqrt(1-(0.995785*0.995785)))/1.71099))+(0.0106936*(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/(exp((((0.995785*x)-sqrt((x*x)+(0.1349770000*0.1349770000)))/sqrt(1-(0.995785*0.995785)))/1.71099)+(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/2.))/(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+116.441*pow(exp(-0.453438*x-0.157659*x*x)+x/0.569422,-6.02001)))))", + "113_pt": "(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+116.441*pow(exp(-0.453438*x-0.157659*x*x)+x/0.569422,-6.02001)))*((x>1.951)*(0.451114/(1+exp(-(x-0.707439)/0.374928))+-0.307087*TMath::Gaus(x,-0.577264,1.60957)+-0.20694*TMath::Gaus(x,-2.50098,4.31777)+0.337195)+(x<=1.951)*((((0.52988/(1+exp(-((x-0.303965)/0.287856))))+(-0.281848*TMath::Gaus(x,0.497102,0.688973)))+(-0.249068*TMath::Gaus(x,1.09157,1.16378)))+0.253347))", + "333_pt": "(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+116.441*pow(exp(-0.453438*x-0.157659*x*x)+x/0.569422,-6.02001)))*(0.8*(0.0822258/(1+exp(-(x-1.07925)/1.16509))+-0.551967*TMath::Gaus(x,0.203349,1.77693)+0.438271*TMath::Gaus(x,0.266864,2.0576)+0.0913634))" + }, + "5TeV_8090_wRatio_pi0corr": { + "histoMtScaleFactor": { + "113": 0.84, + "331": 0.4 + }, + "111_pt": "(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+44.8976*pow(exp(-0.491415*x-0.0563088*x*x)+x/0.585056,-6.11751)))/(7.18342e-01+(9.86860e-01-7.18342e-01)*(1-exp(-x/3.07386e-01)))", + "221_pt": "((TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+44.8976*pow(exp(-0.491415*x-0.0563088*x*x)+x/0.585056,-6.11751)))/(7.18342e-01+(9.86860e-01-7.18342e-01)*(1-exp(-x/3.07386e-01))))*(((TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+44.8976*pow(exp(-0.491415*x-0.0563088*x*x)+x/0.585056,-6.11751)))*((0.401729/(1+exp(-(x-0.329328)/0.283064))+-0.165522*TMath::Gaus(x,0.572808,1.12455)+-0.098106*TMath::Gaus(x,1.55187,2.85629)+0.154946+((0.550454*exp((((0.995785*x)-sqrt((x*x)+(0.5478530000*0.5478530000)))/sqrt(1-(0.995785*0.995785)))/1.71099))+(0.0106936*(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/(exp((((0.995785*x)-sqrt((x*x)+(0.1349770000*0.1349770000)))/sqrt(1-(0.995785*0.995785)))/1.71099)+(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/2.))/(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+44.8976*pow(exp(-0.491415*x-0.0563088*x*x)+x/0.585056,-6.11751))))", + "223_pt": "(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+44.8976*pow(exp(-0.491415*x-0.0563088*x*x)+x/0.585056,-6.11751)))*(0.324086/(1+exp(-(x-0.573304)/0.143352))+-0.106092*TMath::Gaus(x,0.857984,0.27756)+-0.35343*TMath::Gaus(x,-0.266188,-1.4987)+0.358613)", + "333_pt": "(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+44.8976*pow(exp(-0.491415*x-0.0563088*x*x)+x/0.585056,-6.11751)))*(0.105659/(1+exp(-(x-0.781147)/3.13076))+-0.564081*TMath::Gaus(x,-0.134319,1.22474)+0.426234*TMath::Gaus(x,-0.173703,1.17147)+0.093734)" + }, + "5TeV_8090_wRatio_pi0corrup": { + "histoMtScaleFactor": { + "113": 0.84, + "331": 0.4 + }, + "111_pt": "(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+44.8976*pow(exp(-0.491415*x-0.0563088*x*x)+x/0.585056,-6.11751)))/(7.18342e-01*0.95+(9.86860e-01-7.18342e-01*0.95)*(1-exp(-x/3.07386e-01)))", + "221_pt": "((TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+44.8976*pow(exp(-0.491415*x-0.0563088*x*x)+x/0.585056,-6.11751)))/(7.18342e-01*0.95+(9.86860e-01-7.18342e-01*0.95)*(1-exp(-x/3.07386e-01))))*(((TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+44.8976*pow(exp(-0.491415*x-0.0563088*x*x)+x/0.585056,-6.11751)))*((0.401729/(1+exp(-(x-0.329328)/0.283064))+-0.165522*TMath::Gaus(x,0.572808,1.12455)+-0.098106*TMath::Gaus(x,1.55187,2.85629)+0.154946+((0.550454*exp((((0.995785*x)-sqrt((x*x)+(0.5478530000*0.5478530000)))/sqrt(1-(0.995785*0.995785)))/1.71099))+(0.0106936*(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/(exp((((0.995785*x)-sqrt((x*x)+(0.1349770000*0.1349770000)))/sqrt(1-(0.995785*0.995785)))/1.71099)+(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/2.))/(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+44.8976*pow(exp(-0.491415*x-0.0563088*x*x)+x/0.585056,-6.11751))))", + "223_pt": "(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+44.8976*pow(exp(-0.491415*x-0.0563088*x*x)+x/0.585056,-6.11751)))*(0.324086/(1+exp(-(x-0.573304)/0.143352))+-0.106092*TMath::Gaus(x,0.857984,0.27756)+-0.35343*TMath::Gaus(x,-0.266188,-1.4987)+0.358613)", + "333_pt": "(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+44.8976*pow(exp(-0.491415*x-0.0563088*x*x)+x/0.585056,-6.11751)))*(0.105659/(1+exp(-(x-0.781147)/3.13076))+-0.564081*TMath::Gaus(x,-0.134319,1.22474)+0.426234*TMath::Gaus(x,-0.173703,1.17147)+0.093734)" + }, + "5TeV_8090_wRatio_pi0corrdown": { + "histoMtScaleFactor": { + "113": 0.84, + "331": 0.4 + }, + "111_pt": "(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+44.8976*pow(exp(-0.491415*x-0.0563088*x*x)+x/0.585056,-6.11751)))/(7.18342e-01*1.05+(9.86860e-01-7.18342e-01*1.05)*(1-exp(-x/3.07386e-01)))", + "221_pt": "((TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+44.8976*pow(exp(-0.491415*x-0.0563088*x*x)+x/0.585056,-6.11751)))/(7.18342e-01*1.05+(9.86860e-01-7.18342e-01*1.05)*(1-exp(-x/3.07386e-01))))*(((TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+44.8976*pow(exp(-0.491415*x-0.0563088*x*x)+x/0.585056,-6.11751)))*((0.401729/(1+exp(-(x-0.329328)/0.283064))+-0.165522*TMath::Gaus(x,0.572808,1.12455)+-0.098106*TMath::Gaus(x,1.55187,2.85629)+0.154946+((0.550454*exp((((0.995785*x)-sqrt((x*x)+(0.5478530000*0.5478530000)))/sqrt(1-(0.995785*0.995785)))/1.71099))+(0.0106936*(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/(exp((((0.995785*x)-sqrt((x*x)+(0.1349770000*0.1349770000)))/sqrt(1-(0.995785*0.995785)))/1.71099)+(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/2.))/(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+44.8976*pow(exp(-0.491415*x-0.0563088*x*x)+x/0.585056,-6.11751))))", + "223_pt": "(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+44.8976*pow(exp(-0.491415*x-0.0563088*x*x)+x/0.585056,-6.11751)))*(0.324086/(1+exp(-(x-0.573304)/0.143352))+-0.106092*TMath::Gaus(x,0.857984,0.27756)+-0.35343*TMath::Gaus(x,-0.266188,-1.4987)+0.358613)", + "333_pt": "(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+44.8976*pow(exp(-0.491415*x-0.0563088*x*x)+x/0.585056,-6.11751)))*(0.105659/(1+exp(-(x-0.781147)/3.13076))+-0.564081*TMath::Gaus(x,-0.134319,1.22474)+0.426234*TMath::Gaus(x,-0.173703,1.17147)+0.093734)" + }, + "5TeV_8090_wRatio_pi0corr_up": { + "histoMtScaleFactor": { + "113": 0.84, + "331": 0.4 + }, + "111_pt": "(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+48.3871*pow(exp(-0.489465*x-0.105892*x*x)+x/0.566265,-6.04512)))/(7.18342e-01+(9.86860e-01-7.18342e-01)*(1-exp(-x/3.07386e-01)))", + "221_pt": "((TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+48.3871*pow(exp(-0.489465*x-0.105892*x*x)+x/0.566265,-6.04512)))/(7.18342e-01+(9.86860e-01-7.18342e-01)*(1-exp(-x/3.07386e-01))))*(((TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+48.3871*pow(exp(-0.489465*x-0.105892*x*x)+x/0.566265,-6.04512)))*((0.401729/(1+exp(-(x-0.329328)/0.283064))+-0.165522*TMath::Gaus(x,0.572808,1.12455)+-0.098106*TMath::Gaus(x,1.55187,2.85629)+0.154946+((0.550454*exp((((0.995785*x)-sqrt((x*x)+(0.5478530000*0.5478530000)))/sqrt(1-(0.995785*0.995785)))/1.71099))+(0.0106936*(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/(exp((((0.995785*x)-sqrt((x*x)+(0.1349770000*0.1349770000)))/sqrt(1-(0.995785*0.995785)))/1.71099)+(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/2.))/(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+48.3871*pow(exp(-0.489465*x-0.105892*x*x)+x/0.566265,-6.04512))))", + "223_pt": "(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+48.3871*pow(exp(-0.489465*x-0.105892*x*x)+x/0.566265,-6.04512)))*(0.324086/(1+exp(-(x-0.573304)/0.143352))+-0.106092*TMath::Gaus(x,0.857984,0.27756)+-0.35343*TMath::Gaus(x,-0.266188,-1.4987)+0.358613)", + "333_pt": "(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+48.3871*pow(exp(-0.489465*x-0.105892*x*x)+x/0.566265,-6.04512)))*(0.105659/(1+exp(-(x-0.781147)/3.13076))+-0.564081*TMath::Gaus(x,-0.134319,1.22474)+0.426234*TMath::Gaus(x,-0.173703,1.17147)+0.093734)" + }, + "5TeV_8090_wRatio_pi0corr_down": { + "histoMtScaleFactor": { + "113": 0.84, + "331": 0.4 + }, + "111_pt": "(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+41.8184*pow(exp(-0.45903*x-0.00764471*x*x)+x/0.618416,-6.24444)))/(7.18342e-01+(9.86860e-01-7.18342e-01)*(1-exp(-x/3.07386e-01)))", + "221_pt": "((TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+41.8184*pow(exp(-0.45903*x-0.00764471*x*x)+x/0.618416,-6.24444)))/(7.18342e-01+(9.86860e-01-7.18342e-01)*(1-exp(-x/3.07386e-01))))*(((TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+41.8184*pow(exp(-0.45903*x-0.00764471*x*x)+x/0.618416,-6.24444)))*((0.401729/(1+exp(-(x-0.329328)/0.283064))+-0.165522*TMath::Gaus(x,0.572808,1.12455)+-0.098106*TMath::Gaus(x,1.55187,2.85629)+0.154946+((0.550454*exp((((0.995785*x)-sqrt((x*x)+(0.5478530000*0.5478530000)))/sqrt(1-(0.995785*0.995785)))/1.71099))+(0.0106936*(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/(exp((((0.995785*x)-sqrt((x*x)+(0.1349770000*0.1349770000)))/sqrt(1-(0.995785*0.995785)))/1.71099)+(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/2.))/(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+41.8184*pow(exp(-0.45903*x-0.00764471*x*x)+x/0.618416,-6.24444))))", + "223_pt": "(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+41.8184*pow(exp(-0.45903*x-0.00764471*x*x)+x/0.618416,-6.24444)))*(0.324086/(1+exp(-(x-0.573304)/0.143352))+-0.106092*TMath::Gaus(x,0.857984,0.27756)+-0.35343*TMath::Gaus(x,-0.266188,-1.4987)+0.358613)", + "333_pt": "(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+41.8184*pow(exp(-0.45903*x-0.00764471*x*x)+x/0.618416,-6.24444)))*(0.105659/(1+exp(-(x-0.781147)/3.13076))+-0.564081*TMath::Gaus(x,-0.134319,1.22474)+0.426234*TMath::Gaus(x,-0.173703,1.17147)+0.093734)" + }, + "5TeV_8090_wRatio_pi0corr_ratiosup": { + "histoMtScaleFactor": { + "113": 1.01, + "331": 0.48 + }, + "111_pt": "(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+44.8976*pow(exp(-0.491415*x-0.0563088*x*x)+x/0.585056,-6.11751)))/(7.18342e-01+(9.86860e-01-7.18342e-01)*(1-exp(-x/3.07386e-01)))", + "221_pt": "((TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+44.8976*pow(exp(-0.491415*x-0.0563088*x*x)+x/0.585056,-6.11751)))/(7.18342e-01+(9.86860e-01-7.18342e-01)*(1-exp(-x/3.07386e-01))))*(((x<0.25)*(1.5*(1-0.8*x)))*(((TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+44.8976*pow(exp(-0.491415*x-0.0563088*x*x)+x/0.585056,-6.11751)))*((0.401729/(1+exp(-(x-0.329328)/0.283064))+-0.165522*TMath::Gaus(x,0.572808,1.12455)+-0.098106*TMath::Gaus(x,1.55187,2.85629)+0.154946+((0.550454*exp((((0.995785*x)-sqrt((x*x)+(0.5478530000*0.5478530000)))/sqrt(1-(0.995785*0.995785)))/1.71099))+(0.0106936*(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/(exp((((0.995785*x)-sqrt((x*x)+(0.1349770000*0.1349770000)))/sqrt(1-(0.995785*0.995785)))/1.71099)+(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/2.))/(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+44.8976*pow(exp(-0.491415*x-0.0563088*x*x)+x/0.585056,-6.11751))))+(x>=0.25)*1.2*(((TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+44.8976*pow(exp(-0.491415*x-0.0563088*x*x)+x/0.585056,-6.11751)))*((0.401729/(1+exp(-(x-0.329328)/0.283064))+-0.165522*TMath::Gaus(x,0.572808,1.12455)+-0.098106*TMath::Gaus(x,1.55187,2.85629)+0.154946+((0.550454*exp((((0.995785*x)-sqrt((x*x)+(0.5478530000*0.5478530000)))/sqrt(1-(0.995785*0.995785)))/1.71099))+(0.0106936*(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/(exp((((0.995785*x)-sqrt((x*x)+(0.1349770000*0.1349770000)))/sqrt(1-(0.995785*0.995785)))/1.71099)+(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/2.))/(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+44.8976*pow(exp(-0.491415*x-0.0563088*x*x)+x/0.585056,-6.11751)))))", + "223_pt": "(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+44.8976*pow(exp(-0.491415*x-0.0563088*x*x)+x/0.585056,-6.11751)))*(((x<0.25)*(1.5*(1-0.8*x)))*(0.324086/(1+exp(-(x-0.573304)/0.143352))+-0.106092*TMath::Gaus(x,0.857984,0.27756)+-0.35343*TMath::Gaus(x,-0.266188,-1.4987)+0.358613)+((x>=0.25)*1.2*(0.324086/(1+exp(-(x-0.573304)/0.143352))+-0.106092*TMath::Gaus(x,0.857984,0.27756)+-0.35343*TMath::Gaus(x,-0.266188,-1.4987)+0.358613)))", + "333_pt": "(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+44.8976*pow(exp(-0.491415*x-0.0563088*x*x)+x/0.585056,-6.11751)))*(1.2*(0.105659/(1+exp(-(x-0.781147)/3.13076))+-0.564081*TMath::Gaus(x,-0.134319,1.22474)+0.426234*TMath::Gaus(x,-0.173703,1.17147)+0.093734))" + }, + "5TeV_8090_wRatio_pi0corr_ratiosdown": { + "histoMtScaleFactor": { + "223": 0.62, + "331": 0.32 + }, + "111_pt": "(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+44.8976*pow(exp(-0.491415*x-0.0563088*x*x)+x/0.585056,-6.11751)))/(7.18342e-01+(9.86860e-01-7.18342e-01)*(1-exp(-x/3.07386e-01)))", + "221_pt": "((TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+44.8976*pow(exp(-0.491415*x-0.0563088*x*x)+x/0.585056,-6.11751)))/(7.18342e-01+(9.86860e-01-7.18342e-01)*(1-exp(-x/3.07386e-01))))*(((x<0.4)*(0.4*(1+2.5*x)))*(((TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+44.8976*pow(exp(-0.491415*x-0.0563088*x*x)+x/0.585056,-6.11751)))*((0.401729/(1+exp(-(x-0.329328)/0.283064))+-0.165522*TMath::Gaus(x,0.572808,1.12455)+-0.098106*TMath::Gaus(x,1.55187,2.85629)+0.154946+((0.550454*exp((((0.995785*x)-sqrt((x*x)+(0.5478530000*0.5478530000)))/sqrt(1-(0.995785*0.995785)))/1.71099))+(0.0106936*(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/(exp((((0.995785*x)-sqrt((x*x)+(0.1349770000*0.1349770000)))/sqrt(1-(0.995785*0.995785)))/1.71099)+(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/2.))/(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+44.8976*pow(exp(-0.491415*x-0.0563088*x*x)+x/0.585056,-6.11751))))+(x>=0.4)*0.8*(((TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+44.8976*pow(exp(-0.491415*x-0.0563088*x*x)+x/0.585056,-6.11751)))*((0.401729/(1+exp(-(x-0.329328)/0.283064))+-0.165522*TMath::Gaus(x,0.572808,1.12455)+-0.098106*TMath::Gaus(x,1.55187,2.85629)+0.154946+((0.550454*exp((((0.995785*x)-sqrt((x*x)+(0.5478530000*0.5478530000)))/sqrt(1-(0.995785*0.995785)))/1.71099))+(0.0106936*(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/(exp((((0.995785*x)-sqrt((x*x)+(0.1349770000*0.1349770000)))/sqrt(1-(0.995785*0.995785)))/1.71099)+(0.620714*TMath::Power(1+((x/29.189)*(x/29.189)),-(506.895)))))/2.))/(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+44.8976*pow(exp(-0.491415*x-0.0563088*x*x)+x/0.585056,-6.11751)))))", + "113_pt": "(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+44.8976*pow(exp(-0.491415*x-0.0563088*x*x)+x/0.585056,-6.11751)))*((x>1.951)*(0.451114/(1+exp(-(x-0.707439)/0.374928))+-0.307087*TMath::Gaus(x,-0.577264,1.60957)+-0.20694*TMath::Gaus(x,-2.50098,4.31777)+0.337195)+(x<=1.951)*((((0.52988/(1+exp(-((x-0.303965)/0.287856))))+(-0.281848*TMath::Gaus(x,0.497102,0.688973)))+(-0.249068*TMath::Gaus(x,1.09157,1.16378)))+0.253347))", + "333_pt": "(TMath::TwoPi()*x*(0*exp(-(sqrt(x*x+0.139571*0.139571)-0.139571)/0.1)+44.8976*pow(exp(-0.491415*x-0.0563088*x*x)+x/0.585056,-6.11751)))*(0.8*(0.105659/(1+exp(-(x-0.781147)/3.13076))+-0.564081*TMath::Gaus(x,-0.134319,1.22474)+0.426234*TMath::Gaus(x,-0.173703,1.17147)+0.093734))" + } +} diff --git a/MC/config/PWGEM/parametrizations/pp136TeV_run3.json b/MC/config/PWGEM/parametrizations/pp136TeV_run3.json new file mode 100755 index 000000000..974cd8545 --- /dev/null +++ b/MC/config/PWGEM/parametrizations/pp136TeV_run3.json @@ -0,0 +1,41 @@ +{ + "13TeV_MB_default": { + "histoMtScaleFactor": { + "113": 0.87, + "223": 0.572, + "331": 0.4 + }, + "221_pt": "((0.52015*1.)+(43.159*(-0.011541*TMath::Power(1+((x/1.11668)*(x/1.11668)),-(0.696078)))))/(1.+(-0.011541*TMath::Power(1+((x/1.11668)*(x/1.11668)),-(0.696078)))) * (x*(55.4505*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.14849)+2.93224*TMath::Power(1+(x*x)/(0.64965*0.64965*2.97448),-2.97448)))", + "111_pt": "( x*(55.4505*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.14849)+2.93224*TMath::Power(1+(x*x)/(0.64965*0.64965*2.97448),-2.97448)) )", + "333_pt": "(x*0.162735*TMath::Power(TMath::Exp(-0.455874*x-TMath::Abs(-0.0589095)*x*x)+x/1.30284,-5.7907))", + "443_pt": "x*3.76783e-05/TMath::Power((1+TMath::Power(x/4.06472,2.0)),3.03429)", + "321_pt": "1.2778632*x*7.70776*TMath::Power(TMath::Exp(-0.618249*x-TMath::Abs(3.16962e-09)*x*x)+x/1.02821,-5.47764)", + "2212_pt": "1.2778632*x*1.4891*TMath::Power(TMath::Exp(-0.4799*x-TMath::Abs(0.0225614)*x*x)+x/1.6315,-6.82731)" + }, + "13TeV_MB_etadown": { + "histoMtScaleFactor": { + "113": 0.87, + "223": 0.572, + "331": 0.4 + }, + "221_pt": "(1+(-0.012*x+0.2))*(((0.512943*1.)+(50.7191*(-0.00955168*TMath::Power(1+((x/1.27138)*(x/1.27138)),-(0.806776)))))/(1.+(-0.00955168*TMath::Power(1+((x/1.27138)*(x/1.27138)),-(0.806776))))) * (x*(55.4505*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.14849)+2.93224*TMath::Power(1+(x*x)/(0.64965*0.64965*2.97448),-2.97448)))", + "111_pt": "( x*(55.4505*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.14849)+2.93224*TMath::Power(1+(x*x)/(0.64965*0.64965*2.97448),-2.97448)) )", + "333_pt": "(x*0.162735*TMath::Power(TMath::Exp(-0.455874*x-TMath::Abs(-0.0589095)*x*x)+x/1.30284,-5.7907))", + "443_pt": "x*3.76783e-05/TMath::Power((1+TMath::Power(x/4.06472,2.0)),3.03429)", + "321_pt": "1.2778632*x*7.70776*TMath::Power(TMath::Exp(-0.618249*x-TMath::Abs(3.16962e-09)*x*x)+x/1.02821,-5.47764)", + "2212_pt": "1.2778632*x*1.4891*TMath::Power(TMath::Exp(-0.4799*x-TMath::Abs(0.0225614)*x*x)+x/1.6315,-6.82731)" + }, + "13TeV_MB_etaup": { + "histoMtScaleFactor": { + "113": 0.87, + "223": 0.572, + "331": 0.4 + }, + "221_pt": "(1-(-0.012*x+0.2))*(((0.539777*1.)+(44.4999*(-0.0119051*TMath::Power(1+((x/0.935105)*(x/0.935105)),-(0.559079)))))/(1.+(-0.0119051*TMath::Power(1+((x/0.935105)*(x/0.935105)),-(0.559079))))) * (x*(55.4505*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.14849)+2.93224*TMath::Power(1+(x*x)/(0.64965*0.64965*2.97448),-2.97448)))", + "111_pt": "( x*(55.4505*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.14849)+2.93224*TMath::Power(1+(x*x)/(0.64965*0.64965*2.97448),-2.97448)) )", + "333_pt": "(x*0.162735*TMath::Power(TMath::Exp(-0.455874*x-TMath::Abs(-0.0589095)*x*x)+x/1.30284,-5.7907))", + "443_pt": "x*3.76783e-05/TMath::Power((1+TMath::Power(x/4.06472,2.0)),3.03429)", + "321_pt": "1.2778632*x*7.70776*TMath::Power(TMath::Exp(-0.618249*x-TMath::Abs(3.16962e-09)*x*x)+x/1.02821,-5.47764)", + "2212_pt": "1.2778632*x*1.4891*TMath::Power(TMath::Exp(-0.4799*x-TMath::Abs(0.0225614)*x*x)+x/1.6315,-6.82731)" + } +} \ No newline at end of file diff --git a/MC/config/PWGEM/parametrizations/pp13TeV_run2.json b/MC/config/PWGEM/parametrizations/pp13TeV_run2.json new file mode 100644 index 000000000..6863d8e4b --- /dev/null +++ b/MC/config/PWGEM/parametrizations/pp13TeV_run2.json @@ -0,0 +1,899 @@ +{ + "13TeV_MB_default": { + "histoMtScaleFactor": { + "113": 0.87, + "223": 0.572, + "331": 0.4 + }, + "111_pt": "( x*(55.4505*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.14849)+2.93224*TMath::Power(1+(x*x)/(0.64965*0.64965*2.97448),-2.97448)) )", + "221_pt": "( x*(55.4505*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.14849)+2.93224*TMath::Power(1+(x*x)/(0.64965*0.64965*2.97448),-2.97448)) ) * ((0.554328*1.)+(176.93*(-0.00310177*TMath::Power(1.+((x/0.993569)*(x/0.993569)),-(0.480035)))))/(1.+(-0.00310177*TMath::Power(1.+((x/0.993569)*(x/0.993569)),-(0.480035))))", + "333_pt": "(x*0.162735*TMath::Power(TMath::Exp(-0.455874*x-TMath::Abs(-0.0589095)*x*x)+x/1.30284,-5.7907))", + "443_pt": "x*3.76783e-05/TMath::Power((1+TMath::Power(x/4.06472,2.0)),3.03429)", + "321_pt": "1.2778632*x*7.70776*TMath::Power(TMath::Exp(-0.618249*x-TMath::Abs(3.16962e-09)*x*x)+x/1.02821,-5.47764)", + "2212_pt": "1.2778632*x*1.4891*TMath::Power(TMath::Exp(-0.4799*x-TMath::Abs(0.0225614)*x*x)+x/1.6315,-6.82731)" + }, + "13TeV_MB_etaup": { + "histoMtScaleFactor": { + "113": 0.87, + "223": 0.572, + "331": 0.4 + }, + "111_pt": "( x*(55.4505*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.14849)+2.93224*TMath::Power(1+(x*x)/(0.64965*0.64965*2.97448),-2.97448)) )", + "221_pt": "( x*(55.4505*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.14849)+2.93224*TMath::Power(1+(x*x)/(0.64965*0.64965*2.97448),-2.97448)) ) * ((0.609299*1.)+(185.522*(-0.00325225*TMath::Power(1.+((x/0.861816)*(x/0.861816)),-(0.384348)))))/(1.+(-0.00325225*TMath::Power(1.+((x/0.861816)*(x/0.861816)),-(0.384348))))", + "333_pt": "(x*0.162735*TMath::Power(TMath::Exp(-0.455874*x-TMath::Abs(-0.0589095)*x*x)+x/1.30284,-5.7907))", + "443_pt": "x*3.76783e-05/TMath::Power((1+TMath::Power(x/4.06472,2.0)),3.03429)", + "321_pt": "1.2778632*x*7.70776*TMath::Power(TMath::Exp(-0.618249*x-TMath::Abs(3.16962e-09)*x*x)+x/1.02821,-5.47764)", + "2212_pt": "1.2778632*x*1.4891*TMath::Power(TMath::Exp(-0.4799*x-TMath::Abs(0.0225614)*x*x)+x/1.6315,-6.82731)" + }, + "13TeV_MB_etadown": { + "histoMtScaleFactor": { + "113": 0.87, + "223": 0.572, + "331": 0.4 + }, + "111_pt": "( x*(55.4505*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.14849)+2.93224*TMath::Power(1+(x*x)/(0.64965*0.64965*2.97448),-2.97448)) )", + "221_pt": "( x*(55.4505*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.14849)+2.93224*TMath::Power(1+(x*x)/(0.64965*0.64965*2.97448),-2.97448)) ) * ((0.509237*1.)+(169.821*(-0.00297709*TMath::Power(1.+((x/1.1418)*(x/1.1418)),-(0.59809)))))/(1.+(-0.00297709*TMath::Power(1.+((x/1.1418)*(x/1.1418)),-(0.59809))))", + "333_pt": "(x*0.162735*TMath::Power(TMath::Exp(-0.455874*x-TMath::Abs(-0.0589095)*x*x)+x/1.30284,-5.7907))", + "443_pt": "x*3.76783e-05/TMath::Power((1+TMath::Power(x/4.06472,2.0)),3.03429)", + "321_pt": "1.2778632*x*7.70776*TMath::Power(TMath::Exp(-0.618249*x-TMath::Abs(3.16962e-09)*x*x)+x/1.02821,-5.47764)", + "2212_pt": "1.2778632*x*1.4891*TMath::Power(TMath::Exp(-0.4799*x-TMath::Abs(0.0225614)*x*x)+x/1.6315,-6.82731)" + }, + "13TeV_MB_etaextra": { + "histoMtScaleFactor": { + "113": 0.87, + "223": 0.572, + "331": 0.4 + }, + "111_pt": "( x*(55.4505*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.14849)+2.93224*TMath::Power(1+(x*x)/(0.64965*0.64965*2.97448),-2.97448)) )", + "221_pt": "( x*(55.4505*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.14849)+2.93224*TMath::Power(1+(x*x)/(0.64965*0.64965*2.97448),-2.97448)) ) * ((0.591597*1.)+(179.421*(-0.00314491*TMath::Power(1.+((x/0.887987)*(x/0.887987)),-(0.374403)))))/(1.+(-0.00314491*TMath::Power(1.+((x/0.887987)*(x/0.887987)),-(0.374403))))", + "333_pt": "(x*0.162735*TMath::Power(TMath::Exp(-0.455874*x-TMath::Abs(-0.0589095)*x*x)+x/1.30284,-5.7907))", + "443_pt": "x*3.76783e-05/TMath::Power((1+TMath::Power(x/4.06472,2.0)),3.03429)", + "321_pt": "1.2778632*x*7.70776*TMath::Power(TMath::Exp(-0.618249*x-TMath::Abs(3.16962e-09)*x*x)+x/1.02821,-5.47764)", + "2212_pt": "1.2778632*x*1.4891*TMath::Power(TMath::Exp(-0.4799*x-TMath::Abs(0.0225614)*x*x)+x/1.6315,-6.82731)" + }, + "13TeV_MB_pi0up_eta": { + "histoMtScaleFactor": { + "113": 0.87, + "223": 0.572, + "331": 0.4 + }, + "111_pt": "( x*(55.4505*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.14849)+2.93224*TMath::Power(1+(x*x)/(0.64965*0.64965*2.97448),-2.97448)) )", + "221_pt": "( x*(86.1264*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.13135)+3.52458*TMath::Power(1+(x*x)/(0.627488*0.627488*2.95669),-2.95669)) ) * ((0.554328*1.)+(176.93*(-0.00310177*TMath::Power(1.+((x/0.993569)*(x/0.993569)),-(0.480035)))))/(1.+(-0.00310177*TMath::Power(1.+((x/0.993569)*(x/0.993569)),-(0.480035))))", + "333_pt": "(x*0.162735*TMath::Power(TMath::Exp(-0.455874*x-TMath::Abs(-0.0589095)*x*x)+x/1.30284,-5.7907))", + "443_pt": "x*3.76783e-05/TMath::Power((1+TMath::Power(x/4.06472,2.0)),3.03429)", + "321_pt": "1.2778632*x*7.70776*TMath::Power(TMath::Exp(-0.618249*x-TMath::Abs(3.16962e-09)*x*x)+x/1.02821,-5.47764)", + "2212_pt": "1.2778632*x*1.4891*TMath::Power(TMath::Exp(-0.4799*x-TMath::Abs(0.0225614)*x*x)+x/1.6315,-6.82731)" + }, + "13TeV_MB_pi0up_etaup": { + "histoMtScaleFactor": { + "113": 0.87, + "223": 0.572, + "331": 0.4 + }, + "111_pt": "( x*(55.4505*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.14849)+2.93224*TMath::Power(1+(x*x)/(0.64965*0.64965*2.97448),-2.97448)) )", + "221_pt": "( x*(86.1264*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.13135)+3.52458*TMath::Power(1+(x*x)/(0.627488*0.627488*2.95669),-2.95669)) ) * ((0.609299*1.)+(185.522*(-0.00325225*TMath::Power(1.+((x/0.861816)*(x/0.861816)),-(0.384348)))))/(1.+(-0.00325225*TMath::Power(1.+((x/0.861816)*(x/0.861816)),-(0.384348))))", + "333_pt": "(x*0.162735*TMath::Power(TMath::Exp(-0.455874*x-TMath::Abs(-0.0589095)*x*x)+x/1.30284,-5.7907))", + "443_pt": "x*3.76783e-05/TMath::Power((1+TMath::Power(x/4.06472,2.0)),3.03429)", + "321_pt": "1.2778632*x*7.70776*TMath::Power(TMath::Exp(-0.618249*x-TMath::Abs(3.16962e-09)*x*x)+x/1.02821,-5.47764)", + "2212_pt": "1.2778632*x*1.4891*TMath::Power(TMath::Exp(-0.4799*x-TMath::Abs(0.0225614)*x*x)+x/1.6315,-6.82731)" + }, + "13TeV_MB_pi0up_etadown": { + "histoMtScaleFactor": { + "113": 0.87, + "223": 0.572, + "331": 0.4 + }, + "111_pt": "( x*(55.4505*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.14849)+2.93224*TMath::Power(1+(x*x)/(0.64965*0.64965*2.97448),-2.97448)) )", + "221_pt": "( x*(86.1264*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.13135)+3.52458*TMath::Power(1+(x*x)/(0.627488*0.627488*2.95669),-2.95669)) ) * ((0.509237*1.)+(169.821*(-0.00297709*TMath::Power(1.+((x/1.1418)*(x/1.1418)),-(0.59809)))))/(1.+(-0.00297709*TMath::Power(1.+((x/1.1418)*(x/1.1418)),-(0.59809))))", + "333_pt": "(x*0.162735*TMath::Power(TMath::Exp(-0.455874*x-TMath::Abs(-0.0589095)*x*x)+x/1.30284,-5.7907))", + "443_pt": "x*3.76783e-05/TMath::Power((1+TMath::Power(x/4.06472,2.0)),3.03429)", + "321_pt": "1.2778632*x*7.70776*TMath::Power(TMath::Exp(-0.618249*x-TMath::Abs(3.16962e-09)*x*x)+x/1.02821,-5.47764)", + "2212_pt": "1.2778632*x*1.4891*TMath::Power(TMath::Exp(-0.4799*x-TMath::Abs(0.0225614)*x*x)+x/1.6315,-6.82731)" + }, + "13TeV_MB_pi0down_eta": { + "histoMtScaleFactor": { + "113": 0.87, + "223": 0.572, + "331": 0.4 + }, + "111_pt": "( x*(55.4505*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.14849)+2.93224*TMath::Power(1+(x*x)/(0.64965*0.64965*2.97448),-2.97448)) )", + "221_pt": "( x*(41.9814*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.159935)+2.55206*TMath::Power(1+(x*x)/(0.66629*0.66629*2.99049),-2.99049)) ) * ((0.554328*1.)+(176.93*(-0.00310177*TMath::Power(1.+((x/0.993569)*(x/0.993569)),-(0.480035)))))/(1.+(-0.00310177*TMath::Power(1.+((x/0.993569)*(x/0.993569)),-(0.480035))))", + "333_pt": "(x*0.162735*TMath::Power(TMath::Exp(-0.455874*x-TMath::Abs(-0.0589095)*x*x)+x/1.30284,-5.7907))", + "443_pt": "x*3.76783e-05/TMath::Power((1+TMath::Power(x/4.06472,2.0)),3.03429)", + "321_pt": "1.2778632*x*7.70776*TMath::Power(TMath::Exp(-0.618249*x-TMath::Abs(3.16962e-09)*x*x)+x/1.02821,-5.47764)", + "2212_pt": "1.2778632*x*1.4891*TMath::Power(TMath::Exp(-0.4799*x-TMath::Abs(0.0225614)*x*x)+x/1.6315,-6.82731)" + }, + "13TeV_MB_pi0down_etaup": { + "histoMtScaleFactor": { + "113": 0.87, + "223": 0.572, + "331": 0.4 + }, + "111_pt": "( x*(55.4505*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.14849)+2.93224*TMath::Power(1+(x*x)/(0.64965*0.64965*2.97448),-2.97448)) )", + "221_pt": "( x*(41.9814*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.159935)+2.55206*TMath::Power(1+(x*x)/(0.66629*0.66629*2.99049),-2.99049)) ) * ((0.609299*1.)+(185.522*(-0.00325225*TMath::Power(1.+((x/0.861816)*(x/0.861816)),-(0.384348)))))/(1.+(-0.00325225*TMath::Power(1.+((x/0.861816)*(x/0.861816)),-(0.384348))))", + "333_pt": "(x*0.162735*TMath::Power(TMath::Exp(-0.455874*x-TMath::Abs(-0.0589095)*x*x)+x/1.30284,-5.7907))", + "443_pt": "x*3.76783e-05/TMath::Power((1+TMath::Power(x/4.06472,2.0)),3.03429)", + "321_pt": "1.2778632*x*7.70776*TMath::Power(TMath::Exp(-0.618249*x-TMath::Abs(3.16962e-09)*x*x)+x/1.02821,-5.47764)", + "2212_pt": "1.2778632*x*1.4891*TMath::Power(TMath::Exp(-0.4799*x-TMath::Abs(0.0225614)*x*x)+x/1.6315,-6.82731)" + }, + "13TeV_MB_pi0down_etadown": { + "histoMtScaleFactor": { + "113": 0.87, + "223": 0.572, + "331": 0.4 + }, + "111_pt": "( x*(55.4505*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.14849)+2.93224*TMath::Power(1+(x*x)/(0.64965*0.64965*2.97448),-2.97448)) )", + "221_pt": "( x*(41.9814*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.159935)+2.55206*TMath::Power(1+(x*x)/(0.66629*0.66629*2.99049),-2.99049)) ) * ((0.509237*1.)+(169.821*(-0.00297709*TMath::Power(1.+((x/1.1418)*(x/1.1418)),-(0.59809)))))/(1.+(-0.00297709*TMath::Power(1.+((x/1.1418)*(x/1.1418)),-(0.59809))))", + "333_pt": "(x*0.162735*TMath::Power(TMath::Exp(-0.455874*x-TMath::Abs(-0.0589095)*x*x)+x/1.30284,-5.7907))", + "443_pt": "x*3.76783e-05/TMath::Power((1+TMath::Power(x/4.06472,2.0)),3.03429)", + "321_pt": "1.2778632*x*7.70776*TMath::Power(TMath::Exp(-0.618249*x-TMath::Abs(3.16962e-09)*x*x)+x/1.02821,-5.47764)", + "2212_pt": "1.2778632*x*1.4891*TMath::Power(TMath::Exp(-0.4799*x-TMath::Abs(0.0225614)*x*x)+x/1.6315,-6.82731)" + }, + "13TeV_MB_phiup": { + "histoMtScaleFactor": { + "113": 0.87, + "223": 0.572, + "331": 0.4 + }, + "111_pt": "( x*(55.4505*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.14849)+2.93224*TMath::Power(1+(x*x)/(0.64965*0.64965*2.97448),-2.97448)) )", + "221_pt": "( x*(55.4505*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.14849)+2.93224*TMath::Power(1+(x*x)/(0.64965*0.64965*2.97448),-2.97448)) ) * ((0.554328*1.)+(176.93*(-0.00310177*TMath::Power(1.+((x/0.993569)*(x/0.993569)),-(0.480035)))))/(1.+(-0.00310177*TMath::Power(1.+((x/0.993569)*(x/0.993569)),-(0.480035))))", + "333_pt": "1.1*(x*0.162735*TMath::Power(TMath::Exp(-0.455874*x-TMath::Abs(-0.0589095)*x*x)+x/1.30284,-5.7907))", + "443_pt": "x*3.76783e-05/TMath::Power((1+TMath::Power(x/4.06472,2.0)),3.03429)", + "321_pt": "1.2778632*x*7.70776*TMath::Power(TMath::Exp(-0.618249*x-TMath::Abs(3.16962e-09)*x*x)+x/1.02821,-5.47764)", + "2212_pt": "1.2778632*x*1.4891*TMath::Power(TMath::Exp(-0.4799*x-TMath::Abs(0.0225614)*x*x)+x/1.6315,-6.82731)" + }, + "13TeV_MB_phidown": { + "histoMtScaleFactor": { + "113": 0.87, + "223": 0.572, + "331": 0.4 + }, + "111_pt": "( x*(55.4505*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.14849)+2.93224*TMath::Power(1+(x*x)/(0.64965*0.64965*2.97448),-2.97448)) )", + "221_pt": "( x*(55.4505*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.14849)+2.93224*TMath::Power(1+(x*x)/(0.64965*0.64965*2.97448),-2.97448)) ) * ((0.554328*1.)+(176.93*(-0.00310177*TMath::Power(1.+((x/0.993569)*(x/0.993569)),-(0.480035)))))/(1.+(-0.00310177*TMath::Power(1.+((x/0.993569)*(x/0.993569)),-(0.480035))))", + "333_pt": "0.9*(x*0.162735*TMath::Power(TMath::Exp(-0.455874*x-TMath::Abs(-0.0589095)*x*x)+x/1.30284,-5.7907))", + "443_pt": "x*3.76783e-05/TMath::Power((1+TMath::Power(x/4.06472,2.0)),3.03429)", + "321_pt": "1.2778632*x*7.70776*TMath::Power(TMath::Exp(-0.618249*x-TMath::Abs(3.16962e-09)*x*x)+x/1.02821,-5.47764)", + "2212_pt": "1.2778632*x*1.4891*TMath::Power(TMath::Exp(-0.4799*x-TMath::Abs(0.0225614)*x*x)+x/1.6315,-6.82731)" + }, + "13TeV_MB_etapup": { + "histoMtScaleFactor": { + "113": 0.87, + "223": 0.572, + "331": 0.48 + }, + "111_pt": "( x*(55.4505*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.14849)+2.93224*TMath::Power(1+(x*x)/(0.64965*0.64965*2.97448),-2.97448)) )", + "221_pt": "( x*(55.4505*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.14849)+2.93224*TMath::Power(1+(x*x)/(0.64965*0.64965*2.97448),-2.97448)) ) * ((0.554328*1.)+(176.93*(-0.00310177*TMath::Power(1.+((x/0.993569)*(x/0.993569)),-(0.480035)))))/(1.+(-0.00310177*TMath::Power(1.+((x/0.993569)*(x/0.993569)),-(0.480035))))", + "333_pt": "(x*0.162735*TMath::Power(TMath::Exp(-0.455874*x-TMath::Abs(-0.0589095)*x*x)+x/1.30284,-5.7907))", + "443_pt": "x*3.76783e-05/TMath::Power((1+TMath::Power(x/4.06472,2.0)),3.03429)", + "321_pt": "1.2778632*x*7.70776*TMath::Power(TMath::Exp(-0.618249*x-TMath::Abs(3.16962e-09)*x*x)+x/1.02821,-5.47764)", + "2212_pt": "1.2778632*x*1.4891*TMath::Power(TMath::Exp(-0.4799*x-TMath::Abs(0.0225614)*x*x)+x/1.6315,-6.82731)" + }, + "13TeV_MB_etapdown": { + "histoMtScaleFactor": { + "113": 0.87, + "223": 0.572, + "331": 0.32 + }, + "111_pt": "( x*(55.4505*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.14849)+2.93224*TMath::Power(1+(x*x)/(0.64965*0.64965*2.97448),-2.97448)) )", + "221_pt": "( x*(55.4505*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.14849)+2.93224*TMath::Power(1+(x*x)/(0.64965*0.64965*2.97448),-2.97448)) ) * ((0.554328*1.)+(176.93*(-0.00310177*TMath::Power(1.+((x/0.993569)*(x/0.993569)),-(0.480035)))))/(1.+(-0.00310177*TMath::Power(1.+((x/0.993569)*(x/0.993569)),-(0.480035))))", + "333_pt": "(x*0.162735*TMath::Power(TMath::Exp(-0.455874*x-TMath::Abs(-0.0589095)*x*x)+x/1.30284,-5.7907))", + "443_pt": "x*3.76783e-05/TMath::Power((1+TMath::Power(x/4.06472,2.0)),3.03429)", + "321_pt": "1.2778632*x*7.70776*TMath::Power(TMath::Exp(-0.618249*x-TMath::Abs(3.16962e-09)*x*x)+x/1.02821,-5.47764)", + "2212_pt": "1.2778632*x*1.4891*TMath::Power(TMath::Exp(-0.4799*x-TMath::Abs(0.0225614)*x*x)+x/1.6315,-6.82731)" + }, + "13TeV_MB_rhoup": { + "histoMtScaleFactor": { + "113": 1.044, + "223": 0.572, + "331": 0.4 + }, + "111_pt": "( x*(55.4505*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.14849)+2.93224*TMath::Power(1+(x*x)/(0.64965*0.64965*2.97448),-2.97448)) )", + "221_pt": "( x*(55.4505*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.14849)+2.93224*TMath::Power(1+(x*x)/(0.64965*0.64965*2.97448),-2.97448)) ) * ((0.554328*1.)+(176.93*(-0.00310177*TMath::Power(1.+((x/0.993569)*(x/0.993569)),-(0.480035)))))/(1.+(-0.00310177*TMath::Power(1.+((x/0.993569)*(x/0.993569)),-(0.480035))))", + "333_pt": "(x*0.162735*TMath::Power(TMath::Exp(-0.455874*x-TMath::Abs(-0.0589095)*x*x)+x/1.30284,-5.7907))", + "443_pt": "x*3.76783e-05/TMath::Power((1+TMath::Power(x/4.06472,2.0)),3.03429)", + "321_pt": "1.2778632*x*7.70776*TMath::Power(TMath::Exp(-0.618249*x-TMath::Abs(3.16962e-09)*x*x)+x/1.02821,-5.47764)", + "2212_pt": "1.2778632*x*1.4891*TMath::Power(TMath::Exp(-0.4799*x-TMath::Abs(0.0225614)*x*x)+x/1.6315,-6.82731)" + }, + "13TeV_MB_rhodown": { + "histoMtScaleFactor": { + "113": 0.696, + "223": 0.572, + "331": 0.4 + }, + "111_pt": "( x*(55.4505*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.14849)+2.93224*TMath::Power(1+(x*x)/(0.64965*0.64965*2.97448),-2.97448)) )", + "221_pt": "( x*(55.4505*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.14849)+2.93224*TMath::Power(1+(x*x)/(0.64965*0.64965*2.97448),-2.97448)) ) * ((0.554328*1.)+(176.93*(-0.00310177*TMath::Power(1.+((x/0.993569)*(x/0.993569)),-(0.480035)))))/(1.+(-0.00310177*TMath::Power(1.+((x/0.993569)*(x/0.993569)),-(0.480035))))", + "333_pt": "(x*0.162735*TMath::Power(TMath::Exp(-0.455874*x-TMath::Abs(-0.0589095)*x*x)+x/1.30284,-5.7907))", + "443_pt": "x*3.76783e-05/TMath::Power((1+TMath::Power(x/4.06472,2.0)),3.03429)", + "321_pt": "1.2778632*x*7.70776*TMath::Power(TMath::Exp(-0.618249*x-TMath::Abs(3.16962e-09)*x*x)+x/1.02821,-5.47764)", + "2212_pt": "1.2778632*x*1.4891*TMath::Power(TMath::Exp(-0.4799*x-TMath::Abs(0.0225614)*x*x)+x/1.6315,-6.82731)" + }, + "13TeV_MB_omegaup": { + "histoMtScaleFactor": { + "113": 0.87, + "223": 0.6864, + "331": 0.4 + }, + "111_pt": "( x*(55.4505*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.14849)+2.93224*TMath::Power(1+(x*x)/(0.64965*0.64965*2.97448),-2.97448)) )", + "221_pt": "( x*(55.4505*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.14849)+2.93224*TMath::Power(1+(x*x)/(0.64965*0.64965*2.97448),-2.97448)) ) * ((0.554328*1.)+(176.93*(-0.00310177*TMath::Power(1.+((x/0.993569)*(x/0.993569)),-(0.480035)))))/(1.+(-0.00310177*TMath::Power(1.+((x/0.993569)*(x/0.993569)),-(0.480035))))", + "333_pt": "(x*0.162735*TMath::Power(TMath::Exp(-0.455874*x-TMath::Abs(-0.0589095)*x*x)+x/1.30284,-5.7907))", + "443_pt": "x*3.76783e-05/TMath::Power((1+TMath::Power(x/4.06472,2.0)),3.03429)", + "321_pt": "1.2778632*x*7.70776*TMath::Power(TMath::Exp(-0.618249*x-TMath::Abs(3.16962e-09)*x*x)+x/1.02821,-5.47764)", + "2212_pt": "1.2778632*x*1.4891*TMath::Power(TMath::Exp(-0.4799*x-TMath::Abs(0.0225614)*x*x)+x/1.6315,-6.82731)" + }, + "13TeV_MB_omegadown": { + "histoMtScaleFactor": { + "113": 0.87, + "223": 0.4576, + "331": 0.4 + }, + "111_pt": "( x*(55.4505*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.14849)+2.93224*TMath::Power(1+(x*x)/(0.64965*0.64965*2.97448),-2.97448)) )", + "221_pt": "( x*(55.4505*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.14849)+2.93224*TMath::Power(1+(x*x)/(0.64965*0.64965*2.97448),-2.97448)) ) * ((0.554328*1.)+(176.93*(-0.00310177*TMath::Power(1.+((x/0.993569)*(x/0.993569)),-(0.480035)))))/(1.+(-0.00310177*TMath::Power(1.+((x/0.993569)*(x/0.993569)),-(0.480035))))", + "333_pt": "(x*0.162735*TMath::Power(TMath::Exp(-0.455874*x-TMath::Abs(-0.0589095)*x*x)+x/1.30284,-5.7907))", + "443_pt": "x*3.76783e-05/TMath::Power((1+TMath::Power(x/4.06472,2.0)),3.03429)", + "321_pt": "1.2778632*x*7.70776*TMath::Power(TMath::Exp(-0.618249*x-TMath::Abs(3.16962e-09)*x*x)+x/1.02821,-5.47764)", + "2212_pt": "1.2778632*x*1.4891*TMath::Power(TMath::Exp(-0.4799*x-TMath::Abs(0.0225614)*x*x)+x/1.6315,-6.82731)" + }, + "13TeV_MB_jpsiup": { + "histoMtScaleFactor": { + "113": 0.87, + "223": 0.572, + "331": 0.4 + }, + "111_pt": "( x*(55.4505*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.14849)+2.93224*TMath::Power(1+(x*x)/(0.64965*0.64965*2.97448),-2.97448)) )", + "221_pt": "( x*(55.4505*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.14849)+2.93224*TMath::Power(1+(x*x)/(0.64965*0.64965*2.97448),-2.97448)) ) * ((0.554328*1.)+(176.93*(-0.00310177*TMath::Power(1.+((x/0.993569)*(x/0.993569)),-(0.480035)))))/(1.+(-0.00310177*TMath::Power(1.+((x/0.993569)*(x/0.993569)),-(0.480035))))", + "333_pt": "(x*0.162735*TMath::Power(TMath::Exp(-0.455874*x-TMath::Abs(-0.0589095)*x*x)+x/1.30284,-5.7907))", + "443_pt": "x*4.1893e-05/TMath::Power((1+TMath::Power(x/4.01254,2.0)),3.00288)", + "321_pt": "1.2778632*x*7.70776*TMath::Power(TMath::Exp(-0.618249*x-TMath::Abs(3.16962e-09)*x*x)+x/1.02821,-5.47764)", + "2212_pt": "1.2778632*x*1.4891*TMath::Power(TMath::Exp(-0.4799*x-TMath::Abs(0.0225614)*x*x)+x/1.6315,-6.82731)" + }, + "13TeV_MB_jpsidown": { + "histoMtScaleFactor": { + "113": 0.87, + "223": 0.572, + "331": 0.4 + }, + "111_pt": "( x*(55.4505*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.14849)+2.93224*TMath::Power(1+(x*x)/(0.64965*0.64965*2.97448),-2.97448)) )", + "221_pt": "( x*(55.4505*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.14849)+2.93224*TMath::Power(1+(x*x)/(0.64965*0.64965*2.97448),-2.97448)) ) * ((0.554328*1.)+(176.93*(-0.00310177*TMath::Power(1.+((x/0.993569)*(x/0.993569)),-(0.480035)))))/(1.+(-0.00310177*TMath::Power(1.+((x/0.993569)*(x/0.993569)),-(0.480035))))", + "333_pt": "(x*0.162735*TMath::Power(TMath::Exp(-0.455874*x-TMath::Abs(-0.0589095)*x*x)+x/1.30284,-5.7907))", + "443_pt": "x*3.34811e-05/TMath::Power((1+TMath::Power(x/4.12933,2.0)),3.07406)", + "321_pt": "1.2778632*x*7.70776*TMath::Power(TMath::Exp(-0.618249*x-TMath::Abs(3.16962e-09)*x*x)+x/1.02821,-5.47764)", + "2212_pt": "1.2778632*x*1.4891*TMath::Power(TMath::Exp(-0.4799*x-TMath::Abs(0.0225614)*x*x)+x/1.6315,-6.82731)" + }, + "13TeV_MB_16_jpsidefault": { + "histoMtScaleFactor": { + "113": 0.87, + "223": 0.572, + "331": 0.4 + }, + "111_pt": "( x*(55.4505*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.14849)+2.93224*TMath::Power(1+(x*x)/(0.64965*0.64965*2.97448),-2.97448)) )", + "221_pt": "( x*(55.4505*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.14849)+2.93224*TMath::Power(1+(x*x)/(0.64965*0.64965*2.97448),-2.97448)) ) * ((0.554328*1.)+(176.93*(-0.00310177*TMath::Power(1.+((x/0.993569)*(x/0.993569)),-(0.480035)))))/(1.+(-0.00310177*TMath::Power(1.+((x/0.993569)*(x/0.993569)),-(0.480035))))", + "333_pt": "(x*0.162735*TMath::Power(TMath::Exp(-0.455874*x-TMath::Abs(-0.0589095)*x*x)+x/1.30284,-5.7907))", + "443_pt": "x*3.72142e-05/TMath::Power((1+TMath::Power(x/4.06472,2.0)),3.03429)", + "321_pt": "1.2778632*x*7.70776*TMath::Power(TMath::Exp(-0.618249*x-TMath::Abs(3.16962e-09)*x*x)+x/1.02821,-5.47764)", + "2212_pt": "1.2778632*x*1.4891*TMath::Power(TMath::Exp(-0.4799*x-TMath::Abs(0.0225614)*x*x)+x/1.6315,-6.82731)" + }, + "13TeV_MB_16_jpsiup": { + "histoMtScaleFactor": { + "113": 0.87, + "223": 0.572, + "331": 0.4 + }, + "111_pt": "( x*(55.4505*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.14849)+2.93224*TMath::Power(1+(x*x)/(0.64965*0.64965*2.97448),-2.97448)) )", + "221_pt": "( x*(55.4505*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.14849)+2.93224*TMath::Power(1+(x*x)/(0.64965*0.64965*2.97448),-2.97448)) ) * ((0.554328*1.)+(176.93*(-0.00310177*TMath::Power(1.+((x/0.993569)*(x/0.993569)),-(0.480035)))))/(1.+(-0.00310177*TMath::Power(1.+((x/0.993569)*(x/0.993569)),-(0.480035))))", + "333_pt": "(x*0.162735*TMath::Power(TMath::Exp(-0.455874*x-TMath::Abs(-0.0589095)*x*x)+x/1.30284,-5.7907))", + "443_pt": "x*4.13769e-05/TMath::Power((1+TMath::Power(x/4.01254,2.0)),3.00288)", + "321_pt": "1.2778632*x*7.70776*TMath::Power(TMath::Exp(-0.618249*x-TMath::Abs(3.16962e-09)*x*x)+x/1.02821,-5.47764)", + "2212_pt": "1.2778632*x*1.4891*TMath::Power(TMath::Exp(-0.4799*x-TMath::Abs(0.0225614)*x*x)+x/1.6315,-6.82731)" + }, + "13TeV_MB_16_jpsidown": { + "histoMtScaleFactor": { + "113": 0.87, + "223": 0.572, + "331": 0.4 + }, + "111_pt": "( x*(55.4505*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.14849)+2.93224*TMath::Power(1+(x*x)/(0.64965*0.64965*2.97448),-2.97448)) )", + "221_pt": "( x*(55.4505*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.14849)+2.93224*TMath::Power(1+(x*x)/(0.64965*0.64965*2.97448),-2.97448)) ) * ((0.554328*1.)+(176.93*(-0.00310177*TMath::Power(1.+((x/0.993569)*(x/0.993569)),-(0.480035)))))/(1.+(-0.00310177*TMath::Power(1.+((x/0.993569)*(x/0.993569)),-(0.480035))))", + "333_pt": "(x*0.162735*TMath::Power(TMath::Exp(-0.455874*x-TMath::Abs(-0.0589095)*x*x)+x/1.30284,-5.7907))", + "443_pt": "x*3.30686e-05/TMath::Power((1+TMath::Power(x/4.12933,2.0)),3.07406)", + "321_pt": "1.2778632*x*7.70776*TMath::Power(TMath::Exp(-0.618249*x-TMath::Abs(3.16962e-09)*x*x)+x/1.02821,-5.47764)", + "2212_pt": "1.2778632*x*1.4891*TMath::Power(TMath::Exp(-0.4799*x-TMath::Abs(0.0225614)*x*x)+x/1.6315,-6.82731)" + }, + "13TeV_MB_17_jpsidefault": { + "histoMtScaleFactor": { + "113": 0.87, + "223": 0.572, + "331": 0.4 + }, + "111_pt": "( x*(55.4505*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.14849)+2.93224*TMath::Power(1+(x*x)/(0.64965*0.64965*2.97448),-2.97448)) )", + "221_pt": "( x*(55.4505*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.14849)+2.93224*TMath::Power(1+(x*x)/(0.64965*0.64965*2.97448),-2.97448)) ) * ((0.554328*1.)+(176.93*(-0.00310177*TMath::Power(1.+((x/0.993569)*(x/0.993569)),-(0.480035)))))/(1.+(-0.00310177*TMath::Power(1.+((x/0.993569)*(x/0.993569)),-(0.480035))))", + "333_pt": "(x*0.162735*TMath::Power(TMath::Exp(-0.455874*x-TMath::Abs(-0.0589095)*x*x)+x/1.30284,-5.7907))", + "443_pt": "x*3.74318e-05/TMath::Power((1+TMath::Power(x/4.0647,2.0)),3.03428)", + "321_pt": "1.2778632*x*7.70776*TMath::Power(TMath::Exp(-0.618249*x-TMath::Abs(3.16962e-09)*x*x)+x/1.02821,-5.47764)", + "2212_pt": "1.2778632*x*1.4891*TMath::Power(TMath::Exp(-0.4799*x-TMath::Abs(0.0225614)*x*x)+x/1.6315,-6.82731)" + }, + "13TeV_MB_17_jpsiup": { + "histoMtScaleFactor": { + "113": 0.87, + "223": 0.572, + "331": 0.4 + }, + "111_pt": "( x*(55.4505*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.14849)+2.93224*TMath::Power(1+(x*x)/(0.64965*0.64965*2.97448),-2.97448)) )", + "221_pt": "( x*(55.4505*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.14849)+2.93224*TMath::Power(1+(x*x)/(0.64965*0.64965*2.97448),-2.97448)) ) * ((0.554328*1.)+(176.93*(-0.00310177*TMath::Power(1.+((x/0.993569)*(x/0.993569)),-(0.480035)))))/(1.+(-0.00310177*TMath::Power(1.+((x/0.993569)*(x/0.993569)),-(0.480035))))", + "333_pt": "(x*0.162735*TMath::Power(TMath::Exp(-0.455874*x-TMath::Abs(-0.0589095)*x*x)+x/1.30284,-5.7907))", + "443_pt": "x*4.16191e-05/TMath::Power((1+TMath::Power(x/4.01253,2.0)),3.00288)", + "321_pt": "1.2778632*x*7.70776*TMath::Power(TMath::Exp(-0.618249*x-TMath::Abs(3.16962e-09)*x*x)+x/1.02821,-5.47764)", + "2212_pt": "1.2778632*x*1.4891*TMath::Power(TMath::Exp(-0.4799*x-TMath::Abs(0.0225614)*x*x)+x/1.6315,-6.82731)" + }, + "13TeV_MB_17_jpsidown": { + "histoMtScaleFactor": { + "113": 0.87, + "223": 0.572, + "331": 0.4 + }, + "111_pt": "( x*(55.4505*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.14849)+2.93224*TMath::Power(1+(x*x)/(0.64965*0.64965*2.97448),-2.97448)) )", + "221_pt": "( x*(55.4505*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.14849)+2.93224*TMath::Power(1+(x*x)/(0.64965*0.64965*2.97448),-2.97448)) ) * ((0.554328*1.)+(176.93*(-0.00310177*TMath::Power(1.+((x/0.993569)*(x/0.993569)),-(0.480035)))))/(1.+(-0.00310177*TMath::Power(1.+((x/0.993569)*(x/0.993569)),-(0.480035))))", + "333_pt": "(x*0.162735*TMath::Power(TMath::Exp(-0.455874*x-TMath::Abs(-0.0589095)*x*x)+x/1.30284,-5.7907))", + "443_pt": "x*3.32621e-05/TMath::Power((1+TMath::Power(x/4.12933,2.0)),3.07406)", + "321_pt": "1.2778632*x*7.70776*TMath::Power(TMath::Exp(-0.618249*x-TMath::Abs(3.16962e-09)*x*x)+x/1.02821,-5.47764)", + "2212_pt": "1.2778632*x*1.4891*TMath::Power(TMath::Exp(-0.4799*x-TMath::Abs(0.0225614)*x*x)+x/1.6315,-6.82731)" + }, + "13TeV_MB_Prelim_default": { + "histoMtScaleFactor": { + "113": 0.87, + "223": 0.572, + "331": 0.4 + }, + "111_pt": "( x*(75.2389*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.147008)+2.927*TMath::Power(1+(x*x)/(0.655054*0.655054*2.96614),-2.96614)) )", + "221_pt": "( x*(75.2389*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.147008)+2.927*TMath::Power(1+(x*x)/(0.655054*0.655054*2.96614),-2.96614)) ) * ( ((0.554112*1.)+(158.594*(-0.00305691*TMath::Power(1.+((x/1.52352)*(x/1.52352)),-(0.656663)))))/(1.+(-0.00305691*TMath::Power(1.+((x/1.52352)*(x/1.52352)),-(0.656663)))) )", + "333_pt": "(x*0.162735*TMath::Power(TMath::Exp(-0.455874*x-TMath::Abs(-0.0589095)*x*x)+x/1.30284,-5.7907))", + "443_pt": "x*3.76783e-05/TMath::Power((1+TMath::Power(x/4.06472,2.0)),3.03429)", + "321_pt": "1.2778632*x*7.70776*TMath::Power(TMath::Exp(-0.618249*x-TMath::Abs(3.16962e-09)*x*x)+x/1.02821,-5.47764)", + "2212_pt": "1.2778632*x*1.4891*TMath::Power(TMath::Exp(-0.4799*x-TMath::Abs(0.0225614)*x*x)+x/1.6315,-6.82731)" + }, + "13TeV_MB_Prelim_etaup": { + "histoMtScaleFactor": { + "113": 0.87, + "223": 0.572, + "331": 0.4 + }, + "111_pt": "( x*(75.2389*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.147008)+2.927*TMath::Power(1+(x*x)/(0.655054*0.655054*2.96614),-2.96614)) )", + "221_pt": "( x*(75.2389*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.147008)+2.927*TMath::Power(1+(x*x)/(0.655054*0.655054*2.96614),-2.96614)) ) * ( ((0.61306*1.)+(167.497*(-0.00322916*TMath::Power(1.+((x/1.29507)*(x/1.29507)),-(0.491599)))))/(1.+(-0.00322916*TMath::Power(1.+((x/1.29507)*(x/1.29507)),-(0.491599)))) )", + "333_pt": "(x*0.162735*TMath::Power(TMath::Exp(-0.455874*x-TMath::Abs(-0.0589095)*x*x)+x/1.30284,-5.7907))", + "443_pt": "x*3.76783e-05/TMath::Power((1+TMath::Power(x/4.06472,2.0)),3.03429)", + "321_pt": "1.2778632*x*7.70776*TMath::Power(TMath::Exp(-0.618249*x-TMath::Abs(3.16962e-09)*x*x)+x/1.02821,-5.47764)", + "2212_pt": "1.2778632*x*1.4891*TMath::Power(TMath::Exp(-0.4799*x-TMath::Abs(0.0225614)*x*x)+x/1.6315,-6.82731)" + }, + "13TeV_MB_Prelim_etadown": { + "histoMtScaleFactor": { + "113": 0.87, + "223": 0.572, + "331": 0.4 + }, + "111_pt": "( x*(75.2389*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.147008)+2.927*TMath::Power(1+(x*x)/(0.655054*0.655054*2.96614),-2.96614)) )", + "221_pt": "( x*(75.2389*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.147008)+2.927*TMath::Power(1+(x*x)/(0.655054*0.655054*2.96614),-2.96614)) ) * ( ((0.507127*1.)+(151.313*(-0.00291596*TMath::Power(1.+((x/1.81365)*(x/1.81365)),-(0.888791)))))/(1.+(-0.00291596*TMath::Power(1.+((x/1.81365)*(x/1.81365)),-(0.888791)))) )", + "333_pt": "(x*0.162735*TMath::Power(TMath::Exp(-0.455874*x-TMath::Abs(-0.0589095)*x*x)+x/1.30284,-5.7907))", + "443_pt": "x*3.76783e-05/TMath::Power((1+TMath::Power(x/4.06472,2.0)),3.03429)", + "321_pt": "1.2778632*x*7.70776*TMath::Power(TMath::Exp(-0.618249*x-TMath::Abs(3.16962e-09)*x*x)+x/1.02821,-5.47764)", + "2212_pt": "1.2778632*x*1.4891*TMath::Power(TMath::Exp(-0.4799*x-TMath::Abs(0.0225614)*x*x)+x/1.6315,-6.82731)" + }, + "13TeV_MB_Prelim_pi0up_eta": { + "histoMtScaleFactor": { + "113": 0.87, + "223": 0.572, + "331": 0.4 + }, + "111_pt": "( x*(75.2389*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.147008)+2.927*TMath::Power(1+(x*x)/(0.655054*0.655054*2.96614),-2.96614)) )", + "221_pt": "( x*(104.34*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.135354)+3.60886*TMath::Power(1+(x*x)/(0.62862*0.62862*2.9374),-2.9374)) ) * ( ((0.554112*1.)+(158.594*(-0.00305691*TMath::Power(1.+((x/1.52352)*(x/1.52352)),-(0.656663)))))/(1.+(-0.00305691*TMath::Power(1.+((x/1.52352)*(x/1.52352)),-(0.656663)))) )", + "333_pt": "(x*0.162735*TMath::Power(TMath::Exp(-0.455874*x-TMath::Abs(-0.0589095)*x*x)+x/1.30284,-5.7907))", + "443_pt": "x*3.76783e-05/TMath::Power((1+TMath::Power(x/4.06472,2.0)),3.03429)", + "321_pt": "1.2778632*x*7.70776*TMath::Power(TMath::Exp(-0.618249*x-TMath::Abs(3.16962e-09)*x*x)+x/1.02821,-5.47764)", + "2212_pt": "1.2778632*x*1.4891*TMath::Power(TMath::Exp(-0.4799*x-TMath::Abs(0.0225614)*x*x)+x/1.6315,-6.82731)" + }, + "13TeV_MB_Prelim_pi0up_etaup": { + "histoMtScaleFactor": { + "113": 0.87, + "223": 0.572, + "331": 0.4 + }, + "111_pt": "( x*(75.2389*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.147008)+2.927*TMath::Power(1+(x*x)/(0.655054*0.655054*2.96614),-2.96614)) )", + "221_pt": "( x*(104.34*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.135354)+3.60886*TMath::Power(1+(x*x)/(0.62862*0.62862*2.9374),-2.9374)) ) * ( ((0.61306*1.)+(167.497*(-0.00322916*TMath::Power(1.+((x/1.29507)*(x/1.29507)),-(0.491599)))))/(1.+(-0.00322916*TMath::Power(1.+((x/1.29507)*(x/1.29507)),-(0.491599)))) )", + "333_pt": "(x*0.162735*TMath::Power(TMath::Exp(-0.455874*x-TMath::Abs(-0.0589095)*x*x)+x/1.30284,-5.7907))", + "443_pt": "x*3.76783e-05/TMath::Power((1+TMath::Power(x/4.06472,2.0)),3.03429)", + "321_pt": "1.2778632*x*7.70776*TMath::Power(TMath::Exp(-0.618249*x-TMath::Abs(3.16962e-09)*x*x)+x/1.02821,-5.47764)", + "2212_pt": "1.2778632*x*1.4891*TMath::Power(TMath::Exp(-0.4799*x-TMath::Abs(0.0225614)*x*x)+x/1.6315,-6.82731)" + }, + "13TeV_MB_Prelim_pi0up_etadown": { + "histoMtScaleFactor": { + "113": 0.87, + "223": 0.572, + "331": 0.4 + }, + "111_pt": "( x*(75.2389*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.147008)+2.927*TMath::Power(1+(x*x)/(0.655054*0.655054*2.96614),-2.96614)) )", + "221_pt": "( x*(104.34*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.135354)+3.60886*TMath::Power(1+(x*x)/(0.62862*0.62862*2.9374),-2.9374)) ) * ( ((0.507127*1.)+(151.313*(-0.00291596*TMath::Power(1.+((x/1.81365)*(x/1.81365)),-(0.888791)))))/(1.+(-0.00291596*TMath::Power(1.+((x/1.81365)*(x/1.81365)),-(0.888791)))) )", + "333_pt": "(x*0.162735*TMath::Power(TMath::Exp(-0.455874*x-TMath::Abs(-0.0589095)*x*x)+x/1.30284,-5.7907))", + "443_pt": "x*3.76783e-05/TMath::Power((1+TMath::Power(x/4.06472,2.0)),3.03429)", + "321_pt": "1.2778632*x*7.70776*TMath::Power(TMath::Exp(-0.618249*x-TMath::Abs(3.16962e-09)*x*x)+x/1.02821,-5.47764)", + "2212_pt": "1.2778632*x*1.4891*TMath::Power(TMath::Exp(-0.4799*x-TMath::Abs(0.0225614)*x*x)+x/1.6315,-6.82731)" + }, + "13TeV_MB_Prelim_pi0down_eta": { + "histoMtScaleFactor": { + "113": 0.87, + "223": 0.572, + "331": 0.4 + }, + "111_pt": "( x*(75.2389*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.147008)+2.927*TMath::Power(1+(x*x)/(0.655054*0.655054*2.96614),-2.96614)) )", + "221_pt": "( x*(52.8954*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.162106)+2.25247*TMath::Power(1+(x*x)/(0.690119*0.690119*3.00143),-3.00143)) ) * ( ((0.554112*1.)+(158.594*(-0.00305691*TMath::Power(1.+((x/1.52352)*(x/1.52352)),-(0.656663)))))/(1.+(-0.00305691*TMath::Power(1.+((x/1.52352)*(x/1.52352)),-(0.656663)))) )", + "333_pt": "(x*0.162735*TMath::Power(TMath::Exp(-0.455874*x-TMath::Abs(-0.0589095)*x*x)+x/1.30284,-5.7907))", + "443_pt": "x*3.76783e-05/TMath::Power((1+TMath::Power(x/4.06472,2.0)),3.03429)", + "321_pt": "1.2778632*x*7.70776*TMath::Power(TMath::Exp(-0.618249*x-TMath::Abs(3.16962e-09)*x*x)+x/1.02821,-5.47764)", + "2212_pt": "1.2778632*x*1.4891*TMath::Power(TMath::Exp(-0.4799*x-TMath::Abs(0.0225614)*x*x)+x/1.6315,-6.82731)" + }, + "13TeV_MB_Prelim_pi0down_etaup": { + "histoMtScaleFactor": { + "113": 0.87, + "223": 0.572, + "331": 0.4 + }, + "111_pt": "( x*(75.2389*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.147008)+2.927*TMath::Power(1+(x*x)/(0.655054*0.655054*2.96614),-2.96614)) )", + "221_pt": "( x*(52.8954*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.162106)+2.25247*TMath::Power(1+(x*x)/(0.690119*0.690119*3.00143),-3.00143)) ) * ( ((0.61306*1.)+(167.497*(-0.00322916*TMath::Power(1.+((x/1.29507)*(x/1.29507)),-(0.491599)))))/(1.+(-0.00322916*TMath::Power(1.+((x/1.29507)*(x/1.29507)),-(0.491599)))) )", + "333_pt": "(x*0.162735*TMath::Power(TMath::Exp(-0.455874*x-TMath::Abs(-0.0589095)*x*x)+x/1.30284,-5.7907))", + "443_pt": "x*3.76783e-05/TMath::Power((1+TMath::Power(x/4.06472,2.0)),3.03429)", + "321_pt": "1.2778632*x*7.70776*TMath::Power(TMath::Exp(-0.618249*x-TMath::Abs(3.16962e-09)*x*x)+x/1.02821,-5.47764)", + "2212_pt": "1.2778632*x*1.4891*TMath::Power(TMath::Exp(-0.4799*x-TMath::Abs(0.0225614)*x*x)+x/1.6315,-6.82731)" + }, + "13TeV_MB_Prelim_pi0down_etadown": { + "histoMtScaleFactor": { + "113": 0.87, + "223": 0.572, + "331": 0.4 + }, + "111_pt": "( x*(75.2389*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.147008)+2.927*TMath::Power(1+(x*x)/(0.655054*0.655054*2.96614),-2.96614)) )", + "221_pt": "( x*(52.8954*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.162106)+2.25247*TMath::Power(1+(x*x)/(0.690119*0.690119*3.00143),-3.00143)) ) * ( ((0.507127*1.)+(151.313*(-0.00291596*TMath::Power(1.+((x/1.81365)*(x/1.81365)),-(0.888791)))))/(1.+(-0.00291596*TMath::Power(1.+((x/1.81365)*(x/1.81365)),-(0.888791)))) )", + "333_pt": "(x*0.162735*TMath::Power(TMath::Exp(-0.455874*x-TMath::Abs(-0.0589095)*x*x)+x/1.30284,-5.7907))", + "443_pt": "x*3.76783e-05/TMath::Power((1+TMath::Power(x/4.06472,2.0)),3.03429)", + "321_pt": "1.2778632*x*7.70776*TMath::Power(TMath::Exp(-0.618249*x-TMath::Abs(3.16962e-09)*x*x)+x/1.02821,-5.47764)", + "2212_pt": "1.2778632*x*1.4891*TMath::Power(TMath::Exp(-0.4799*x-TMath::Abs(0.0225614)*x*x)+x/1.6315,-6.82731)" + }, + "13TeV_HM_default": { + "histoMtScaleFactor": { + "113": 0.87, + "223": 0.572, + "331": 0.4 + }, + "111_pt": "( x*(326.754*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.140039)+17.4259*TMath::Power(1+(x*x)/(0.685364*0.685364*2.88243),-2.88243)) )", + "221_pt": "( x*(326.754*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.140039)+17.4259*TMath::Power(1+(x*x)/(0.685364*0.685364*2.88243),-2.88243)) ) * ((0.691819*1.)+(180.263*(-0.00350264*TMath::Power(1.+((x/0.91991)*(x/0.91991)),-(0.242107)))))/(1.+(-0.00350264*TMath::Power(1.+((x/0.91991)*(x/0.91991)),-(0.242107))))", + "333_pt": "(1.217*x*0.44822*TMath::Power(TMath::Exp(-0.364259*x-TMath::Abs(-0.12213)*x*x)+x/1.42716,-5.37383))", + "443_pt": "x*3.76783e-05/TMath::Power((1+TMath::Power(x/4.06472,2.0)),3.03429)", + "321_pt": "1.2778632*x*7.70776*TMath::Power(TMath::Exp(-0.618249*x-TMath::Abs(3.16962e-09)*x*x)+x/1.02821,-5.47764)", + "2212_pt": "1.2778632*x*1.4891*TMath::Power(TMath::Exp(-0.4799*x-TMath::Abs(0.0225614)*x*x)+x/1.6315,-6.82731)" + }, + "13TeV_HM_etaup": { + "histoMtScaleFactor": { + "113": 0.87, + "223": 0.572, + "331": 0.4 + }, + "111_pt": "( x*(326.754*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.140039)+17.4259*TMath::Power(1+(x*x)/(0.685364*0.685364*2.88243),-2.88243)) )", + "221_pt": "( x*(326.754*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.140039)+17.4259*TMath::Power(1+(x*x)/(0.685364*0.685364*2.88243),-2.88243)) ) * ((0.799845*1.)+(192.141*(-0.00373188*TMath::Power(1.+((x/0.953155)*(x/0.953155)),-(0.199252)))))/(1.+(-0.00373188*TMath::Power(1.+((x/0.953155)*(x/0.953155)),-(0.199252))))", + "333_pt": "(1.217*x*0.44822*TMath::Power(TMath::Exp(-0.364259*x-TMath::Abs(-0.12213)*x*x)+x/1.42716,-5.37383))", + "443_pt": "x*3.76783e-05/TMath::Power((1+TMath::Power(x/4.06472,2.0)),3.03429)", + "321_pt": "1.2778632*x*7.70776*TMath::Power(TMath::Exp(-0.618249*x-TMath::Abs(3.16962e-09)*x*x)+x/1.02821,-5.47764)", + "2212_pt": "1.2778632*x*1.4891*TMath::Power(TMath::Exp(-0.4799*x-TMath::Abs(0.0225614)*x*x)+x/1.6315,-6.82731)" + }, + "13TeV_HM_etadown": { + "histoMtScaleFactor": { + "113": 0.87, + "223": 0.572, + "331": 0.4 + }, + "111_pt": "( x*(326.754*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.140039)+17.4259*TMath::Power(1+(x*x)/(0.685364*0.685364*2.88243),-2.88243)) )", + "221_pt": "( x*(326.754*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.140039)+17.4259*TMath::Power(1+(x*x)/(0.685364*0.685364*2.88243),-2.88243)) ) * ((0.60826*1.)+(171.512*(-0.00333377*TMath::Power(1.+((x/0.891623)*(x/0.891623)),-(0.289837)))))/(1.+(-0.00333377*TMath::Power(1.+((x/0.891623)*(x/0.891623)),-(0.289837))))", + "333_pt": "(1.217*x*0.44822*TMath::Power(TMath::Exp(-0.364259*x-TMath::Abs(-0.12213)*x*x)+x/1.42716,-5.37383))", + "443_pt": "x*3.76783e-05/TMath::Power((1+TMath::Power(x/4.06472,2.0)),3.03429)", + "321_pt": "1.2778632*x*7.70776*TMath::Power(TMath::Exp(-0.618249*x-TMath::Abs(3.16962e-09)*x*x)+x/1.02821,-5.47764)", + "2212_pt": "1.2778632*x*1.4891*TMath::Power(TMath::Exp(-0.4799*x-TMath::Abs(0.0225614)*x*x)+x/1.6315,-6.82731)" + }, + "13TeV_HM_pi0up_eta": { + "histoMtScaleFactor": { + "113": 0.87, + "223": 0.572, + "331": 0.4 + }, + "111_pt": "( x*(326.754*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.140039)+17.4259*TMath::Power(1+(x*x)/(0.685364*0.685364*2.88243),-2.88243)) )", + "221_pt": "( x*(497.744*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.128497)+19.8436*TMath::Power(1+(x*x)/(0.669985*0.669985*2.86007),-2.86007)) ) * ((0.691819*1.)+(180.263*(-0.00350264*TMath::Power(1.+((x/0.91991)*(x/0.91991)),-(0.242107)))))/(1.+(-0.00350264*TMath::Power(1.+((x/0.91991)*(x/0.91991)),-(0.242107))))", + "333_pt": "(1.217*x*0.44822*TMath::Power(TMath::Exp(-0.364259*x-TMath::Abs(-0.12213)*x*x)+x/1.42716,-5.37383))", + "443_pt": "x*3.76783e-05/TMath::Power((1+TMath::Power(x/4.06472,2.0)),3.03429)", + "321_pt": "1.2778632*x*7.70776*TMath::Power(TMath::Exp(-0.618249*x-TMath::Abs(3.16962e-09)*x*x)+x/1.02821,-5.47764)", + "2212_pt": "1.2778632*x*1.4891*TMath::Power(TMath::Exp(-0.4799*x-TMath::Abs(0.0225614)*x*x)+x/1.6315,-6.82731)" + }, + "13TeV_HM_pi0up_etaup": { + "histoMtScaleFactor": { + "113": 0.87, + "223": 0.572, + "331": 0.4 + }, + "111_pt": "( x*(326.754*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.140039)+17.4259*TMath::Power(1+(x*x)/(0.685364*0.685364*2.88243),-2.88243)) )", + "221_pt": "( x*(497.744*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.128497)+19.8436*TMath::Power(1+(x*x)/(0.669985*0.669985*2.86007),-2.86007)) ) * ((0.799845*1.)+(192.141*(-0.00373188*TMath::Power(1.+((x/0.953155)*(x/0.953155)),-(0.199252)))))/(1.+(-0.00373188*TMath::Power(1.+((x/0.953155)*(x/0.953155)),-(0.199252))))", + "333_pt": "(1.217*x*0.44822*TMath::Power(TMath::Exp(-0.364259*x-TMath::Abs(-0.12213)*x*x)+x/1.42716,-5.37383))", + "443_pt": "x*3.76783e-05/TMath::Power((1+TMath::Power(x/4.06472,2.0)),3.03429)", + "321_pt": "1.2778632*x*7.70776*TMath::Power(TMath::Exp(-0.618249*x-TMath::Abs(3.16962e-09)*x*x)+x/1.02821,-5.47764)", + "2212_pt": "1.2778632*x*1.4891*TMath::Power(TMath::Exp(-0.4799*x-TMath::Abs(0.0225614)*x*x)+x/1.6315,-6.82731)" + }, + "13TeV_HM_pi0up_etadown": { + "histoMtScaleFactor": { + "113": 0.87, + "223": 0.572, + "331": 0.4 + }, + "111_pt": "( x*(326.754*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.140039)+17.4259*TMath::Power(1+(x*x)/(0.685364*0.685364*2.88243),-2.88243)) )", + "221_pt": "( x*(497.744*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.128497)+19.8436*TMath::Power(1+(x*x)/(0.669985*0.669985*2.86007),-2.86007)) ) * ((0.60826*1.)+(171.512*(-0.00333377*TMath::Power(1.+((x/0.891623)*(x/0.891623)),-(0.289837)))))/(1.+(-0.00333377*TMath::Power(1.+((x/0.891623)*(x/0.891623)),-(0.289837))))", + "333_pt": "(1.217*x*0.44822*TMath::Power(TMath::Exp(-0.364259*x-TMath::Abs(-0.12213)*x*x)+x/1.42716,-5.37383))", + "443_pt": "x*3.76783e-05/TMath::Power((1+TMath::Power(x/4.06472,2.0)),3.03429)", + "321_pt": "1.2778632*x*7.70776*TMath::Power(TMath::Exp(-0.618249*x-TMath::Abs(3.16962e-09)*x*x)+x/1.02821,-5.47764)", + "2212_pt": "1.2778632*x*1.4891*TMath::Power(TMath::Exp(-0.4799*x-TMath::Abs(0.0225614)*x*x)+x/1.6315,-6.82731)" + }, + "13TeV_HM_pi0down_eta": { + "histoMtScaleFactor": { + "113": 0.87, + "223": 0.572, + "331": 0.4 + }, + "111_pt": "( x*(326.754*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.140039)+17.4259*TMath::Power(1+(x*x)/(0.685364*0.685364*2.88243),-2.88243)) )", + "221_pt": "( x*(204.529*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.155686)+14.9262*TMath::Power(1+(x*x)/(0.704871*0.704871*2.90918),-2.90918)) ) * ((0.691819*1.)+(180.263*(-0.00350264*TMath::Power(1.+((x/0.91991)*(x/0.91991)),-(0.242107)))))/(1.+(-0.00350264*TMath::Power(1.+((x/0.91991)*(x/0.91991)),-(0.242107))))", + "333_pt": "(1.217*x*0.44822*TMath::Power(TMath::Exp(-0.364259*x-TMath::Abs(-0.12213)*x*x)+x/1.42716,-5.37383))", + "443_pt": "x*3.76783e-05/TMath::Power((1+TMath::Power(x/4.06472,2.0)),3.03429)", + "321_pt": "1.2778632*x*7.70776*TMath::Power(TMath::Exp(-0.618249*x-TMath::Abs(3.16962e-09)*x*x)+x/1.02821,-5.47764)", + "2212_pt": "1.2778632*x*1.4891*TMath::Power(TMath::Exp(-0.4799*x-TMath::Abs(0.0225614)*x*x)+x/1.6315,-6.82731)" + }, + "13TeV_HM_pi0down_etaup": { + "histoMtScaleFactor": { + "113": 0.87, + "223": 0.572, + "331": 0.4 + }, + "111_pt": "( x*(326.754*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.140039)+17.4259*TMath::Power(1+(x*x)/(0.685364*0.685364*2.88243),-2.88243)) )", + "221_pt": "( x*(204.529*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.155686)+14.9262*TMath::Power(1+(x*x)/(0.704871*0.704871*2.90918),-2.90918)) ) * ((0.799845*1.)+(192.141*(-0.00373188*TMath::Power(1.+((x/0.953155)*(x/0.953155)),-(0.199252)))))/(1.+(-0.00373188*TMath::Power(1.+((x/0.953155)*(x/0.953155)),-(0.199252))))", + "333_pt": "(1.217*x*0.44822*TMath::Power(TMath::Exp(-0.364259*x-TMath::Abs(-0.12213)*x*x)+x/1.42716,-5.37383))", + "443_pt": "x*3.76783e-05/TMath::Power((1+TMath::Power(x/4.06472,2.0)),3.03429)", + "321_pt": "1.2778632*x*7.70776*TMath::Power(TMath::Exp(-0.618249*x-TMath::Abs(3.16962e-09)*x*x)+x/1.02821,-5.47764)", + "2212_pt": "1.2778632*x*1.4891*TMath::Power(TMath::Exp(-0.4799*x-TMath::Abs(0.0225614)*x*x)+x/1.6315,-6.82731)" + }, + "13TeV_HM_pi0down_etadown": { + "histoMtScaleFactor": { + "113": 0.87, + "223": 0.572, + "331": 0.4 + }, + "111_pt": "( x*(326.754*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.140039)+17.4259*TMath::Power(1+(x*x)/(0.685364*0.685364*2.88243),-2.88243)) )", + "221_pt": "( x*(204.529*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.155686)+14.9262*TMath::Power(1+(x*x)/(0.704871*0.704871*2.90918),-2.90918)) ) * ((0.60826*1.)+(171.512*(-0.00333377*TMath::Power(1.+((x/0.891623)*(x/0.891623)),-(0.289837)))))/(1.+(-0.00333377*TMath::Power(1.+((x/0.891623)*(x/0.891623)),-(0.289837))))", + "333_pt": "(1.217*x*0.44822*TMath::Power(TMath::Exp(-0.364259*x-TMath::Abs(-0.12213)*x*x)+x/1.42716,-5.37383))", + "443_pt": "x*3.76783e-05/TMath::Power((1+TMath::Power(x/4.06472,2.0)),3.03429)", + "321_pt": "1.2778632*x*7.70776*TMath::Power(TMath::Exp(-0.618249*x-TMath::Abs(3.16962e-09)*x*x)+x/1.02821,-5.47764)", + "2212_pt": "1.2778632*x*1.4891*TMath::Power(TMath::Exp(-0.4799*x-TMath::Abs(0.0225614)*x*x)+x/1.6315,-6.82731)" + }, + "13TeV_HM_phiup": { + "histoMtScaleFactor": { + "113": 0.87, + "223": 0.572, + "331": 0.4 + }, + "111_pt": "( x*(326.754*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.140039)+17.4259*TMath::Power(1+(x*x)/(0.685364*0.685364*2.88243),-2.88243)) )", + "221_pt": "( x*(326.754*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.140039)+17.4259*TMath::Power(1+(x*x)/(0.685364*0.685364*2.88243),-2.88243)) ) * ((0.691819*1.)+(180.263*(-0.00350264*TMath::Power(1.+((x/0.91991)*(x/0.91991)),-(0.242107)))))/(1.+(-0.00350264*TMath::Power(1.+((x/0.91991)*(x/0.91991)),-(0.242107))))", + "333_pt": "1.1*((1.217*x*0.44822*TMath::Power(TMath::Exp(-0.364259*x-TMath::Abs(-0.12213)*x*x)+x/1.42716,-5.37383)))", + "443_pt": "x*3.76783e-05/TMath::Power((1+TMath::Power(x/4.06472,2.0)),3.03429)", + "321_pt": "1.2778632*x*7.70776*TMath::Power(TMath::Exp(-0.618249*x-TMath::Abs(3.16962e-09)*x*x)+x/1.02821,-5.47764)", + "2212_pt": "1.2778632*x*1.4891*TMath::Power(TMath::Exp(-0.4799*x-TMath::Abs(0.0225614)*x*x)+x/1.6315,-6.82731)" + }, + "13TeV_HM_phidown": { + "histoMtScaleFactor": { + "113": 0.87, + "223": 0.572, + "331": 0.4 + }, + "111_pt": "( x*(326.754*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.140039)+17.4259*TMath::Power(1+(x*x)/(0.685364*0.685364*2.88243),-2.88243)) )", + "221_pt": "( x*(326.754*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.140039)+17.4259*TMath::Power(1+(x*x)/(0.685364*0.685364*2.88243),-2.88243)) ) * ((0.691819*1.)+(180.263*(-0.00350264*TMath::Power(1.+((x/0.91991)*(x/0.91991)),-(0.242107)))))/(1.+(-0.00350264*TMath::Power(1.+((x/0.91991)*(x/0.91991)),-(0.242107))))", + "333_pt": "0.9*((1.217*x*0.44822*TMath::Power(TMath::Exp(-0.364259*x-TMath::Abs(-0.12213)*x*x)+x/1.42716,-5.37383)))", + "443_pt": "x*3.76783e-05/TMath::Power((1+TMath::Power(x/4.06472,2.0)),3.03429)", + "321_pt": "1.2778632*x*7.70776*TMath::Power(TMath::Exp(-0.618249*x-TMath::Abs(3.16962e-09)*x*x)+x/1.02821,-5.47764)", + "2212_pt": "1.2778632*x*1.4891*TMath::Power(TMath::Exp(-0.4799*x-TMath::Abs(0.0225614)*x*x)+x/1.6315,-6.82731)" + }, + "13TeV_HM_phimultup": { + "histoMtScaleFactor": { + "113": 0.87, + "223": 0.572, + "331": 0.4 + }, + "111_pt": "( x*(326.754*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.140039)+17.4259*TMath::Power(1+(x*x)/(0.685364*0.685364*2.88243),-2.88243)) )", + "221_pt": "( x*(326.754*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.140039)+17.4259*TMath::Power(1+(x*x)/(0.685364*0.685364*2.88243),-2.88243)) ) * ((0.691819*1.)+(180.263*(-0.00350264*TMath::Power(1.+((x/0.91991)*(x/0.91991)),-(0.242107)))))/(1.+(-0.00350264*TMath::Power(1.+((x/0.91991)*(x/0.91991)),-(0.242107))))", + "333_pt": "(1.2446720*x*0.44822*TMath::Power(TMath::Exp(-0.364259*x-TMath::Abs(-0.12213)*x*x)+x/1.42716,-5.37383))", + "443_pt": "x*3.76783e-05/TMath::Power((1+TMath::Power(x/4.06472,2.0)),3.03429)", + "321_pt": "1.2778632*x*7.70776*TMath::Power(TMath::Exp(-0.618249*x-TMath::Abs(3.16962e-09)*x*x)+x/1.02821,-5.47764)", + "2212_pt": "1.2778632*x*1.4891*TMath::Power(TMath::Exp(-0.4799*x-TMath::Abs(0.0225614)*x*x)+x/1.6315,-6.82731)" + }, + "13TeV_HM_phimultdown": { + "histoMtScaleFactor": { + "113": 0.87, + "223": 0.572, + "331": 0.4 + }, + "111_pt": "( x*(326.754*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.140039)+17.4259*TMath::Power(1+(x*x)/(0.685364*0.685364*2.88243),-2.88243)) )", + "221_pt": "( x*(326.754*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.140039)+17.4259*TMath::Power(1+(x*x)/(0.685364*0.685364*2.88243),-2.88243)) ) * ((0.691819*1.)+(180.263*(-0.00350264*TMath::Power(1.+((x/0.91991)*(x/0.91991)),-(0.242107)))))/(1.+(-0.00350264*TMath::Power(1.+((x/0.91991)*(x/0.91991)),-(0.242107))))", + "333_pt": "(1.1893474*x*0.44822*TMath::Power(TMath::Exp(-0.364259*x-TMath::Abs(-0.12213)*x*x)+x/1.42716,-5.37383))", + "443_pt": "x*3.76783e-05/TMath::Power((1+TMath::Power(x/4.06472,2.0)),3.03429)", + "321_pt": "1.2778632*x*7.70776*TMath::Power(TMath::Exp(-0.618249*x-TMath::Abs(3.16962e-09)*x*x)+x/1.02821,-5.47764)", + "2212_pt": "1.2778632*x*1.4891*TMath::Power(TMath::Exp(-0.4799*x-TMath::Abs(0.0225614)*x*x)+x/1.6315,-6.82731)" + }, + "13TeV_HM_etapup": { + "histoMtScaleFactor": { + "113": 0.87, + "223": 0.572, + "331": 0.48 + }, + "111_pt": "( x*(326.754*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.140039)+17.4259*TMath::Power(1+(x*x)/(0.685364*0.685364*2.88243),-2.88243)) )", + "221_pt": "( x*(326.754*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.140039)+17.4259*TMath::Power(1+(x*x)/(0.685364*0.685364*2.88243),-2.88243)) ) * ((0.691819*1.)+(180.263*(-0.00350264*TMath::Power(1.+((x/0.91991)*(x/0.91991)),-(0.242107)))))/(1.+(-0.00350264*TMath::Power(1.+((x/0.91991)*(x/0.91991)),-(0.242107))))", + "333_pt": "(1.217*x*0.44822*TMath::Power(TMath::Exp(-0.364259*x-TMath::Abs(-0.12213)*x*x)+x/1.42716,-5.37383))", + "443_pt": "x*3.76783e-05/TMath::Power((1+TMath::Power(x/4.06472,2.0)),3.03429)", + "321_pt": "1.2778632*x*7.70776*TMath::Power(TMath::Exp(-0.618249*x-TMath::Abs(3.16962e-09)*x*x)+x/1.02821,-5.47764)", + "2212_pt": "1.2778632*x*1.4891*TMath::Power(TMath::Exp(-0.4799*x-TMath::Abs(0.0225614)*x*x)+x/1.6315,-6.82731)" + }, + "13TeV_HM_etapdown": { + "histoMtScaleFactor": { + "113": 0.87, + "223": 0.572, + "331": 0.32 + }, + "111_pt": "( x*(326.754*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.140039)+17.4259*TMath::Power(1+(x*x)/(0.685364*0.685364*2.88243),-2.88243)) )", + "221_pt": "( x*(326.754*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.140039)+17.4259*TMath::Power(1+(x*x)/(0.685364*0.685364*2.88243),-2.88243)) ) * ((0.691819*1.)+(180.263*(-0.00350264*TMath::Power(1.+((x/0.91991)*(x/0.91991)),-(0.242107)))))/(1.+(-0.00350264*TMath::Power(1.+((x/0.91991)*(x/0.91991)),-(0.242107))))", + "333_pt": "(1.217*x*0.44822*TMath::Power(TMath::Exp(-0.364259*x-TMath::Abs(-0.12213)*x*x)+x/1.42716,-5.37383))", + "443_pt": "x*3.76783e-05/TMath::Power((1+TMath::Power(x/4.06472,2.0)),3.03429)", + "321_pt": "1.2778632*x*7.70776*TMath::Power(TMath::Exp(-0.618249*x-TMath::Abs(3.16962e-09)*x*x)+x/1.02821,-5.47764)", + "2212_pt": "1.2778632*x*1.4891*TMath::Power(TMath::Exp(-0.4799*x-TMath::Abs(0.0225614)*x*x)+x/1.6315,-6.82731)" + }, + "13TeV_HM_rhoup": { + "histoMtScaleFactor": { + "113": 1.044, + "223": 0.572, + "331": 0.4 + }, + "111_pt": "( x*(326.754*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.140039)+17.4259*TMath::Power(1+(x*x)/(0.685364*0.685364*2.88243),-2.88243)) )", + "221_pt": "( x*(326.754*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.140039)+17.4259*TMath::Power(1+(x*x)/(0.685364*0.685364*2.88243),-2.88243)) ) * ((0.691819*1.)+(180.263*(-0.00350264*TMath::Power(1.+((x/0.91991)*(x/0.91991)),-(0.242107)))))/(1.+(-0.00350264*TMath::Power(1.+((x/0.91991)*(x/0.91991)),-(0.242107))))", + "333_pt": "(1.217*x*0.44822*TMath::Power(TMath::Exp(-0.364259*x-TMath::Abs(-0.12213)*x*x)+x/1.42716,-5.37383))", + "443_pt": "x*3.76783e-05/TMath::Power((1+TMath::Power(x/4.06472,2.0)),3.03429)", + "321_pt": "1.2778632*x*7.70776*TMath::Power(TMath::Exp(-0.618249*x-TMath::Abs(3.16962e-09)*x*x)+x/1.02821,-5.47764)", + "2212_pt": "1.2778632*x*1.4891*TMath::Power(TMath::Exp(-0.4799*x-TMath::Abs(0.0225614)*x*x)+x/1.6315,-6.82731)" + }, + "13TeV_HM_rhodown": { + "histoMtScaleFactor": { + "113": 0.696, + "223": 0.572, + "331": 0.4 + }, + "111_pt": "( x*(326.754*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.140039)+17.4259*TMath::Power(1+(x*x)/(0.685364*0.685364*2.88243),-2.88243)) )", + "221_pt": "( x*(326.754*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.140039)+17.4259*TMath::Power(1+(x*x)/(0.685364*0.685364*2.88243),-2.88243)) ) * ((0.691819*1.)+(180.263*(-0.00350264*TMath::Power(1.+((x/0.91991)*(x/0.91991)),-(0.242107)))))/(1.+(-0.00350264*TMath::Power(1.+((x/0.91991)*(x/0.91991)),-(0.242107))))", + "333_pt": "(1.217*x*0.44822*TMath::Power(TMath::Exp(-0.364259*x-TMath::Abs(-0.12213)*x*x)+x/1.42716,-5.37383))", + "443_pt": "x*3.76783e-05/TMath::Power((1+TMath::Power(x/4.06472,2.0)),3.03429)", + "321_pt": "1.2778632*x*7.70776*TMath::Power(TMath::Exp(-0.618249*x-TMath::Abs(3.16962e-09)*x*x)+x/1.02821,-5.47764)", + "2212_pt": "1.2778632*x*1.4891*TMath::Power(TMath::Exp(-0.4799*x-TMath::Abs(0.0225614)*x*x)+x/1.6315,-6.82731)" + }, + "13TeV_HM_omegaup": { + "histoMtScaleFactor": { + "113": 0.87, + "223": 0.6864, + "331": 0.4 + }, + "111_pt": "( x*(326.754*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.140039)+17.4259*TMath::Power(1+(x*x)/(0.685364*0.685364*2.88243),-2.88243)) )", + "221_pt": "( x*(326.754*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.140039)+17.4259*TMath::Power(1+(x*x)/(0.685364*0.685364*2.88243),-2.88243)) ) * ((0.691819*1.)+(180.263*(-0.00350264*TMath::Power(1.+((x/0.91991)*(x/0.91991)),-(0.242107)))))/(1.+(-0.00350264*TMath::Power(1.+((x/0.91991)*(x/0.91991)),-(0.242107))))", + "333_pt": "(1.217*x*0.44822*TMath::Power(TMath::Exp(-0.364259*x-TMath::Abs(-0.12213)*x*x)+x/1.42716,-5.37383))", + "443_pt": "x*3.76783e-05/TMath::Power((1+TMath::Power(x/4.06472,2.0)),3.03429)", + "321_pt": "1.2778632*x*7.70776*TMath::Power(TMath::Exp(-0.618249*x-TMath::Abs(3.16962e-09)*x*x)+x/1.02821,-5.47764)", + "2212_pt": "1.2778632*x*1.4891*TMath::Power(TMath::Exp(-0.4799*x-TMath::Abs(0.0225614)*x*x)+x/1.6315,-6.82731)" + }, + "13TeV_HM_omegadown": { + "histoMtScaleFactor": { + "113": 0.87, + "223": 0.4576, + "331": 0.4 + }, + "111_pt": "( x*(326.754*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.140039)+17.4259*TMath::Power(1+(x*x)/(0.685364*0.685364*2.88243),-2.88243)) )", + "221_pt": "( x*(326.754*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.140039)+17.4259*TMath::Power(1+(x*x)/(0.685364*0.685364*2.88243),-2.88243)) ) * ((0.691819*1.)+(180.263*(-0.00350264*TMath::Power(1.+((x/0.91991)*(x/0.91991)),-(0.242107)))))/(1.+(-0.00350264*TMath::Power(1.+((x/0.91991)*(x/0.91991)),-(0.242107))))", + "333_pt": "(1.217*x*0.44822*TMath::Power(TMath::Exp(-0.364259*x-TMath::Abs(-0.12213)*x*x)+x/1.42716,-5.37383))", + "443_pt": "x*3.76783e-05/TMath::Power((1+TMath::Power(x/4.06472,2.0)),3.03429)", + "321_pt": "1.2778632*x*7.70776*TMath::Power(TMath::Exp(-0.618249*x-TMath::Abs(3.16962e-09)*x*x)+x/1.02821,-5.47764)", + "2212_pt": "1.2778632*x*1.4891*TMath::Power(TMath::Exp(-0.4799*x-TMath::Abs(0.0225614)*x*x)+x/1.6315,-6.82731)" + }, + "13TeV_HM_default_ptlimit": { + "histoMtScaleFactor": { + "113": 0.87, + "223": 0.572, + "331": 0.4 + }, + "111_pt": "( x*(326.754*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.140039)+17.4259*TMath::Power(1+(x*x)/(0.685364*0.685364*2.88243),-2.88243)) )", + "221_pt": "( x*(204.529*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.155686)+14.9262*TMath::Power(1+(x*x)/(0.704871*0.704871*2.90918),-2.90918)) ) * ((1.16587*1.)+(255.088*(-0.00446422*TMath::Power(1.+((x/0.561068)*(x/0.561068)),-(0.0952805)))))/(1.+(-0.00446422*TMath::Power(1.+((x/0.561068)*(x/0.561068)),-(0.0952805)))) ", + "333_pt": "(1.217*x*0.44822*TMath::Power(TMath::Exp(-0.364259*x-TMath::Abs(-0.12213)*x*x)+x/1.42716,-5.37383))", + "443_pt": "x*3.76783e-05/TMath::Power((1+TMath::Power(x/4.06472,2.0)),3.03429)", + "321_pt": "1.2778632*x*7.70776*TMath::Power(TMath::Exp(-0.618249*x-TMath::Abs(3.16962e-09)*x*x)+x/1.02821,-5.47764)", + "2212_pt": "1.2778632*x*1.4891*TMath::Power(TMath::Exp(-0.4799*x-TMath::Abs(0.0225614)*x*x)+x/1.6315,-6.82731)" + }, + "13TeV_HM_etaup_ptlimit": { + "histoMtScaleFactor": { + "113": 0.87, + "223": 0.572, + "331": 0.4 + }, + "111_pt": "( x*(326.754*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.140039)+17.4259*TMath::Power(1+(x*x)/(0.685364*0.685364*2.88243),-2.88243)) )", + "221_pt": "( x*(204.529*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.155686)+14.9262*TMath::Power(1+(x*x)/(0.704871*0.704871*2.90918),-2.90918)) ) * ((2.05473*1.)+(339.781*(-0.00588883*TMath::Power(1.+((x/0.574557)*(x/0.574557)),-(0.0482478)))))/(1.+(-0.00588883*TMath::Power(1.+((x/0.574557)*(x/0.574557)),-(0.0482478)))) ", + "333_pt": "(1.217*x*0.44822*TMath::Power(TMath::Exp(-0.364259*x-TMath::Abs(-0.12213)*x*x)+x/1.42716,-5.37383))", + "443_pt": "x*3.76783e-05/TMath::Power((1+TMath::Power(x/4.06472,2.0)),3.03429)", + "321_pt": "1.2778632*x*7.70776*TMath::Power(TMath::Exp(-0.618249*x-TMath::Abs(3.16962e-09)*x*x)+x/1.02821,-5.47764)", + "2212_pt": "1.2778632*x*1.4891*TMath::Power(TMath::Exp(-0.4799*x-TMath::Abs(0.0225614)*x*x)+x/1.6315,-6.82731)" + }, + "13TeV_HM_etadown_ptlimit": { + "histoMtScaleFactor": { + "113": 0.87, + "223": 0.572, + "331": 0.4 + }, + "111_pt": "( x*(326.754*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.140039)+17.4259*TMath::Power(1+(x*x)/(0.685364*0.685364*2.88243),-2.88243)) )", + "221_pt": "( x*(204.529*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.155686)+14.9262*TMath::Power(1+(x*x)/(0.704871*0.704871*2.90918),-2.90918)) ) * ((0.810298*1.)+(214.309*(-0.0037561*TMath::Power(1.+((x/0.590486)*(x/0.590486)),-(0.157333)))))/(1.+(-0.0037561*TMath::Power(1.+((x/0.590486)*(x/0.590486)),-(0.157333)))) ", + "333_pt": "(1.217*x*0.44822*TMath::Power(TMath::Exp(-0.364259*x-TMath::Abs(-0.12213)*x*x)+x/1.42716,-5.37383))", + "443_pt": "x*3.76783e-05/TMath::Power((1+TMath::Power(x/4.06472,2.0)),3.03429)", + "321_pt": "1.2778632*x*7.70776*TMath::Power(TMath::Exp(-0.618249*x-TMath::Abs(3.16962e-09)*x*x)+x/1.02821,-5.47764)", + "2212_pt": "1.2778632*x*1.4891*TMath::Power(TMath::Exp(-0.4799*x-TMath::Abs(0.0225614)*x*x)+x/1.6315,-6.82731)" + }, + "13TeV_HM_Prelim_default": { + "histoMtScaleFactor": { + "113": 0.87, + "223": 0.572, + "331": 0.4 + }, + "111_pt": "( x*(302.631*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.144834)+16.3521*TMath::Power(1+(x*x)/(0.696618*0.696618*2.89513),-2.89513)) )", + "221_pt": "( x*(302.631*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.144834)+16.3521*TMath::Power(1+(x*x)/(0.696618*0.696618*2.89513),-2.89513)) ) * ((0.691819*1.)+(180.263*(-0.00350264*TMath::Power(1.+((x/0.91991)*(x/0.91991)),-(0.242107)))))/(1.+(-0.00350264*TMath::Power(1.+((x/0.91991)*(x/0.91991)),-(0.242107))))", + "333_pt": "(1.217*x*0.44822*TMath::Power(TMath::Exp(-0.364259*x-TMath::Abs(-0.12213)*x*x)+x/1.42716,-5.37383))", + "443_pt": "x*3.76783e-05/TMath::Power((1+TMath::Power(x/4.06472,2.0)),3.03429)", + "321_pt": "1.2778632*x*7.70776*TMath::Power(TMath::Exp(-0.618249*x-TMath::Abs(3.16962e-09)*x*x)+x/1.02821,-5.47764)", + "2212_pt": "1.2778632*x*1.4891*TMath::Power(TMath::Exp(-0.4799*x-TMath::Abs(0.0225614)*x*x)+x/1.6315,-6.82731)" + }, + "13TeV_HM_Prelim_etaup": { + "histoMtScaleFactor": { + "113": 0.87, + "223": 0.572, + "331": 0.4 + }, + "111_pt": "( x*(302.631*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.144834)+16.3521*TMath::Power(1+(x*x)/(0.696618*0.696618*2.89513),-2.89513)) )", + "221_pt": "( x*(302.631*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.144834)+16.3521*TMath::Power(1+(x*x)/(0.696618*0.696618*2.89513),-2.89513)) ) * ((0.799845*1.)+(192.141*(-0.00373188*TMath::Power(1.+((x/0.953155)*(x/0.953155)),-(0.199252)))))/(1.+(-0.00373188*TMath::Power(1.+((x/0.953155)*(x/0.953155)),-(0.199252))))", + "333_pt": "(1.217*x*0.44822*TMath::Power(TMath::Exp(-0.364259*x-TMath::Abs(-0.12213)*x*x)+x/1.42716,-5.37383))", + "443_pt": "x*3.76783e-05/TMath::Power((1+TMath::Power(x/4.06472,2.0)),3.03429)", + "321_pt": "1.2778632*x*7.70776*TMath::Power(TMath::Exp(-0.618249*x-TMath::Abs(3.16962e-09)*x*x)+x/1.02821,-5.47764)", + "2212_pt": "1.2778632*x*1.4891*TMath::Power(TMath::Exp(-0.4799*x-TMath::Abs(0.0225614)*x*x)+x/1.6315,-6.82731)" + }, + "13TeV_HM_Prelim_etadown": { + "histoMtScaleFactor": { + "113": 0.87, + "223": 0.572, + "331": 0.4 + }, + "111_pt": "( x*(302.631*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.144834)+16.3521*TMath::Power(1+(x*x)/(0.696618*0.696618*2.89513),-2.89513)) )", + "221_pt": "( x*(302.631*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.144834)+16.3521*TMath::Power(1+(x*x)/(0.696618*0.696618*2.89513),-2.89513)) ) * ((0.60826*1.)+(171.512*(-0.00333377*TMath::Power(1.+((x/0.891623)*(x/0.891623)),-(0.289837)))))/(1.+(-0.00333377*TMath::Power(1.+((x/0.891623)*(x/0.891623)),-(0.289837))))", + "333_pt": "(1.217*x*0.44822*TMath::Power(TMath::Exp(-0.364259*x-TMath::Abs(-0.12213)*x*x)+x/1.42716,-5.37383))", + "443_pt": "x*3.76783e-05/TMath::Power((1+TMath::Power(x/4.06472,2.0)),3.03429)", + "321_pt": "1.2778632*x*7.70776*TMath::Power(TMath::Exp(-0.618249*x-TMath::Abs(3.16962e-09)*x*x)+x/1.02821,-5.47764)", + "2212_pt": "1.2778632*x*1.4891*TMath::Power(TMath::Exp(-0.4799*x-TMath::Abs(0.0225614)*x*x)+x/1.6315,-6.82731)" + }, + "13TeV_HM_Prelim_pi0up_eta": { + "histoMtScaleFactor": { + "113": 0.87, + "223": 0.572, + "331": 0.4 + }, + "111_pt": "( x*(302.631*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.144834)+16.3521*TMath::Power(1+(x*x)/(0.696618*0.696618*2.89513),-2.89513)) )", + "221_pt": "( x*(458.576*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.132629)+18.7291*TMath::Power(1+(x*x)/(0.680161*0.680161*2.87203),-2.87203)) ) * ((0.691819*1.)+(180.263*(-0.00350264*TMath::Power(1.+((x/0.91991)*(x/0.91991)),-(0.242107)))))/(1.+(-0.00350264*TMath::Power(1.+((x/0.91991)*(x/0.91991)),-(0.242107))))", + "333_pt": "(1.217*x*0.44822*TMath::Power(TMath::Exp(-0.364259*x-TMath::Abs(-0.12213)*x*x)+x/1.42716,-5.37383))", + "443_pt": "x*3.76783e-05/TMath::Power((1+TMath::Power(x/4.06472,2.0)),3.03429)", + "321_pt": "1.2778632*x*7.70776*TMath::Power(TMath::Exp(-0.618249*x-TMath::Abs(3.16962e-09)*x*x)+x/1.02821,-5.47764)", + "2212_pt": "1.2778632*x*1.4891*TMath::Power(TMath::Exp(-0.4799*x-TMath::Abs(0.0225614)*x*x)+x/1.6315,-6.82731)" + }, + "13TeV_HM_Prelim_pi0up_etaup": { + "histoMtScaleFactor": { + "113": 0.87, + "223": 0.572, + "331": 0.4 + }, + "111_pt": "( x*(302.631*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.144834)+16.3521*TMath::Power(1+(x*x)/(0.696618*0.696618*2.89513),-2.89513)) )", + "221_pt": "( x*(458.576*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.132629)+18.7291*TMath::Power(1+(x*x)/(0.680161*0.680161*2.87203),-2.87203)) ) * ((0.799845*1.)+(192.141*(-0.00373188*TMath::Power(1.+((x/0.953155)*(x/0.953155)),-(0.199252)))))/(1.+(-0.00373188*TMath::Power(1.+((x/0.953155)*(x/0.953155)),-(0.199252))))", + "333_pt": "(1.217*x*0.44822*TMath::Power(TMath::Exp(-0.364259*x-TMath::Abs(-0.12213)*x*x)+x/1.42716,-5.37383))", + "443_pt": "x*3.76783e-05/TMath::Power((1+TMath::Power(x/4.06472,2.0)),3.03429)", + "321_pt": "1.2778632*x*7.70776*TMath::Power(TMath::Exp(-0.618249*x-TMath::Abs(3.16962e-09)*x*x)+x/1.02821,-5.47764)", + "2212_pt": "1.2778632*x*1.4891*TMath::Power(TMath::Exp(-0.4799*x-TMath::Abs(0.0225614)*x*x)+x/1.6315,-6.82731)" + }, + "13TeV_HM_Prelim_pi0up_etadown": { + "histoMtScaleFactor": { + "113": 0.87, + "223": 0.572, + "331": 0.4 + }, + "111_pt": "( x*(302.631*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.144834)+16.3521*TMath::Power(1+(x*x)/(0.696618*0.696618*2.89513),-2.89513)) )", + "221_pt": "( x*(458.576*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.132629)+18.7291*TMath::Power(1+(x*x)/(0.680161*0.680161*2.87203),-2.87203)) ) * ((0.60826*1.)+(171.512*(-0.00333377*TMath::Power(1.+((x/0.891623)*(x/0.891623)),-(0.289837)))))/(1.+(-0.00333377*TMath::Power(1.+((x/0.891623)*(x/0.891623)),-(0.289837))))", + "333_pt": "(1.217*x*0.44822*TMath::Power(TMath::Exp(-0.364259*x-TMath::Abs(-0.12213)*x*x)+x/1.42716,-5.37383))", + "443_pt": "x*3.76783e-05/TMath::Power((1+TMath::Power(x/4.06472,2.0)),3.03429)", + "321_pt": "1.2778632*x*7.70776*TMath::Power(TMath::Exp(-0.618249*x-TMath::Abs(3.16962e-09)*x*x)+x/1.02821,-5.47764)", + "2212_pt": "1.2778632*x*1.4891*TMath::Power(TMath::Exp(-0.4799*x-TMath::Abs(0.0225614)*x*x)+x/1.6315,-6.82731)" + }, + "13TeV_HM_Prelim_pi0down_eta": { + "histoMtScaleFactor": { + "113": 0.87, + "223": 0.572, + "331": 0.4 + }, + "111_pt": "( x*(302.631*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.144834)+16.3521*TMath::Power(1+(x*x)/(0.696618*0.696618*2.89513),-2.89513)) )", + "221_pt": "( x*(190.467*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.161605)+13.8504*TMath::Power(1+(x*x)/(0.718129*0.718129*2.92313),-2.92313)) ) * ((0.691819*1.)+(180.263*(-0.00350264*TMath::Power(1.+((x/0.91991)*(x/0.91991)),-(0.242107)))))/(1.+(-0.00350264*TMath::Power(1.+((x/0.91991)*(x/0.91991)),-(0.242107))))", + "333_pt": "(1.217*x*0.44822*TMath::Power(TMath::Exp(-0.364259*x-TMath::Abs(-0.12213)*x*x)+x/1.42716,-5.37383))", + "443_pt": "x*3.76783e-05/TMath::Power((1+TMath::Power(x/4.06472,2.0)),3.03429)", + "321_pt": "1.2778632*x*7.70776*TMath::Power(TMath::Exp(-0.618249*x-TMath::Abs(3.16962e-09)*x*x)+x/1.02821,-5.47764)", + "2212_pt": "1.2778632*x*1.4891*TMath::Power(TMath::Exp(-0.4799*x-TMath::Abs(0.0225614)*x*x)+x/1.6315,-6.82731)" + }, + "13TeV_HM_Prelim_pi0down_etaup": { + "histoMtScaleFactor": { + "113": 0.87, + "223": 0.572, + "331": 0.4 + }, + "111_pt": "( x*(302.631*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.144834)+16.3521*TMath::Power(1+(x*x)/(0.696618*0.696618*2.89513),-2.89513)) )", + "221_pt": "( x*(190.467*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.161605)+13.8504*TMath::Power(1+(x*x)/(0.718129*0.718129*2.92313),-2.92313)) ) * ((0.799845*1.)+(192.141*(-0.00373188*TMath::Power(1.+((x/0.953155)*(x/0.953155)),-(0.199252)))))/(1.+(-0.00373188*TMath::Power(1.+((x/0.953155)*(x/0.953155)),-(0.199252))))", + "333_pt": "(1.217*x*0.44822*TMath::Power(TMath::Exp(-0.364259*x-TMath::Abs(-0.12213)*x*x)+x/1.42716,-5.37383))", + "443_pt": "x*3.76783e-05/TMath::Power((1+TMath::Power(x/4.06472,2.0)),3.03429)", + "321_pt": "1.2778632*x*7.70776*TMath::Power(TMath::Exp(-0.618249*x-TMath::Abs(3.16962e-09)*x*x)+x/1.02821,-5.47764)", + "2212_pt": "1.2778632*x*1.4891*TMath::Power(TMath::Exp(-0.4799*x-TMath::Abs(0.0225614)*x*x)+x/1.6315,-6.82731)" + }, + "13TeV_HM_Prelim_pi0down_etadown": { + "histoMtScaleFactor": { + "113": 0.87, + "223": 0.572, + "331": 0.4 + }, + "111_pt": "( x*(302.631*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.144834)+16.3521*TMath::Power(1+(x*x)/(0.696618*0.696618*2.89513),-2.89513)) )", + "221_pt": "( x*(190.467*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.161605)+13.8504*TMath::Power(1+(x*x)/(0.718129*0.718129*2.92313),-2.92313)) ) * ((0.60826*1.)+(171.512*(-0.00333377*TMath::Power(1.+((x/0.891623)*(x/0.891623)),-(0.289837)))))/(1.+(-0.00333377*TMath::Power(1.+((x/0.891623)*(x/0.891623)),-(0.289837))))", + "333_pt": "(1.217*x*0.44822*TMath::Power(TMath::Exp(-0.364259*x-TMath::Abs(-0.12213)*x*x)+x/1.42716,-5.37383))", + "443_pt": "x*3.76783e-05/TMath::Power((1+TMath::Power(x/4.06472,2.0)),3.03429)", + "321_pt": "1.2778632*x*7.70776*TMath::Power(TMath::Exp(-0.618249*x-TMath::Abs(3.16962e-09)*x*x)+x/1.02821,-5.47764)", + "2212_pt": "1.2778632*x*1.4891*TMath::Power(TMath::Exp(-0.4799*x-TMath::Abs(0.0225614)*x*x)+x/1.6315,-6.82731)" + }, + "13TeV_HM_pi0_CPextra1": { + "histoMtScaleFactor": { + "113": 0.87, + "223": 0.572, + "331": 0.4 + }, + "111_pt": "( x*(269.338*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.0845415)+47.3995*TMath::Power(1+(x*x)/(0.549138*0.549138*2.80868),-2.80868)) ) * ((0.691819*1.)+(180.263*(-0.00350264*TMath::Power(1.+((x/0.91991)*(x/0.91991)),-(0.242107)))))/(1.+(-0.00350264*TMath::Power(1.+((x/0.91991)*(x/0.91991)),-(0.242107))))", + "221_pt": "( x*(326.754*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.140039)+17.4259*TMath::Power(1+(x*x)/(0.685364*0.685364*2.88243),-2.88243)) ) * ((0.691819*1.)+(180.263*(-0.00350264*TMath::Power(1.+((x/0.91991)*(x/0.91991)),-(0.242107)))))/(1.+(-0.00350264*TMath::Power(1.+((x/0.91991)*(x/0.91991)),-(0.242107))))", + "333_pt": "(1.217*x*0.44822*TMath::Power(TMath::Exp(-0.364259*x-TMath::Abs(-0.12213)*x*x)+x/1.42716,-5.37383))", + "443_pt": "x*3.76783e-05/TMath::Power((1+TMath::Power(x/4.06472,2.0)),3.03429)", + "321_pt": "1.2778632*x*7.70776*TMath::Power(TMath::Exp(-0.618249*x-TMath::Abs(3.16962e-09)*x*x)+x/1.02821,-5.47764)", + "2212_pt": "1.2778632*x*1.4891*TMath::Power(TMath::Exp(-0.4799*x-TMath::Abs(0.0225614)*x*x)+x/1.6315,-6.82731)" + }, + "13TeV_HM_pi0_CPextra2": { + "histoMtScaleFactor": { + "113": 0.87, + "223": 0.572, + "331": 0.4 + }, + "111_pt": "( x*(319.201*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.10158)+35.8138*TMath::Power(1+(x*x)/(0.583998*0.583998*2.82716),-2.82716)) ) * ((0.691819*1.)+(180.263*(-0.00350264*TMath::Power(1.+((x/0.91991)*(x/0.91991)),-(0.242107)))))/(1.+(-0.00350264*TMath::Power(1.+((x/0.91991)*(x/0.91991)),-(0.242107))))", + "221_pt": "( x*(326.754*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.140039)+17.4259*TMath::Power(1+(x*x)/(0.685364*0.685364*2.88243),-2.88243)) ) * ((0.691819*1.)+(180.263*(-0.00350264*TMath::Power(1.+((x/0.91991)*(x/0.91991)),-(0.242107)))))/(1.+(-0.00350264*TMath::Power(1.+((x/0.91991)*(x/0.91991)),-(0.242107))))", + "333_pt": "(1.217*x*0.44822*TMath::Power(TMath::Exp(-0.364259*x-TMath::Abs(-0.12213)*x*x)+x/1.42716,-5.37383))", + "443_pt": "x*3.76783e-05/TMath::Power((1+TMath::Power(x/4.06472,2.0)),3.03429)", + "321_pt": "1.2778632*x*7.70776*TMath::Power(TMath::Exp(-0.618249*x-TMath::Abs(3.16962e-09)*x*x)+x/1.02821,-5.47764)", + "2212_pt": "1.2778632*x*1.4891*TMath::Power(TMath::Exp(-0.4799*x-TMath::Abs(0.0225614)*x*x)+x/1.6315,-6.82731)" + }, + "13TeV_HM_pi0_CPextra3": { + "histoMtScaleFactor": { + "113": 0.87, + "223": 0.572, + "331": 0.4 + }, + "111_pt": "( x*(375.238*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.110871)+27.7713*TMath::Power(1+(x*x)/(0.617826*0.617826*2.84554),-2.84554)) ) * ((0.691819*1.)+(180.263*(-0.00350264*TMath::Power(1.+((x/0.91991)*(x/0.91991)),-(0.242107)))))/(1.+(-0.00350264*TMath::Power(1.+((x/0.91991)*(x/0.91991)),-(0.242107))))", + "221_pt": "( x*(326.754*TMath::Exp(-(TMath::Sqrt(x*x+0.134977*0.134977)-0.134977)/0.140039)+17.4259*TMath::Power(1+(x*x)/(0.685364*0.685364*2.88243),-2.88243)) ) * ((0.691819*1.)+(180.263*(-0.00350264*TMath::Power(1.+((x/0.91991)*(x/0.91991)),-(0.242107)))))/(1.+(-0.00350264*TMath::Power(1.+((x/0.91991)*(x/0.91991)),-(0.242107))))", + "333_pt": "(1.217*x*0.44822*TMath::Power(TMath::Exp(-0.364259*x-TMath::Abs(-0.12213)*x*x)+x/1.42716,-5.37383))", + "443_pt": "x*3.76783e-05/TMath::Power((1+TMath::Power(x/4.06472,2.0)),3.03429)", + "321_pt": "1.2778632*x*7.70776*TMath::Power(TMath::Exp(-0.618249*x-TMath::Abs(3.16962e-09)*x*x)+x/1.02821,-5.47764)", + "2212_pt": "1.2778632*x*1.4891*TMath::Power(TMath::Exp(-0.4799*x-TMath::Abs(0.0225614)*x*x)+x/1.6315,-6.82731)" + } +} diff --git a/MC/config/PWGEM/pythia8/decayer/force_dummy.cfg b/MC/config/PWGEM/pythia8/decayer/force_dummy.cfg new file mode 100644 index 000000000..e69de29bb diff --git a/MC/config/PWGEM/pythia8/decayer/force_semileptonic.cfg b/MC/config/PWGEM/pythia8/decayer/force_semileptonic.cfg new file mode 100644 index 000000000..0951a2341 --- /dev/null +++ b/MC/config/PWGEM/pythia8/decayer/force_semileptonic.cfg @@ -0,0 +1,77 @@ +### semileptonic decays for charm +### D+ +411:oneChannel = 1 0.087 0 -311 -11 12 +411:addChannel = 1 0.040 0 -321 211 -11 12 +411:addChannel = 1 0.037 0 -313 -11 12 +### D0 +421:oneChannel = 1 0.035 0 -321 -11 12 +421:addChannel = 1 0.022 0 -323 -11 12 +421:addChannel = 1 0.016 0 -321 111 -11 12 +421:addChannel = 1 0.014 0 -311 -211 -11 12 +### Ds +431:oneChannel = 1 0.025 0 333 -11 12 +431:addChannel = 1 0.027 0 221 -11 12 +### Lambdac +4122:oneChannel = 1 0.036 0 3122 -11 12 +### chi_{c}^{+} +4232:oneChannel = 1 0.07 0 3322 -11 12 +### chi_{c}^{0} +4132:oneChannel = 1 0.014 0 3312 -11 12 +### Omega_{c} +4332:oneChannel = 1 0.01224 0 3334 -11 12 + +### semileptonic decays for beauty +### B0 +511:oneChannel = 1 0.0207000 0 12 -11 -411 +511:addChannel = 1 0.0570000 0 12 -11 -413 +511:addChannel = 1 0.0023000 0 12 -11 -415 +511:addChannel = 1 0.0001330 0 12 -11 -211 +511:addChannel = 1 0.0002690 0 12 -11 -213 +511:addChannel = 1 0.0045000 0 12 -11 -10411 +511:addChannel = 1 0.0052000 0 12 -11 -10413 +511:addChannel = 1 0.0083000 0 12 -11 -20413 + +### B+ +521:oneChannel = 1 0.0000720 0 12 -11 111 +521:addChannel = 1 0.0001450 0 12 -11 113 +521:addChannel = 1 0.0000840 0 12 -11 221 +521:addChannel = 1 0.0001450 0 12 -11 223 +521:addChannel = 1 0.0000840 0 12 -11 331 +521:addChannel = 1 0.0224000 0 12 -11 -421 +521:addChannel = 1 0.0617000 0 12 -11 -423 +521:addChannel = 1 0.0030000 0 12 -11 -425 +521:addChannel = 1 0.0049000 0 12 -11 -10421 +521:addChannel = 1 0.0056000 0 12 -11 -10423 +521:addChannel = 1 0.0090000 0 12 -11 -20423 + +### Bs +531:oneChannel = 1 0.0002000 0 12 -11 -321 +531:addChannel = 1 0.0003000 0 12 -11 -323 +531:addChannel = 1 0.0210000 0 12 -11 -431 +531:addChannel = 1 0.0490000 0 12 -11 -433 +531:addChannel = 1 0.0070000 0 12 -11 -435 +531:addChannel = 1 0.0003000 0 12 -11 -10323 +531:addChannel = 1 0.0040000 0 12 -11 -10431 +531:addChannel = 1 0.0070000 0 12 -11 -10433 +531:addChannel = 1 0.0002000 0 12 -11 -20323 +531:addChannel = 1 0.0040000 0 12 -11 -20433 + +### Lambdab +5122:oneChannel = 1 0.0546000 0 -12 11 4122 +5122:addChannel = 1 0.0096000 0 -12 11 4124 +5122:addChannel = 1 0.0128000 0 -12 11 14122 + +### Chi_{b}^{-} +5132:oneChannel = 1 0.1080010 0 -12 11 4 3101 +5132:addChannel = 1 0.0020000 0 -12 11 2 3101 +### Chi_{b}^{0} +5232:oneChannel = 1 0.1080010 0 -12 11 4 3201 +5232:addChannel = 1 0.0020000 0 -12 11 2 3201 +### Omega_{b}^{-} +5332:oneChannel = 1 0.1080010 1 -12 11 4 3303 +5332:oneChannel = 1 0.0020000 1 -12 11 2 3303 + +# Correct OmegaC decay length (wrong in PYTHIA8 decay table) (mm/c) +4332:tau0 = 0.08000000000 +# Correct Lb decay length (wrong in PYTHIA8 decay table) +5122:tau0 = 4.41000e-01 \ No newline at end of file diff --git a/MC/config/PWGEM/pythia8/decayer/force_semileptonic_beauty.cfg b/MC/config/PWGEM/pythia8/decayer/force_semileptonic_beauty.cfg new file mode 100644 index 000000000..a9f15fff9 --- /dev/null +++ b/MC/config/PWGEM/pythia8/decayer/force_semileptonic_beauty.cfg @@ -0,0 +1,56 @@ +### only semileptonic decays for beauty +### B0 +511:oneChannel = 1 0.0207000 0 12 -11 -411 +511:addChannel = 1 0.0570000 0 12 -11 -413 +511:addChannel = 1 0.0023000 0 12 -11 -415 +511:addChannel = 1 0.0001330 0 12 -11 -211 +511:addChannel = 1 0.0002690 0 12 -11 -213 +511:addChannel = 1 0.0045000 0 12 -11 -10411 +511:addChannel = 1 0.0052000 0 12 -11 -10413 +511:addChannel = 1 0.0083000 0 12 -11 -20413 + +### B+ +521:oneChannel = 1 0.0000720 0 12 -11 111 +521:addChannel = 1 0.0001450 0 12 -11 113 +521:addChannel = 1 0.0000840 0 12 -11 221 +521:addChannel = 1 0.0001450 0 12 -11 223 +521:addChannel = 1 0.0000840 0 12 -11 331 +521:addChannel = 1 0.0224000 0 12 -11 -421 +521:addChannel = 1 0.0617000 0 12 -11 -423 +521:addChannel = 1 0.0030000 0 12 -11 -425 +521:addChannel = 1 0.0049000 0 12 -11 -10421 +521:addChannel = 1 0.0056000 0 12 -11 -10423 +521:addChannel = 1 0.0090000 0 12 -11 -20423 + +### Bs +531:oneChannel = 1 0.0002000 0 12 -11 -321 +531:addChannel = 1 0.0003000 0 12 -11 -323 +531:addChannel = 1 0.0210000 0 12 -11 -431 +531:addChannel = 1 0.0490000 0 12 -11 -433 +531:addChannel = 1 0.0070000 0 12 -11 -435 +531:addChannel = 1 0.0003000 0 12 -11 -10323 +531:addChannel = 1 0.0040000 0 12 -11 -10431 +531:addChannel = 1 0.0070000 0 12 -11 -10433 +531:addChannel = 1 0.0002000 0 12 -11 -20323 +531:addChannel = 1 0.0040000 0 12 -11 -20433 + +### Lambdab +5122:oneChannel = 1 0.0546000 0 -12 11 4122 +5122:addChannel = 1 0.0096000 0 -12 11 4124 +5122:addChannel = 1 0.0128000 0 -12 11 14122 + +### Chi_{b}^{-} +5132:oneChannel = 1 0.1080010 0 -12 11 4 3101 +5132:addChannel = 1 0.0020000 0 -12 11 2 3101 +### Chi_{b}^{0} +5232:oneChannel = 1 0.1080010 0 -12 11 4 3201 +5232:addChannel = 1 0.0020000 0 -12 11 2 3201 +### Omega_{b}^{-} +5332:oneChannel = 1 0.1080010 1 -12 11 4 3303 +5332:oneChannel = 1 0.0020000 1 -12 11 2 3303 + + +# Correct OmegaC decay length (wrong in PYTHIA8 decay table) (mm/c) +4332:tau0 = 0.08000000000 +# Correct Lb decay length (wrong in PYTHIA8 decay table) +5122:tau0 = 4.41000e-01 \ No newline at end of file diff --git a/MC/config/PWGEM/pythia8/decayer/force_semileptonic_charm.cfg b/MC/config/PWGEM/pythia8/decayer/force_semileptonic_charm.cfg new file mode 100644 index 000000000..d605ab18d --- /dev/null +++ b/MC/config/PWGEM/pythia8/decayer/force_semileptonic_charm.cfg @@ -0,0 +1,26 @@ +### only semileptonic decays for charm +### D+ +411:oneChannel = 1 0.087 0 -311 -11 12 +411:addChannel = 1 0.040 0 -321 211 -11 12 +411:addChannel = 1 0.037 0 -313 -11 12 +### D0 +421:oneChannel = 1 0.035 0 -321 -11 12 +421:addChannel = 1 0.022 0 -323 -11 12 +421:addChannel = 1 0.016 0 -321 111 -11 12 +421:addChannel = 1 0.014 0 -311 -211 -11 12 +### Ds +431:oneChannel = 1 0.025 0 333 -11 12 +431:addChannel = 1 0.027 0 221 -11 12 +### Lambdac +4122:oneChannel = 1 0.036 0 3122 -11 12 +### chi_{c}^{+} +4232:oneChannel = 1 0.07 0 3322 -11 12 +### chi_{c}^{0} +4132:oneChannel = 1 0.014 0 3312 -11 12 +### Omega_{c} +4332:oneChannel = 1 0.01224 0 3334 -11 12 + +# Correct OmegaC decay length (wrong in PYTHIA8 decay table) (mm/c) +4332:tau0 = 0.08000000000 +# Correct Lb decay length (wrong in PYTHIA8 decay table) +5122:tau0 = 4.41000e-01 \ No newline at end of file diff --git a/MC/config/PWGEM/pythia8/generator/configPythia_ForcedDalitz.cfg b/MC/config/PWGEM/pythia8/generator/configPythia_ForcedDalitz.cfg new file mode 100644 index 000000000..fca369b49 --- /dev/null +++ b/MC/config/PWGEM/pythia8/generator/configPythia_ForcedDalitz.cfg @@ -0,0 +1,20 @@ +ProcessLevel:all on + +### beams +Beams:idA 2212 # proton +Beams:idB 2212 # proton +Beams:eCM 13600. # GeV + +### processes +SoftQCD:inelastic on # all inelastic processes + +ParticleDecays:limitTau0 on +ParticleDecays:tau0Max 10 + + +#decay eta -> e+ e- gamma, BR = 100% +221:oneChannel = 1 1 0 22 11 -11 + +#decay pi0 -> e+ e- gamma BR = 10% +111:oneChannel = 1 0.9 0 22 22 +111:addChannel = 1 0.1 0 22 11 -11 diff --git a/MC/config/PWGEM/pythia8/generator/pythia8_DY.cfg b/MC/config/PWGEM/pythia8/generator/pythia8_DY.cfg new file mode 100644 index 000000000..d839106eb --- /dev/null +++ b/MC/config/PWGEM/pythia8/generator/pythia8_DY.cfg @@ -0,0 +1,16 @@ +### beams +Beams:idA 2212 # proton +Beams:idB 2212 # proton +Beams:eCM 13600. # GeV #user can change eCM from command line. + +### processes +WeakSingleBoson:ffbar2gmZ on # ffbar -> Z/gamma* +PhaseSpace:mHatMin 0.5 +PhaseSpace:mHatMax 15 +23:mMin 0.5 +TimeShower:mMaxGamma 0.5 +23:onMode off # switch off all Z0 decay channels. # User must switch back on Z0 going to l+ l- in C or ini. gamma* is tread as 23, too. + +### decays +ParticleDecays:limitTau0 on +ParticleDecays:tau0Max 10. diff --git a/MC/config/PWGEM/pythia8/generator/pythia8_OO_536_VM2ll.cfg b/MC/config/PWGEM/pythia8/generator/pythia8_OO_536_VM2ll.cfg new file mode 100644 index 000000000..596a85beb --- /dev/null +++ b/MC/config/PWGEM/pythia8/generator/pythia8_OO_536_VM2ll.cfg @@ -0,0 +1,21 @@ +### OO beams +Beams:idA = 1000080160 +Beams:idB = 1000080160 +Beams:eCM = 5360.0 ### energy + +Beams:frameType = 1 +ParticleDecays:limitTau0 = on +ParticleDecays:tau0Max = 10. ### match alice: 1cm/c = 10.0mm/c + +### Save some CPU at init of jobs +### To avoid refitting, add the following lines to your configuration file: +HeavyIon:SigFitNGen = 0 +HeavyIon:SigFitDefPar = 2.15,18.42,0.33 + +Random:setSeed = on + +# change omega, phi meson's BR below +223:oneChannel = 1 0.5 0 -11 11 +223:addChannel = 1 0.5 0 -13 13 +333:oneChannel = 1 0.5 0 -11 11 +333:addChannel = 1 0.5 0 -13 13 diff --git a/MC/config/PWGEM/pythia8/generator/pythia8_OO_536_bbbar.cfg b/MC/config/PWGEM/pythia8/generator/pythia8_OO_536_bbbar.cfg new file mode 100644 index 000000000..47d52bfbd --- /dev/null +++ b/MC/config/PWGEM/pythia8/generator/pythia8_OO_536_bbbar.cfg @@ -0,0 +1,22 @@ +### beams +Beams:idA = 1000080160 +Beams:idB = 1000080160 +Beams:eCM = 5360.0 +Beams:frameType = 1 + +### Save some CPU at init of jobs +### To avoid refitting, add the following lines to your configuration file: +HeavyIon:SigFitNGen = 0 +HeavyIon:SigFitDefPar = 2.15,18.42,0.33 +### processes +HardQCD:hardbbbar on # scatterings g-g / q-qbar -> b-bbar + +### decays +ParticleDecays:limitTau0 on +ParticleDecays:tau0Max 10. + + +# Correct OmegaC decay length (wrong in PYTHIA8 decay table) (mm/c) +4332:tau0 = 0.08000000000 +# Correct Lb decay length (wrong in PYTHIA8 decay table) +5122:tau0 = 4.41000e-01 diff --git a/MC/config/PWGEM/pythia8/generator/pythia8_OO_536_ccbar.cfg b/MC/config/PWGEM/pythia8/generator/pythia8_OO_536_ccbar.cfg new file mode 100644 index 000000000..07d252517 --- /dev/null +++ b/MC/config/PWGEM/pythia8/generator/pythia8_OO_536_ccbar.cfg @@ -0,0 +1,45 @@ +### beams +Beams:idA = 1000080160 +Beams:idB = 1000080160 +Beams:eCM = 5360.0 +Beams:frameType = 1 + +### Save some CPU at init of jobs +### To avoid refitting, add the following lines to your configuration file: +HeavyIon:SigFitNGen = 0 +HeavyIon:SigFitDefPar = 2.15,18.42,0.33 + +### processes +# HardQCD:hardccbar on # ccbar production +SoftQCD:inelastic = on + +### decays +ParticleDecays:limitTau0 on +ParticleDecays:tau0Max 10. + + +### only semileptonic decays +### D+ +411:oneChannel = 1 0.087 0 -311 -11 12 +411:addChannel = 1 0.040 0 -321 211 -11 12 +411:addChannel = 1 0.037 0 -313 -11 12 +### D0 +421:oneChannel = 1 0.035 0 -321 -11 12 +421:addChannel = 1 0.022 0 -323 -11 12 +421:addChannel = 1 0.016 0 -321 111 -11 12 +### Ds +431:oneChannel = 1 0.025 0 333 -11 12 +431:addChannel = 1 0.027 0 221 -11 12 +### Lambdac +4122:oneChannel = 1 0.036 0 3122 -11 12 +### chi_{c}^{+} +4232:oneChannel = 1 0.07 0 3322 -11 12 +### chi_{c}^{0} +4132:oneChannel = 1 0.014 0 3312 -11 12 +### Omega_{c} +4332:oneChannel = 1 0.01224 0 3334 -11 12 + +# Correct OmegaC decay length (wrong in PYTHIA8 decay table) (mm/c) +4332:tau0 = 0.08000000000 +# Correct Lb decay length (wrong in PYTHIA8 decay table) +5122:tau0 = 4.41000e-01 \ No newline at end of file diff --git a/MC/config/PWGEM/pythia8/generator/pythia8_PbPb_5360_VM2ll.cfg b/MC/config/PWGEM/pythia8/generator/pythia8_PbPb_5360_VM2ll.cfg new file mode 100644 index 000000000..d356956f7 --- /dev/null +++ b/MC/config/PWGEM/pythia8/generator/pythia8_PbPb_5360_VM2ll.cfg @@ -0,0 +1,23 @@ +### Specify beams +Beams:idA = 1000822080 +Beams:idB = 1000822080 +Beams:eCM = 5360.0 ### energy + +Beams:frameType = 1 +ParticleDecays:limitTau0 = on +ParticleDecays:tau0Max = 10. ### match alice: 1cm/c = 10.0mm/c + +### Initialize the Angantyr model to fit the total and semi-includive +### cross sections in Pythia within some tolerance. +HeavyIon:SigFitErr = 0.02,0.02,0.1,0.05,0.05,0.0,0.1,0.0 + +### These parameters are typicall suitable for sqrt(S_NN)=5TeV +HeavyIon:SigFitDefPar = 17.24,2.15,0.33,0.0,0.0,0.0,0.0,0.0 + +Random:setSeed = on + +# change omega, phi meson's BR below +223:oneChannel = 1 0.5 0 -11 11 +223:addChannel = 1 0.5 0 -13 13 +333:oneChannel = 1 0.5 0 -11 11 +333:addChannel = 1 0.5 0 -13 13 diff --git a/MC/config/PWGEM/pythia8/generator/pythia8_bbbar_OO_536_forceddecayscharmbeauty.cfg b/MC/config/PWGEM/pythia8/generator/pythia8_bbbar_OO_536_forceddecayscharmbeauty.cfg new file mode 100644 index 000000000..4305eb7dc --- /dev/null +++ b/MC/config/PWGEM/pythia8/generator/pythia8_bbbar_OO_536_forceddecayscharmbeauty.cfg @@ -0,0 +1,101 @@ +### beams +Beams:idA = 1000080160 +Beams:idB = 1000080160 +Beams:eCM = 5360.0 +Beams:frameType = 1 + +### Save some CPU at init of jobs +### To avoid refitting, add the following lines to your configuration file: +HeavyIon:SigFitNGen = 0 +HeavyIon:SigFitDefPar = 2.15,18.42,0.33 +### processes +HardQCD:hardbbbar on # scatterings g-g / q-qbar -> b-bbar + +### decays +ParticleDecays:limitTau0 on +ParticleDecays:tau0Max 10. + + +# Correct OmegaC decay length (wrong in PYTHIA8 decay table) (mm/c) +4332:tau0 = 0.08000000000 +# Correct Lb decay length (wrong in PYTHIA8 decay table) +5122:tau0 = 4.41000e-01 + +### only semileptonic decays for charm +### D+ +411:oneChannel = 1 0.087 0 -311 -11 12 +411:addChannel = 1 0.040 0 -321 211 -11 12 +411:addChannel = 1 0.037 0 -313 -11 12 +### D0 +421:oneChannel = 1 0.035 0 -321 -11 12 +421:addChannel = 1 0.022 0 -323 -11 12 +421:addChannel = 1 0.016 0 -321 111 -11 12 +421:addChannel = 1 0.014 0 -311 -211 -11 12 +### Ds +431:oneChannel = 1 0.025 0 333 -11 12 +431:addChannel = 1 0.027 0 221 -11 12 +### Lambdac +4122:oneChannel = 1 0.036 0 3122 -11 12 +### chi_{c}^{+} +4232:oneChannel = 1 0.07 0 3322 -11 12 +### chi_{c}^{0} +4132:oneChannel = 1 0.014 0 3312 -11 12 +### Omega_{c} +4332:oneChannel = 1 0.01224 0 3334 -11 12 + +### only semileptonic decays for beauty +### B0 +511:oneChannel = 1 0.0207000 0 12 -11 -411 +511:addChannel = 1 0.0570000 0 12 -11 -413 +511:addChannel = 1 0.0023000 0 12 -11 -415 +511:addChannel = 1 0.0001330 0 12 -11 -211 +511:addChannel = 1 0.0002690 0 12 -11 -213 +511:addChannel = 1 0.0045000 0 12 -11 -10411 +511:addChannel = 1 0.0052000 0 12 -11 -10413 +511:addChannel = 1 0.0083000 0 12 -11 -20413 + +### B+ +521:oneChannel = 1 0.0000720 0 12 -11 111 +521:addChannel = 1 0.0001450 0 12 -11 113 +521:addChannel = 1 0.0000840 0 12 -11 221 +521:addChannel = 1 0.0001450 0 12 -11 223 +521:addChannel = 1 0.0000840 0 12 -11 331 +521:addChannel = 1 0.0224000 0 12 -11 -421 +521:addChannel = 1 0.0617000 0 12 -11 -423 +521:addChannel = 1 0.0030000 0 12 -11 -425 +521:addChannel = 1 0.0049000 0 12 -11 -10421 +521:addChannel = 1 0.0056000 0 12 -11 -10423 +521:addChannel = 1 0.0090000 0 12 -11 -20423 + +### Bs +531:oneChannel = 1 0.0002000 0 12 -11 -321 +531:addChannel = 1 0.0003000 0 12 -11 -323 +531:addChannel = 1 0.0210000 0 12 -11 -431 +531:addChannel = 1 0.0490000 0 12 -11 -433 +531:addChannel = 1 0.0070000 0 12 -11 -435 +531:addChannel = 1 0.0003000 0 12 -11 -10323 +531:addChannel = 1 0.0040000 0 12 -11 -10431 +531:addChannel = 1 0.0070000 0 12 -11 -10433 +531:addChannel = 1 0.0002000 0 12 -11 -20323 +531:addChannel = 1 0.0040000 0 12 -11 -20433 + +### Lambdab +5122:oneChannel = 1 0.0546000 0 -12 11 4122 +5122:addChannel = 1 0.0096000 0 -12 11 4124 +5122:addChannel = 1 0.0128000 0 -12 11 14122 + +### Chi_{b}^{-} +5132:oneChannel = 1 0.1080010 0 -12 11 4 3101 +5132:addChannel = 1 0.0020000 0 -12 11 2 3101 +### Chi_{b}^{0} +5232:oneChannel = 1 0.1080010 0 -12 11 4 3201 +5232:addChannel = 1 0.0020000 0 -12 11 2 3201 +### Omega_{b}^{-} +5332:oneChannel = 1 0.1080010 1 -12 11 4 3303 +5332:oneChannel = 1 0.0020000 1 -12 11 2 3303 + + +# Correct OmegaC decay length (wrong in PYTHIA8 decay table) (mm/c) +4332:tau0 = 0.08000000000 +# Correct Lb decay length (wrong in PYTHIA8 decay table) +5122:tau0 = 4.41000e-01 diff --git a/MC/config/PWGEM/pythia8/generator/pythia8_bbbar_forceddecayscharmbeauty_semimuonic.cfg b/MC/config/PWGEM/pythia8/generator/pythia8_bbbar_forceddecayscharmbeauty_semimuonic.cfg new file mode 100644 index 000000000..7dd200525 --- /dev/null +++ b/MC/config/PWGEM/pythia8/generator/pythia8_bbbar_forceddecayscharmbeauty_semimuonic.cfg @@ -0,0 +1,105 @@ +### beams +Beams:idA 2212 # proton +Beams:idB 2212 # proton +Beams:eCM 13600. # GeV + +### processes +#HardQCD:hardccbar on # scatterings g-g / q-qbar -> c-cbar +HardQCD:hardbbbar on # scatterings g-g / q-qbar -> b-bbar + +### decays +ParticleDecays:limitTau0 on +ParticleDecays:tau0Max 10. + +### switch on color reconnection in mode 2 (https://arxiv.org/pdf/1505.01681.pdf) +Tune:pp = 14 +ColourReconnection:mode = 1 +ColourReconnection:allowDoubleJunRem = off +ColourReconnection:m0 = 0.3 +ColourReconnection:allowJunctions = on +ColourReconnection:junctionCorrection = 1.20 +ColourReconnection:timeDilationMode = 2 +ColourReconnection:timeDilationPar = 0.18 +StringPT:sigma = 0.335 +StringZ:aLund = 0.36 +StringZ:bLund = 0.56 +StringFlav:probQQtoQ = 0.078 +StringFlav:ProbStoUD = 0.2 +StringFlav:probQQ1toQQ0join = 0.0275,0.0275,0.0275,0.0275 +MultiPartonInteractions:pT0Ref = 2.15 +BeamRemnants:remnantMode = 1 +BeamRemnants:saturation =5 + +### only semileptonic decays for charm +### D+ +411:oneChannel = 1 0.0868 0 -311 -13 14 +411:addChannel = 1 0.0527 0 -313 -13 14 +411:addChannel = 1 0.0019 0 -321 211 -13 14 +### D0 +421:oneChannel = 1 0.03418 0 -321 -13 14 +421:addChannel = 1 0.02060 0 -323 -13 14 +421:addChannel = 1 0.000421 0 -321 111 -13 14 +421:addChannel = 1 0.00267 0 -211 -13 14 +421:addChannel = 1 0.00145 0 -211 111 -13 14 +### Ds +431:oneChannel = 1 0.0224 0 333 -13 14 +431:addChannel = 1 0.0224 0 221 -13 14 +431:addChannel = 1 0.0080 0 331 -13 14 + +### Lambdac +4122:oneChannel = 1 0.036 0 3122 -13 14 +### Xi_{c}^{+} +4232:oneChannel = 1 0.07 0 3322 -13 14 +### Xi_{c}^{0} +4132:oneChannel = 1 0.014 0 3312 -13 14 +### Omega_{c} +4332:oneChannel = 1 0.01224 0 3334 -13 14 + + +### only semileptonic decays for beauty +### B0 +511:oneChannel = 1 0.0212000 0 -411 -13 14 +511:addChannel = 1 0.0490000 0 -413 -13 14 +511:addChannel = 1 0.0028500 0 -10413 -13 14 +511:addChannel = 1 0.0025000 0 -20413 -13 14 +511:addChannel = 1 0.0014100 0 -415 -13 14 +511:addChannel = 1 0.0001500 0 -211 -13 14 +511:addChannel = 1 0.0002940 0 -213 -13 14 + +### B+ +521:oneChannel = 1 0.022100 0 -421 -13 14 +521:addChannel = 1 0.055300 0 -423 -13 14 +521:addChannel = 1 0.000900 0 -10421 -13 14 +521:addChannel = 1 0.002840 0 -10423 -13 14 +521:addChannel = 1 0.001700 0 -20423 -13 14 +521:addChannel = 1 0.001590 0 -425 -13 14 +521:addChannel = 1 0.000300 0 -431 321 -13 14 +521:addChannel = 1 0.000290 0 -433 321 -13 14 +521:addChannel = 1 0.000078 0 111 -13 14 +521:addChannel = 1 0.000035 0 221 -13 14 +521:addChannel = 1 0.000024 0 331 -13 14 +521:addChannel = 1 0.000119 0 223 -13 14 +521:addChannel = 1 0.000158 0 113 -13 14 + +### Bs +531:oneChannel = 1 0.023100 0 -431 -13 14 +531:addChannel = 1 0.052000 0 -433 -13 14 +531:addChannel = 1 0.000106 0 -321 -13 14 + +### Lambdab +5122:oneChannel = 1 0.06200 0 4122 13 -14 +5122:addChannel = 1 0.00790 0 14122 13 -14 +5122:addChannel = 1 0.01300 0 4124 13 -14 + +# Forced exclusive decay; BR=1 is not a physical branching fraction +### Xi_{b}^{-} +5132:oneChannel = 1 1.0 0 4132 13 -14 +### Xi_{b}^{0} +5232:oneChannel = 1 1.0 0 4232 13 -14 +### Omega_{b}^{-} +5332:oneChannel = 1 1.0 0 4332 13 -14 + +# Correct OmegaC decay length (wrong in PYTHIA8 decay table) (mm/c) +4332:tau0 = 0.08000000000 +# Correct Lb decay length (wrong in PYTHIA8 decay table) +5122:tau0 = 4.41000e-01 diff --git a/MC/config/PWGEM/pythia8/generator/pythia8_hf_cocktail.cfg b/MC/config/PWGEM/pythia8/generator/pythia8_hf_cocktail.cfg new file mode 100644 index 000000000..d1cb747e0 --- /dev/null +++ b/MC/config/PWGEM/pythia8/generator/pythia8_hf_cocktail.cfg @@ -0,0 +1,30 @@ +### beams +Beams:idA 2212 # proton +Beams:idB 2212 # proton +Beams:eCM 13600. # GeV + +### processes +SoftQCD:inelastic on + +### decays +ParticleDecays:limitTau0 on +ParticleDecays:tau0Max 10. + +### switch on color reconnection in mode 2 (https://arxiv.org/pdf/1505.01681.pdf) +Tune:pp = 14 +ColourReconnection:mode = 1 +ColourReconnection:allowDoubleJunRem = off +ColourReconnection:m0 = 0.3 +ColourReconnection:allowJunctions = on +ColourReconnection:junctionCorrection = 1.20 +ColourReconnection:timeDilationMode = 2 +ColourReconnection:timeDilationPar = 0.18 +StringPT:sigma = 0.335 +StringZ:aLund = 0.36 +StringZ:bLund = 0.56 +StringFlav:probQQtoQ = 0.078 +StringFlav:ProbStoUD = 0.2 +StringFlav:probQQ1toQQ0join = 0.0275,0.0275,0.0275,0.0275 +MultiPartonInteractions:pT0Ref = 2.15 +BeamRemnants:remnantMode = 1 +BeamRemnants:saturation =5 \ No newline at end of file diff --git a/MC/config/PWGEM/pythia8/generator/pythia8_hf_cocktail_pp13.cfg b/MC/config/PWGEM/pythia8/generator/pythia8_hf_cocktail_pp13.cfg new file mode 100644 index 000000000..f7884e068 --- /dev/null +++ b/MC/config/PWGEM/pythia8/generator/pythia8_hf_cocktail_pp13.cfg @@ -0,0 +1,30 @@ +### beams +Beams:idA 2212 # proton +Beams:idB 2212 # proton +Beams:eCM 13000. # GeV + +### processes +SoftQCD:inelastic on + +### decays +ParticleDecays:limitTau0 on +ParticleDecays:tau0Max 10. + +### switch on color reconnection in mode 2 (https://arxiv.org/pdf/1505.01681.pdf) +Tune:pp = 14 +ColourReconnection:mode = 1 +ColourReconnection:allowDoubleJunRem = off +ColourReconnection:m0 = 0.3 +ColourReconnection:allowJunctions = on +ColourReconnection:junctionCorrection = 1.20 +ColourReconnection:timeDilationMode = 2 +ColourReconnection:timeDilationPar = 0.18 +StringPT:sigma = 0.335 +StringZ:aLund = 0.36 +StringZ:bLund = 0.56 +StringFlav:probQQtoQ = 0.078 +StringFlav:ProbStoUD = 0.2 +StringFlav:probQQ1toQQ0join = 0.0275,0.0275,0.0275,0.0275 +MultiPartonInteractions:pT0Ref = 2.15 +BeamRemnants:remnantMode = 1 +BeamRemnants:saturation =5 \ No newline at end of file diff --git a/MC/config/PWGEM/pythia8/generator/pythia8_hf_cocktail_pp502.cfg b/MC/config/PWGEM/pythia8/generator/pythia8_hf_cocktail_pp502.cfg new file mode 100644 index 000000000..83d28efba --- /dev/null +++ b/MC/config/PWGEM/pythia8/generator/pythia8_hf_cocktail_pp502.cfg @@ -0,0 +1,30 @@ +### beams +Beams:idA 2212 # proton +Beams:idB 2212 # proton +Beams:eCM 5200. # GeV + +### processes +SoftQCD:inelastic on + +### decays +ParticleDecays:limitTau0 on +ParticleDecays:tau0Max 10. + +### switch on color reconnection in mode 2 (https://arxiv.org/pdf/1505.01681.pdf) +Tune:pp = 14 +ColourReconnection:mode = 1 +ColourReconnection:allowDoubleJunRem = off +ColourReconnection:m0 = 0.3 +ColourReconnection:allowJunctions = on +ColourReconnection:junctionCorrection = 1.20 +ColourReconnection:timeDilationMode = 2 +ColourReconnection:timeDilationPar = 0.18 +StringPT:sigma = 0.335 +StringZ:aLund = 0.36 +StringZ:bLund = 0.56 +StringFlav:probQQtoQ = 0.078 +StringFlav:ProbStoUD = 0.2 +StringFlav:probQQ1toQQ0join = 0.0275,0.0275,0.0275,0.0275 +MultiPartonInteractions:pT0Ref = 2.15 +BeamRemnants:remnantMode = 1 +BeamRemnants:saturation =5 \ No newline at end of file diff --git a/MC/config/PWGEM/pythia8/generator/pythia8_hf_cocktail_pp536.cfg b/MC/config/PWGEM/pythia8/generator/pythia8_hf_cocktail_pp536.cfg new file mode 100644 index 000000000..ccaa31ce8 --- /dev/null +++ b/MC/config/PWGEM/pythia8/generator/pythia8_hf_cocktail_pp536.cfg @@ -0,0 +1,30 @@ +### beams +Beams:idA 2212 # proton +Beams:idB 2212 # proton +Beams:eCM 5360. # GeV + +### processes +SoftQCD:inelastic on + +### decays +ParticleDecays:limitTau0 on +ParticleDecays:tau0Max 10. + +### switch on color reconnection in mode 2 (https://arxiv.org/pdf/1505.01681.pdf) +Tune:pp = 14 +ColourReconnection:mode = 1 +ColourReconnection:allowDoubleJunRem = off +ColourReconnection:m0 = 0.3 +ColourReconnection:allowJunctions = on +ColourReconnection:junctionCorrection = 1.20 +ColourReconnection:timeDilationMode = 2 +ColourReconnection:timeDilationPar = 0.18 +StringPT:sigma = 0.335 +StringZ:aLund = 0.36 +StringZ:bLund = 0.56 +StringFlav:probQQtoQ = 0.078 +StringFlav:ProbStoUD = 0.2 +StringFlav:probQQ1toQQ0join = 0.0275,0.0275,0.0275,0.0275 +MultiPartonInteractions:pT0Ref = 2.15 +BeamRemnants:remnantMode = 1 +BeamRemnants:saturation =5 \ No newline at end of file diff --git a/MC/config/PWGEM/pythia8/generator/pythia8_pp_13600_VM2ll.cfg b/MC/config/PWGEM/pythia8/generator/pythia8_pp_13600_VM2ll.cfg new file mode 100644 index 000000000..b8a0c3923 --- /dev/null +++ b/MC/config/PWGEM/pythia8/generator/pythia8_pp_13600_VM2ll.cfg @@ -0,0 +1,23 @@ +### Specify beams +Beams:idA = 2212 +Beams:idB = 2212 +Beams:eCM = 13600. ### energy + +Beams:frameType = 1 +ParticleDecays:limitTau0 = on +ParticleDecays:tau0Max = 10. ### match alice: 1cm/c = 10.0mm/c + +### processes +SoftQCD:inelastic = on # all inelastic processes + +# default: do nothing, Monash 2013 will do its thing +Tune:pp = 14 + +Random:setSeed = on + +# change eta, omega, phi meson's BR below +221:oneChannel = 1 0.5 0 -13 13 +223:oneChannel = 1 0.5 0 -11 11 +223:addChannel = 1 0.5 0 -13 13 +333:oneChannel = 1 0.5 0 -11 11 +333:addChannel = 1 0.5 0 -13 13 diff --git a/MC/config/PWGEM/pythia8/generator/pythia8_pp_5360_MB_gapevent.cfg b/MC/config/PWGEM/pythia8/generator/pythia8_pp_5360_MB_gapevent.cfg new file mode 100644 index 000000000..b3fdfdd99 --- /dev/null +++ b/MC/config/PWGEM/pythia8/generator/pythia8_pp_5360_MB_gapevent.cfg @@ -0,0 +1,15 @@ +### beams +Beams:idA 2212 # proton +Beams:idB 2212 # proton +Beams:eCM 5360 # GeV + +### processes +SoftQCD:inelastic on # all inelastic processes + +### per default it is Monash anyway +Tune:pp = 14 + +### decays +ParticleDecays:limitTau0 on +ParticleDecays:tau0Max 10. + diff --git a/MC/config/PWGEM/pythia8/generator/pythia8_pp_5360_VM2ee.cfg b/MC/config/PWGEM/pythia8/generator/pythia8_pp_5360_VM2ee.cfg new file mode 100644 index 000000000..b767c9ea3 --- /dev/null +++ b/MC/config/PWGEM/pythia8/generator/pythia8_pp_5360_VM2ee.cfg @@ -0,0 +1,37 @@ +### Specify beams +Beams:idA = 2212 +Beams:idB = 2212 +Beams:eCM = 5360. ### energy + +Beams:frameType = 1 +ParticleDecays:limitTau0 = on +ParticleDecays:tau0Max = 10. ### match alice: 1cm/c = 10.0mm/c + +### processes +SoftQCD:inelastic = on # all inelastic processes + +# default: do nothing, Monash 2013 will do its thing +Tune:pp = 14 + +Random:setSeed = on + +# don't modify pion's PR as charged multiplicity is drived by them. + +# eta +221:oneChannel = 1 1.0 0 -11 11 22 + +# eta' +331:oneChannel = 1 0.294 0 -11 11 111 +331:addChannel = 1 0.706 0 -11 11 223 + +# rho +113:oneChannel = 1 1.0 0 -11 11 + +# omega +223:oneChannel = 1 0.088 0 -11 11 +223:addChannel = 1 0.912 0 -11 11 111 + +# phi +333:oneChannel = 1 0.710 0 -11 11 +333:addChannel = 1 0.032 0 -11 11 111 +333:addChannel = 1 0.258 0 -11 11 221 diff --git a/MC/config/PWGEM/pythia8/generator/pythia8_pp_5360_VM2ll.cfg b/MC/config/PWGEM/pythia8/generator/pythia8_pp_5360_VM2ll.cfg new file mode 100644 index 000000000..6d9191759 --- /dev/null +++ b/MC/config/PWGEM/pythia8/generator/pythia8_pp_5360_VM2ll.cfg @@ -0,0 +1,23 @@ +### Specify beams +Beams:idA = 2212 +Beams:idB = 2212 +Beams:eCM = 5360. ### energy + +Beams:frameType = 1 +ParticleDecays:limitTau0 = on +ParticleDecays:tau0Max = 10. ### match alice: 1cm/c = 10.0mm/c + +### processes +SoftQCD:inelastic = on # all inelastic processes + +# default: do nothing, Monash 2013 will do its thing +Tune:pp = 14 + +Random:setSeed = on + +# change eta, omega, phi meson's BR below +221:oneChannel = 1 0.5 0 -13 13 +223:oneChannel = 1 0.5 0 -11 11 +223:addChannel = 1 0.5 0 -13 13 +333:oneChannel = 1 0.5 0 -11 11 +333:addChannel = 1 0.5 0 -13 13 diff --git a/MC/config/PWGEM/pythia8/generator/pythia8_pp_5360_bbbar.cfg b/MC/config/PWGEM/pythia8/generator/pythia8_pp_5360_bbbar.cfg new file mode 100644 index 000000000..3e1df36b7 --- /dev/null +++ b/MC/config/PWGEM/pythia8/generator/pythia8_pp_5360_bbbar.cfg @@ -0,0 +1,36 @@ +### beams +Beams:idA 2212 # proton +Beams:idB 2212 # proton +Beams:eCM 5360 # GeV + +### processes +#HardQCD:hardccbar on # scatterings g-g / q-qbar -> c-cbar +HardQCD:hardbbbar on # scatterings g-g / q-qbar -> b-bbar + +### decays +ParticleDecays:limitTau0 on +ParticleDecays:tau0Max 10. + +### switch on color reconnection in mode 2 (https://arxiv.org/pdf/1505.01681.pdf) +Tune:pp = 14 +ColourReconnection:mode = 1 +ColourReconnection:allowDoubleJunRem = off +ColourReconnection:m0 = 0.3 +ColourReconnection:allowJunctions = on +ColourReconnection:junctionCorrection = 1.20 +ColourReconnection:timeDilationMode = 2 +ColourReconnection:timeDilationPar = 0.18 +StringPT:sigma = 0.335 +StringZ:aLund = 0.36 +StringZ:bLund = 0.56 +StringFlav:probQQtoQ = 0.078 +StringFlav:ProbStoUD = 0.2 +StringFlav:probQQ1toQQ0join = 0.0275,0.0275,0.0275,0.0275 +MultiPartonInteractions:pT0Ref = 2.15 +BeamRemnants:remnantMode = 1 +BeamRemnants:saturation =5 + +# Correct OmegaC decay length (wrong in PYTHIA8 decay table) (mm/c) +4332:tau0 = 0.08000000000 +# Correct Lb decay length (wrong in PYTHIA8 decay table) +5122:tau0 = 4.41000e-01 diff --git a/MC/config/PWGEM/pythia8/generator/pythia8_pp_5360_bbbar_forceddecayscharmbeauty.cfg b/MC/config/PWGEM/pythia8/generator/pythia8_pp_5360_bbbar_forceddecayscharmbeauty.cfg new file mode 100644 index 000000000..2cc3486fe --- /dev/null +++ b/MC/config/PWGEM/pythia8/generator/pythia8_pp_5360_bbbar_forceddecayscharmbeauty.cfg @@ -0,0 +1,110 @@ +### beams +Beams:idA 2212 # proton +Beams:idB 2212 # proton +Beams:eCM 5360 # GeV + +### processes +#HardQCD:hardccbar on # scatterings g-g / q-qbar -> c-cbar +HardQCD:hardbbbar on # scatterings g-g / q-qbar -> b-bbar + +### decays +ParticleDecays:limitTau0 on +ParticleDecays:tau0Max 10. + +### switch on color reconnection in mode 2 (https://arxiv.org/pdf/1505.01681.pdf) +Tune:pp = 14 +ColourReconnection:mode = 1 +ColourReconnection:allowDoubleJunRem = off +ColourReconnection:m0 = 0.3 +ColourReconnection:allowJunctions = on +ColourReconnection:junctionCorrection = 1.20 +ColourReconnection:timeDilationMode = 2 +ColourReconnection:timeDilationPar = 0.18 +StringPT:sigma = 0.335 +StringZ:aLund = 0.36 +StringZ:bLund = 0.56 +StringFlav:probQQtoQ = 0.078 +StringFlav:ProbStoUD = 0.2 +StringFlav:probQQ1toQQ0join = 0.0275,0.0275,0.0275,0.0275 +MultiPartonInteractions:pT0Ref = 2.15 +BeamRemnants:remnantMode = 1 +BeamRemnants:saturation =5 + +### only semileptonic decays for charm +### D+ +411:oneChannel = 1 0.087 0 -311 -11 12 +411:addChannel = 1 0.040 0 -321 211 -11 12 +411:addChannel = 1 0.037 0 -313 -11 12 +### D0 +421:oneChannel = 1 0.035 0 -321 -11 12 +421:addChannel = 1 0.022 0 -323 -11 12 +421:addChannel = 1 0.016 0 -321 111 -11 12 +421:addChannel = 1 0.014 0 -311 -211 -11 12 +### Ds +431:oneChannel = 1 0.025 0 333 -11 12 +431:addChannel = 1 0.027 0 221 -11 12 +### Lambdac +4122:oneChannel = 1 0.036 0 3122 -11 12 +### chi_{c}^{+} +4232:oneChannel = 1 0.07 0 3322 -11 12 +### chi_{c}^{0} +4132:oneChannel = 1 0.014 0 3312 -11 12 +### Omega_{c} +4332:oneChannel = 1 0.01224 0 3334 -11 12 + +### only semileptonic decays for beauty +### B0 +511:oneChannel = 1 0.0207000 0 12 -11 -411 +511:addChannel = 1 0.0570000 0 12 -11 -413 +511:addChannel = 1 0.0023000 0 12 -11 -415 +511:addChannel = 1 0.0001330 0 12 -11 -211 +511:addChannel = 1 0.0002690 0 12 -11 -213 +511:addChannel = 1 0.0045000 0 12 -11 -10411 +511:addChannel = 1 0.0052000 0 12 -11 -10413 +511:addChannel = 1 0.0083000 0 12 -11 -20413 + +### B+ +521:oneChannel = 1 0.0000720 0 12 -11 111 +521:addChannel = 1 0.0001450 0 12 -11 113 +521:addChannel = 1 0.0000840 0 12 -11 221 +521:addChannel = 1 0.0001450 0 12 -11 223 +521:addChannel = 1 0.0000840 0 12 -11 331 +521:addChannel = 1 0.0224000 0 12 -11 -421 +521:addChannel = 1 0.0617000 0 12 -11 -423 +521:addChannel = 1 0.0030000 0 12 -11 -425 +521:addChannel = 1 0.0049000 0 12 -11 -10421 +521:addChannel = 1 0.0056000 0 12 -11 -10423 +521:addChannel = 1 0.0090000 0 12 -11 -20423 + +### Bs +531:oneChannel = 1 0.0002000 0 12 -11 -321 +531:addChannel = 1 0.0003000 0 12 -11 -323 +531:addChannel = 1 0.0210000 0 12 -11 -431 +531:addChannel = 1 0.0490000 0 12 -11 -433 +531:addChannel = 1 0.0070000 0 12 -11 -435 +531:addChannel = 1 0.0003000 0 12 -11 -10323 +531:addChannel = 1 0.0040000 0 12 -11 -10431 +531:addChannel = 1 0.0070000 0 12 -11 -10433 +531:addChannel = 1 0.0002000 0 12 -11 -20323 +531:addChannel = 1 0.0040000 0 12 -11 -20433 + +### Lambdab +5122:oneChannel = 1 0.0546000 0 -12 11 4122 +5122:addChannel = 1 0.0096000 0 -12 11 4124 +5122:addChannel = 1 0.0128000 0 -12 11 14122 + +### Chi_{b}^{-} +5132:oneChannel = 1 0.1080010 0 -12 11 4 3101 +5132:addChannel = 1 0.0020000 0 -12 11 2 3101 +### Chi_{b}^{0} +5232:oneChannel = 1 0.1080010 0 -12 11 4 3201 +5232:addChannel = 1 0.0020000 0 -12 11 2 3201 +### Omega_{b}^{-} +5332:oneChannel = 1 0.1080010 1 -12 11 4 3303 +5332:oneChannel = 1 0.0020000 1 -12 11 2 3303 + + +# Correct OmegaC decay length (wrong in PYTHIA8 decay table) (mm/c) +4332:tau0 = 0.08000000000 +# Correct Lb decay length (wrong in PYTHIA8 decay table) +5122:tau0 = 4.41000e-01 diff --git a/MC/config/PWGEM/pythia8/generator/pythia8_pp_5360_cr2_forceddecayscharm.cfg b/MC/config/PWGEM/pythia8/generator/pythia8_pp_5360_cr2_forceddecayscharm.cfg new file mode 100644 index 000000000..7f5a2bcab --- /dev/null +++ b/MC/config/PWGEM/pythia8/generator/pythia8_pp_5360_cr2_forceddecayscharm.cfg @@ -0,0 +1,63 @@ +### main + +Main:timesAllowErrors 2000 +#allow more errors in the pythia. + + +### beams +Beams:idA 2212 # proton +Beams:idB 2212 # proton +Beams:eCM 5360 # GeV + +### processes +# HardQCD:hardccbar on # ccbar production +SoftQCD:inelastic = on + +### decays +ParticleDecays:limitTau0 on +ParticleDecays:tau0Max 10. + +### switch on color reconnection in mode 2 (https://arxiv.org/pdf/1505.01681.pdf) +Tune:pp = 14 +ColourReconnection:mode = 1 +ColourReconnection:allowDoubleJunRem = off +ColourReconnection:m0 = 0.3 +ColourReconnection:allowJunctions = on +ColourReconnection:junctionCorrection = 1.20 +ColourReconnection:timeDilationMode = 2 +ColourReconnection:timeDilationPar = 0.18 +StringPT:sigma = 0.335 +StringZ:aLund = 0.36 +StringZ:bLund = 0.56 +StringFlav:probQQtoQ = 0.078 +StringFlav:ProbStoUD = 0.2 +StringFlav:probQQ1toQQ0join = 0.0275,0.0275,0.0275,0.0275 +MultiPartonInteractions:pT0Ref = 2.15 +BeamRemnants:remnantMode = 1 +BeamRemnants:saturation =5 + + ### only semileptonic decays +### D+ +411:oneChannel = 1 0.087 0 -311 -11 12 +411:addChannel = 1 0.040 0 -321 211 -11 12 +411:addChannel = 1 0.037 0 -313 -11 12 +### D0 +421:oneChannel = 1 0.035 0 -321 -11 12 +421:addChannel = 1 0.022 0 -323 -11 12 +421:addChannel = 1 0.016 0 -321 111 -11 12 +### Ds +431:oneChannel = 1 0.025 0 333 -11 12 +431:addChannel = 1 0.027 0 221 -11 12 +### Lambdac +4122:oneChannel = 1 0.036 0 3122 -11 12 +### chi_{c}^{+} +4232:oneChannel = 1 0.07 0 3322 -11 12 +### chi_{c}^{0} +4132:oneChannel = 1 0.014 0 3312 -11 12 +### Omega_{c} +4332:oneChannel = 1 0.01224 0 3334 -11 12 + +# Correct OmegaC decay length (wrong in PYTHIA8 decay table) (mm/c) +4332:tau0 = 0.08000000000 +# Correct Lb decay length (wrong in PYTHIA8 decay table) +5122:tau0 = 4.41000e-01 diff --git a/MC/config/PWGEM/pythia8/generator/pythia8_pp_cr2_forceddecayscharm_semimuonic.cfg b/MC/config/PWGEM/pythia8/generator/pythia8_pp_cr2_forceddecayscharm_semimuonic.cfg new file mode 100644 index 000000000..330b31a23 --- /dev/null +++ b/MC/config/PWGEM/pythia8/generator/pythia8_pp_cr2_forceddecayscharm_semimuonic.cfg @@ -0,0 +1,67 @@ +### main + +Main:timesAllowErrors 2000 +#allow more errors in the pythia. + + +### beams +Beams:idA 2212 # proton +Beams:idB 2212 # proton +Beams:eCM 13600 # GeV + +### processes +# HardQCD:hardccbar on # ccbar production +SoftQCD:inelastic = on + +### decays +ParticleDecays:limitTau0 on +ParticleDecays:tau0Max 10. + +### switch on color reconnection in mode 2 (https://arxiv.org/pdf/1505.01681.pdf) +Tune:pp = 14 +ColourReconnection:mode = 1 +ColourReconnection:allowDoubleJunRem = off +ColourReconnection:m0 = 0.3 +ColourReconnection:allowJunctions = on +ColourReconnection:junctionCorrection = 1.20 +ColourReconnection:timeDilationMode = 2 +ColourReconnection:timeDilationPar = 0.18 +StringPT:sigma = 0.335 +StringZ:aLund = 0.36 +StringZ:bLund = 0.56 +StringFlav:probQQtoQ = 0.078 +StringFlav:ProbStoUD = 0.2 +StringFlav:probQQ1toQQ0join = 0.0275,0.0275,0.0275,0.0275 +MultiPartonInteractions:pT0Ref = 2.15 +BeamRemnants:remnantMode = 1 +BeamRemnants:saturation =5 + +### only semileptonic decays +### D+ +411:oneChannel = 1 0.0868 0 -311 -13 14 +411:addChannel = 1 0.0527 0 -313 -13 14 +411:addChannel = 1 0.0019 0 -321 211 -13 14 +### D0 +421:oneChannel = 1 0.03418 0 -321 -13 14 +421:addChannel = 1 0.02060 0 -323 -13 14 +421:addChannel = 1 0.000421 0 -321 111 -13 14 +421:addChannel = 1 0.00267 0 -211 -13 14 +421:addChannel = 1 0.00145 0 -211 111 -13 14 +### Ds +431:oneChannel = 1 0.0224 0 333 -13 14 +431:addChannel = 1 0.0224 0 221 -13 14 +431:addChannel = 1 0.0080 0 331 -13 14 + +### Lambdac +4122:oneChannel = 1 0.036 0 3122 -13 14 +### chi_{c}^{+} +4232:oneChannel = 1 0.07 0 3322 -13 14 +### chi_{c}^{0} +4132:oneChannel = 1 0.014 0 3312 -13 14 +### Omega_{c} +4332:oneChannel = 1 0.01224 0 3334 -13 14 + +# Correct OmegaC decay length (wrong in PYTHIA8 decay table) (mm/c) +4332:tau0 = 0.08000000000 +# Correct Lb decay length (wrong in PYTHIA8 decay table) +5122:tau0 = 4.41000e-01 diff --git a/MC/config/PWGGAJE/external/generator/generator_pythia8_gaptrigger_hook.C b/MC/config/PWGGAJE/external/generator/generator_pythia8_gaptrigger_hook.C new file mode 100644 index 000000000..bfcd55f45 --- /dev/null +++ b/MC/config/PWGGAJE/external/generator/generator_pythia8_gaptrigger_hook.C @@ -0,0 +1,279 @@ +R__ADD_INCLUDE_PATH($O2DPG_MC_CONFIG_ROOT) +///#include "FairGenerator.h" +//#include "Generators/GeneratorPythia8.h" +#include "Pythia8/Pythia.h" +#include "MC/config/PWGGAJE/hooks/prompt_gamma_hook.C" +//#include "TRandom.h" +//#include +// +//#include +//#include + +// Prompt-photon custom event generator +// that alternates between 2 gun generators. This is the non-triggered version, without hooks, and would work with any PYTHIA cfg +// set up to inject MB events alongside prompt-photon events +// in 'MB-gap' mode. +// The number of MB events injected, and the PYTHIA config +// for each event type is defined by the user in the .ini +// generator file (e.g. GeneratorJE_gapgen5_hook.ini) +// +// Author: Adrian Fereydon Nassirpour (adrian.fereydon.nassirpour@cern.ch), based on code from Jaime Norman (jaime.norman@cern.ch) + +namespace o2 +{ +namespace eventgen +{ + +using namespace Pythia8; + + +/// A very simple gap generator alternating between 2 different particle guns +class GeneratorPythia8GapGenJEMB : public o2::eventgen::GeneratorPythia8 +{ +public: + /// default constructor + GeneratorPythia8GapGenJEMB(int inputTriggerRatio = 5,std::string pathMB = "",std::string pathSignal = "") { + + mGeneratedEvents = 0; + mInverseTriggerRatio = inputTriggerRatio; + + auto seed = (gRandom->TRandom::GetSeed() % 900000000); + + cout << "Initalizing extra PYTHIA object used to generate min-bias events..." << endl; + TString pathconfigMB = gSystem->ExpandPathName(TString(pathMB)); + pythiaObjectMinimumBias.readFile(pathconfigMB.Data()); + pythiaObjectMinimumBias.readString("Random:setSeed on"); + pythiaObjectMinimumBias.readString("Random:seed " + std::to_string(seed)); + pythiaObjectMinimumBias.init(); + cout << "Initalization complete" << endl; + cout << "Initalizing extra PYTHIA object used to generate signal events..." << endl; + TString pathconfigSignal = gSystem->ExpandPathName(TString(pathSignal)); + pythiaObjectSignal.readFile(pathconfigSignal.Data()); + pythiaObjectSignal.readString("Random:setSeed on"); + pythiaObjectSignal.readString("Random:seed " + std::to_string(seed)); + // Here we could loada hook, but we want to run MB for this production + // Pythia8::UserHooks *hook = pythia8_userhooks_promptgamma(); + // pythiaObjectSignal.setUserHooksPtr(std::shared_ptr(hook)); + pythiaObjectSignal.init(); + cout << "Initalization complete" << endl; + // Add Sub generators + addSubGenerator(0, "MB generator"); + addSubGenerator(1, "jet-jet generator"); + } + + + /// Destructor + ~GeneratorPythia8GapGenJEMB() = default; + + void setUsedSeed(unsigned int seed) + { + mUsedSeed = seed; + }; + unsigned int getUsedSeed() const + { + return mUsedSeed; + }; + + bool generateEvent() override + { + + // Simple straightforward check to alternate generators + mPythia.event.reset(); + + if (mGeneratedEvents % mInverseTriggerRatio == 0) { + LOG(info) << "Event " << mGeneratedEvents << ", generate signal event"; + // Generate event of interest + Bool_t mGenerationOK = kFALSE; + while (!mGenerationOK) { + mGenerationOK = pythiaObjectSignal.next(); + } + mPythia.event = pythiaObjectSignal.event; + setEventHeaderProperties(pythiaObjectSignal); + LOG(info) << "--- Print info properties custom..."; + printEventHeaderProperties(pythiaObjectSignal); + notifySubGenerator(1); + } + else { + LOG(info) << "Event " << mGeneratedEvents << ", generate mb event"; + // Generate minimum-bias event + Bool_t mGenerationOK = kFALSE; + while (!mGenerationOK) { + mGenerationOK = pythiaObjectMinimumBias.next(); + } + mPythia.event = pythiaObjectMinimumBias.event; + setEventHeaderProperties(pythiaObjectMinimumBias); + LOG(info) << "--- Print info properties custom..."; + printEventHeaderProperties(pythiaObjectMinimumBias); + notifySubGenerator(0); + } + mGeneratedEvents++; + return true; + } + + // for testing + void printEventHeaderProperties (Pythia8::Pythia &pythiaObject) { + LOG(info) << "Info name = " << pythiaObject.info.name(); + LOG(info) << "Info code = " << pythiaObject.info.code(); + LOG(info) << "Info weight = " << pythiaObject.info.weight(); + LOG(info) << "Info id1pdf = " << pythiaObject.info.id1pdf(); + LOG(info) << "Info id2pdf = " << pythiaObject.info.id2pdf(); + + LOG(info) << "Info x1pdf = " << pythiaObject.info.x1pdf(); + LOG(info) << "Info x2pdf = " << pythiaObject.info.x2pdf(); + LOG(info) << "Info QFac = " << pythiaObject.info.QFac(); + LOG(info) << "Info pdf1 = " << pythiaObject.info.pdf1(); + LOG(info) << "Info pdf2 = " << pythiaObject.info.pdf2(); + + // Set cross section + LOG(info) << "Info sigmaGen = " << pythiaObject.info.sigmaGen(); + LOG(info) << "Info sigmaErr = " << pythiaObject.info.sigmaErr(); + + // Set event scale and nMPI + LOG(info) << "Info QRen = " << pythiaObject.info.QRen(); + LOG(info) << "Info nMPI = " << pythiaObject.info.nMPI(); + + // Set accepted and attempted values + LOG(info) << "Info accepted = " << pythiaObject.info.nAccepted(); + LOG(info) << "Info attempted = " << pythiaObject.info.nTried(); + + // Set weights (overrides cross-section for each weight) + size_t iw = 0; + auto xsecErr = pythiaObject.info.weightContainerPtr->getTotalXsecErr(); + for (auto w : pythiaObject.info.weightContainerPtr->getTotalXsec()) { + std::string post = (iw == 0 ? "" : "_" + std::to_string(iw)); + LOG(info) << "Info weight by index = " << pythiaObject.info.weightValueByIndex(iw); + iw++; + } + + } + + // in order to save the event weight we need to override the following function + // from the inherited o2::eventgen::GeneratorPythia8 class. The event header properties + // are created as members of this class, and are set using the active event generator + // (MB or jet-jet), then propagated to the event header + void updateHeader(o2::dataformats::MCEventHeader* eventHeader) override { + /** update header **/ + using Key = o2::dataformats::MCInfoKeys; + + eventHeader->putInfo(Key::generator, "pythia8"); + eventHeader->putInfo(Key::generatorVersion, PYTHIA_VERSION_INTEGER); + eventHeader->putInfo(Key::processName, name); + eventHeader->putInfo(Key::processCode, code); + eventHeader->putInfo(Key::weight, weight); + + // Set PDF information + eventHeader->putInfo(Key::pdfParton1Id, id1pdf); + eventHeader->putInfo(Key::pdfParton2Id, id2pdf); + eventHeader->putInfo(Key::pdfX1, x1pdf); + eventHeader->putInfo(Key::pdfX2, x2pdf); + eventHeader->putInfo(Key::pdfScale, QFac); + eventHeader->putInfo(Key::pdfXF1, pdf1); + eventHeader->putInfo(Key::pdfXF2, pdf2); + + // Set cross section + eventHeader->putInfo(Key::xSection, sigmaGen * 1e9); + eventHeader->putInfo(Key::xSectionError, sigmaErr * 1e9); + + // Set event scale and nMPI + eventHeader->putInfo(Key::eventScale, QRen); + eventHeader->putInfo(Key::mpi, nMPI); + + // Set accepted and attempted events + eventHeader->putInfo(Key::acceptedEvents, accepted); + eventHeader->putInfo(Key::attemptedEvents, attempted); + + LOG(info) << "--- updated header weight = " << weight; + + // The following is also set in the base class updateHeader function + // but as far as I can tell, there is no Xsec weight in the default + // header so this is not copied over for now + + //size_t iw = 0; + //auto xsecErr = info.weightContainerPtr->getTotalXsecErr(); + //for (auto w : info.weightContainerPtr->getTotalXsec()) { + // std::string post = (iw == 0 ? "" : "_" + std::to_string(iw)); + // eventHeader->putInfo(Key::weight + post, info.weightValueByIndex(iw)); + // eventHeader->putInfo(Key::xSection + post, w * 1e9); + // eventHeader->putInfo(Key::xSectionError + post, xsecErr[iw] * 1e9); + // iw++; + //} + } + + void setEventHeaderProperties (Pythia8::Pythia &pythiaObject) { + + auto& info = pythiaObject.info; + + name = info.name(); + code = info.code(); + weight = info.weight(); + // Set PDF information + id1pdf = info.id1pdf(); + id2pdf = info.id2pdf(); + x1pdf = info.x1pdf(); + x2pdf = info.x2pdf(); + QFac = info.QFac(); + pdf1 = info.pdf1(); + pdf2 = info.pdf2(); + // Set cross section + sigmaGen = info.sigmaGen(); + sigmaErr = info.sigmaErr(); + // Set event scale and nMPI + QRen = info.QRen(); + nMPI = info.nMPI(); + // Set accepted and attempted events + accepted = info.nAccepted(); + attempted = info.nTried(); + } + +private: + // Interface to override import particles + Pythia8::Event mOutputEvent; + + // Properties of selection + unsigned int mUsedSeed; + + // Control gap-triggering + unsigned long long mGeneratedEvents; + int mInverseTriggerRatio; + + // Handling generators + Pythia8::Pythia pythiaObjectMinimumBias; + Pythia8::Pythia pythiaObjectSignal; + + // header info - needed to save event properties + std::string name; + int code; + float weight; + // PDF information + int id1pdf; + int id2pdf; + float x1pdf; + float x2pdf; + float QFac; + float pdf1; + float pdf2; + // cross section + float sigmaGen; + float sigmaErr; + // event scale and nMPI + float QRen; + int nMPI; + // accepted and attempted events + int accepted; + int attempted; +}; + +} // namespace eventgen +} // namespace o2 + +/** generator instance and settings **/ + +FairGenerator* getGeneratorPythia8GapGenJEMB(int inputTriggerRatio = 5, std::string pathMB = "",std::string pathSignal = "") { + auto myGen = new o2::eventgen::GeneratorPythia8GapGenJEMB(inputTriggerRatio, pathMB, pathSignal); + auto seed = (gRandom->TRandom::GetSeed() % 900000000); + myGen->setUsedSeed(seed); + myGen->readString("Random:setSeed on"); + myGen->readString("Random:seed " + std::to_string(seed)); + myGen->readString("HardQCD:all = on"); + return myGen; +} diff --git a/MC/config/PWGGAJE/external/generator/generator_pythia8_gaptrigger_jets_hook.C b/MC/config/PWGGAJE/external/generator/generator_pythia8_gaptrigger_jets_hook.C new file mode 100644 index 000000000..c3db3294c --- /dev/null +++ b/MC/config/PWGGAJE/external/generator/generator_pythia8_gaptrigger_jets_hook.C @@ -0,0 +1,281 @@ +R__ADD_INCLUDE_PATH($O2DPG_MC_CONFIG_ROOT) +///#include "FairGenerator.h" +//#include "Generators/GeneratorPythia8.h" +#include "Pythia8/Pythia.h" +#include "MC/config/PWGGAJE/hooks/jets_hook.C" +//#include "TRandom.h" +//#include +// +//#include +//#include + +// Jet-jet custom event generator +// that alternates between 2 gun generators. +// set up to inject MB events alongside jet-jet events +// in 'MB-gap' mode. +// The number of MB events injected, and the PYTHIA config +// for each event type is defined by the user in the .ini +// generator file (e.g. GeneratorJE_gapgen5_hook.ini) +// +// Author: Jaime Norman (jaime.norman@cern.ch) + +// o2-sim-dpl-eventgen --nEvents 10 --generator external --configKeyValues "GeneratorExternal.fileName=generator_pythia8_gaptrigger_jets_pythiabase.C;GeneratorExternal.funcName=getGeneratorPythia8GapGenJE()" + +namespace o2 +{ +namespace eventgen +{ + +using namespace Pythia8; + + +/// A very simple gap generator alternating between 2 different particle guns +class GeneratorPythia8GapGenJE : public o2::eventgen::GeneratorPythia8 +{ +public: + /// default constructor + GeneratorPythia8GapGenJE(int inputTriggerRatio = 5,std::string pathMB = "",std::string pathSignal = "") { + + mGeneratedEvents = 0; + mInverseTriggerRatio = inputTriggerRatio; + + auto seed = (gRandom->TRandom::GetSeed() % 900000000); + + cout << "Initalizing extra PYTHIA object used to generate min-bias events..." << endl; + TString pathconfigMB = gSystem->ExpandPathName(TString(pathMB)); + pythiaObjectMinimumBias.readFile(pathconfigMB.Data()); + pythiaObjectMinimumBias.readString("Random:setSeed on"); + pythiaObjectMinimumBias.readString("Random:seed " + std::to_string(seed)); + pythiaObjectMinimumBias.init(); + cout << "Initalization complete" << endl; + cout << "Initalizing extra PYTHIA object used to generate signal events..." << endl; + TString pathconfigSignal = gSystem->ExpandPathName(TString(pathSignal)); + pythiaObjectSignal.readFile(pathconfigSignal.Data()); + pythiaObjectSignal.readString("Random:setSeed on"); + pythiaObjectSignal.readString("Random:seed " + std::to_string(seed)); + // load jet hook to ensure at least one jet is within detector acceptance + Pythia8::UserHooks *hook = pythia8_userhooks_jets(); + pythiaObjectSignal.setUserHooksPtr(std::shared_ptr(hook)); + pythiaObjectSignal.init(); + cout << "Initalization complete" << endl; + // Add Sub generators + addSubGenerator(0, "MB generator"); + addSubGenerator(1, "jet-jet generator"); + } + + + /// Destructor + ~GeneratorPythia8GapGenJE() = default; + + void setUsedSeed(unsigned int seed) + { + mUsedSeed = seed; + }; + unsigned int getUsedSeed() const + { + return mUsedSeed; + }; + + bool generateEvent() override + { + + // Simple straightforward check to alternate generators + mPythia.event.reset(); + + if (mGeneratedEvents % mInverseTriggerRatio == 0) { + LOG(info) << "Event " << mGeneratedEvents << ", generate signal event"; + // Generate event of interest + Bool_t mGenerationOK = kFALSE; + while (!mGenerationOK) { + mGenerationOK = pythiaObjectSignal.next(); + } + mPythia.event = pythiaObjectSignal.event; + setEventHeaderProperties(pythiaObjectSignal); + LOG(info) << "--- Print info properties custom..."; + printEventHeaderProperties(pythiaObjectSignal); + notifySubGenerator(1); + } + else { + LOG(info) << "Event " << mGeneratedEvents << ", generate mb event"; + // Generate minimum-bias event + Bool_t mGenerationOK = kFALSE; + while (!mGenerationOK) { + mGenerationOK = pythiaObjectMinimumBias.next(); + } + mPythia.event = pythiaObjectMinimumBias.event; + setEventHeaderProperties(pythiaObjectMinimumBias); + LOG(info) << "--- Print info properties custom..."; + printEventHeaderProperties(pythiaObjectMinimumBias); + notifySubGenerator(0); + } + mGeneratedEvents++; + return true; + } + + // for testing + void printEventHeaderProperties (Pythia8::Pythia &pythiaObject) { + LOG(info) << "Info name = " << pythiaObject.info.name(); + LOG(info) << "Info code = " << pythiaObject.info.code(); + LOG(info) << "Info weight = " << pythiaObject.info.weight(); + LOG(info) << "Info id1pdf = " << pythiaObject.info.id1pdf(); + LOG(info) << "Info id2pdf = " << pythiaObject.info.id2pdf(); + + LOG(info) << "Info x1pdf = " << pythiaObject.info.x1pdf(); + LOG(info) << "Info x2pdf = " << pythiaObject.info.x2pdf(); + LOG(info) << "Info QFac = " << pythiaObject.info.QFac(); + LOG(info) << "Info pdf1 = " << pythiaObject.info.pdf1(); + LOG(info) << "Info pdf2 = " << pythiaObject.info.pdf2(); + + // Set cross section + LOG(info) << "Info sigmaGen = " << pythiaObject.info.sigmaGen(); + LOG(info) << "Info sigmaErr = " << pythiaObject.info.sigmaErr(); + + // Set event scale and nMPI + LOG(info) << "Info QRen = " << pythiaObject.info.QRen(); + LOG(info) << "Info nMPI = " << pythiaObject.info.nMPI(); + + // Set accepted and attempted values + LOG(info) << "Info accepted = " << pythiaObject.info.nAccepted(); + LOG(info) << "Info attempted = " << pythiaObject.info.nTried(); + + // Set weights (overrides cross-section for each weight) + size_t iw = 0; + auto xsecErr = pythiaObject.info.weightContainerPtr->getTotalXsecErr(); + for (auto w : pythiaObject.info.weightContainerPtr->getTotalXsec()) { + std::string post = (iw == 0 ? "" : "_" + std::to_string(iw)); + LOG(info) << "Info weight by index = " << pythiaObject.info.weightValueByIndex(iw); + iw++; + } + + } + + // in order to save the event weight we need to override the following function + // from the inherited o2::eventgen::GeneratorPythia8 class. The event header properties + // are created as members of this class, and are set using the active event generator + // (MB or jet-jet), then propagated to the event header + void updateHeader(o2::dataformats::MCEventHeader* eventHeader) override { + /** update header **/ + using Key = o2::dataformats::MCInfoKeys; + + eventHeader->putInfo(Key::generator, "pythia8"); + eventHeader->putInfo(Key::generatorVersion, PYTHIA_VERSION_INTEGER); + eventHeader->putInfo(Key::processName, name); + eventHeader->putInfo(Key::processCode, code); + eventHeader->putInfo(Key::weight, weight); + + // Set PDF information + eventHeader->putInfo(Key::pdfParton1Id, id1pdf); + eventHeader->putInfo(Key::pdfParton2Id, id2pdf); + eventHeader->putInfo(Key::pdfX1, x1pdf); + eventHeader->putInfo(Key::pdfX2, x2pdf); + eventHeader->putInfo(Key::pdfScale, QFac); + eventHeader->putInfo(Key::pdfXF1, pdf1); + eventHeader->putInfo(Key::pdfXF2, pdf2); + + // Set cross section + eventHeader->putInfo(Key::xSection, sigmaGen * 1e9); + eventHeader->putInfo(Key::xSectionError, sigmaErr * 1e9); + + // Set event scale and nMPI + eventHeader->putInfo(Key::eventScale, QRen); + eventHeader->putInfo(Key::mpi, nMPI); + + // Set accepted and attempted events + eventHeader->putInfo(Key::acceptedEvents, accepted); + eventHeader->putInfo(Key::attemptedEvents, attempted); + + LOG(info) << "--- updated header weight = " << weight; + + // The following is also set in the base class updateHeader function + // but as far as I can tell, there is no Xsec weight in the default + // header so this is not copied over for now + + //size_t iw = 0; + //auto xsecErr = info.weightContainerPtr->getTotalXsecErr(); + //for (auto w : info.weightContainerPtr->getTotalXsec()) { + // std::string post = (iw == 0 ? "" : "_" + std::to_string(iw)); + // eventHeader->putInfo(Key::weight + post, info.weightValueByIndex(iw)); + // eventHeader->putInfo(Key::xSection + post, w * 1e9); + // eventHeader->putInfo(Key::xSectionError + post, xsecErr[iw] * 1e9); + // iw++; + //} + } + + void setEventHeaderProperties (Pythia8::Pythia &pythiaObject) { + + auto& info = pythiaObject.info; + + name = info.name(); + code = info.code(); + weight = info.weight(); + // Set PDF information + id1pdf = info.id1pdf(); + id2pdf = info.id2pdf(); + x1pdf = info.x1pdf(); + x2pdf = info.x2pdf(); + QFac = info.QFac(); + pdf1 = info.pdf1(); + pdf2 = info.pdf2(); + // Set cross section + sigmaGen = info.sigmaGen(); + sigmaErr = info.sigmaErr(); + // Set event scale and nMPI + QRen = info.QRen(); + nMPI = info.nMPI(); + // Set accepted and attempted events + accepted = info.nAccepted(); + attempted = info.nTried(); + } + +private: + // Interface to override import particles + Pythia8::Event mOutputEvent; + + // Properties of selection + unsigned int mUsedSeed; + + // Control gap-triggering + unsigned long long mGeneratedEvents; + int mInverseTriggerRatio; + + // Handling generators + Pythia8::Pythia pythiaObjectMinimumBias; + Pythia8::Pythia pythiaObjectSignal; + + // header info - needed to save event properties + std::string name; + int code; + float weight; + // PDF information + int id1pdf; + int id2pdf; + float x1pdf; + float x2pdf; + float QFac; + float pdf1; + float pdf2; + // cross section + float sigmaGen; + float sigmaErr; + // event scale and nMPI + float QRen; + int nMPI; + // accepted and attempted events + int accepted; + int attempted; +}; + +} // namespace eventgen +} // namespace o2 + +/** generator instance and settings **/ + +FairGenerator* getGeneratorPythia8GapGenJE(int inputTriggerRatio = 5, std::string pathMB = "",std::string pathSignal = "") { + auto myGen = new o2::eventgen::GeneratorPythia8GapGenJE(inputTriggerRatio, pathMB, pathSignal); + auto seed = (gRandom->TRandom::GetSeed() % 900000000); + myGen->setUsedSeed(seed); + myGen->readString("Random:setSeed on"); + myGen->readString("Random:seed " + std::to_string(seed)); + myGen->readString("HardQCD:all = on"); + return myGen; +} diff --git a/MC/config/PWGGAJE/external/generator/generator_pythia8_gaptrigger_promptphotons_hook.C b/MC/config/PWGGAJE/external/generator/generator_pythia8_gaptrigger_promptphotons_hook.C new file mode 100644 index 000000000..b138fccb3 --- /dev/null +++ b/MC/config/PWGGAJE/external/generator/generator_pythia8_gaptrigger_promptphotons_hook.C @@ -0,0 +1,279 @@ +R__ADD_INCLUDE_PATH($O2DPG_MC_CONFIG_ROOT) +///#include "FairGenerator.h" +//#include "Generators/GeneratorPythia8.h" +#include "Pythia8/Pythia.h" +#include "MC/config/PWGGAJE/hooks/prompt_gamma_hook.C" +//#include "TRandom.h" +//#include +// +//#include +//#include + +// Prompt-photon custom event generator +// that alternates between 2 gun generators. +// set up to inject MB events alongside prompt-photon events +// in 'MB-gap' mode. +// The number of MB events injected, and the PYTHIA config +// for each event type is defined by the user in the .ini +// generator file (e.g. GeneratorJE_gapgen5_hook.ini) +// +// Author: Adrian Fereydon Nassirpour (adrian.fereydon.nassirpour@cern.ch), based on code from Jaime Norman (jaime.norman@cern.ch) + +namespace o2 +{ +namespace eventgen +{ + +using namespace Pythia8; + + +/// A very simple gap generator alternating between 2 different particle guns +class GeneratorPythia8GapGenJEPhoton : public o2::eventgen::GeneratorPythia8 +{ +public: + /// default constructor + GeneratorPythia8GapGenJEPhoton(int inputTriggerRatio = 5,std::string pathMB = "",std::string pathSignal = "") { + + mGeneratedEvents = 0; + mInverseTriggerRatio = inputTriggerRatio; + + auto seed = (gRandom->TRandom::GetSeed() % 900000000); + + cout << "Initalizing extra PYTHIA object used to generate min-bias events..." << endl; + TString pathconfigMB = gSystem->ExpandPathName(TString(pathMB)); + pythiaObjectMinimumBias.readFile(pathconfigMB.Data()); + pythiaObjectMinimumBias.readString("Random:setSeed on"); + pythiaObjectMinimumBias.readString("Random:seed " + std::to_string(seed)); + pythiaObjectMinimumBias.init(); + cout << "Initalization complete" << endl; + cout << "Initalizing extra PYTHIA object used to generate signal events..." << endl; + TString pathconfigSignal = gSystem->ExpandPathName(TString(pathSignal)); + pythiaObjectSignal.readFile(pathconfigSignal.Data()); + pythiaObjectSignal.readString("Random:setSeed on"); + pythiaObjectSignal.readString("Random:seed " + std::to_string(seed)); + // load jet hook to ensure at least one prompt photon is within detector acceptance + Pythia8::UserHooks *hook = pythia8_userhooks_promptgamma(); + pythiaObjectSignal.setUserHooksPtr(std::shared_ptr(hook)); + pythiaObjectSignal.init(); + cout << "Initalization complete" << endl; + // Add Sub generators + addSubGenerator(0, "MB generator"); + addSubGenerator(1, "jet-jet generator"); + } + + + /// Destructor + ~GeneratorPythia8GapGenJEPhoton() = default; + + void setUsedSeed(unsigned int seed) + { + mUsedSeed = seed; + }; + unsigned int getUsedSeed() const + { + return mUsedSeed; + }; + + bool generateEvent() override + { + + // Simple straightforward check to alternate generators + mPythia.event.reset(); + + if (mGeneratedEvents % mInverseTriggerRatio == 0) { + LOG(info) << "Event " << mGeneratedEvents << ", generate signal event"; + // Generate event of interest + Bool_t mGenerationOK = kFALSE; + while (!mGenerationOK) { + mGenerationOK = pythiaObjectSignal.next(); + } + mPythia.event = pythiaObjectSignal.event; + setEventHeaderProperties(pythiaObjectSignal); + LOG(info) << "--- Print info properties custom..."; + printEventHeaderProperties(pythiaObjectSignal); + notifySubGenerator(1); + } + else { + LOG(info) << "Event " << mGeneratedEvents << ", generate mb event"; + // Generate minimum-bias event + Bool_t mGenerationOK = kFALSE; + while (!mGenerationOK) { + mGenerationOK = pythiaObjectMinimumBias.next(); + } + mPythia.event = pythiaObjectMinimumBias.event; + setEventHeaderProperties(pythiaObjectMinimumBias); + LOG(info) << "--- Print info properties custom..."; + printEventHeaderProperties(pythiaObjectMinimumBias); + notifySubGenerator(0); + } + mGeneratedEvents++; + return true; + } + + // for testing + void printEventHeaderProperties (Pythia8::Pythia &pythiaObject) { + LOG(info) << "Info name = " << pythiaObject.info.name(); + LOG(info) << "Info code = " << pythiaObject.info.code(); + LOG(info) << "Info weight = " << pythiaObject.info.weight(); + LOG(info) << "Info id1pdf = " << pythiaObject.info.id1pdf(); + LOG(info) << "Info id2pdf = " << pythiaObject.info.id2pdf(); + + LOG(info) << "Info x1pdf = " << pythiaObject.info.x1pdf(); + LOG(info) << "Info x2pdf = " << pythiaObject.info.x2pdf(); + LOG(info) << "Info QFac = " << pythiaObject.info.QFac(); + LOG(info) << "Info pdf1 = " << pythiaObject.info.pdf1(); + LOG(info) << "Info pdf2 = " << pythiaObject.info.pdf2(); + + // Set cross section + LOG(info) << "Info sigmaGen = " << pythiaObject.info.sigmaGen(); + LOG(info) << "Info sigmaErr = " << pythiaObject.info.sigmaErr(); + + // Set event scale and nMPI + LOG(info) << "Info QRen = " << pythiaObject.info.QRen(); + LOG(info) << "Info nMPI = " << pythiaObject.info.nMPI(); + + // Set accepted and attempted values + LOG(info) << "Info accepted = " << pythiaObject.info.nAccepted(); + LOG(info) << "Info attempted = " << pythiaObject.info.nTried(); + + // Set weights (overrides cross-section for each weight) + size_t iw = 0; + auto xsecErr = pythiaObject.info.weightContainerPtr->getTotalXsecErr(); + for (auto w : pythiaObject.info.weightContainerPtr->getTotalXsec()) { + std::string post = (iw == 0 ? "" : "_" + std::to_string(iw)); + LOG(info) << "Info weight by index = " << pythiaObject.info.weightValueByIndex(iw); + iw++; + } + + } + + // in order to save the event weight we need to override the following function + // from the inherited o2::eventgen::GeneratorPythia8 class. The event header properties + // are created as members of this class, and are set using the active event generator + // (MB or jet-jet), then propagated to the event header + void updateHeader(o2::dataformats::MCEventHeader* eventHeader) override { + /** update header **/ + using Key = o2::dataformats::MCInfoKeys; + + eventHeader->putInfo(Key::generator, "pythia8"); + eventHeader->putInfo(Key::generatorVersion, PYTHIA_VERSION_INTEGER); + eventHeader->putInfo(Key::processName, name); + eventHeader->putInfo(Key::processCode, code); + eventHeader->putInfo(Key::weight, weight); + + // Set PDF information + eventHeader->putInfo(Key::pdfParton1Id, id1pdf); + eventHeader->putInfo(Key::pdfParton2Id, id2pdf); + eventHeader->putInfo(Key::pdfX1, x1pdf); + eventHeader->putInfo(Key::pdfX2, x2pdf); + eventHeader->putInfo(Key::pdfScale, QFac); + eventHeader->putInfo(Key::pdfXF1, pdf1); + eventHeader->putInfo(Key::pdfXF2, pdf2); + + // Set cross section + eventHeader->putInfo(Key::xSection, sigmaGen * 1e9); + eventHeader->putInfo(Key::xSectionError, sigmaErr * 1e9); + + // Set event scale and nMPI + eventHeader->putInfo(Key::eventScale, QRen); + eventHeader->putInfo(Key::mpi, nMPI); + + // Set accepted and attempted events + eventHeader->putInfo(Key::acceptedEvents, accepted); + eventHeader->putInfo(Key::attemptedEvents, attempted); + + LOG(info) << "--- updated header weight = " << weight; + + // The following is also set in the base class updateHeader function + // but as far as I can tell, there is no Xsec weight in the default + // header so this is not copied over for now + + //size_t iw = 0; + //auto xsecErr = info.weightContainerPtr->getTotalXsecErr(); + //for (auto w : info.weightContainerPtr->getTotalXsec()) { + // std::string post = (iw == 0 ? "" : "_" + std::to_string(iw)); + // eventHeader->putInfo(Key::weight + post, info.weightValueByIndex(iw)); + // eventHeader->putInfo(Key::xSection + post, w * 1e9); + // eventHeader->putInfo(Key::xSectionError + post, xsecErr[iw] * 1e9); + // iw++; + //} + } + + void setEventHeaderProperties (Pythia8::Pythia &pythiaObject) { + + auto& info = pythiaObject.info; + + name = info.name(); + code = info.code(); + weight = info.weight(); + // Set PDF information + id1pdf = info.id1pdf(); + id2pdf = info.id2pdf(); + x1pdf = info.x1pdf(); + x2pdf = info.x2pdf(); + QFac = info.QFac(); + pdf1 = info.pdf1(); + pdf2 = info.pdf2(); + // Set cross section + sigmaGen = info.sigmaGen(); + sigmaErr = info.sigmaErr(); + // Set event scale and nMPI + QRen = info.QRen(); + nMPI = info.nMPI(); + // Set accepted and attempted events + accepted = info.nAccepted(); + attempted = info.nTried(); + } + +private: + // Interface to override import particles + Pythia8::Event mOutputEvent; + + // Properties of selection + unsigned int mUsedSeed; + + // Control gap-triggering + unsigned long long mGeneratedEvents; + int mInverseTriggerRatio; + + // Handling generators + Pythia8::Pythia pythiaObjectMinimumBias; + Pythia8::Pythia pythiaObjectSignal; + + // header info - needed to save event properties + std::string name; + int code; + float weight; + // PDF information + int id1pdf; + int id2pdf; + float x1pdf; + float x2pdf; + float QFac; + float pdf1; + float pdf2; + // cross section + float sigmaGen; + float sigmaErr; + // event scale and nMPI + float QRen; + int nMPI; + // accepted and attempted events + int accepted; + int attempted; +}; + +} // namespace eventgen +} // namespace o2 + +/** generator instance and settings **/ + +FairGenerator* getGeneratorPythia8GapGenJEPhoton(int inputTriggerRatio = 5, std::string pathMB = "",std::string pathSignal = "") { + auto myGen = new o2::eventgen::GeneratorPythia8GapGenJEPhoton(inputTriggerRatio, pathMB, pathSignal); + auto seed = (gRandom->TRandom::GetSeed() % 900000000); + myGen->setUsedSeed(seed); + myGen->readString("Random:setSeed on"); + myGen->readString("Random:seed " + std::to_string(seed)); + myGen->readString("HardQCD:all = on"); + return myGen; +} diff --git a/MC/config/PWGGAJE/external/generator/generator_pythia8_powheg.C b/MC/config/PWGGAJE/external/generator/generator_pythia8_powheg.C new file mode 100644 index 000000000..5c3afd206 --- /dev/null +++ b/MC/config/PWGGAJE/external/generator/generator_pythia8_powheg.C @@ -0,0 +1,258 @@ +R__ADD_INCLUDE_PATH($O2DPG_MC_CONFIG_ROOT) +///#include "FairGenerator.h" +#include "Generators/GeneratorPythia8.h" +#include "Pythia8/Pythia.h" +#include "Generators/GeneratorPythia8Param.h" +#include "CommonUtils/FileSystemUtils.h" +#include +// Pythia8 generator with POWHEG +// +// Author: Marco Giacalone (marco.giacalone@cern.ch) + +// o2-sim-dpl-eventgen --nEvents 10 --generator external\ + --configKeyValues "GeneratorExternal.fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/external/\ + generator/generator_pythia8_powheg.C;GeneratorExternal.funcName=\ + getGeneratorJEPythia8POWHEG(\"powheg.input\",\"${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/pythia8/generator/pythia8_powheg.cfg\")" +// or with iniFile +// o2-sim -g external --noGeant -n 2 -j 8 --configFile $O2DPG_MC_CONFIG_ROOT/MC/config/PWGGAJE/ini/GeneratorPythia8POWHEG_beauty.ini + +namespace o2 +{ +namespace eventgen +{ + +using namespace Pythia8; + +// Pythia8 generator using POWHEG data that are generated partially during the initialization +// of the external generator and then during the generateEvent when mNMaxPerJob is reached. The first time +// all the configuration files are created so that the other jobs can be run much faster (and in parallel in the future) +// The POWHEG configuration file is copied to the current directory with the right name and the POWHEG events are generated +// using the executable specified via the type parameter, namely: +// 0: pwhg_main_hvq +// 1: pwhg_main_W +// 2: pwhg_main_Z +// 3: pwhg_main_dijet +// 4: pwhg_main_directphoton +class GeneratorJEPythia8POWHEG : public o2::eventgen::GeneratorPythia8 +{ +public: + /// default constructor + GeneratorJEPythia8POWHEG(std::string confpath = "pwgpath", short int type = 0, int maxEventsPerJob = 50) + { + // Assign events to generate with POWHEG + unsigned int nPowhegEvents = getTotalNEvents(); + if (nPowhegEvents == 0) { + LOG(fatal) << "Number of events not set or set to 0."; + exit(1); + } + // POWHEG has an integer limit hardcoded for the nEvents, but + // with the multiple jobs setup this is not an issue (an error will automatically be thrown) + mNMaxPerJob = maxEventsPerJob; + if (mNMaxPerJob < 1) { + LOG(fatal) << "Number of events per job are set to 0 or lower."; + exit(1); + } + mNFiles = nPowhegEvents / mNMaxPerJob; + if (nPowhegEvents % mNMaxPerJob != 0) + { + mNFiles++; + } + gRandom->SetSeed(0); + if(!confMaker(confpath)) + { + LOG(fatal) << "Failed to edit POWHEG configuration file"; + exit(1); + } + mPowhegConf = confpath; + // Get POWHEG executable to use + if (type >= mPowhegGen.size()) { + LOG(warn) << "Available POWHEG generators are:"; + for (int k = 0; k < mPowhegGen.size(); k++) + { + LOG(warn) << "\t" << k << ": " << mPowhegGen[k]; + } + LOG(fatal) << "POWHEG generator type " << type << " not found"; + exit(1); + } else { + LOG(info) << "Running POWHEG using the " << mPowhegGen[type] << " executable"; + // Generate the POWHEG events + mExePOW = mPowhegGen[type] + " &"; + system(mExePOW.c_str()); + } + }; + + Bool_t confMaker(std::string confpath = "pwgpath", bool parallel = false) + { + // Check if file exist and is not empty + if (std::filesystem::exists(confpath) && std::filesystem::file_size(confpath) > 0) { + // Copy the file to the current directory + ifstream src(confpath); + ofstream dst("powheg.input"); + int seed = gRandom->Integer(900000000); + bool isseed = false; + bool isnumevts = false; + if (mCurrFile == mNFiles - 1 && getTotalNEvents() % mNMaxPerJob != 0) { + mNMaxPerJob = getTotalNEvents() % mNMaxPerJob; + } + std::string line; + while (std::getline(src, line)) + { + if (line.find("iseed") != std::string::npos) + { + // Set the seed to the random number + line = "iseed " + std::to_string(seed); + isseed = true; + } + if (line.find("numevts") != std::string::npos) + { + // Set the number of events to the number of events defined in the configuration + line = "numevts " + std::to_string(mNMaxPerJob); + // replace it in the file + isnumevts = true; + } + dst << line << std::endl; + } + if (!isseed) + { + dst << "iseed " << seed << std::endl; + } + if (!isnumevts) + { + dst << "numevts " << mNMaxPerJob << std::endl; + } + if (parallel) + { + dst << "manyseeds 1" << std::endl; // Enables the usage of pwgseeds.dat file to set the seed in parallel mode + dst << "parallelstage 4" << std::endl; // Allows event generation based on pre-generated POWHEG configuration files (needed for certain configurations) + } + src.close(); + dst.close(); + } else { + LOG(fatal) << "POWHEG configuration file not found or empty" << std::endl; + return false; + } + return true; + } + + Bool_t startPOW() + { + if(mCurrFile == 1) { + if (!confMaker(mPowhegConf, true)) { + LOG(fatal) << "Failed to edit POWHEG configuration with parallelisation"; + return false; + } + } + LOG(info) << "Starting POWHEG job " << mCurrFile+1 << " of " << mNFiles; + system(("echo " + std::to_string(mCurrFile - 1) + " | " + mExePOW).c_str()); + return true; + } + + Bool_t checkEOF() { + // Check if the POWHEG generation is done + int result = system(("grep -q /LesHouchesEvents " + mLHEFoutput).c_str()); + if (result == 0) + { + return true; + } else { + return false; + } + } + + Bool_t POWchecker() { + // Check if the POWHEG events file exists + LOG(info) << "Waiting for " << mLHEFoutput << " to exist"; + while (!std::filesystem::exists(mLHEFoutput.c_str())) + { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + LOG(info) << "POWHEG events file for job " << mCurrFile << " found"; + while (!checkEOF()) + { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + LOG(info) << "POWHEG events ready"; + return true; + } + + // Check for the POWHEG events file existance + Bool_t Init() override + { + // Check if the POWHEG events file exists + if(POWchecker()) { + return GeneratorPythia8::Init(); + } else { + return false; + } + }; + + // Set Generator ReadEvent to wait for the POWHEG events + Bool_t generateEvent() override + { + /** Reinitialise when EOF is reached **/ + if (mPythia.info.atEndOfFile()) + { + if(mCurrFile == 0) + { + mPythia.readString("Beams:newLHEFsameInit = on"); + // Create pwgseeds.dat file with a random seed for each line + std::ofstream seedfile("pwgseeds.dat"); + for (int i = 0; i < mNFiles - 1; i++) + { + seedfile << gRandom->Integer(900000000) << std::endl; + } + seedfile.close(); + } + mCurrFile++; + mLHEFoutput = Form("pwgevents-%04d.lhe", mCurrFile - 1); + mPythia.readString(Form("Beams:LHEF = %s", mLHEFoutput.c_str())); + if(!startPOW()) + { + return false; + } + if (POWchecker()) { + // If Pythia fails to initialize, exit with error. + if (!mPythia.init()) + { + LOG(fatal) << "Failed to init \'Pythia8\': init returned with error"; + return false; + } + } + } + return GeneratorPythia8::generateEvent(); + }; + +private: + const std::vector mPowhegGen = {"pwhg_main_hvq", "pwhg_main_W", "pwhg_main_Z", "pwhg_main_dijet", "pwhg_main_directphoton"}; // POWHEG executables + short int mNFiles = 1; + short int mCurrFile = 0; + std::string mExePOW = ""; + std::string mPowhegConf = ""; + std::string mLHEFoutput = "pwgevents.lhe"; + int mNMaxPerJob = 50; +}; + +} // namespace eventgen +} // namespace o2 + +/** generator instance and settings **/ + +FairGenerator *getGeneratorJEPythia8POWHEG(std::string powhegconf = "pwgpath", std::string pythia8conf = "", short int type = 0, int maxEventsPerJob = 1e4) +{ + using namespace o2::eventgen; + // Expand paths for the POWHEG configuration file + powhegconf = o2::utils::expandShellVarsInFileName(powhegconf); + LOG(info) << "Using POWHEG configuration file: " << powhegconf; + auto myGen = new GeneratorJEPythia8POWHEG(powhegconf, type, maxEventsPerJob); + if(GeneratorPythia8Param::Instance().config.empty() && pythia8conf.empty()) { + LOG(fatal) << "No configuration provided for Pythia8"; + } + else if (!pythia8conf.empty()) + { + // Force the configuration for Pythia8 in case it is provided. + // Useful for setting up the generator in the hybrid configuration + // making it more versatile and not relying entirely on the parameters provided + // by ini file or static parameters + myGen->setConfig(pythia8conf); + } + return myGen; +} diff --git a/MC/config/PWGGAJE/external/generator/parametrisedJetModel/generator_pythia8_box_parametrisedModel_pythia6Fragmentation.C b/MC/config/PWGGAJE/external/generator/parametrisedJetModel/generator_pythia8_box_parametrisedModel_pythia6Fragmentation.C new file mode 100644 index 000000000..69ae93711 --- /dev/null +++ b/MC/config/PWGGAJE/external/generator/parametrisedJetModel/generator_pythia8_box_parametrisedModel_pythia6Fragmentation.C @@ -0,0 +1,462 @@ +#if !defined(__CLING__) || defined(__ROOTCLING__) + +#include "FairGenerator.h" +#include "FairPrimaryGenerator.h" +#include "Generators/GeneratorPythia8.h" +#include "TDatabasePDG.h" +#include "TF1.h" +#include "TGrid.h" +#include "TMath.h" +#include "TParticlePDG.h" +#include "TRandom3.h" +#include +#endif + +#include "Pythia8/Pythia.h" +#include +#include + +R__LOAD_LIBRARY(libpythia6) +R__LOAD_LIBRARY(libGeneratorParam) +#include "TPythia6.h" + +using namespace Pythia8; + +// #include "SimulationDataFormat/ParticleStatus.h" +// #include "SimulationDataFormat/MCEventHeader.h" + +// Input to simulation: +// inputFilePathName file is expected to be a json file with the structure like +// so: +// { +// "simLog": false, +// "sglGenRAA": 0.45, +// "sglGenTAA": 23.38, +// "sglCutoffSteepness": 15, +// "sglCutoffAbscissa": 10, +// "fallSpecterSlopeLog": 10.833274, +// "fallSpecterAffinePowerConstantTerm": -5.476804, +// "fallSpecterAffinePowerSlope": 0.001110, +// "bkgAveragePt": 0.670, +// "collMultPowerLawAmplitude": ?, +// "collMultPowerLawExponent": ?, +// "collMultMin": ?, +// "collMultMax": ? +// } +// can be uploaded to grid using for example: alien.py cp +// file:/local/path/parametrisedModel_PbPb_5p36TeV_cent0010.json +// alien:/path/on/alien/parametrisedModel_PbPb_5p36TeV_cent0010.json + +class GeneratorParametrisedJetModel : public o2::eventgen::GeneratorPythia8 { +public: + /// constructor + GeneratorParametrisedJetModel(std::string inputSimParametersFileName, bool generateUE, bool generateSignal = true) : mInputSimParametersFileName{inputSimParametersFileName}, mGenerateUE{generateUE}, mGenerateSignal{generateSignal} { + + if (!generateUE && !generateSignal) { + cout << "Both the UE and the signal generation should not be off at the same time" << endl; + exit(1); + } + + // old: retrieving from GRID; very flexible but to be avoided because poor reproduceability if file is changed etc for CI tests and analyses + // std::string inputFilePathName = "alien://" + inputSimParametersPath + inputSimParametersFileName; + // if (!gGrid) { + // TGrid::Connect("alien://"); + // if (!gGrid) { + // LOG(fatal) << "AliEn connection failed, check token."; + // exit(1); + // } + // } + // // fetch and copy the .json file to the sim work directory + // std::string outputPath = "./"; + // TString aliencp = Form("alien_cp alien://%s%s file:%s%s", inputSimParametersPath.c_str(), + // inputSimParametersFileName.c_str(), outputPath.c_str(), + // inputSimParametersFileName.c_str()); // an internal operation in ROOT that discards the + // // JSON file directly (smaller than 300 bytes and if + // // a bit bigger it would discard the file because it + // // understands it's not a ROOT file), thus one + // // cannot use TFile::Cp() + // if (gSystem->Exec(aliencp.Data()) != 0) { + // cout << "Error: Sim parameters .json file " << inputFilePathName << " does not exist!" << endl; + // exit(1); + // } + std::string O2DPG_ROOT(getenv("O2DPG_MC_CONFIG_ROOT")); + std::string inputSimParametersPath = O2DPG_ROOT+"/MC/config/PWGGAJE/external/generator/parametrisedJetModel/paramConfigs/"; + std::string inputFilePathName = inputSimParametersPath + inputSimParametersFileName; + // open the file + std::FILE *fjson = std::fopen(inputFilePathName.c_str(), "r"); + if (!fjson) { + cout << "Could not open sim parameters file " << inputFilePathName << endl; + exit(1); + } + + // create streamer + char readBuffer[65536]; + rapidjson::FileReadStream jsonStream(fjson, readBuffer, sizeof(readBuffer)); + + // parse the json file + rapidjson::Document jsonDocument; + jsonDocument.ParseStream(jsonStream); + + // is it a proper json document? + if (jsonDocument.HasParseError()) { + cout << "Check the sim parameters file! There is a problem with the format!" << endl; + exit(1); + } + + // are the parameter names as expected? + for (const std::string ¶meterName : mConfigurableSimParameterNames) { + if (!jsonDocument.HasMember(parameterName.c_str())) { + cout << "Check the sim parameters file! Item " << parameterName << " is missing!" << endl; + exit(1); + } + } + + // write log of simulation? + std::string writeLog("simLog"); + if (!jsonDocument.HasMember(writeLog.c_str())) { + cout << "Check the sim parameters file! Item " << writeLog.c_str() << " is missing!" << endl; + exit(1); + } + mDebug = jsonDocument[writeLog.c_str()].GetBool(); + + // get parameters for sim + mSglGenRAA = jsonDocument[mConfigurableSimParameterNames.at(0).c_str()].GetDouble(); + mSglGenTAA = jsonDocument[mConfigurableSimParameterNames.at(1).c_str()].GetDouble(); + msglCutoffSteepness = jsonDocument[mConfigurableSimParameterNames.at(2).c_str()].GetDouble(); + mSglCutoffAbscissa = jsonDocument[mConfigurableSimParameterNames.at(3).c_str()].GetDouble(); + mFallSpecterSlopeLog = jsonDocument[mConfigurableSimParameterNames.at(4).c_str()].GetDouble(); + mFallSpecterAffinePowerConstantTerm = jsonDocument[mConfigurableSimParameterNames.at(5).c_str()].GetDouble(); + mFallSpecterAffinePowerSlope = jsonDocument[mConfigurableSimParameterNames.at(6).c_str()].GetDouble(); + mBkgAveragePt = jsonDocument[mConfigurableSimParameterNames.at(7).c_str()].GetDouble(); + mCollMultPowerLawAmplitude = + jsonDocument[mConfigurableSimParameterNames.at(8).c_str()].GetDouble(); + mCollMultPowerLawExponent = + jsonDocument[mConfigurableSimParameterNames.at(9).c_str()].GetDouble(); + mCollMultMin = + jsonDocument[mConfigurableSimParameterNames.at(10).c_str()].GetInt(); + mCollMultMax = + jsonDocument[mConfigurableSimParameterNames.at(11).c_str()].GetInt(); + + // clean up + std::fclose(fjson); + + cout << "param retrieved: mSglGenRAA = " << mSglGenRAA << endl; + cout << "param retrieved: mSglGenTAA = " << mSglGenTAA << endl; + cout << "param retrieved: msglCutoffSteepness = " << msglCutoffSteepness << endl; + cout << "param retrieved: mSglCutoffAbscissa = " << mSglCutoffAbscissa << endl; + cout << "param retrieved: mFallSpecterSlopeLog = " << mFallSpecterSlopeLog << endl; + cout << "param retrieved: mFallSpecterAffinePowerConstantTerm = " << mFallSpecterAffinePowerConstantTerm << endl; + cout << "param retrieved: mFallSpecterAffinePowerSlope = " << mFallSpecterAffinePowerSlope << endl; + cout << "param retrieved: mBkgAveragePt = " << mBkgAveragePt << endl; + cout << "param retrieved: mCollMultPowerLawAmplitude = " + << mCollMultPowerLawAmplitude << endl; + cout << "param retrieved: mCollMultPowerLawExponent = " + << mCollMultPowerLawExponent << endl; + cout << "param retrieved: mcollMultMin = " << mCollMultMin << endl; + cout << "param retrieved: mcollMultMax = " << mCollMultMax << endl; + + // thermal background pdf + mBoltzmannPDF = new TF1("f1", "[0]*[0]*x*exp(-[0]*x)", mBkgGenPtMin, mPtInfinity); + mBoltzmannPDF->SetParameter(0, 2. / mBkgAveragePt); + + // collision multiplicity pdf + mCollisionMultPDF = + new TF1("f1", "exp([0])*pow(x,[1])", mCollMultMin, mCollMultMax); + mCollisionMultPDF->SetParameter(0, mCollMultPowerLawAmplitude); + mCollisionMultPDF->SetParameter(1, mCollMultPowerLawExponent); + + // jet signal pdf + // this thesis says that the jet distrib used to sample parton pt is + // actually full jet -> solves neutral particle fragments issue (better than + // scaling) https://drupal.star.bnl.gov/STAR/files/phd_thesis_rusnak.pdf for + // the fragments radiating outside of the jet radius issue: instead of using + // this PYTHIA distrib directly as the truth distrib in unfolding closure, + // instead we do the same sim+jet reco, but without the bkg; so need to have + // the option to run with and without UE bkg probably should use biggest + // Radius one can find with good stats for the pp PYTHIA + mJetYieldFit = new TF1("f2", "[0]*[1]*exp(-exp(-[2]*(x-[3]))) * exp([4])*pow(x, [5]+[6]*x)", 0, mPtInfinity); // RAA * TAA * sigmoid(cutoff at [2]) * fit to fulljetSpectrum in pp PYTHIA + mJetYieldFit->SetParameter(0, mSglGenRAA); // rAA (single value for all pt) + mJetYieldFit->SetParameter(1, mSglGenTAA); // + mJetYieldFit->SetParameter(2, msglCutoffSteepness); // steepness for the cutoff shape; higher is + // steeper, but looks more and more like a + // Heavyside step function as it gets to 50 or 100 + mJetYieldFit->SetParameter(3, mSglCutoffAbscissa); // cutoffpoint of hard population, in GeV/c + mJetYieldFit->SetParameter(4, mFallSpecterSlopeLog); // falling spectrum ln() of proportionality factor + mJetYieldFit->SetParameter(5, mFallSpecterAffinePowerConstantTerm); // falling spectrum affine power: constant term + mJetYieldFit->SetParameter(6, mFallSpecterAffinePowerSlope); // falling spectrum affine power: proportionality factor + // values from full jet spectra fit of R=0.6 jets in + // https://journals.aps.org/prc/abstract/10.1103/PhysRevC.101.034911; + // hepdata link: https://www.hepdata.net/record/ins1755387 done using TF1* + // fitFullJetSpectrum = new TF1(fname, "exp([0]) * pow(x, [1] + [2]*x)", + // fitMin, fitMax); fit, parameters first initialised by fitting + // log(fullSpectrum) with log(fitFullJetSpectrum tf1); + + mNJetsAverage = mJetYieldFit->Integral(mSglCutoffAbscissa, mPtInfinity); // careful, this script assumes the mJetYieldFit is the + // pt-differential yield, integrated over eta + + mPythia.init(); // Initialize + } + + /// Destructor + ~GeneratorParametrisedJetModel() = default; + + Bool_t generateEvent() override { + if (mDebug) { + cout << "##########################################################################################################" << endl; + cout << "###################################### Beginning of generateEvent() ######################################" << endl; + cout << "##########################################################################################################" << endl; + } + + mPythia.event.reset(); + + /////////////////////////////////////////////// + ////////////////// Jet signal ///////////////// + /////////////////////////////////////////////// + + if (mGenerateSignal) { + int nJets = gRandom->Poisson(mNJetsAverage); + + if (mDebug) { + cout << "####################### creating partons signal #######################" << endl; + cout << "signal: count " << nJets << " for mNJetsAverage = " << mNJetsAverage << "" << endl; + } + + TClonesArray *genParticlesArray = new TClonesArray("TParticle", 1000); + + int particleCountCurrent = 1; // counts the system fake particle pdg 90 + for (int iJet{0}; iJet < nJets; ++iJet) { + genParticlesArray->Delete(); + + const bool isQuark = gRandom->Uniform(0, 1) > 1. / 3 ? true : false; // ratio quarks:gluons = 2:1 + const int pdgQuark = gRandom->Uniform(0, 1) > 1. / 2 ? mPdgQuarkU : mPdgQuarkD; // half and half for u and d quarks + const int pdgJet = (int)isQuark * pdgQuark + (1 - (int)isQuark) * mPdgGluon; + const double sglMass = TDatabasePDG::Instance()->GetParticle(pdgJet)->Mass(); + const double sglPt = mJetYieldFit->GetRandom(mSglCutoffAbscissa, mPtInfinity); + const double sglEta = gRandom->Uniform(mGenMinEta, mGenMaxEta); + const double sglPhi = gRandom->Uniform(0, o2::constants::math::TwoPI); + const double sglPx{sglPt * std::cos(sglPhi)}; + const double sglPy{sglPt * std::sin(sglPhi)}; + const double sglPz{sglPt * std::sinh(sglEta)}; + const double sglEt{std::hypot(std::hypot(sglPt, sglPz), sglMass)}; + + Particle myJet; + myJet.id(pdgJet); + myJet.status(23); + myJet.px(sglPx); + myJet.py(sglPy); + myJet.pz(sglPz); + myJet.e(sglEt); + myJet.m(sglMass); + myJet.xProd(0); + myJet.yProd(0); + myJet.zProd(0); + int jetCol = 101 + 2 * iJet; // each jet gets a different colour and acolour value, + // so that the pythia knows they're distinct colour + // lines, i.e. different strings + int jetACol = 101 + 2 * iJet + 1; + + if (mDebug) { + cout << "-- || jet parton #" << iJet + << ", index=" << mPythia.event.back().index() + 1 + << ": isQuark = " << isQuark << ", pdg = " << pdgJet + << ", pt = " << sglPt << ", mass = " << sglMass << endl; + } + + auto pythia6Event = TPythia6::Instance(); + + int lineNumber = 0; // line number seems to be index of particle in array; set to 0 in + // PM code? but doc says it runs Pyexec() right after; iJet doesn't + // work for ijet=1+; set to 0 and it seems to work + double sglTheta = 2.0 * std::atan(std::exp(-1 * sglEta)); + pythia6Event->Py1ent(lineNumber, pdgJet, sglEt, sglTheta, sglPhi); + + int final = pythia6Event->ImportParticles(genParticlesArray, "Final"); // only saves final state particles; "All" would instead give all the particles + int nConstituents = genParticlesArray->GetEntries(); + + mPythia.event.append(myJet); + for (int iParticle = 0; iParticle < nConstituents; ++iParticle) { + TParticle *tParticle = (TParticle *)genParticlesArray->At(iParticle); + + Particle pythiaParticle; + pythiaParticle.id(tParticle->GetPdgCode()); + pythiaParticle.status(tParticle->GetStatusCode()); // in pythia6 all the particles that are not + // the initial partons have status code 1; + // this is because apparently pythia6 does + // not decay them automatically yet; they + // are by no means all stable particles; but + // we do not care for this study + pythiaParticle.px(tParticle->Px()); + pythiaParticle.py(tParticle->Py()); + pythiaParticle.pz(tParticle->Pz()); + pythiaParticle.e(tParticle->Energy()); + pythiaParticle.m(tParticle->GetMass()); + pythiaParticle.xProd(tParticle->Vx()); + pythiaParticle.yProd(tParticle->Vy()); + pythiaParticle.zProd(tParticle->Vz()); + pythiaParticle.mother1(particleCountCurrent); // particleCountCurrent is the offset to account for existing IDs of constituents of previous jets. + // Not saving the actual mother ID because ImportParticles(genParticlesArray, "Final") only saves the final + // particles, and the mother id still refer to particles not saved in + // genParticlesArray a priori the mother-daughter links aren't needed + // for the closure test this will be used for; keeping the initial + // parton as mother for now + + mPythia.event.append(pythiaParticle); + } + particleCountCurrent += nConstituents + 1; // +1 is for the jet parton itself, which is not in the final state + } + delete genParticlesArray; + + if (mDebug) { + mPythia.event.list(); + } + } + + /////////////////////////////////////////////// + // Underlying Event (UE): thermal background // + /////////////////////////////////////////////// + + if (mGenerateUE) { + int nHardParticles = mPythia.event.size() - 1; // number of particles after hadronisation of jet partons and pruning, -1 because of system particle with pdg id 90 + int sign = 1; + if (mDebug) { + cout << "####################### Adding Thermal Background #######################" << endl; + } + + int mCollTotalMultWithBkg = 0; + if (std::abs(mCollMultMax - mCollMultMin) == 0) { + mCollTotalMultWithBkg = mCollMultMin; + } else { + mCollTotalMultWithBkg = + mCollisionMultPDF->GetRandom(mCollMultMin, mCollMultMax); + } + + if (mDebug) { + cout << "mCollTotalMultWithBkg = " << mCollTotalMultWithBkg << endl; + } + + int nBkgParticles = (mCollTotalMultWithBkg - nHardParticles) > 0 + ? mCollTotalMultWithBkg - nHardParticles + : 0; + + for (int iBkg{0}; iBkg < nBkgParticles; ++iBkg) { + const double bkgPt = mBoltzmannPDF->GetRandom(mBkgGenPtMin, mPtInfinity); + const double bkgEta = gRandom->Uniform(mGenMinEta, mGenMaxEta); + const double bkgPhi = gRandom->Uniform(0, o2::constants::math::TwoPI); + const double bkgPx{bkgPt * std::cos(bkgPhi)}; + const double bkgPy{bkgPt * std::sin(bkgPhi)}; + const double bkgPz{bkgPt * std::sinh(bkgEta)}; + const double bkgEt{std::hypot(std::hypot(bkgPt, bkgPz), mBkgMass)}; + sign *= gRandom->Uniform(0, 1) > 0.5 ? 1 : -1; + + Particle myParticle; + myParticle.id(sign * + mPdgPion); // let's make them pions until further notice + myParticle.status(201); // 201+ free to use by anybody; using 201 to know if's from background + // https://pythia.org/latest-manual/ParticleProperties.html; + myParticle.px(bkgPx); + myParticle.py(bkgPy); + myParticle.pz(bkgPz); + myParticle.e(bkgEt); + myParticle.m(mBkgMass); + myParticle.xProd(0); + myParticle.yProd(0); + myParticle.zProd(0); + + if (iBkg % 100 == 0 && mDebug) { + cout << "------- ||||| bkg particle " << iBkg << ": pt = " << bkgPt << endl; + } + + mPythia.event.append(myParticle); + } + } + + if (mDebug) { + cout << "##########################################################################################################" << endl; + cout << "######################################## End of generateEvent() ##########################################" << endl; + cout << "##########################################################################################################" << endl; + } + return true; + } + + //__________________________________________________________________ + +private: + bool mDebug = false; // setting to true will display particle lists + std::string mInputSimParametersFileName; // input path and file name of .json used to read simulation parameters + + //////////////////////////////////////////////// + ///////// Common signal and background parameters //////// + //////////////////////////////////////////////// + + const double mPtInfinity = 300; // maximum pt (in GeV/c) for generated particles, and upper pT limit for integral and TF1 purposes; too high and GetRandom struggles + const double mGenMinEta = -0.9; /// minimum pseudorapidity for generated particles + const double mGenMaxEta = + +0.9; /// maximum pseudorapidity for generated particles + + TF1 *mCollisionMultPDF; /// TF1 to store pdf function from which collision + /// multiplicity is drawn + double mCollMultPowerLawAmplitude; /// collision total multiplicity: power law + /// amplitude of the PDF + double mCollMultPowerLawExponent; /// collision total multiplicity: power law + /// exponent of the PDF + int mCollMultMin; /// collision total multiplicity: minimum abscissa of the + /// PDF + int mCollMultMax; /// collision total multiplicity: maximum abscissa of the + /// PDF + + bool mGenerateSignal = true; /// boolean to request (or not) the generation of the jet signal + bool mGenerateUE = false; /// boolean to request (or not) embedding of the jet signal inside underlying event modelled by a thermal background; if mGenerateSignal = false, only the UE is generated + const std::vector mConfigurableSimParameterNames = { + "sglGenRAA", + "sglGenTAA", + "sglCutoffSteepness", + "sglCutoffAbscissa", + "fallSpecterSlopeLog", + "fallSpecterAffinePowerConstantTerm", + "fallSpecterAffinePowerSlope", + "bkgAveragePt", + "collMultPowerLawAmplitude", + "collMultPowerLawExponent", + "collMultMin", + "collMultMax"}; + + ///////////////////////////////////////////// + /////// Thermal background parameters /////// + ///////////////////////////////////////////// + + // bkg fit + TF1 *mBoltzmannPDF; /// TF1 to store pdf function from which pt is drawn + double mBkgAveragePt; + const double mBkgGenPtMin = 0; /// minimum pt (in GeV/c) for generated particles + const double mBkgMass = 0; /// particle mass [GeV/c^2] + + // bkg pdg code + const int mPdgPion = 211; /// particle pdg code of pion check that is not + /// defined somewhere already + + ///////////////////////////////////// + /////// Jet signal parameters /////// + ///////////////////////////////////// + + // signal fit + double mSglGenRAA; + double mSglGenTAA; + double msglCutoffSteepness; /// steepness for the cutoff shape; higher is steeper, but looks more and more like a Heavyside step function as it gets to 50 or 100 + double mSglCutoffAbscissa; /// minimum pt (in GeV/c) for jet signal distribution; it's a smooth cutoff + double mFallSpecterSlopeLog; + double mFallSpecterAffinePowerConstantTerm; + double mFallSpecterAffinePowerSlope; + TF1 *mJetYieldFit; /// TF1 to store pdf function from which pt is drawn + double mNJetsAverage; /// average number of jets in a collision + + // signal pdg codes + const int mPdgQuarkU = 1; /// particle pdg code of quark u + const int mPdgQuarkD = 2; /// particle pdg code of quark d + const int mPdgGluon = 21; /// particle pdg code of gluon +}; + +///___________________________________________________________ +FairGenerator *generateParametrisedJetModel(std::string inputSimParametersFileName, + bool generateUE, bool generateSignal = true) { + return new GeneratorParametrisedJetModel(inputSimParametersFileName, generateUE, generateSignal); +} \ No newline at end of file diff --git a/MC/config/PWGGAJE/external/generator/parametrisedJetModel/paramConfigs/parametrisedModel_PbPb_5p36TeV_cent0010.json b/MC/config/PWGGAJE/external/generator/parametrisedJetModel/paramConfigs/parametrisedModel_PbPb_5p36TeV_cent0010.json new file mode 100644 index 000000000..78ed15e99 --- /dev/null +++ b/MC/config/PWGGAJE/external/generator/parametrisedJetModel/paramConfigs/parametrisedModel_PbPb_5p36TeV_cent0010.json @@ -0,0 +1,15 @@ +{ + "simLog": false, + "sglGenRAA": 0.45, + "sglGenTAA": 23.38, + "sglCutoffSteepness": 15, + "sglCutoffAbscissa": 4, + "fallSpecterSlopeLog": 6.619076, + "fallSpecterAffinePowerConstantTerm": -3.918165, + "fallSpecterAffinePowerSlope": -0.002032, + "bkgAveragePt": 0.764567, + "collMultPowerLawAmplitude": 20.66, + "collMultPowerLawExponent": -1.26971, + "collMultMin": 1681, + "collMultMax": 2400 +} \ No newline at end of file diff --git a/MC/config/PWGGAJE/external/generator/parametrisedJetModel/paramConfigs/parametrisedModel_PbPb_5p36TeV_cent5080.json b/MC/config/PWGGAJE/external/generator/parametrisedJetModel/paramConfigs/parametrisedModel_PbPb_5p36TeV_cent5080.json new file mode 100644 index 000000000..891e7b5fb --- /dev/null +++ b/MC/config/PWGGAJE/external/generator/parametrisedJetModel/paramConfigs/parametrisedModel_PbPb_5p36TeV_cent5080.json @@ -0,0 +1,15 @@ +{ + "simLog": false, + "sglGenRAA": 0.8, + "sglGenTAA": 0.6933, + "sglCutoffSteepness": 15, + "sglCutoffAbscissa": 4, + "fallSpecterSlopeLog": 6.619076, + "fallSpecterAffinePowerConstantTerm": -3.918165, + "fallSpecterAffinePowerSlope": -0.002032, + "bkgAveragePt": 0.732677, + "collMultPowerLawAmplitude": 16.43, + "collMultPowerLawExponent": -0.683784, + "collMultMin": 47, + "collMultMax": 302 +} \ No newline at end of file diff --git a/MC/config/PWGGAJE/external/powheg/powheg_beauty_F05.input b/MC/config/PWGGAJE/external/powheg/powheg_beauty_F05.input new file mode 100644 index 000000000..7418cbf9a --- /dev/null +++ b/MC/config/PWGGAJE/external/powheg/powheg_beauty_F05.input @@ -0,0 +1,37 @@ +! Random number generator initializing parameters +iseed 0 ! initialize random number sequence + +! Heavy flavour production parameters +numevts 100000 ! number of events to be generated +ih1 1 ! hadron 1 type (1: proton; -1: antiproton) +ih2 1 ! hadron 2 type (1: proton; -1: antiproton) +!ndns1 131 ! pdf for hadron 1 (when using the hvqpdf package, hvqpdf numbering) +!ndns2 131 ! pdf for hadron 2 (when using the hvqpdf package, hvqpdf numbering) +lhans1 11000 ! pdf set for hadron 1 (LHAGLUE number) (11000 = CT10NLO, 10550 = CTEQ66) +lhans2 11000 ! pdf set for hadron 2 (LHAGLUE number) (11000 = CT10NLO, 10550 = CTEQ66) +ebeam1 6800 ! energy of beam 1 in GeV +ebeam2 6800 ! energy of beam 2 in GeV +qmass 4.75 ! mass of heavy quark in GeV +facscfact 0.5 ! factorization scale factor: mufact=muref*facscfact +renscfact 1 ! renormalization scale factor: muren=muref*renscfact +!fixedscale 1 ! use ref. scale=qmass (default 0, use running scale) + +! Parameters to allow or not the use of stored data +use-old-grid 1 ! if 1 use old grid if file pwggrids.dat is present (# 1: regenerate) +use-old-ubound 1 ! if 1 use norm of upper bounding function stored in pwgubound.dat, if present; # 1: regenerate + +! Parameters that control the grid for Born variables generation +ncall1 10000 ! number of calls for initializing the integration grid +itmx1 5 ! number of iterations for initializing the integration grid +ncall2 100000 ! number of calls for computing the integral and finding upper bound +itmx2 5 ! number of iterations for computing the integral and finding upper bound +foldcsi 5 ! number of folds on x integration +foldy 5 ! number of folds on y integration +foldphi 1 ! number of folds on phi integration +nubound 500000 ! number of bbarra calls to setup norm of upper bounding function +iymax 1 ! <= 10, normalization of upper bounding function in iunorm X iunorm square in y, log(m2qq) +ixmax 1 ! <= 10, normalization of upper bounding function in iunorm X iunorm square in y, log(m2qq) +xupbound 2 ! increase upper bound for radiation generation +bornktmin 0 ! (default 0d0) Generation cut: minimum kt in underlying Born +bornsuppfact 0 ! (default 0d0) Mass parameter for Born suppression factor. If < 0 suppfact = 1 +storemintupb 0 ! store function calls in binary file for more precise generation diff --git a/MC/config/PWGGAJE/external/powheg/powheg_beauty_F05_R05.input b/MC/config/PWGGAJE/external/powheg/powheg_beauty_F05_R05.input new file mode 100644 index 000000000..4dc7895b2 --- /dev/null +++ b/MC/config/PWGGAJE/external/powheg/powheg_beauty_F05_R05.input @@ -0,0 +1,37 @@ +! Random number generator initializing parameters +iseed 0 ! initialize random number sequence + +! Heavy flavour production parameters +numevts 100000 ! number of events to be generated +ih1 1 ! hadron 1 type (1: proton; -1: antiproton) +ih2 1 ! hadron 2 type (1: proton; -1: antiproton) +!ndns1 131 ! pdf for hadron 1 (when using the hvqpdf package, hvqpdf numbering) +!ndns2 131 ! pdf for hadron 2 (when using the hvqpdf package, hvqpdf numbering) +lhans1 11000 ! pdf set for hadron 1 (LHAGLUE number) (11000 = CT10NLO, 10550 = CTEQ66) +lhans2 11000 ! pdf set for hadron 2 (LHAGLUE number) (11000 = CT10NLO, 10550 = CTEQ66) +ebeam1 6800 ! energy of beam 1 in GeV +ebeam2 6800 ! energy of beam 2 in GeV +qmass 4.75 ! mass of heavy quark in GeV +facscfact 0.5 ! factorization scale factor: mufact=muref*facscfact +renscfact 0.5 ! renormalization scale factor: muren=muref*renscfact +!fixedscale 1 ! use ref. scale=qmass (default 0, use running scale) + +! Parameters to allow or not the use of stored data +use-old-grid 1 ! if 1 use old grid if file pwggrids.dat is present (# 1: regenerate) +use-old-ubound 1 ! if 1 use norm of upper bounding function stored in pwgubound.dat, if present; # 1: regenerate + +! Parameters that control the grid for Born variables generation +ncall1 10000 ! number of calls for initializing the integration grid +itmx1 5 ! number of iterations for initializing the integration grid +ncall2 100000 ! number of calls for computing the integral and finding upper bound +itmx2 5 ! number of iterations for computing the integral and finding upper bound +foldcsi 5 ! number of folds on x integration +foldy 5 ! number of folds on y integration +foldphi 1 ! number of folds on phi integration +nubound 500000 ! number of bbarra calls to setup norm of upper bounding function +iymax 1 ! <= 10, normalization of upper bounding function in iunorm X iunorm square in y, log(m2qq) +ixmax 1 ! <= 10, normalization of upper bounding function in iunorm X iunorm square in y, log(m2qq) +xupbound 2 ! increase upper bound for radiation generation +bornktmin 0 ! (default 0d0) Generation cut: minimum kt in underlying Born +bornsuppfact 0 ! (default 0d0) Mass parameter for Born suppression factor. If < 0 suppfact = 1 +storemintupb 0 ! store function calls in binary file for more precise generation diff --git a/MC/config/PWGGAJE/external/powheg/powheg_beauty_F2.input b/MC/config/PWGGAJE/external/powheg/powheg_beauty_F2.input new file mode 100644 index 000000000..753049c7d --- /dev/null +++ b/MC/config/PWGGAJE/external/powheg/powheg_beauty_F2.input @@ -0,0 +1,37 @@ +! Random number generator initializing parameters +iseed 0 ! initialize random number sequence + +! Heavy flavour production parameters +numevts 100000 ! number of events to be generated +ih1 1 ! hadron 1 type (1: proton; -1: antiproton) +ih2 1 ! hadron 2 type (1: proton; -1: antiproton) +!ndns1 131 ! pdf for hadron 1 (when using the hvqpdf package, hvqpdf numbering) +!ndns2 131 ! pdf for hadron 2 (when using the hvqpdf package, hvqpdf numbering) +lhans1 11000 ! pdf set for hadron 1 (LHAGLUE number) (11000 = CT10NLO, 10550 = CTEQ66) +lhans2 11000 ! pdf set for hadron 2 (LHAGLUE number) (11000 = CT10NLO, 10550 = CTEQ66) +ebeam1 6800 ! energy of beam 1 in GeV +ebeam2 6800 ! energy of beam 2 in GeV +qmass 4.75 ! mass of heavy quark in GeV +facscfact 2 ! factorization scale factor: mufact=muref*facscfact +renscfact 1 ! renormalization scale factor: muren=muref*renscfact +!fixedscale 1 ! use ref. scale=qmass (default 0, use running scale) + +! Parameters to allow or not the use of stored data +use-old-grid 1 ! if 1 use old grid if file pwggrids.dat is present (# 1: regenerate) +use-old-ubound 1 ! if 1 use norm of upper bounding function stored in pwgubound.dat, if present; # 1: regenerate + +! Parameters that control the grid for Born variables generation +ncall1 10000 ! number of calls for initializing the integration grid +itmx1 5 ! number of iterations for initializing the integration grid +ncall2 100000 ! number of calls for computing the integral and finding upper bound +itmx2 5 ! number of iterations for computing the integral and finding upper bound +foldcsi 5 ! number of folds on x integration +foldy 5 ! number of folds on y integration +foldphi 1 ! number of folds on phi integration +nubound 500000 ! number of bbarra calls to setup norm of upper bounding function +iymax 1 ! <= 10, normalization of upper bounding function in iunorm X iunorm square in y, log(m2qq) +ixmax 1 ! <= 10, normalization of upper bounding function in iunorm X iunorm square in y, log(m2qq) +xupbound 2 ! increase upper bound for radiation generation +bornktmin 0 ! (default 0d0) Generation cut: minimum kt in underlying Born +bornsuppfact 0 ! (default 0d0) Mass parameter for Born suppression factor. If < 0 suppfact = 1 +storemintupb 0 ! store function calls in binary file for more precise generation diff --git a/MC/config/PWGGAJE/external/powheg/powheg_beauty_F2_R2.input b/MC/config/PWGGAJE/external/powheg/powheg_beauty_F2_R2.input new file mode 100644 index 000000000..f4ae5ef68 --- /dev/null +++ b/MC/config/PWGGAJE/external/powheg/powheg_beauty_F2_R2.input @@ -0,0 +1,37 @@ +! Random number generator initializing parameters +iseed 0 ! initialize random number sequence + +! Heavy flavour production parameters +numevts 100000 ! number of events to be generated +ih1 1 ! hadron 1 type (1: proton; -1: antiproton) +ih2 1 ! hadron 2 type (1: proton; -1: antiproton) +!ndns1 131 ! pdf for hadron 1 (when using the hvqpdf package, hvqpdf numbering) +!ndns2 131 ! pdf for hadron 2 (when using the hvqpdf package, hvqpdf numbering) +lhans1 11000 ! pdf set for hadron 1 (LHAGLUE number) (11000 = CT10NLO, 10550 = CTEQ66) +lhans2 11000 ! pdf set for hadron 2 (LHAGLUE number) (11000 = CT10NLO, 10550 = CTEQ66) +ebeam1 6800 ! energy of beam 1 in GeV +ebeam2 6800 ! energy of beam 2 in GeV +qmass 4.75 ! mass of heavy quark in GeV +facscfact 2 ! factorization scale factor: mufact=muref*facscfact +renscfact 2 ! renormalization scale factor: muren=muref*renscfact +!fixedscale 1 ! use ref. scale=qmass (default 0, use running scale) + +! Parameters to allow or not the use of stored data +use-old-grid 1 ! if 1 use old grid if file pwggrids.dat is present (# 1: regenerate) +use-old-ubound 1 ! if 1 use norm of upper bounding function stored in pwgubound.dat, if present; # 1: regenerate + +! Parameters that control the grid for Born variables generation +ncall1 10000 ! number of calls for initializing the integration grid +itmx1 5 ! number of iterations for initializing the integration grid +ncall2 100000 ! number of calls for computing the integral and finding upper bound +itmx2 5 ! number of iterations for computing the integral and finding upper bound +foldcsi 5 ! number of folds on x integration +foldy 5 ! number of folds on y integration +foldphi 1 ! number of folds on phi integration +nubound 500000 ! number of bbarra calls to setup norm of upper bounding function +iymax 1 ! <= 10, normalization of upper bounding function in iunorm X iunorm square in y, log(m2qq) +ixmax 1 ! <= 10, normalization of upper bounding function in iunorm X iunorm square in y, log(m2qq) +xupbound 2 ! increase upper bound for radiation generation +bornktmin 0 ! (default 0d0) Generation cut: minimum kt in underlying Born +bornsuppfact 0 ! (default 0d0) Mass parameter for Born suppression factor. If < 0 suppfact = 1 +storemintupb 0 ! store function calls in binary file for more precise generation diff --git a/MC/config/PWGGAJE/external/powheg/powheg_beauty_MHigh.input b/MC/config/PWGGAJE/external/powheg/powheg_beauty_MHigh.input new file mode 100644 index 000000000..a3a6fe3aa --- /dev/null +++ b/MC/config/PWGGAJE/external/powheg/powheg_beauty_MHigh.input @@ -0,0 +1,37 @@ +! Random number generator initializing parameters +iseed 0 ! initialize random number sequence + +! Heavy flavour production parameters +numevts 100000 ! number of events to be generated +ih1 1 ! hadron 1 type (1: proton; -1: antiproton) +ih2 1 ! hadron 2 type (1: proton; -1: antiproton) +!ndns1 131 ! pdf for hadron 1 (when using the hvqpdf package, hvqpdf numbering) +!ndns2 131 ! pdf for hadron 2 (when using the hvqpdf package, hvqpdf numbering) +lhans1 11000 ! pdf set for hadron 1 (LHAGLUE number) (11000 = CT10NLO, 10550 = CTEQ66) +lhans2 11000 ! pdf set for hadron 2 (LHAGLUE number) (11000 = CT10NLO, 10550 = CTEQ66) +ebeam1 6800 ! energy of beam 1 in GeV +ebeam2 6800 ! energy of beam 2 in GeV +qmass 5.00 ! mass of heavy quark in GeV +facscfact 1 ! factorization scale factor: mufact=muref*facscfact +renscfact 1 ! renormalization scale factor: muren=muref*renscfact +!fixedscale 1 ! use ref. scale=qmass (default 0, use running scale) + +! Parameters to allow or not the use of stored data +use-old-grid 1 ! if 1 use old grid if file pwggrids.dat is present (# 1: regenerate) +use-old-ubound 1 ! if 1 use norm of upper bounding function stored in pwgubound.dat, if present; # 1: regenerate + +! Parameters that control the grid for Born variables generation +ncall1 10000 ! number of calls for initializing the integration grid +itmx1 5 ! number of iterations for initializing the integration grid +ncall2 100000 ! number of calls for computing the integral and finding upper bound +itmx2 5 ! number of iterations for computing the integral and finding upper bound +foldcsi 5 ! number of folds on x integration +foldy 5 ! number of folds on y integration +foldphi 1 ! number of folds on phi integration +nubound 500000 ! number of bbarra calls to setup norm of upper bounding function +iymax 1 ! <= 10, normalization of upper bounding function in iunorm X iunorm square in y, log(m2qq) +ixmax 1 ! <= 10, normalization of upper bounding function in iunorm X iunorm square in y, log(m2qq) +xupbound 2 ! increase upper bound for radiation generation +bornktmin 0 ! (default 0d0) Generation cut: minimum kt in underlying Born +bornsuppfact 0 ! (default 0d0) Mass parameter for Born suppression factor. If < 0 suppfact = 1 +storemintupb 0 ! store function calls in binary file for more precise generation diff --git a/MC/config/PWGGAJE/external/powheg/powheg_beauty_MLow.input b/MC/config/PWGGAJE/external/powheg/powheg_beauty_MLow.input new file mode 100644 index 000000000..8f689db88 --- /dev/null +++ b/MC/config/PWGGAJE/external/powheg/powheg_beauty_MLow.input @@ -0,0 +1,37 @@ +! Random number generator initializing parameters +iseed 0 ! initialize random number sequence + +! Heavy flavour production parameters +numevts 100000 ! number of events to be generated +ih1 1 ! hadron 1 type (1: proton; -1: antiproton) +ih2 1 ! hadron 2 type (1: proton; -1: antiproton) +!ndns1 131 ! pdf for hadron 1 (when using the hvqpdf package, hvqpdf numbering) +!ndns2 131 ! pdf for hadron 2 (when using the hvqpdf package, hvqpdf numbering) +lhans1 11000 ! pdf set for hadron 1 (LHAGLUE number) (11000 = CT10NLO, 10550 = CTEQ66) +lhans2 11000 ! pdf set for hadron 2 (LHAGLUE number) (11000 = CT10NLO, 10550 = CTEQ66) +ebeam1 6800 ! energy of beam 1 in GeV +ebeam2 6800 ! energy of beam 2 in GeV +qmass 4.50 ! mass of heavy quark in GeV +facscfact 1 ! factorization scale factor: mufact=muref*facscfact +renscfact 1 ! renormalization scale factor: muren=muref*renscfact +!fixedscale 1 ! use ref. scale=qmass (default 0, use running scale) + +! Parameters to allow or not the use of stored data +use-old-grid 1 ! if 1 use old grid if file pwggrids.dat is present (# 1: regenerate) +use-old-ubound 1 ! if 1 use norm of upper bounding function stored in pwgubound.dat, if present; # 1: regenerate + +! Parameters that control the grid for Born variables generation +ncall1 10000 ! number of calls for initializing the integration grid +itmx1 5 ! number of iterations for initializing the integration grid +ncall2 100000 ! number of calls for computing the integral and finding upper bound +itmx2 5 ! number of iterations for computing the integral and finding upper bound +foldcsi 5 ! number of folds on x integration +foldy 5 ! number of folds on y integration +foldphi 1 ! number of folds on phi integration +nubound 500000 ! number of bbarra calls to setup norm of upper bounding function +iymax 1 ! <= 10, normalization of upper bounding function in iunorm X iunorm square in y, log(m2qq) +ixmax 1 ! <= 10, normalization of upper bounding function in iunorm X iunorm square in y, log(m2qq) +xupbound 2 ! increase upper bound for radiation generation +bornktmin 0 ! (default 0d0) Generation cut: minimum kt in underlying Born +bornsuppfact 0 ! (default 0d0) Mass parameter for Born suppression factor. If < 0 suppfact = 1 +storemintupb 0 ! store function calls in binary file for more precise generation diff --git a/MC/config/PWGGAJE/external/powheg/powheg_beauty_R05.input b/MC/config/PWGGAJE/external/powheg/powheg_beauty_R05.input new file mode 100644 index 000000000..0a685eba5 --- /dev/null +++ b/MC/config/PWGGAJE/external/powheg/powheg_beauty_R05.input @@ -0,0 +1,37 @@ +! Random number generator initializing parameters +iseed 0 ! initialize random number sequence + +! Heavy flavour production parameters +numevts 100000 ! number of events to be generated +ih1 1 ! hadron 1 type (1: proton; -1: antiproton) +ih2 1 ! hadron 2 type (1: proton; -1: antiproton) +!ndns1 131 ! pdf for hadron 1 (when using the hvqpdf package, hvqpdf numbering) +!ndns2 131 ! pdf for hadron 2 (when using the hvqpdf package, hvqpdf numbering) +lhans1 11000 ! pdf set for hadron 1 (LHAGLUE number) (11000 = CT10NLO, 10550 = CTEQ66) +lhans2 11000 ! pdf set for hadron 2 (LHAGLUE number) (11000 = CT10NLO, 10550 = CTEQ66) +ebeam1 6800 ! energy of beam 1 in GeV +ebeam2 6800 ! energy of beam 2 in GeV +qmass 4.75 ! mass of heavy quark in GeV +facscfact 1 ! factorization scale factor: mufact=muref*facscfact +renscfact 0.5 ! renormalization scale factor: muren=muref*renscfact +!fixedscale 1 ! use ref. scale=qmass (default 0, use running scale) + +! Parameters to allow or not the use of stored data +use-old-grid 1 ! if 1 use old grid if file pwggrids.dat is present (# 1: regenerate) +use-old-ubound 1 ! if 1 use norm of upper bounding function stored in pwgubound.dat, if present; # 1: regenerate + +! Parameters that control the grid for Born variables generation +ncall1 10000 ! number of calls for initializing the integration grid +itmx1 5 ! number of iterations for initializing the integration grid +ncall2 100000 ! number of calls for computing the integral and finding upper bound +itmx2 5 ! number of iterations for computing the integral and finding upper bound +foldcsi 5 ! number of folds on x integration +foldy 5 ! number of folds on y integration +foldphi 1 ! number of folds on phi integration +nubound 500000 ! number of bbarra calls to setup norm of upper bounding function +iymax 1 ! <= 10, normalization of upper bounding function in iunorm X iunorm square in y, log(m2qq) +ixmax 1 ! <= 10, normalization of upper bounding function in iunorm X iunorm square in y, log(m2qq) +xupbound 2 ! increase upper bound for radiation generation +bornktmin 0 ! (default 0d0) Generation cut: minimum kt in underlying Born +bornsuppfact 0 ! (default 0d0) Mass parameter for Born suppression factor. If < 0 suppfact = 1 +storemintupb 0 ! store function calls in binary file for more precise generation diff --git a/MC/config/PWGGAJE/external/powheg/powheg_beauty_R2.input b/MC/config/PWGGAJE/external/powheg/powheg_beauty_R2.input new file mode 100644 index 000000000..3209b3e50 --- /dev/null +++ b/MC/config/PWGGAJE/external/powheg/powheg_beauty_R2.input @@ -0,0 +1,37 @@ +! Random number generator initializing parameters +iseed 0 ! initialize random number sequence + +! Heavy flavour production parameters +numevts 100000 ! number of events to be generated +ih1 1 ! hadron 1 type (1: proton; -1: antiproton) +ih2 1 ! hadron 2 type (1: proton; -1: antiproton) +!ndns1 131 ! pdf for hadron 1 (when using the hvqpdf package, hvqpdf numbering) +!ndns2 131 ! pdf for hadron 2 (when using the hvqpdf package, hvqpdf numbering) +lhans1 11000 ! pdf set for hadron 1 (LHAGLUE number) (11000 = CT10NLO, 10550 = CTEQ66) +lhans2 11000 ! pdf set for hadron 2 (LHAGLUE number) (11000 = CT10NLO, 10550 = CTEQ66) +ebeam1 6800 ! energy of beam 1 in GeV +ebeam2 6800 ! energy of beam 2 in GeV +qmass 4.75 ! mass of heavy quark in GeV +facscfact 1 ! factorization scale factor: mufact=muref*facscfact +renscfact 2 ! renormalization scale factor: muren=muref*renscfact +!fixedscale 1 ! use ref. scale=qmass (default 0, use running scale) + +! Parameters to allow or not the use of stored data +use-old-grid 1 ! if 1 use old grid if file pwggrids.dat is present (# 1: regenerate) +use-old-ubound 1 ! if 1 use norm of upper bounding function stored in pwgubound.dat, if present; # 1: regenerate + +! Parameters that control the grid for Born variables generation +ncall1 10000 ! number of calls for initializing the integration grid +itmx1 5 ! number of iterations for initializing the integration grid +ncall2 100000 ! number of calls for computing the integral and finding upper bound +itmx2 5 ! number of iterations for computing the integral and finding upper bound +foldcsi 5 ! number of folds on x integration +foldy 5 ! number of folds on y integration +foldphi 1 ! number of folds on phi integration +nubound 500000 ! number of bbarra calls to setup norm of upper bounding function +iymax 1 ! <= 10, normalization of upper bounding function in iunorm X iunorm square in y, log(m2qq) +ixmax 1 ! <= 10, normalization of upper bounding function in iunorm X iunorm square in y, log(m2qq) +xupbound 2 ! increase upper bound for radiation generation +bornktmin 0 ! (default 0d0) Generation cut: minimum kt in underlying Born +bornsuppfact 0 ! (default 0d0) Mass parameter for Born suppression factor. If < 0 suppfact = 1 +storemintupb 0 ! store function calls in binary file for more precise generation diff --git a/MC/config/PWGGAJE/external/powheg/powheg_beauty_default.input b/MC/config/PWGGAJE/external/powheg/powheg_beauty_default.input new file mode 100644 index 000000000..ab3d3a176 --- /dev/null +++ b/MC/config/PWGGAJE/external/powheg/powheg_beauty_default.input @@ -0,0 +1,37 @@ +! Random number generator initializing parameters +iseed 0 ! initialize random number sequence + +! Heavy flavour production parameters +numevts 100000 ! number of events to be generated +ih1 1 ! hadron 1 type (1: proton; -1: antiproton) +ih2 1 ! hadron 2 type (1: proton; -1: antiproton) +!ndns1 131 ! pdf for hadron 1 (when using the hvqpdf package, hvqpdf numbering) +!ndns2 131 ! pdf for hadron 2 (when using the hvqpdf package, hvqpdf numbering) +lhans1 11000 ! pdf set for hadron 1 (LHAGLUE number) (11000 = CT10NLO, 10550 = CTEQ66) +lhans2 11000 ! pdf set for hadron 2 (LHAGLUE number) (11000 = CT10NLO, 10550 = CTEQ66) +ebeam1 6800 ! energy of beam 1 in GeV +ebeam2 6800 ! energy of beam 2 in GeV +qmass 4.75 ! mass of heavy quark in GeV +facscfact 1 ! factorization scale factor: mufact=muref*facscfact +renscfact 1 ! renormalization scale factor: muren=muref*renscfact +!fixedscale 1 ! use ref. scale=qmass (default 0, use running scale) + +! Parameters to allow or not the use of stored data +use-old-grid 1 ! if 1 use old grid if file pwggrids.dat is present (# 1: regenerate) +use-old-ubound 1 ! if 1 use norm of upper bounding function stored in pwgubound.dat, if present; # 1: regenerate + +! Parameters that control the grid for Born variables generation +ncall1 10000 ! number of calls for initializing the integration grid +itmx1 5 ! number of iterations for initializing the integration grid +ncall2 100000 ! number of calls for computing the integral and finding upper bound +itmx2 5 ! number of iterations for computing the integral and finding upper bound +foldcsi 5 ! number of folds on x integration +foldy 5 ! number of folds on y integration +foldphi 1 ! number of folds on phi integration +nubound 500000 ! number of bbarra calls to setup norm of upper bounding function +iymax 1 ! <= 10, normalization of upper bounding function in iunorm X iunorm square in y, log(m2qq) +ixmax 1 ! <= 10, normalization of upper bounding function in iunorm X iunorm square in y, log(m2qq) +xupbound 2 ! increase upper bound for radiation generation +bornktmin 0 ! (default 0d0) Generation cut: minimum kt in underlying Born +bornsuppfact 0 ! (default 0d0) Mass parameter for Born suppression factor. If < 0 suppfact = 1 +storemintupb 0 ! store function calls in binary file for more precise generation diff --git a/MC/config/PWGGAJE/external/powheg/powheg_charm_F05.input b/MC/config/PWGGAJE/external/powheg/powheg_charm_F05.input new file mode 100644 index 000000000..9031ef445 --- /dev/null +++ b/MC/config/PWGGAJE/external/powheg/powheg_charm_F05.input @@ -0,0 +1,37 @@ +! Random number generator initializing parameters +iseed 0 ! initialize random number sequence + +! Heavy flavour production parameters +numevts 100000 ! number of events to be generated +ih1 1 ! hadron 1 type (1: proton; -1: antiproton) +ih2 1 ! hadron 2 type (1: proton; -1: antiproton) +!ndns1 131 ! pdf for hadron 1 (when using the hvqpdf package, hvqpdf numbering) +!ndns2 131 ! pdf for hadron 2 (when using the hvqpdf package, hvqpdf numbering) +lhans1 11000 ! pdf set for hadron 1 (LHAGLUE number) (11000 = CT10NLO, 10550 = CTEQ66) +lhans2 11000 ! pdf set for hadron 2 (LHAGLUE number) (11000 = CT10NLO, 10550 = CTEQ66) +ebeam1 6800 ! energy of beam 1 in GeV +ebeam2 6800 ! energy of beam 2 in GeV +qmass 1.5 ! mass of heavy quark in GeV +facscfact 0.5 ! factorization scale factor: mufact=muref*facscfact +renscfact 1 ! renormalization scale factor: muren=muref*renscfact +!fixedscale 1 ! use ref. scale=qmass (default 0, use running scale) + +! Parameters to allow or not the use of stored data +use-old-grid 1 ! if 1 use old grid if file pwggrids.dat is present (# 1: regenerate) +use-old-ubound 1 ! if 1 use norm of upper bounding function stored in pwgubound.dat, if present; # 1: regenerate + +! Parameters that control the grid for Born variables generation +ncall1 50000 ! number of calls for initializing the integration grid +itmx1 5 ! number of iterations for initializing the integration grid +ncall2 100000 ! number of calls for computing the integral and finding upper bound +itmx2 5 ! number of iterations for computing the integral and finding upper bound +foldcsi 5 ! number of folds on x integration +foldy 5 ! number of folds on y integration +foldphi 1 ! number of folds on phi integration +nubound 500000 ! number of bbarra calls to setup norm of upper bounding function +iymax 1 ! <= 10, normalization of upper bounding function in iunorm X iunorm square in y, log(m2qq) +ixmax 1 ! <= 10, normalization of upper bounding function in iunorm X iunorm square in y, log(m2qq) +xupbound 2 ! increase upper bound for radiation generation +bornktmin 0 ! (default 0d0) Generation cut: minimum kt in underlying Born +bornsuppfact 0 ! (default 0d0) Mass parameter for Born suppression factor. If < 0 suppfact = 1 +storemintupb 0 ! store function calls in binary file for more precise generation diff --git a/MC/config/PWGGAJE/external/powheg/powheg_charm_F05_R05.input b/MC/config/PWGGAJE/external/powheg/powheg_charm_F05_R05.input new file mode 100644 index 000000000..2366cbbbb --- /dev/null +++ b/MC/config/PWGGAJE/external/powheg/powheg_charm_F05_R05.input @@ -0,0 +1,37 @@ +! Random number generator initializing parameters +iseed 0 ! initialize random number sequence + +! Heavy flavour production parameters +numevts 100000 ! number of events to be generated +ih1 1 ! hadron 1 type (1: proton; -1: antiproton) +ih2 1 ! hadron 2 type (1: proton; -1: antiproton) +!ndns1 131 ! pdf for hadron 1 (when using the hvqpdf package, hvqpdf numbering) +!ndns2 131 ! pdf for hadron 2 (when using the hvqpdf package, hvqpdf numbering) +lhans1 11000 ! pdf set for hadron 1 (LHAGLUE number) (11000 = CT10NLO, 10550 = CTEQ66) +lhans2 11000 ! pdf set for hadron 2 (LHAGLUE number) (11000 = CT10NLO, 10550 = CTEQ66) +ebeam1 6800 ! energy of beam 1 in GeV +ebeam2 6800 ! energy of beam 2 in GeV +qmass 1.5 ! mass of heavy quark in GeV +facscfact 0.5 ! factorization scale factor: mufact=muref*facscfact +renscfact 0.5 ! renormalization scale factor: muren=muref*renscfact +!fixedscale 1 ! use ref. scale=qmass (default 0, use running scale) + +! Parameters to allow or not the use of stored data +use-old-grid 1 ! if 1 use old grid if file pwggrids.dat is present (# 1: regenerate) +use-old-ubound 1 ! if 1 use norm of upper bounding function stored in pwgubound.dat, if present; # 1: regenerate + +! Parameters that control the grid for Born variables generation +ncall1 50000 ! number of calls for initializing the integration grid +itmx1 5 ! number of iterations for initializing the integration grid +ncall2 100000 ! number of calls for computing the integral and finding upper bound +itmx2 5 ! number of iterations for computing the integral and finding upper bound +foldcsi 5 ! number of folds on x integration +foldy 5 ! number of folds on y integration +foldphi 1 ! number of folds on phi integration +nubound 500000 ! number of bbarra calls to setup norm of upper bounding function +iymax 1 ! <= 10, normalization of upper bounding function in iunorm X iunorm square in y, log(m2qq) +ixmax 1 ! <= 10, normalization of upper bounding function in iunorm X iunorm square in y, log(m2qq) +xupbound 2 ! increase upper bound for radiation generation +bornktmin 0 ! (default 0d0) Generation cut: minimum kt in underlying Born +bornsuppfact 0 ! (default 0d0) Mass parameter for Born suppression factor. If < 0 suppfact = 1 +storemintupb 0 ! store function calls in binary file for more precise generation diff --git a/MC/config/PWGGAJE/external/powheg/powheg_charm_F2.input b/MC/config/PWGGAJE/external/powheg/powheg_charm_F2.input new file mode 100644 index 000000000..9b1a2466d --- /dev/null +++ b/MC/config/PWGGAJE/external/powheg/powheg_charm_F2.input @@ -0,0 +1,37 @@ +! Random number generator initializing parameters +iseed 0 ! initialize random number sequence + +! Heavy flavour production parameters +numevts 100000 ! number of events to be generated +ih1 1 ! hadron 1 type (1: proton; -1: antiproton) +ih2 1 ! hadron 2 type (1: proton; -1: antiproton) +!ndns1 131 ! pdf for hadron 1 (when using the hvqpdf package, hvqpdf numbering) +!ndns2 131 ! pdf for hadron 2 (when using the hvqpdf package, hvqpdf numbering) +lhans1 11000 ! pdf set for hadron 1 (LHAGLUE number) (11000 = CT10NLO, 10550 = CTEQ66) +lhans2 11000 ! pdf set for hadron 2 (LHAGLUE number) (11000 = CT10NLO, 10550 = CTEQ66) +ebeam1 6800 ! energy of beam 1 in GeV +ebeam2 6800 ! energy of beam 2 in GeV +qmass 1.5 ! mass of heavy quark in GeV +facscfact 2 ! factorization scale factor: mufact=muref*facscfact +renscfact 1 ! renormalization scale factor: muren=muref*renscfact +!fixedscale 1 ! use ref. scale=qmass (default 0, use running scale) + +! Parameters to allow or not the use of stored data +use-old-grid 1 ! if 1 use old grid if file pwggrids.dat is present (# 1: regenerate) +use-old-ubound 1 ! if 1 use norm of upper bounding function stored in pwgubound.dat, if present; # 1: regenerate + +! Parameters that control the grid for Born variables generation +ncall1 50000 ! number of calls for initializing the integration grid +itmx1 5 ! number of iterations for initializing the integration grid +ncall2 100000 ! number of calls for computing the integral and finding upper bound +itmx2 5 ! number of iterations for computing the integral and finding upper bound +foldcsi 5 ! number of folds on x integration +foldy 5 ! number of folds on y integration +foldphi 1 ! number of folds on phi integration +nubound 500000 ! number of bbarra calls to setup norm of upper bounding function +iymax 1 ! <= 10, normalization of upper bounding function in iunorm X iunorm square in y, log(m2qq) +ixmax 1 ! <= 10, normalization of upper bounding function in iunorm X iunorm square in y, log(m2qq) +xupbound 2 ! increase upper bound for radiation generation +bornktmin 0 ! (default 0d0) Generation cut: minimum kt in underlying Born +bornsuppfact 0 ! (default 0d0) Mass parameter for Born suppression factor. If < 0 suppfact = 1 +storemintupb 0 ! store function calls in binary file for more precise generation diff --git a/MC/config/PWGGAJE/external/powheg/powheg_charm_F2_R2.input b/MC/config/PWGGAJE/external/powheg/powheg_charm_F2_R2.input new file mode 100644 index 000000000..e5f77b0bf --- /dev/null +++ b/MC/config/PWGGAJE/external/powheg/powheg_charm_F2_R2.input @@ -0,0 +1,37 @@ +! Random number generator initializing parameters +iseed 0 ! initialize random number sequence + +! Heavy flavour production parameters +numevts 100000 ! number of events to be generated +ih1 1 ! hadron 1 type (1: proton; -1: antiproton) +ih2 1 ! hadron 2 type (1: proton; -1: antiproton) +!ndns1 131 ! pdf for hadron 1 (when using the hvqpdf package, hvqpdf numbering) +!ndns2 131 ! pdf for hadron 2 (when using the hvqpdf package, hvqpdf numbering) +lhans1 11000 ! pdf set for hadron 1 (LHAGLUE number) (11000 = CT10NLO, 10550 = CTEQ66) +lhans2 11000 ! pdf set for hadron 2 (LHAGLUE number) (11000 = CT10NLO, 10550 = CTEQ66) +ebeam1 6800 ! energy of beam 1 in GeV +ebeam2 6800 ! energy of beam 2 in GeV +qmass 1.5 ! mass of heavy quark in GeV +facscfact 2 ! factorization scale factor: mufact=muref*facscfact +renscfact 2 ! renormalization scale factor: muren=muref*renscfact +!fixedscale 1 ! use ref. scale=qmass (default 0, use running scale) + +! Parameters to allow or not the use of stored data +use-old-grid 1 ! if 1 use old grid if file pwggrids.dat is present (# 1: regenerate) +use-old-ubound 1 ! if 1 use norm of upper bounding function stored in pwgubound.dat, if present; # 1: regenerate + +! Parameters that control the grid for Born variables generation +ncall1 50000 ! number of calls for initializing the integration grid +itmx1 5 ! number of iterations for initializing the integration grid +ncall2 100000 ! number of calls for computing the integral and finding upper bound +itmx2 5 ! number of iterations for computing the integral and finding upper bound +foldcsi 5 ! number of folds on x integration +foldy 5 ! number of folds on y integration +foldphi 1 ! number of folds on phi integration +nubound 500000 ! number of bbarra calls to setup norm of upper bounding function +iymax 1 ! <= 10, normalization of upper bounding function in iunorm X iunorm square in y, log(m2qq) +ixmax 1 ! <= 10, normalization of upper bounding function in iunorm X iunorm square in y, log(m2qq) +xupbound 2 ! increase upper bound for radiation generation +bornktmin 0 ! (default 0d0) Generation cut: minimum kt in underlying Born +bornsuppfact 0 ! (default 0d0) Mass parameter for Born suppression factor. If < 0 suppfact = 1 +storemintupb 0 ! store function calls in binary file for more precise generation diff --git a/MC/config/PWGGAJE/external/powheg/powheg_charm_MHigh.input b/MC/config/PWGGAJE/external/powheg/powheg_charm_MHigh.input new file mode 100644 index 000000000..214aba28b --- /dev/null +++ b/MC/config/PWGGAJE/external/powheg/powheg_charm_MHigh.input @@ -0,0 +1,37 @@ +! Random number generator initializing parameters +iseed 0 ! initialize random number sequence + +! Heavy flavour production parameters +numevts 100000 ! number of events to be generated +ih1 1 ! hadron 1 type (1: proton; -1: antiproton) +ih2 1 ! hadron 2 type (1: proton; -1: antiproton) +!ndns1 131 ! pdf for hadron 1 (when using the hvqpdf package, hvqpdf numbering) +!ndns2 131 ! pdf for hadron 2 (when using the hvqpdf package, hvqpdf numbering) +lhans1 11000 ! pdf set for hadron 1 (LHAGLUE number) (11000 = CT10NLO, 10550 = CTEQ66) +lhans2 11000 ! pdf set for hadron 2 (LHAGLUE number) (11000 = CT10NLO, 10550 = CTEQ66) +ebeam1 6800 ! energy of beam 1 in GeV +ebeam2 6800 ! energy of beam 2 in GeV +qmass 1.7 ! mass of heavy quark in GeV +facscfact 1 ! factorization scale factor: mufact=muref*facscfact +renscfact 1 ! renormalization scale factor: muren=muref*renscfact +!fixedscale 1 ! use ref. scale=qmass (default 0, use running scale) + +! Parameters to allow or not the use of stored data +use-old-grid 1 ! if 1 use old grid if file pwggrids.dat is present (# 1: regenerate) +use-old-ubound 1 ! if 1 use norm of upper bounding function stored in pwgubound.dat, if present; # 1: regenerate + +! Parameters that control the grid for Born variables generation +ncall1 50000 ! number of calls for initializing the integration grid +itmx1 5 ! number of iterations for initializing the integration grid +ncall2 100000 ! number of calls for computing the integral and finding upper bound +itmx2 5 ! number of iterations for computing the integral and finding upper bound +foldcsi 5 ! number of folds on x integration +foldy 5 ! number of folds on y integration +foldphi 1 ! number of folds on phi integration +nubound 500000 ! number of bbarra calls to setup norm of upper bounding function +iymax 1 ! <= 10, normalization of upper bounding function in iunorm X iunorm square in y, log(m2qq) +ixmax 1 ! <= 10, normalization of upper bounding function in iunorm X iunorm square in y, log(m2qq) +xupbound 2 ! increase upper bound for radiation generation +bornktmin 0 ! (default 0d0) Generation cut: minimum kt in underlying Born +bornsuppfact 0 ! (default 0d0) Mass parameter for Born suppression factor. If < 0 suppfact = 1 +storemintupb 0 ! store function calls in binary file for more precise generation diff --git a/MC/config/PWGGAJE/external/powheg/powheg_charm_MLow.input b/MC/config/PWGGAJE/external/powheg/powheg_charm_MLow.input new file mode 100644 index 000000000..16df9c118 --- /dev/null +++ b/MC/config/PWGGAJE/external/powheg/powheg_charm_MLow.input @@ -0,0 +1,37 @@ +! Random number generator initializing parameters +iseed 0 ! initialize random number sequence + +! Heavy flavour production parameters +numevts 100000 ! number of events to be generated +ih1 1 ! hadron 1 type (1: proton; -1: antiproton) +ih2 1 ! hadron 2 type (1: proton; -1: antiproton) +!ndns1 131 ! pdf for hadron 1 (when using the hvqpdf package, hvqpdf numbering) +!ndns2 131 ! pdf for hadron 2 (when using the hvqpdf package, hvqpdf numbering) +lhans1 11000 ! pdf set for hadron 1 (LHAGLUE number) (11000 = CT10NLO, 10550 = CTEQ66) +lhans2 11000 ! pdf set for hadron 2 (LHAGLUE number) (11000 = CT10NLO, 10550 = CTEQ66) +ebeam1 6800 ! energy of beam 1 in GeV +ebeam2 6800 ! energy of beam 2 in GeV +qmass 1.3 ! mass of heavy quark in GeV +facscfact 1 ! factorization scale factor: mufact=muref*facscfact +renscfact 1 ! renormalization scale factor: muren=muref*renscfact +!fixedscale 1 ! use ref. scale=qmass (default 0, use running scale) + +! Parameters to allow or not the use of stored data +use-old-grid 1 ! if 1 use old grid if file pwggrids.dat is present (# 1: regenerate) +use-old-ubound 1 ! if 1 use norm of upper bounding function stored in pwgubound.dat, if present; # 1: regenerate + +! Parameters that control the grid for Born variables generation +ncall1 50000 ! number of calls for initializing the integration grid +itmx1 5 ! number of iterations for initializing the integration grid +ncall2 100000 ! number of calls for computing the integral and finding upper bound +itmx2 5 ! number of iterations for computing the integral and finding upper bound +foldcsi 5 ! number of folds on x integration +foldy 5 ! number of folds on y integration +foldphi 1 ! number of folds on phi integration +nubound 500000 ! number of bbarra calls to setup norm of upper bounding function +iymax 1 ! <= 10, normalization of upper bounding function in iunorm X iunorm square in y, log(m2qq) +ixmax 1 ! <= 10, normalization of upper bounding function in iunorm X iunorm square in y, log(m2qq) +xupbound 2 ! increase upper bound for radiation generation +bornktmin 0 ! (default 0d0) Generation cut: minimum kt in underlying Born +bornsuppfact 0 ! (default 0d0) Mass parameter for Born suppression factor. If < 0 suppfact = 1 +storemintupb 0 ! store function calls in binary file for more precise generation diff --git a/MC/config/PWGGAJE/external/powheg/powheg_charm_R05.input b/MC/config/PWGGAJE/external/powheg/powheg_charm_R05.input new file mode 100644 index 000000000..e794c82be --- /dev/null +++ b/MC/config/PWGGAJE/external/powheg/powheg_charm_R05.input @@ -0,0 +1,37 @@ +! Random number generator initializing parameters +iseed 0 ! initialize random number sequence + +! Heavy flavour production parameters +numevts 100000 ! number of events to be generated +ih1 1 ! hadron 1 type (1: proton; -1: antiproton) +ih2 1 ! hadron 2 type (1: proton; -1: antiproton) +!ndns1 131 ! pdf for hadron 1 (when using the hvqpdf package, hvqpdf numbering) +!ndns2 131 ! pdf for hadron 2 (when using the hvqpdf package, hvqpdf numbering) +lhans1 11000 ! pdf set for hadron 1 (LHAGLUE number) (11000 = CT10NLO, 10550 = CTEQ66) +lhans2 11000 ! pdf set for hadron 2 (LHAGLUE number) (11000 = CT10NLO, 10550 = CTEQ66) +ebeam1 6800 ! energy of beam 1 in GeV +ebeam2 6800 ! energy of beam 2 in GeV +qmass 1.5 ! mass of heavy quark in GeV +facscfact 1 ! factorization scale factor: mufact=muref*facscfact +renscfact 0.5 ! renormalization scale factor: muren=muref*renscfact +!fixedscale 1 ! use ref. scale=qmass (default 0, use running scale) + +! Parameters to allow or not the use of stored data +use-old-grid 1 ! if 1 use old grid if file pwggrids.dat is present (# 1: regenerate) +use-old-ubound 1 ! if 1 use norm of upper bounding function stored in pwgubound.dat, if present; # 1: regenerate + +! Parameters that control the grid for Born variables generation +ncall1 50000 ! number of calls for initializing the integration grid +itmx1 5 ! number of iterations for initializing the integration grid +ncall2 100000 ! number of calls for computing the integral and finding upper bound +itmx2 5 ! number of iterations for computing the integral and finding upper bound +foldcsi 5 ! number of folds on x integration +foldy 5 ! number of folds on y integration +foldphi 1 ! number of folds on phi integration +nubound 500000 ! number of bbarra calls to setup norm of upper bounding function +iymax 1 ! <= 10, normalization of upper bounding function in iunorm X iunorm square in y, log(m2qq) +ixmax 1 ! <= 10, normalization of upper bounding function in iunorm X iunorm square in y, log(m2qq) +xupbound 2 ! increase upper bound for radiation generation +bornktmin 0 ! (default 0d0) Generation cut: minimum kt in underlying Born +bornsuppfact 0 ! (default 0d0) Mass parameter for Born suppression factor. If < 0 suppfact = 1 +storemintupb 0 ! store function calls in binary file for more precise generation diff --git a/MC/config/PWGGAJE/external/powheg/powheg_charm_R2.input b/MC/config/PWGGAJE/external/powheg/powheg_charm_R2.input new file mode 100644 index 000000000..dfc07b4cd --- /dev/null +++ b/MC/config/PWGGAJE/external/powheg/powheg_charm_R2.input @@ -0,0 +1,37 @@ +! Random number generator initializing parameters +iseed 0 ! initialize random number sequence + +! Heavy flavour production parameters +numevts 100000 ! number of events to be generated +ih1 1 ! hadron 1 type (1: proton; -1: antiproton) +ih2 1 ! hadron 2 type (1: proton; -1: antiproton) +!ndns1 131 ! pdf for hadron 1 (when using the hvqpdf package, hvqpdf numbering) +!ndns2 131 ! pdf for hadron 2 (when using the hvqpdf package, hvqpdf numbering) +lhans1 11000 ! pdf set for hadron 1 (LHAGLUE number) (11000 = CT10NLO, 10550 = CTEQ66) +lhans2 11000 ! pdf set for hadron 2 (LHAGLUE number) (11000 = CT10NLO, 10550 = CTEQ66) +ebeam1 6800 ! energy of beam 1 in GeV +ebeam2 6800 ! energy of beam 2 in GeV +qmass 1.5 ! mass of heavy quark in GeV +facscfact 1 ! factorization scale factor: mufact=muref*facscfact +renscfact 2 ! renormalization scale factor: muren=muref*renscfact +!fixedscale 1 ! use ref. scale=qmass (default 0, use running scale) + +! Parameters to allow or not the use of stored data +use-old-grid 1 ! if 1 use old grid if file pwggrids.dat is present (# 1: regenerate) +use-old-ubound 1 ! if 1 use norm of upper bounding function stored in pwgubound.dat, if present; # 1: regenerate + +! Parameters that control the grid for Born variables generation +ncall1 50000 ! number of calls for initializing the integration grid +itmx1 5 ! number of iterations for initializing the integration grid +ncall2 100000 ! number of calls for computing the integral and finding upper bound +itmx2 5 ! number of iterations for computing the integral and finding upper bound +foldcsi 5 ! number of folds on x integration +foldy 5 ! number of folds on y integration +foldphi 1 ! number of folds on phi integration +nubound 500000 ! number of bbarra calls to setup norm of upper bounding function +iymax 1 ! <= 10, normalization of upper bounding function in iunorm X iunorm square in y, log(m2qq) +ixmax 1 ! <= 10, normalization of upper bounding function in iunorm X iunorm square in y, log(m2qq) +xupbound 2 ! increase upper bound for radiation generation +bornktmin 0 ! (default 0d0) Generation cut: minimum kt in underlying Born +bornsuppfact 0 ! (default 0d0) Mass parameter for Born suppression factor. If < 0 suppfact = 1 +storemintupb 0 ! store function calls in binary file for more precise generation diff --git a/MC/config/PWGGAJE/external/powheg/powheg_charm_default.input b/MC/config/PWGGAJE/external/powheg/powheg_charm_default.input new file mode 100644 index 000000000..5d1aaabc8 --- /dev/null +++ b/MC/config/PWGGAJE/external/powheg/powheg_charm_default.input @@ -0,0 +1,37 @@ +! Random number generator initializing parameters +iseed 0 ! initialize random number sequence + +! Heavy flavour production parameters +numevts 100000 ! number of events to be generated +ih1 1 ! hadron 1 type (1: proton; -1: antiproton) +ih2 1 ! hadron 2 type (1: proton; -1: antiproton) +!ndns1 131 ! pdf for hadron 1 (when using the hvqpdf package, hvqpdf numbering) +!ndns2 131 ! pdf for hadron 2 (when using the hvqpdf package, hvqpdf numbering) +lhans1 11000 ! pdf set for hadron 1 (LHAGLUE number) (11000 = CT10NLO, 10550 = CTEQ66) +lhans2 11000 ! pdf set for hadron 2 (LHAGLUE number) (11000 = CT10NLO, 10550 = CTEQ66) +ebeam1 6800 ! energy of beam 1 in GeV +ebeam2 6800 ! energy of beam 2 in GeV +qmass 1.5 ! mass of heavy quark in GeV +facscfact 1 ! factorization scale factor: mufact=muref*facscfact +renscfact 1 ! renormalization scale factor: muren=muref*renscfact +!fixedscale 1 ! use ref. scale=qmass (default 0, use running scale) + +! Parameters to allow or not the use of stored data +use-old-grid 1 ! if 1 use old grid if file pwggrids.dat is present (# 1: regenerate) +use-old-ubound 1 ! if 1 use norm of upper bounding function stored in pwgubound.dat, if present; # 1: regenerate + +! Parameters that control the grid for Born variables generation +ncall1 50000 ! number of calls for initializing the integration grid +itmx1 5 ! number of iterations for initializing the integration grid +ncall2 100000 ! number of calls for computing the integral and finding upper bound +itmx2 5 ! number of iterations for computing the integral and finding upper bound +foldcsi 5 ! number of folds on x integration +foldy 5 ! number of folds on y integration +foldphi 1 ! number of folds on phi integration +nubound 500000 ! number of bbarra calls to setup norm of upper bounding function +iymax 1 ! <= 10, normalization of upper bounding function in iunorm X iunorm square in y, log(m2qq) +ixmax 1 ! <= 10, normalization of upper bounding function in iunorm X iunorm square in y, log(m2qq) +xupbound 2 ! increase upper bound for radiation generation +bornktmin 0 ! (default 0d0) Generation cut: minimum kt in underlying Born +bornsuppfact 0 ! (default 0d0) Mass parameter for Born suppression factor. If < 0 suppfact = 1 +storemintupb 0 ! store function calls in binary file for more precise generation diff --git a/MC/config/PWGGAJE/external/powheg/powheg_jetjet_13600.input b/MC/config/PWGGAJE/external/powheg/powheg_jetjet_13600.input new file mode 100755 index 000000000..c51aebbe8 --- /dev/null +++ b/MC/config/PWGGAJE/external/powheg/powheg_jetjet_13600.input @@ -0,0 +1,90 @@ +numevts 10 ! number of events to be generated +ih1 1 ! hadron 1 (1 for protons, -1 for antiprotons) +ih2 1 ! hadron 2 (1 for protons, -1 for antiprotons) +ebeam1 6800d0 ! energy of beam 1 +ebeam2 6800d0 ! energy of beam 2 + +bornktmin 1d0 ! (default 0d0) Generation cut: minimum kt in underlying Born +bornsuppfact 120d0 ! (default 0d0) Mass parameter for Born suppression factor. + ! If < 0 suppfact = 1. + + +! To be set only if using internal (mlm) pdfs +! 131 cteq6m +! ndns1 131 ! pdf set for hadron 1 (mlm numbering) +! ndns2 131 ! pdf set for hadron 2 (mlm numbering) + +! To be set only if using LHA pdfs +! 10050 cteq6m +! 10550 cteq66 +! 13100 CT14nlo +! 14400 CT18nlo +lhans1 14400 ! pdf set for hadron 1 (LHA numbering) +lhans2 14400 ! pdf set for hadron 2 (LHA numbering) + +! To be set only if using different pdf sets for the two incoming hadrons +# QCDLambda5 0.25 ! for not equal pdf sets + +#renscfact 1d0 ! (default 1d0) ren scale factor: muren = muref * renscfact +#facscfact 1d0 ! (default 1d0) fac scale factor: mufact = muref * facscfact + +! Parameters to allow or not the use of stored data +use-old-grid 1 ! If 1 use old grid if file pwggrids.dat is present (<> 1 regenerate) +use-old-ubound 1 ! If 1 use norm of upper bounding function stored + ! in pwgubound.dat, if present; <> 1 regenerate + +! A typical call uses 1/1400 seconds (1400 calls per second) +ncall1 20000 ! No. calls for the construction of the importance sampling grid +itmx1 5 ! No. iterations for grid: total 100000 calls ~ 70 seconds +ncall2 20000 ! No. calls for the computation of the upper bounding + ! envelope for the generation of radiation +itmx2 5 ! No. iterations for the above + +! Notice: the total number of calls is ncall2*itmx2*foldcsi*foldy*foldphi +! these folding numbers yield a negative fraction of 0.5% with bornktmin=10 GeV. +! With these settings: ncall2*itmx2*foldcsi*foldy*foldphi=5M, 60 minutes +foldcsi 5 ! No. folds on csi integration +foldy 5 ! No. folds on y integration +foldphi 2 ! No. folds on phi integration + +nubound 500000 ! No. calls to set up the upper bounding norms for radiation. + ! This is performed using only the Born cross section (fast) + +! OPTIONAL PARAMETERS + +withnegweights 0 ! (default 0). If 1 use negative weights. +#bornonly 1 ! (default 0). If 1 compute underlying Born using LO + ! cross section only. + +#ptsqmin 0.8 ! (default 0.8 GeV) minimum pt for generation of radiation +#charmthr 1.5 ! (default 1.5 GeV) charm treshold for gluon splitting +#bottomthr 5.0 ! (default 5.0 GeV) bottom treshold for gluon splitting +#testplots 1 ! (default 0, do not) do NLO and PWHG distributions +#charmthrpdf 1.5 ! (default 1.5 GeV) pdf charm treshold +#bottomthrpdf 5.0 ! (default 5.0 GeV) pdf bottom treshold + +#xupbound 2d0 ! increase upper bound for radiation generation + +#iseed 5421 ! Start the random number generator with seed iseed +#rand1 0 ! skipping rand2*100000000+rand1 numbers (see RM48 +#rand2 0 ! short writeup in CERNLIB). +#manyseeds 1 ! Used to perform multiple runs with different random + ! seeds in the same directory. + ! If set to 1, the program asks for an integer j; + ! The file pwgseeds.dat at line j is read, and the + ! integer at line j is used to initialize the random + ! sequence for the generation of the event. + ! The event file is called pwgevents-'j'.lhe + +doublefsr 1 ! Default 0; if 1 use new mechanism to generate regions + ! such that the emitted harder than the + ! emitter in FSR is suppressed. If doublefsr=0 this is + ! only the case for emitted gluons (old behaviour). If + ! 1 it is also applied to emitted quarks. + ! If set, it strongly reduces spikes on showered output. + + + +par_diexp 4 ! default is 2. With 4 there is a stronger separation +par_dijexp 4 ! of regions, it may help to reduce spikes when generating +par_2gsupp 4 ! weighted events. diff --git a/MC/config/PWGGAJE/external/powheg/powheg_jetjet_13600_mini.input b/MC/config/PWGGAJE/external/powheg/powheg_jetjet_13600_mini.input new file mode 100755 index 000000000..e151c10d9 --- /dev/null +++ b/MC/config/PWGGAJE/external/powheg/powheg_jetjet_13600_mini.input @@ -0,0 +1,90 @@ +numevts 10 ! number of events to be generated +ih1 1 ! hadron 1 (1 for protons, -1 for antiprotons) +ih2 1 ! hadron 2 (1 for protons, -1 for antiprotons) +ebeam1 6800d0 ! energy of beam 1 +ebeam2 6800d0 ! energy of beam 2 + +bornktmin 10d0 ! (default 0d0) Generation cut: minimum kt in underlying Born +bornsuppfact 120d0 ! (default 0d0) Mass parameter for Born suppression factor. + ! If < 0 suppfact = 1. + + +! To be set only if using internal (mlm) pdfs +! 131 cteq6m +! ndns1 131 ! pdf set for hadron 1 (mlm numbering) +! ndns2 131 ! pdf set for hadron 2 (mlm numbering) + +! To be set only if using LHA pdfs +! 10050 cteq6m +! 10550 cteq66 +! 13100 CT14nlo +! 14400 CT18nlo +lhans1 14400 ! pdf set for hadron 1 (LHA numbering) +lhans2 14400 ! pdf set for hadron 2 (LHA numbering) + +! To be set only if using different pdf sets for the two incoming hadrons +# QCDLambda5 0.25 ! for not equal pdf sets + +#renscfact 1d0 ! (default 1d0) ren scale factor: muren = muref * renscfact +#facscfact 1d0 ! (default 1d0) fac scale factor: mufact = muref * facscfact + +! Parameters to allow or not the use of stored data +use-old-grid 1 ! If 1 use old grid if file pwggrids.dat is present (<> 1 regenerate) +use-old-ubound 1 ! If 1 use norm of upper bounding function stored + ! in pwgubound.dat, if present; <> 1 regenerate + +! A typical call uses 1/1400 seconds (1400 calls per second) +ncall1 20000 ! No. calls for the construction of the importance sampling grid +itmx1 5 ! No. iterations for grid: total 100000 calls ~ 70 seconds +ncall2 10000 ! No. calls for the computation of the upper bounding + ! envelope for the generation of radiation +itmx2 3 ! No. iterations for the above + +! Notice: the total number of calls is ncall2*itmx2*foldcsi*foldy*foldphi +! these folding numbers yield a negative fraction of 0.5% with bornktmin=10 GeV. +! With these settings: ncall2*itmx2*foldcsi*foldy*foldphi=5M, 60 minutes +foldcsi 2 ! No. folds on csi integration +foldy 2 ! No. folds on y integration +foldphi 1 ! No. folds on phi integration + +nubound 100000 ! No. calls to set up the upper bounding norms for radiation. + ! This is performed using only the Born cross section (fast) + +! OPTIONAL PARAMETERS + +withnegweights 0 ! (default 0). If 1 use negative weights. +#bornonly 1 ! (default 0). If 1 compute underlying Born using LO + ! cross section only. + +#ptsqmin 0.8 ! (default 0.8 GeV) minimum pt for generation of radiation +#charmthr 1.5 ! (default 1.5 GeV) charm treshold for gluon splitting +#bottomthr 5.0 ! (default 5.0 GeV) bottom treshold for gluon splitting +#testplots 1 ! (default 0, do not) do NLO and PWHG distributions +#charmthrpdf 1.5 ! (default 1.5 GeV) pdf charm treshold +#bottomthrpdf 5.0 ! (default 5.0 GeV) pdf bottom treshold + +#xupbound 2d0 ! increase upper bound for radiation generation + +#iseed 5421 ! Start the random number generator with seed iseed +#rand1 0 ! skipping rand2*100000000+rand1 numbers (see RM48 +#rand2 0 ! short writeup in CERNLIB). +#manyseeds 1 ! Used to perform multiple runs with different random + ! seeds in the same directory. + ! If set to 1, the program asks for an integer j; + ! The file pwgseeds.dat at line j is read, and the + ! integer at line j is used to initialize the random + ! sequence for the generation of the event. + ! The event file is called pwgevents-'j'.lhe + +doublefsr 1 ! Default 0; if 1 use new mechanism to generate regions + ! such that the emitted harder than the + ! emitter in FSR is suppressed. If doublefsr=0 this is + ! only the case for emitted gluons (old behaviour). If + ! 1 it is also applied to emitted quarks. + ! If set, it strongly reduces spikes on showered output. + + + +par_diexp 4 ! default is 2. With 4 there is a stronger separation +par_dijexp 4 ! of regions, it may help to reduce spikes when generating +par_2gsupp 4 ! weighted events. diff --git a/MC/config/PWGGAJE/hooks/jets_hook.C b/MC/config/PWGGAJE/hooks/jets_hook.C index 5191160b7..b7e8cb4b3 100644 --- a/MC/config/PWGGAJE/hooks/jets_hook.C +++ b/MC/config/PWGGAJE/hooks/jets_hook.C @@ -1,4 +1,4 @@ -R__ADD_INCLUDE_PATH($O2DPG_ROOT) +R__ADD_INCLUDE_PATH($O2DPG_MC_CONFIG_ROOT) #include "MC/run/common/detector_acceptance.C" #include "Pythia8/Pythia.h" @@ -40,7 +40,10 @@ class UserHooks_jets : public Pythia8::UserHooks // Check the first jet // - bool acc1 = detector_acceptance(mAcceptance, event[id1].phi(), event[id1].eta()); + Float_t phi_j1 = event[id1].phi(); + if (phi_j1 < 0) phi_j1 += 2 * TMath::Pi(); + + bool acc1 = detector_acceptance(mAcceptance, phi_j1, event[id1].eta()); bool okpdg1 = true; if (mOutPartonPDG > 0 && TMath::Abs(event[id1].id()) != mOutPartonPDG) @@ -48,7 +51,9 @@ class UserHooks_jets : public Pythia8::UserHooks // Check the second jet // - bool acc2 = detector_acceptance(mAcceptance, event[id2].phi(), event[id2].eta()); + Float_t phi_j2 = event[id2].phi(); + if (phi_j2 < 0) phi_j2 += 2 * TMath::Pi(); + bool acc2 = detector_acceptance(mAcceptance, phi_j2, event[id2].eta()); bool okpdg2 = true; if (mOutPartonPDG > 0 && TMath::Abs(event[id2].id()) != mOutPartonPDG) diff --git a/MC/config/PWGGAJE/hooks/prompt_gamma_hook.C b/MC/config/PWGGAJE/hooks/prompt_gamma_hook.C index ec8d7e900..9ab6836dc 100644 --- a/MC/config/PWGGAJE/hooks/prompt_gamma_hook.C +++ b/MC/config/PWGGAJE/hooks/prompt_gamma_hook.C @@ -1,4 +1,4 @@ -R__ADD_INCLUDE_PATH($O2DPG_ROOT) +R__ADD_INCLUDE_PATH($O2DPG_MC_CONFIG_ROOT) #include "MC/run/common/detector_acceptance.C" #include "Pythia8/Pythia.h" @@ -62,16 +62,17 @@ class UserHooks_promptgamma : public Pythia8::UserHooks return true; } } - // Select photons within acceptance // - if (detector_acceptance(mAcceptance, event[idGam].phi(), event[idGam].eta())) { + Float_t phiGam = event[idGam].phi(); + if (phiGam < 0) phiGam += 2 * TMath::Pi(); + + if (detector_acceptance(mAcceptance, phiGam, event[idGam].eta())) { // printf("+++ Accepted event +++ \n"); printf("Selected gamma, id %d, PDG %d, status %d, mother %d, E %2.2f, pT %2.2f, eta %2.2f, phi %2.2f\n", idGam, event[idGam].id(), event[idGam].status(), event[idGam].mother1(), event[idGam].e(), event[idGam].pT(), - event[idGam].eta(), event[idGam].phi() * TMath::RadToDeg()); - + event[idGam].eta(), phiGam * TMath::RadToDeg()); // printf("Back-to-back parton, id %d, PDG %d, status %d, mother %d, E %2.2f, pT %2.2f, eta %2.2f, phi %2.2f\n", idPar, // event[idPar].id() , event[idPar].status(), event[idPar].mother1(), // event[idPar].e() , event[idPar].pT(), @@ -83,7 +84,6 @@ class UserHooks_promptgamma : public Pythia8::UserHooks // event[idPar].pT() - event[idGam].pT(), // event[idPar].eta()- event[idGam].eta(), // event[idPar].phi()*TMath::RadToDeg()-event[idGam].phi()*TMath::RadToDeg()); - return false; } else { // printf("--- Rejected event ---\n"); diff --git a/MC/config/PWGGAJE/ini/GeneratorHFJETrigger_ccbar.ini b/MC/config/PWGGAJE/ini/GeneratorHFJETrigger_ccbar.ini new file mode 100644 index 000000000..7a7f58298 --- /dev/null +++ b/MC/config/PWGGAJE/ini/GeneratorHFJETrigger_ccbar.ini @@ -0,0 +1,8 @@ +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C +funcName=GeneratorPythia8GapTriggeredCharm(3, -5, 5) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/pythia8/generator/pythia8_jet_charmtriggers_with_decays.cfg + diff --git a/MC/config/PWGGAJE/ini/GeneratorJE_gapgen11_hook_pp13600GeV.ini b/MC/config/PWGGAJE/ini/GeneratorJE_gapgen11_hook_pp13600GeV.ini new file mode 100644 index 000000000..8a061b68b --- /dev/null +++ b/MC/config/PWGGAJE/ini/GeneratorJE_gapgen11_hook_pp13600GeV.ini @@ -0,0 +1,9 @@ +### jet-jet production with MB Gap 11 +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/external/generator/generator_pythia8_gaptrigger_jets_hook.C +funcName = getGeneratorPythia8GapGenJE(11,"${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/pythia8/generator/pythia8_minbias.cfg","${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/pythia8/generator/pythia8_jet.cfg") + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/pythia8/generator/pythia8_jet.cfg +includePartonEvent=true diff --git a/MC/config/PWGGAJE/ini/GeneratorJE_gapgen2_hook_pp13600GeV.ini b/MC/config/PWGGAJE/ini/GeneratorJE_gapgen2_hook_pp13600GeV.ini new file mode 100644 index 000000000..ff17ff484 --- /dev/null +++ b/MC/config/PWGGAJE/ini/GeneratorJE_gapgen2_hook_pp13600GeV.ini @@ -0,0 +1,9 @@ +### jet-jet production with MB Gap 2 +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/external/generator/generator_pythia8_gaptrigger_jets_hook.C +funcName = getGeneratorPythia8GapGenJE(2,"${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/pythia8/generator/pythia8_minbias.cfg","${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/pythia8/generator/pythia8_jet.cfg") + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/pythia8/generator/pythia8_jet.cfg +includePartonEvent=true diff --git a/MC/config/PWGGAJE/ini/GeneratorJE_gapgen2_hook_pp5360GeV.ini b/MC/config/PWGGAJE/ini/GeneratorJE_gapgen2_hook_pp5360GeV.ini new file mode 100644 index 000000000..3c9f9c917 --- /dev/null +++ b/MC/config/PWGGAJE/ini/GeneratorJE_gapgen2_hook_pp5360GeV.ini @@ -0,0 +1,9 @@ +### jet-jet production with MB Gap 2 for pp anchored to Pb-Pb periods +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/external/generator/generator_pythia8_gaptrigger_jets_hook.C +funcName = getGeneratorPythia8GapGenJE(2,"${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/pythia8/generator/pythia8_minbias_pp5360GeV.cfg","${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/pythia8/generator/pythia8_jet_pp5360GeV.cfg") + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/pythia8/generator/pythia8_jet_pp5360GeV.cfg +includePartonEvent=true diff --git a/MC/config/PWGGAJE/ini/GeneratorJE_gapgen3_hook_pp13600GeV.ini b/MC/config/PWGGAJE/ini/GeneratorJE_gapgen3_hook_pp13600GeV.ini new file mode 100644 index 000000000..60872a4fb --- /dev/null +++ b/MC/config/PWGGAJE/ini/GeneratorJE_gapgen3_hook_pp13600GeV.ini @@ -0,0 +1,9 @@ +### jet-jet production with MB Gap 3 +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/external/generator/generator_pythia8_gaptrigger_jets_hook.C +funcName = getGeneratorPythia8GapGenJE(3,"${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/pythia8/generator/pythia8_minbias.cfg","${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/pythia8/generator/pythia8_jet.cfg") + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/pythia8/generator/pythia8_jet.cfg +includePartonEvent=true diff --git a/MC/config/PWGGAJE/ini/GeneratorJE_gapgen3_hook_pp5360GeV.ini b/MC/config/PWGGAJE/ini/GeneratorJE_gapgen3_hook_pp5360GeV.ini new file mode 100644 index 000000000..c708379a8 --- /dev/null +++ b/MC/config/PWGGAJE/ini/GeneratorJE_gapgen3_hook_pp5360GeV.ini @@ -0,0 +1,9 @@ +### jet-jet production with MB Gap 3 for pp anchored to Pb-Pb periods +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/external/generator/generator_pythia8_gaptrigger_jets_hook.C +funcName = getGeneratorPythia8GapGenJE(3,"${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/pythia8/generator/pythia8_minbias_pp5360GeV.cfg","${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/pythia8/generator/pythia8_jet_pp5360GeV.cfg") + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/pythia8/generator/pythia8_jet_pp5360GeV.cfg +includePartonEvent=true diff --git a/MC/config/PWGGAJE/ini/GeneratorJE_gapgen4_hook_pp13600GeV.ini b/MC/config/PWGGAJE/ini/GeneratorJE_gapgen4_hook_pp13600GeV.ini new file mode 100644 index 000000000..a551ef6ca --- /dev/null +++ b/MC/config/PWGGAJE/ini/GeneratorJE_gapgen4_hook_pp13600GeV.ini @@ -0,0 +1,9 @@ +### jet-jet production with MB Gap 4 +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/external/generator/generator_pythia8_gaptrigger_jets_hook.C +funcName = getGeneratorPythia8GapGenJE(4,"${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/pythia8/generator/pythia8_minbias.cfg","${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/pythia8/generator/pythia8_jet.cfg") + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/pythia8/generator/pythia8_jet.cfg +includePartonEvent=true diff --git a/MC/config/PWGGAJE/ini/GeneratorJE_gapgen5_hook_pp13600GeV.ini b/MC/config/PWGGAJE/ini/GeneratorJE_gapgen5_hook_pp13600GeV.ini new file mode 100644 index 000000000..947b04251 --- /dev/null +++ b/MC/config/PWGGAJE/ini/GeneratorJE_gapgen5_hook_pp13600GeV.ini @@ -0,0 +1,9 @@ +### jet-jet production with MB Gap 5 +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/external/generator/generator_pythia8_gaptrigger_jets_hook.C +funcName = getGeneratorPythia8GapGenJE(5,"${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/pythia8/generator/pythia8_minbias.cfg","${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/pythia8/generator/pythia8_jet.cfg") + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/pythia8/generator/pythia8_jet.cfg +includePartonEvent=true diff --git a/MC/config/PWGGAJE/ini/GeneratorJE_gapgen7_hook_pp13600GeV.ini b/MC/config/PWGGAJE/ini/GeneratorJE_gapgen7_hook_pp13600GeV.ini new file mode 100644 index 000000000..6e3625e80 --- /dev/null +++ b/MC/config/PWGGAJE/ini/GeneratorJE_gapgen7_hook_pp13600GeV.ini @@ -0,0 +1,9 @@ +### jet-jet production with MB Gap 7 +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/external/generator/generator_pythia8_gaptrigger_jets_hook.C +funcName = getGeneratorPythia8GapGenJE(7,"${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/pythia8/generator/pythia8_minbias.cfg","${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/pythia8/generator/pythia8_jet.cfg") + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/pythia8/generator/pythia8_jet.cfg +includePartonEvent=true diff --git a/MC/config/PWGGAJE/ini/GeneratorJE_gapgen9_hook_pp13600GeV.ini b/MC/config/PWGGAJE/ini/GeneratorJE_gapgen9_hook_pp13600GeV.ini new file mode 100644 index 000000000..eeb94e5e5 --- /dev/null +++ b/MC/config/PWGGAJE/ini/GeneratorJE_gapgen9_hook_pp13600GeV.ini @@ -0,0 +1,9 @@ +### jet-jet production with MB Gap 9 +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/external/generator/generator_pythia8_gaptrigger_jets_hook.C +funcName = getGeneratorPythia8GapGenJE(9,"${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/pythia8/generator/pythia8_minbias.cfg","${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/pythia8/generator/pythia8_jet.cfg") + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/pythia8/generator/pythia8_jet.cfg +includePartonEvent=true diff --git a/MC/config/PWGGAJE/ini/GeneratorPythia8Box_parametrisedModel_pythia6Fragmentation_PbPb_cent0010_Bkg.ini b/MC/config/PWGGAJE/ini/GeneratorPythia8Box_parametrisedModel_pythia6Fragmentation_PbPb_cent0010_Bkg.ini new file mode 100644 index 000000000..6a83e56fb --- /dev/null +++ b/MC/config/PWGGAJE/ini/GeneratorPythia8Box_parametrisedModel_pythia6Fragmentation_PbPb_cent0010_Bkg.ini @@ -0,0 +1,7 @@ +### bkg production using pythia8 box generator, to embed jet-jet production into +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/external/generator/parametrisedJetModel/generator_pythia8_box_parametrisedModel_pythia6Fragmentation.C +funcName = generateParametrisedJetModel("parametrisedModel_PbPb_5p36TeV_cent0010.json", true, false) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/pythia8/generator/pythia8box_parametrisedModel.cfg \ No newline at end of file diff --git a/MC/config/PWGGAJE/ini/GeneratorPythia8Box_parametrisedModel_pythia6Fragmentation_PbPb_cent0010_Sgl.ini b/MC/config/PWGGAJE/ini/GeneratorPythia8Box_parametrisedModel_pythia6Fragmentation_PbPb_cent0010_Sgl.ini new file mode 100644 index 000000000..6729dc50b --- /dev/null +++ b/MC/config/PWGGAJE/ini/GeneratorPythia8Box_parametrisedModel_pythia6Fragmentation_PbPb_cent0010_Sgl.ini @@ -0,0 +1,7 @@ +### bkg production using pythia8 box generator, to embed jet-jet production into +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/external/generator/parametrisedJetModel/generator_pythia8_box_parametrisedModel_pythia6Fragmentation.C +funcName = generateParametrisedJetModel("parametrisedModel_PbPb_5p36TeV_cent0010.json", false, true) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/pythia8/generator/pythia8box_parametrisedModel.cfg \ No newline at end of file diff --git a/MC/config/PWGGAJE/ini/GeneratorPythia8Box_parametrisedModel_pythia6Fragmentation_PbPb_cent0010_SglBkg.ini b/MC/config/PWGGAJE/ini/GeneratorPythia8Box_parametrisedModel_pythia6Fragmentation_PbPb_cent0010_SglBkg.ini new file mode 100644 index 000000000..04e70fd25 --- /dev/null +++ b/MC/config/PWGGAJE/ini/GeneratorPythia8Box_parametrisedModel_pythia6Fragmentation_PbPb_cent0010_SglBkg.ini @@ -0,0 +1,7 @@ +### bkg production using pythia8 box generator, to embed jet-jet production into +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/external/generator/parametrisedJetModel/generator_pythia8_box_parametrisedModel_pythia6Fragmentation.C +funcName = generateParametrisedJetModel("parametrisedModel_PbPb_5p36TeV_cent0010.json", true, true) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/pythia8/generator/pythia8box_parametrisedModel.cfg \ No newline at end of file diff --git a/MC/config/PWGGAJE/ini/GeneratorPythia8Box_parametrisedModel_pythia6Fragmentation_PbPb_cent5080_Bkg.ini b/MC/config/PWGGAJE/ini/GeneratorPythia8Box_parametrisedModel_pythia6Fragmentation_PbPb_cent5080_Bkg.ini new file mode 100644 index 000000000..383fda3fe --- /dev/null +++ b/MC/config/PWGGAJE/ini/GeneratorPythia8Box_parametrisedModel_pythia6Fragmentation_PbPb_cent5080_Bkg.ini @@ -0,0 +1,7 @@ +### bkg production using pythia8 box generator, to embed jet-jet production into +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/external/generator/parametrisedJetModel/generator_pythia8_box_parametrisedModel_pythia6Fragmentation.C +funcName = generateParametrisedJetModel("parametrisedModel_PbPb_5p36TeV_cent5080.json", true, false) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/pythia8/generator/pythia8box_parametrisedModel.cfg \ No newline at end of file diff --git a/MC/config/PWGGAJE/ini/GeneratorPythia8Box_parametrisedModel_pythia6Fragmentation_PbPb_cent5080_Sgl.ini b/MC/config/PWGGAJE/ini/GeneratorPythia8Box_parametrisedModel_pythia6Fragmentation_PbPb_cent5080_Sgl.ini new file mode 100644 index 000000000..df4f91cb7 --- /dev/null +++ b/MC/config/PWGGAJE/ini/GeneratorPythia8Box_parametrisedModel_pythia6Fragmentation_PbPb_cent5080_Sgl.ini @@ -0,0 +1,7 @@ +### bkg production using pythia8 box generator, to embed jet-jet production into +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/external/generator/parametrisedJetModel/generator_pythia8_box_parametrisedModel_pythia6Fragmentation.C +funcName = generateParametrisedJetModel("parametrisedModel_PbPb_5p36TeV_cent5080.json", false, true) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/pythia8/generator/pythia8box_parametrisedModel.cfg \ No newline at end of file diff --git a/MC/config/PWGGAJE/ini/GeneratorPythia8Box_parametrisedModel_pythia6Fragmentation_PbPb_cent5080_SglBkg.ini b/MC/config/PWGGAJE/ini/GeneratorPythia8Box_parametrisedModel_pythia6Fragmentation_PbPb_cent5080_SglBkg.ini new file mode 100644 index 000000000..3ffde9809 --- /dev/null +++ b/MC/config/PWGGAJE/ini/GeneratorPythia8Box_parametrisedModel_pythia6Fragmentation_PbPb_cent5080_SglBkg.ini @@ -0,0 +1,7 @@ +### bkg production using pythia8 box generator, to embed jet-jet production into +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/external/generator/parametrisedJetModel/generator_pythia8_box_parametrisedModel_pythia6Fragmentation.C +funcName = generateParametrisedJetModel("parametrisedModel_PbPb_5p36TeV_cent5080.json", true, true) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/pythia8/generator/pythia8box_parametrisedModel.cfg \ No newline at end of file diff --git a/MC/config/PWGGAJE/ini/GeneratorPythia8POWHEG_beauty.ini b/MC/config/PWGGAJE/ini/GeneratorPythia8POWHEG_beauty.ini new file mode 100644 index 000000000..c557aa172 --- /dev/null +++ b/MC/config/PWGGAJE/ini/GeneratorPythia8POWHEG_beauty.ini @@ -0,0 +1,10 @@ +### The external generator derives from GeneratorPythia8. +## The generator allows to run Pythia8 with POWHEG +#---> GeneratorPythia8POWHEG +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/external/generator/generator_pythia8_powheg.C +funcName=getGeneratorJEPythia8POWHEG("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/external/powheg/powheg_beauty_default.input") + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/pythia8/generator/pythia8_powheg.cfg + diff --git a/MC/config/PWGGAJE/ini/GeneratorPythia8POWHEG_beauty_F05.ini b/MC/config/PWGGAJE/ini/GeneratorPythia8POWHEG_beauty_F05.ini new file mode 100644 index 000000000..4be1cf530 --- /dev/null +++ b/MC/config/PWGGAJE/ini/GeneratorPythia8POWHEG_beauty_F05.ini @@ -0,0 +1,9 @@ +### The external generator derives from GeneratorPythia8. +## The generator allows to run Pythia8 with POWHEG +#---> GeneratorPythia8POWHEG +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/external/generator/generator_pythia8_powheg.C +funcName=getGeneratorJEPythia8POWHEG("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/external/powheg/powheg_beauty_F05.input") + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/pythia8/generator/pythia8_powheg.cfg diff --git a/MC/config/PWGGAJE/ini/GeneratorPythia8POWHEG_beauty_F05_R05.ini b/MC/config/PWGGAJE/ini/GeneratorPythia8POWHEG_beauty_F05_R05.ini new file mode 100644 index 000000000..24aa494c7 --- /dev/null +++ b/MC/config/PWGGAJE/ini/GeneratorPythia8POWHEG_beauty_F05_R05.ini @@ -0,0 +1,9 @@ +### The external generator derives from GeneratorPythia8. +## The generator allows to run Pythia8 with POWHEG +#---> GeneratorPythia8POWHEG +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/external/generator/generator_pythia8_powheg.C +funcName=getGeneratorJEPythia8POWHEG("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/external/powheg/powheg_beauty_F05_R05.input") + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/pythia8/generator/pythia8_powheg.cfg diff --git a/MC/config/PWGGAJE/ini/GeneratorPythia8POWHEG_beauty_F2.ini b/MC/config/PWGGAJE/ini/GeneratorPythia8POWHEG_beauty_F2.ini new file mode 100644 index 000000000..effba39eb --- /dev/null +++ b/MC/config/PWGGAJE/ini/GeneratorPythia8POWHEG_beauty_F2.ini @@ -0,0 +1,9 @@ +### The external generator derives from GeneratorPythia8. +## The generator allows to run Pythia8 with POWHEG +#---> GeneratorPythia8POWHEG +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/external/generator/generator_pythia8_powheg.C +funcName=getGeneratorJEPythia8POWHEG("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/external/powheg/powheg_beauty_F2.input") + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/pythia8/generator/pythia8_powheg.cfg diff --git a/MC/config/PWGGAJE/ini/GeneratorPythia8POWHEG_beauty_F2_R2.ini b/MC/config/PWGGAJE/ini/GeneratorPythia8POWHEG_beauty_F2_R2.ini new file mode 100644 index 000000000..04457b78c --- /dev/null +++ b/MC/config/PWGGAJE/ini/GeneratorPythia8POWHEG_beauty_F2_R2.ini @@ -0,0 +1,9 @@ +### The external generator derives from GeneratorPythia8. +## The generator allows to run Pythia8 with POWHEG +#---> GeneratorPythia8POWHEG +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/external/generator/generator_pythia8_powheg.C +funcName=getGeneratorJEPythia8POWHEG("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/external/powheg/powheg_beauty_F2_R2.input") + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/pythia8/generator/pythia8_powheg.cfg diff --git a/MC/config/PWGGAJE/ini/GeneratorPythia8POWHEG_beauty_MHigh.ini b/MC/config/PWGGAJE/ini/GeneratorPythia8POWHEG_beauty_MHigh.ini new file mode 100644 index 000000000..1a89f3b9c --- /dev/null +++ b/MC/config/PWGGAJE/ini/GeneratorPythia8POWHEG_beauty_MHigh.ini @@ -0,0 +1,9 @@ +### The external generator derives from GeneratorPythia8. +## The generator allows to run Pythia8 with POWHEG +#---> GeneratorPythia8POWHEG +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/external/generator/generator_pythia8_powheg.C +funcName=getGeneratorJEPythia8POWHEG("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/external/powheg/powheg_beauty_MHigh.input") + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/pythia8/generator/pythia8_powheg.cfg diff --git a/MC/config/PWGGAJE/ini/GeneratorPythia8POWHEG_beauty_MLow.ini b/MC/config/PWGGAJE/ini/GeneratorPythia8POWHEG_beauty_MLow.ini new file mode 100644 index 000000000..647e0b6a6 --- /dev/null +++ b/MC/config/PWGGAJE/ini/GeneratorPythia8POWHEG_beauty_MLow.ini @@ -0,0 +1,9 @@ +### The external generator derives from GeneratorPythia8. +## The generator allows to run Pythia8 with POWHEG +#---> GeneratorPythia8POWHEG +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/external/generator/generator_pythia8_powheg.C +funcName=getGeneratorJEPythia8POWHEG("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/external/powheg/powheg_beauty_MLow.input") + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/pythia8/generator/pythia8_powheg.cfg diff --git a/MC/config/PWGGAJE/ini/GeneratorPythia8POWHEG_beauty_R05.ini b/MC/config/PWGGAJE/ini/GeneratorPythia8POWHEG_beauty_R05.ini new file mode 100644 index 000000000..e940aa088 --- /dev/null +++ b/MC/config/PWGGAJE/ini/GeneratorPythia8POWHEG_beauty_R05.ini @@ -0,0 +1,9 @@ +### The external generator derives from GeneratorPythia8. +## The generator allows to run Pythia8 with POWHEG +#---> GeneratorPythia8POWHEG +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/external/generator/generator_pythia8_powheg.C +funcName=getGeneratorJEPythia8POWHEG("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/external/powheg/powheg_beauty_R05.input") + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/pythia8/generator/pythia8_powheg.cfg diff --git a/MC/config/PWGGAJE/ini/GeneratorPythia8POWHEG_beauty_R2.ini b/MC/config/PWGGAJE/ini/GeneratorPythia8POWHEG_beauty_R2.ini new file mode 100644 index 000000000..e846873b5 --- /dev/null +++ b/MC/config/PWGGAJE/ini/GeneratorPythia8POWHEG_beauty_R2.ini @@ -0,0 +1,9 @@ +### The external generator derives from GeneratorPythia8. +## The generator allows to run Pythia8 with POWHEG +#---> GeneratorPythia8POWHEG +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/external/generator/generator_pythia8_powheg.C +funcName=getGeneratorJEPythia8POWHEG("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/external/powheg/powheg_beauty_R2.input") + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/pythia8/generator/pythia8_powheg.cfg diff --git a/MC/config/PWGGAJE/ini/GeneratorPythia8POWHEG_charm.ini b/MC/config/PWGGAJE/ini/GeneratorPythia8POWHEG_charm.ini new file mode 100644 index 000000000..df8a6a1da --- /dev/null +++ b/MC/config/PWGGAJE/ini/GeneratorPythia8POWHEG_charm.ini @@ -0,0 +1,9 @@ +### The external generator derives from GeneratorPythia8. +## The generator allows to run Pythia8 with POWHEG +#---> GeneratorPythia8POWHEG +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/external/generator/generator_pythia8_powheg.C +funcName=getGeneratorJEPythia8POWHEG("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/external/powheg/powheg_charm_default.input") + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/pythia8/generator/pythia8_powheg.cfg diff --git a/MC/config/PWGGAJE/ini/GeneratorPythia8POWHEG_charm_F05.ini b/MC/config/PWGGAJE/ini/GeneratorPythia8POWHEG_charm_F05.ini new file mode 100644 index 000000000..34bc652e3 --- /dev/null +++ b/MC/config/PWGGAJE/ini/GeneratorPythia8POWHEG_charm_F05.ini @@ -0,0 +1,9 @@ +### The external generator derives from GeneratorPythia8. +## The generator allows to run Pythia8 with POWHEG +#---> GeneratorPythia8POWHEG +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/external/generator/generator_pythia8_powheg.C +funcName=getGeneratorJEPythia8POWHEG("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/external/powheg/powheg_charm_F05.input") + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/pythia8/generator/pythia8_powheg.cfg diff --git a/MC/config/PWGGAJE/ini/GeneratorPythia8POWHEG_charm_F05_R05.ini b/MC/config/PWGGAJE/ini/GeneratorPythia8POWHEG_charm_F05_R05.ini new file mode 100644 index 000000000..e4ab0f479 --- /dev/null +++ b/MC/config/PWGGAJE/ini/GeneratorPythia8POWHEG_charm_F05_R05.ini @@ -0,0 +1,9 @@ +### The external generator derives from GeneratorPythia8. +## The generator allows to run Pythia8 with POWHEG +#---> GeneratorPythia8POWHEG +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/external/generator/generator_pythia8_powheg.C +funcName=getGeneratorJEPythia8POWHEG("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/external/powheg/powheg_charm_F05_R05.input") + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/pythia8/generator/pythia8_powheg.cfg diff --git a/MC/config/PWGGAJE/ini/GeneratorPythia8POWHEG_charm_F2.ini b/MC/config/PWGGAJE/ini/GeneratorPythia8POWHEG_charm_F2.ini new file mode 100644 index 000000000..36aa6b6c2 --- /dev/null +++ b/MC/config/PWGGAJE/ini/GeneratorPythia8POWHEG_charm_F2.ini @@ -0,0 +1,9 @@ +### The external generator derives from GeneratorPythia8. +## The generator allows to run Pythia8 with POWHEG +#---> GeneratorPythia8POWHEG +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/external/generator/generator_pythia8_powheg.C +funcName=getGeneratorJEPythia8POWHEG("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/external/powheg/powheg_charm_F2.input") + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/pythia8/generator/pythia8_powheg.cfg diff --git a/MC/config/PWGGAJE/ini/GeneratorPythia8POWHEG_charm_F2_R2.ini b/MC/config/PWGGAJE/ini/GeneratorPythia8POWHEG_charm_F2_R2.ini new file mode 100644 index 000000000..37d417322 --- /dev/null +++ b/MC/config/PWGGAJE/ini/GeneratorPythia8POWHEG_charm_F2_R2.ini @@ -0,0 +1,9 @@ +### The external generator derives from GeneratorPythia8. +## The generator allows to run Pythia8 with POWHEG +#---> GeneratorPythia8POWHEG +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/external/generator/generator_pythia8_powheg.C +funcName=getGeneratorJEPythia8POWHEG("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/external/powheg/powheg_charm_F2_R2.input") + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/pythia8/generator/pythia8_powheg.cfg diff --git a/MC/config/PWGGAJE/ini/GeneratorPythia8POWHEG_charm_MHigh.ini b/MC/config/PWGGAJE/ini/GeneratorPythia8POWHEG_charm_MHigh.ini new file mode 100644 index 000000000..73f83de02 --- /dev/null +++ b/MC/config/PWGGAJE/ini/GeneratorPythia8POWHEG_charm_MHigh.ini @@ -0,0 +1,9 @@ +### The external generator derives from GeneratorPythia8. +## The generator allows to run Pythia8 with POWHEG +#---> GeneratorPythia8POWHEG +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/external/generator/generator_pythia8_powheg.C +funcName=getGeneratorJEPythia8POWHEG("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/external/powheg/powheg_charm_MHigh.input") + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/pythia8/generator/pythia8_powheg.cfg diff --git a/MC/config/PWGGAJE/ini/GeneratorPythia8POWHEG_charm_MLow.ini b/MC/config/PWGGAJE/ini/GeneratorPythia8POWHEG_charm_MLow.ini new file mode 100644 index 000000000..ed7c5c7b7 --- /dev/null +++ b/MC/config/PWGGAJE/ini/GeneratorPythia8POWHEG_charm_MLow.ini @@ -0,0 +1,9 @@ +### The external generator derives from GeneratorPythia8. +## The generator allows to run Pythia8 with POWHEG +#---> GeneratorPythia8POWHEG +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/external/generator/generator_pythia8_powheg.C +funcName=getGeneratorJEPythia8POWHEG("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/external/powheg/powheg_charm_MLow.input") + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/pythia8/generator/pythia8_powheg.cfg diff --git a/MC/config/PWGGAJE/ini/GeneratorPythia8POWHEG_charm_R05.ini b/MC/config/PWGGAJE/ini/GeneratorPythia8POWHEG_charm_R05.ini new file mode 100644 index 000000000..61680cab2 --- /dev/null +++ b/MC/config/PWGGAJE/ini/GeneratorPythia8POWHEG_charm_R05.ini @@ -0,0 +1,9 @@ +### The external generator derives from GeneratorPythia8. +## The generator allows to run Pythia8 with POWHEG +#---> GeneratorPythia8POWHEG +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/external/generator/generator_pythia8_powheg.C +funcName=getGeneratorJEPythia8POWHEG("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/external/powheg/powheg_charm_R05.input") + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/pythia8/generator/pythia8_powheg.cfg diff --git a/MC/config/PWGGAJE/ini/GeneratorPythia8POWHEG_charm_R2.ini b/MC/config/PWGGAJE/ini/GeneratorPythia8POWHEG_charm_R2.ini new file mode 100644 index 000000000..e8185c68d --- /dev/null +++ b/MC/config/PWGGAJE/ini/GeneratorPythia8POWHEG_charm_R2.ini @@ -0,0 +1,9 @@ +### The external generator derives from GeneratorPythia8. +## The generator allows to run Pythia8 with POWHEG +#---> GeneratorPythia8POWHEG +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/external/generator/generator_pythia8_powheg.C +funcName=getGeneratorJEPythia8POWHEG("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/external/powheg/powheg_charm_R2.input") + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/pythia8/generator/pythia8_powheg.cfg diff --git a/MC/config/PWGGAJE/ini/GeneratorPythia8POWHEG_jetjet_13600.ini b/MC/config/PWGGAJE/ini/GeneratorPythia8POWHEG_jetjet_13600.ini new file mode 100644 index 000000000..201e7a734 --- /dev/null +++ b/MC/config/PWGGAJE/ini/GeneratorPythia8POWHEG_jetjet_13600.ini @@ -0,0 +1,10 @@ +### The external generator derives from GeneratorPythia8. +## The generator allows to run Pythia8 with POWHEG +## The option '3' which is given to getGeneratorJEPythia8POWHEG selects the pwhg_main_dijet process +#---> GeneratorPythia8POWHEG +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/external/generator/generator_pythia8_powheg.C +funcName=getGeneratorJEPythia8POWHEG("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/external/powheg/powheg_jetjet_13600.input","",3) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/pythia8/generator/pythia8_powheg.cfg diff --git a/MC/config/PWGGAJE/ini/GeneratorPythia8POWHEG_jetjet_13600_mini.ini b/MC/config/PWGGAJE/ini/GeneratorPythia8POWHEG_jetjet_13600_mini.ini new file mode 100644 index 000000000..7cedd849e --- /dev/null +++ b/MC/config/PWGGAJE/ini/GeneratorPythia8POWHEG_jetjet_13600_mini.ini @@ -0,0 +1,11 @@ +### The external generator derives from GeneratorPythia8. +## The generator allows to run Pythia8 with POWHEG +## The option '3' which is given to getGeneratorJEPythia8POWHEG selects the pwhg_main_dijet process +## This is a less computing intensive alternative to the original GeneratorPythia8POWHEG_jetjet_13600.ini. It keeps the event generation close to 5 minutes +#---> GeneratorPythia8POWHEG +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/external/generator/generator_pythia8_powheg.C +funcName=getGeneratorJEPythia8POWHEG("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/external/powheg/powheg_jetjet_13600_mini.input","",3) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/pythia8/generator/pythia8_powheg.cfg diff --git a/MC/config/PWGGAJE/ini/hook_jets.ini b/MC/config/PWGGAJE/ini/hook_jets.ini index 054922509..62b470c69 100644 --- a/MC/config/PWGGAJE/ini/hook_jets.ini +++ b/MC/config/PWGGAJE/ini/hook_jets.ini @@ -1,5 +1,6 @@ [GeneratorPythia8] config=pythia8.cfg -hooksFileName=${O2DPG_ROOT}/MC/config/PWGGAJE/hooks/jets_hook.C +hooksFileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/hooks/jets_hook.C hooksFuncName=pythia8_userhooks_jets() +includePartonEvent=true diff --git a/MC/config/PWGGAJE/ini/hook_prompt_gamma.ini b/MC/config/PWGGAJE/ini/hook_prompt_gamma.ini index fc65758a6..b8864f8d7 100644 --- a/MC/config/PWGGAJE/ini/hook_prompt_gamma.ini +++ b/MC/config/PWGGAJE/ini/hook_prompt_gamma.ini @@ -1,5 +1,5 @@ [GeneratorPythia8] config=pythia8.cfg -hooksFileName=${O2DPG_ROOT}/MC/config/PWGGAJE/hooks/prompt_gamma_hook.C +hooksFileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/hooks/prompt_gamma_hook.C hooksFuncName=pythia8_userhooks_promptgamma() diff --git a/MC/config/PWGGAJE/ini/hook_prompt_gamma_allcalo.ini b/MC/config/PWGGAJE/ini/hook_prompt_gamma_allcalo.ini index 88bfaed74..829ebc2e5 100644 --- a/MC/config/PWGGAJE/ini/hook_prompt_gamma_allcalo.ini +++ b/MC/config/PWGGAJE/ini/hook_prompt_gamma_allcalo.ini @@ -1,5 +1,5 @@ [GeneratorPythia8] config=pythia8.cfg -hooksFileName=${O2DPG_ROOT}/MC/config/PWGGAJE/hooks/prompt_gamma_hook.C +hooksFileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/hooks/prompt_gamma_hook.C hooksFuncName=pythia8_userhooks_promptgamma(1) diff --git a/MC/config/PWGGAJE/ini/hook_prompt_gamma_focal.ini b/MC/config/PWGGAJE/ini/hook_prompt_gamma_focal.ini new file mode 100644 index 000000000..d7b8b40fa --- /dev/null +++ b/MC/config/PWGGAJE/ini/hook_prompt_gamma_focal.ini @@ -0,0 +1,4 @@ +[GeneratorPythia8] +config=pythia8.cfg +hooksFileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/hooks/prompt_gamma_hook.C +hooksFuncName=pythia8_userhooks_promptgamma(7) \ No newline at end of file diff --git a/MC/config/PWGGAJE/ini/hook_prompt_gamma_gap.ini b/MC/config/PWGGAJE/ini/hook_prompt_gamma_gap.ini new file mode 100644 index 000000000..7747e0ed5 --- /dev/null +++ b/MC/config/PWGGAJE/ini/hook_prompt_gamma_gap.ini @@ -0,0 +1,7 @@ +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/external/generator/generator_pythia8_gaptrigger_promptphotons_hook.C +funcName = getGeneratorPythia8GapGenJEPhoton(2,"${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/pythia8/generator/pythia8_minbias.cfg","${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/pythia8/generator/pythia8_promptphoton.cfg") + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/pythia8/generator/pythia8_promptphoton.cfg +includePartonEvent=true diff --git a/MC/config/PWGGAJE/ini/hook_prompt_gamma_gap3.ini b/MC/config/PWGGAJE/ini/hook_prompt_gamma_gap3.ini new file mode 100644 index 000000000..c8d7ed83b --- /dev/null +++ b/MC/config/PWGGAJE/ini/hook_prompt_gamma_gap3.ini @@ -0,0 +1,7 @@ +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/external/generator/generator_pythia8_gaptrigger_promptphotons_hook.C +funcName = getGeneratorPythia8GapGenJEPhoton(3,"${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/pythia8/generator/pythia8_minbias.cfg","${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/pythia8/generator/pythia8_promptphoton.cfg") + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/pythia8/generator/pythia8_promptphoton.cfg +includePartonEvent=true diff --git a/MC/config/PWGGAJE/ini/hook_prompt_gamma_gap5.ini b/MC/config/PWGGAJE/ini/hook_prompt_gamma_gap5.ini new file mode 100644 index 000000000..503ad27fe --- /dev/null +++ b/MC/config/PWGGAJE/ini/hook_prompt_gamma_gap5.ini @@ -0,0 +1,7 @@ +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/external/generator/generator_pythia8_gaptrigger_promptphotons_hook.C +funcName = getGeneratorPythia8GapGenJEPhoton(5,"${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/pythia8/generator/pythia8_minbias.cfg","${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/pythia8/generator/pythia8_promptphoton.cfg") + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/pythia8/generator/pythia8_promptphoton.cfg +includePartonEvent=true diff --git a/MC/config/PWGGAJE/ini/hook_prompt_gamma_gap7.ini b/MC/config/PWGGAJE/ini/hook_prompt_gamma_gap7.ini new file mode 100644 index 000000000..8035d312b --- /dev/null +++ b/MC/config/PWGGAJE/ini/hook_prompt_gamma_gap7.ini @@ -0,0 +1,7 @@ +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/external/generator/generator_pythia8_gaptrigger_promptphotons_hook.C +funcName = getGeneratorPythia8GapGenJEPhoton(7,"${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/pythia8/generator/pythia8_minbias.cfg","${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/pythia8/generator/pythia8_promptphoton.cfg") + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/pythia8/generator/pythia8_promptphoton.cfg +includePartonEvent=true diff --git a/MC/config/PWGGAJE/ini/hook_prompt_gamma_gap9.ini b/MC/config/PWGGAJE/ini/hook_prompt_gamma_gap9.ini new file mode 100644 index 000000000..539ff761b --- /dev/null +++ b/MC/config/PWGGAJE/ini/hook_prompt_gamma_gap9.ini @@ -0,0 +1,7 @@ +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/external/generator/generator_pythia8_gaptrigger_promptphotons_hook.C +funcName = getGeneratorPythia8GapGenJEPhoton(9,"${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/pythia8/generator/pythia8_minbias.cfg","${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/pythia8/generator/pythia8_promptphoton.cfg") + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/pythia8/generator/pythia8_promptphoton.cfg +includePartonEvent=true diff --git a/MC/config/PWGGAJE/ini/prompt_gamma_gap.ini b/MC/config/PWGGAJE/ini/prompt_gamma_gap.ini new file mode 100644 index 000000000..dc69687f3 --- /dev/null +++ b/MC/config/PWGGAJE/ini/prompt_gamma_gap.ini @@ -0,0 +1,7 @@ +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/external/generator/generator_pythia8_gaptrigger_hook.C +funcName = getGeneratorPythia8GapGenJEMB(2,"${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/pythia8/generator/pythia8_minbias.cfg","${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/pythia8/generator/pythia8_promptphoton.cfg") + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/pythia8/generator/pythia8_promptphoton.cfg +includePartonEvent=true \ No newline at end of file diff --git a/MC/config/PWGGAJE/ini/tests/GeneratorHFJETrigger_ccbar.C b/MC/config/PWGGAJE/ini/tests/GeneratorHFJETrigger_ccbar.C new file mode 100644 index 000000000..ef24e0d3e --- /dev/null +++ b/MC/config/PWGGAJE/ini/tests/GeneratorHFJETrigger_ccbar.C @@ -0,0 +1,125 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + int checkPdgQuark{4}; + float ratioTrigger = 1. / 3; // one event triggered out of 3 + std::vector checkPdgHadron{411, 421, 431, 443, 4122, 4132, 4232, 4332}; + std::map>> checkHadronDecays{ + // sorted pdg of daughters + {411, {{-321, 211, 211}, {-313, 211}, {211, 311}, {211, 333}}}, // D+ + {421, {{-321, 211}, {-321, 111, 211}}}, // D0 + {431, {{211, 333}}}, // Ds+ + {443, {{-11, 11}}}, // Jpsi + {4122, + {{-313, 2212}, {-321, 2224}, {211, 3124}, {-321, 211, 2212}}}, // Lc+ + {4132, {{211, 3312}}}, // Xic0 + {4232, + {{-313, 2212}, + {-321, 3324}, + {211, 211, 3312}, + {-321, 211, 2212}}}, // Xic+ + {4332, {{211, 3334}}} // Omegac+ + }; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int nQuarks{}, nSignals{}, nSignalGoodDecay{}; + auto nEvents = tree->GetEntries(); + + // Setting up event header to access event weight info + o2::dataformats::MCEventHeader *eventHeader = nullptr; + ; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + int weight1Counter = 0; + bool isvalid; + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + + float eventWeight = eventHeader->getInfo("weight", isvalid); + if (!isvalid) { + std::cerr << "Could not retrieve event weight from MCEventHeader\n"; + return 1; + } + if (abs(eventWeight - 1) < + 1E-5) { // checks that the event weights are not unitary + weight1Counter++; + } + + for (auto &track : *tracks) { + auto pdg = track.GetPdgCode(); + if (std::abs(pdg) == checkPdgQuark) { + nQuarks++; + continue; + } + if (std::find(checkPdgHadron.begin(), checkPdgHadron.end(), + std::abs(pdg)) != checkPdgHadron.end()) // found signal + { + // count signal PDG + nSignals++; + + std::vector pdgsDecay{}; + std::vector pdgsDecayAntiPart{}; + for (int j{track.getFirstDaughterTrackId()}; + j <= track.getLastDaughterTrackId(); ++j) { + auto pdgDau = tracks->at(j).GetPdgCode(); + pdgsDecay.push_back(pdgDau); + if (pdgDau != 333) { // phi is antiparticle of itself + pdgsDecayAntiPart.push_back(-pdgDau); + } else { + pdgsDecayAntiPart.push_back(pdgDau); + } + } + + std::sort(pdgsDecay.begin(), pdgsDecay.end()); + std::sort(pdgsDecayAntiPart.begin(), pdgsDecayAntiPart.end()); + + for (auto &decay : checkHadronDecays[std::abs(pdg)]) { + if (pdgsDecay == decay || pdgsDecayAntiPart == decay) { + nSignalGoodDecay++; + break; + } + } + } + } + } + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + std::cout << Form("# %d (anti)quarks: ", checkPdgQuark) << nQuarks << "\n"; + std::cout << "# signal hadrons: " << nSignals << "\n"; + std::cout << "# signal hadrons decaying in the correct channel: " + << nSignalGoodDecay << "\n"; + + if (nQuarks < + 2 * nEvents * + ratioTrigger) // we expect anyway more because the same quark is + // repeated several time, after each gluon radiation + { + std::cerr << "Number of generated (anti)quarks " << checkPdgQuark + << " lower than expected\n"; + return 1; + } + + float fracForcedDecays = float(nSignalGoodDecay) / nSignals; + if (fracForcedDecays < 0.9) // we put some tolerance (e.g. due to oscillations + // which might change the final state) + { + std::cerr << "Fraction of signals decaying into the correct channel " + << fracForcedDecays << " lower than expected\n"; + return 1; + } + + if (weight1Counter == nEvents) { + std::cerr << "All events have a unitary weight\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGGAJE/ini/tests/GeneratorJE_gapgen11_hook_pp13600GeV.C b/MC/config/PWGGAJE/ini/tests/GeneratorJE_gapgen11_hook_pp13600GeV.C new file mode 100644 index 000000000..f8f35ea10 --- /dev/null +++ b/MC/config/PWGGAJE/ini/tests/GeneratorJE_gapgen11_hook_pp13600GeV.C @@ -0,0 +1,71 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + + float ratioTrigger = 1./11; // one event triggered out of 11 + + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int nEventsMB{}, nEventsJetJet{}; + float sumWeightsMB{}, sumWeightsJetJet{}; + int sumTracks{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + + // check subgenerator information and event weights + if (eventHeader->hasInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID)) { + bool isValid = false; + int subGeneratorId = eventHeader->getInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID, isValid); + if (eventHeader->hasInfo(o2::dataformats::MCInfoKeys::weight)) { + float weight = eventHeader->getInfo(o2::dataformats::MCInfoKeys::weight,isValid); + if (subGeneratorId == 0) { + nEventsMB++; + sumWeightsMB += weight; + } + else if (subGeneratorId == 1) { + nEventsJetJet++; + sumWeightsJetJet += weight; + } + } + } + sumTracks += tracks->size(); + } + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + std::cout << "# MB events: " << nEventsMB << "\n"; + std::cout << " sum of weights for MB events: " << sumWeightsMB << "\n"; + std::cout << "# Jet-jet events " << nEventsJetJet << "\n"; + std::cout << " sum of weights jet-jet events: " << sumWeightsJetJet << "\n"; + std::cout << "# tracks summed over all events (jet-jet + MB): " << sumTracks << "\n"; + + if (nEventsMB < nEvents * (1 - ratioTrigger) * 0.85 || nEventsMB > nEvents * (1 - ratioTrigger) * 1.15) { // we put some tolerance since the number of generated events is small + std::cerr << "Number of generated MB events different than expected\n"; + return 1; + } + if (nEventsJetJet < nEvents * ratioTrigger * 0.85 || nEventsJetJet > nEvents * ratioTrigger * 1.15) { + std::cerr << "Number of jet-jet generated events different than expected\n"; + return 1; + } + if(nEventsMB < sumWeightsMB * 0.85 || nEventsMB > sumWeightsMB * 1.15) { + std::cerr << "Weights of MB events do not = 1 as expected\n"; + return 1; + } + if(sumTracks < 1) { + std::cerr << "No tracks in simulated events\n"; + return 1; + } + return 0; +} diff --git a/MC/config/PWGGAJE/ini/tests/GeneratorJE_gapgen2_hook_pp13600GeV.C b/MC/config/PWGGAJE/ini/tests/GeneratorJE_gapgen2_hook_pp13600GeV.C new file mode 100644 index 000000000..3364b9f6d --- /dev/null +++ b/MC/config/PWGGAJE/ini/tests/GeneratorJE_gapgen2_hook_pp13600GeV.C @@ -0,0 +1,71 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + + float ratioTrigger = 1./2; // one event triggered out of 2 + + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int nEventsMB{}, nEventsJetJet{}; + float sumWeightsMB{}, sumWeightsJetJet{}; + int sumTracks{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + + // check subgenerator information and event weights + if (eventHeader->hasInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID)) { + bool isValid = false; + int subGeneratorId = eventHeader->getInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID, isValid); + if (eventHeader->hasInfo(o2::dataformats::MCInfoKeys::weight)) { + float weight = eventHeader->getInfo(o2::dataformats::MCInfoKeys::weight,isValid); + if (subGeneratorId == 0) { + nEventsMB++; + sumWeightsMB += weight; + } + else if (subGeneratorId == 1) { + nEventsJetJet++; + sumWeightsJetJet += weight; + } + } + } + sumTracks += tracks->size(); + } + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + std::cout << "# MB events: " << nEventsMB << "\n"; + std::cout << " sum of weights for MB events: " << sumWeightsMB << "\n"; + std::cout << "# Jet-jet events " << nEventsJetJet << "\n"; + std::cout << " sum of weights jet-jet events: " << sumWeightsJetJet << "\n"; + std::cout << "# tracks summed over all events (jet-jet + MB): " << sumTracks << "\n"; + + if (nEventsMB < nEvents * (1 - ratioTrigger) * 0.95 || nEventsMB > nEvents * (1 - ratioTrigger) * 1.05) { // we put some tolerance since the number of generated events is small + std::cerr << "Number of generated MB events different than expected\n"; + return 1; + } + if (nEventsJetJet < nEvents * ratioTrigger * 0.95 || nEventsJetJet > nEvents * ratioTrigger * 1.05) { + std::cerr << "Number of jet-jet generated events different than expected\n"; + return 1; + } + if(nEventsMB < sumWeightsMB * 0.95 || nEventsMB > sumWeightsMB * 1.05) { + std::cerr << "Weights of MB events do not = 1 as expected\n"; + return 1; + } + if(sumTracks < 1) { + std::cerr << "No tracks in simulated events\n"; + return 1; + } + return 0; +} diff --git a/MC/config/PWGGAJE/ini/tests/GeneratorJE_gapgen2_hook_pp5360GeV.C b/MC/config/PWGGAJE/ini/tests/GeneratorJE_gapgen2_hook_pp5360GeV.C new file mode 100644 index 000000000..3364b9f6d --- /dev/null +++ b/MC/config/PWGGAJE/ini/tests/GeneratorJE_gapgen2_hook_pp5360GeV.C @@ -0,0 +1,71 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + + float ratioTrigger = 1./2; // one event triggered out of 2 + + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int nEventsMB{}, nEventsJetJet{}; + float sumWeightsMB{}, sumWeightsJetJet{}; + int sumTracks{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + + // check subgenerator information and event weights + if (eventHeader->hasInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID)) { + bool isValid = false; + int subGeneratorId = eventHeader->getInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID, isValid); + if (eventHeader->hasInfo(o2::dataformats::MCInfoKeys::weight)) { + float weight = eventHeader->getInfo(o2::dataformats::MCInfoKeys::weight,isValid); + if (subGeneratorId == 0) { + nEventsMB++; + sumWeightsMB += weight; + } + else if (subGeneratorId == 1) { + nEventsJetJet++; + sumWeightsJetJet += weight; + } + } + } + sumTracks += tracks->size(); + } + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + std::cout << "# MB events: " << nEventsMB << "\n"; + std::cout << " sum of weights for MB events: " << sumWeightsMB << "\n"; + std::cout << "# Jet-jet events " << nEventsJetJet << "\n"; + std::cout << " sum of weights jet-jet events: " << sumWeightsJetJet << "\n"; + std::cout << "# tracks summed over all events (jet-jet + MB): " << sumTracks << "\n"; + + if (nEventsMB < nEvents * (1 - ratioTrigger) * 0.95 || nEventsMB > nEvents * (1 - ratioTrigger) * 1.05) { // we put some tolerance since the number of generated events is small + std::cerr << "Number of generated MB events different than expected\n"; + return 1; + } + if (nEventsJetJet < nEvents * ratioTrigger * 0.95 || nEventsJetJet > nEvents * ratioTrigger * 1.05) { + std::cerr << "Number of jet-jet generated events different than expected\n"; + return 1; + } + if(nEventsMB < sumWeightsMB * 0.95 || nEventsMB > sumWeightsMB * 1.05) { + std::cerr << "Weights of MB events do not = 1 as expected\n"; + return 1; + } + if(sumTracks < 1) { + std::cerr << "No tracks in simulated events\n"; + return 1; + } + return 0; +} diff --git a/MC/config/PWGGAJE/ini/tests/GeneratorJE_gapgen3_hook_pp13600GeV.C b/MC/config/PWGGAJE/ini/tests/GeneratorJE_gapgen3_hook_pp13600GeV.C new file mode 100644 index 000000000..db42706cb --- /dev/null +++ b/MC/config/PWGGAJE/ini/tests/GeneratorJE_gapgen3_hook_pp13600GeV.C @@ -0,0 +1,71 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + + float ratioTrigger = 1./3; // one event triggered out of 3 + + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int nEventsMB{}, nEventsJetJet{}; + float sumWeightsMB{}, sumWeightsJetJet{}; + int sumTracks{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + + // check subgenerator information and event weights + if (eventHeader->hasInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID)) { + bool isValid = false; + int subGeneratorId = eventHeader->getInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID, isValid); + if (eventHeader->hasInfo(o2::dataformats::MCInfoKeys::weight)) { + float weight = eventHeader->getInfo(o2::dataformats::MCInfoKeys::weight,isValid); + if (subGeneratorId == 0) { + nEventsMB++; + sumWeightsMB += weight; + } + else if (subGeneratorId == 1) { + nEventsJetJet++; + sumWeightsJetJet += weight; + } + } + } + sumTracks += tracks->size(); + } + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + std::cout << "# MB events: " << nEventsMB << "\n"; + std::cout << " sum of weights for MB events: " << sumWeightsMB << "\n"; + std::cout << "# Jet-jet events " << nEventsJetJet << "\n"; + std::cout << " sum of weights jet-jet events: " << sumWeightsJetJet << "\n"; + std::cout << "# tracks summed over all events (jet-jet + MB): " << sumTracks << "\n"; + + if (nEventsMB < nEvents * (1 - ratioTrigger) * 0.95 || nEventsMB > nEvents * (1 - ratioTrigger) * 1.05) { // we put some tolerance since the number of generated events is small + std::cerr << "Number of generated MB events different than expected\n"; + return 1; + } + if (nEventsJetJet < nEvents * ratioTrigger * 0.95 || nEventsJetJet > nEvents * ratioTrigger * 1.05) { + std::cerr << "Number of jet-jet generated events different than expected\n"; + return 1; + } + if(nEventsMB < sumWeightsMB * 0.95 || nEventsMB > sumWeightsMB * 1.05) { + std::cerr << "Weights of MB events do not = 1 as expected\n"; + return 1; + } + if(sumTracks < 1) { + std::cerr << "No tracks in simulated events\n"; + return 1; + } + return 0; +} diff --git a/MC/config/PWGGAJE/ini/tests/GeneratorJE_gapgen3_hook_pp5360GeV.C b/MC/config/PWGGAJE/ini/tests/GeneratorJE_gapgen3_hook_pp5360GeV.C new file mode 100644 index 000000000..db42706cb --- /dev/null +++ b/MC/config/PWGGAJE/ini/tests/GeneratorJE_gapgen3_hook_pp5360GeV.C @@ -0,0 +1,71 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + + float ratioTrigger = 1./3; // one event triggered out of 3 + + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int nEventsMB{}, nEventsJetJet{}; + float sumWeightsMB{}, sumWeightsJetJet{}; + int sumTracks{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + + // check subgenerator information and event weights + if (eventHeader->hasInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID)) { + bool isValid = false; + int subGeneratorId = eventHeader->getInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID, isValid); + if (eventHeader->hasInfo(o2::dataformats::MCInfoKeys::weight)) { + float weight = eventHeader->getInfo(o2::dataformats::MCInfoKeys::weight,isValid); + if (subGeneratorId == 0) { + nEventsMB++; + sumWeightsMB += weight; + } + else if (subGeneratorId == 1) { + nEventsJetJet++; + sumWeightsJetJet += weight; + } + } + } + sumTracks += tracks->size(); + } + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + std::cout << "# MB events: " << nEventsMB << "\n"; + std::cout << " sum of weights for MB events: " << sumWeightsMB << "\n"; + std::cout << "# Jet-jet events " << nEventsJetJet << "\n"; + std::cout << " sum of weights jet-jet events: " << sumWeightsJetJet << "\n"; + std::cout << "# tracks summed over all events (jet-jet + MB): " << sumTracks << "\n"; + + if (nEventsMB < nEvents * (1 - ratioTrigger) * 0.95 || nEventsMB > nEvents * (1 - ratioTrigger) * 1.05) { // we put some tolerance since the number of generated events is small + std::cerr << "Number of generated MB events different than expected\n"; + return 1; + } + if (nEventsJetJet < nEvents * ratioTrigger * 0.95 || nEventsJetJet > nEvents * ratioTrigger * 1.05) { + std::cerr << "Number of jet-jet generated events different than expected\n"; + return 1; + } + if(nEventsMB < sumWeightsMB * 0.95 || nEventsMB > sumWeightsMB * 1.05) { + std::cerr << "Weights of MB events do not = 1 as expected\n"; + return 1; + } + if(sumTracks < 1) { + std::cerr << "No tracks in simulated events\n"; + return 1; + } + return 0; +} diff --git a/MC/config/PWGGAJE/ini/tests/GeneratorJE_gapgen4_hook_pp13600GeV.C b/MC/config/PWGGAJE/ini/tests/GeneratorJE_gapgen4_hook_pp13600GeV.C new file mode 100644 index 000000000..6dfa0975e --- /dev/null +++ b/MC/config/PWGGAJE/ini/tests/GeneratorJE_gapgen4_hook_pp13600GeV.C @@ -0,0 +1,71 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + + float ratioTrigger = 1./4; // one event triggered out of 4 + + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int nEventsMB{}, nEventsJetJet{}; + float sumWeightsMB{}, sumWeightsJetJet{}; + int sumTracks{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + + // check subgenerator information and event weights + if (eventHeader->hasInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID)) { + bool isValid = false; + int subGeneratorId = eventHeader->getInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID, isValid); + if (eventHeader->hasInfo(o2::dataformats::MCInfoKeys::weight)) { + float weight = eventHeader->getInfo(o2::dataformats::MCInfoKeys::weight,isValid); + if (subGeneratorId == 0) { + nEventsMB++; + sumWeightsMB += weight; + } + else if (subGeneratorId == 1) { + nEventsJetJet++; + sumWeightsJetJet += weight; + } + } + } + sumTracks += tracks->size(); + } + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + std::cout << "# MB events: " << nEventsMB << "\n"; + std::cout << " sum of weights for MB events: " << sumWeightsMB << "\n"; + std::cout << "# Jet-jet events " << nEventsJetJet << "\n"; + std::cout << " sum of weights jet-jet events: " << sumWeightsJetJet << "\n"; + std::cout << "# tracks summed over all events (jet-jet + MB): " << sumTracks << "\n"; + + if (nEventsMB < nEvents * (1 - ratioTrigger) * 0.95 || nEventsMB > nEvents * (1 - ratioTrigger) * 1.05) { // we put some tolerance since the number of generated events is small + std::cerr << "Number of generated MB events different than expected\n"; + return 1; + } + if (nEventsJetJet < nEvents * ratioTrigger * 0.95 || nEventsJetJet > nEvents * ratioTrigger * 1.05) { + std::cerr << "Number of jet-jet generated events different than expected\n"; + return 1; + } + if(nEventsMB < sumWeightsMB * 0.95 || nEventsMB > sumWeightsMB * 1.05) { + std::cerr << "Weights of MB events do not = 1 as expected\n"; + return 1; + } + if(sumTracks < 1) { + std::cerr << "No tracks in simulated events\n"; + return 1; + } + return 0; +} diff --git a/MC/config/PWGGAJE/ini/tests/GeneratorJE_gapgen5_hook_pp13600GeV.C b/MC/config/PWGGAJE/ini/tests/GeneratorJE_gapgen5_hook_pp13600GeV.C new file mode 100644 index 000000000..5ac7f5173 --- /dev/null +++ b/MC/config/PWGGAJE/ini/tests/GeneratorJE_gapgen5_hook_pp13600GeV.C @@ -0,0 +1,71 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + + float ratioTrigger = 1./5; // one event triggered out of 5 + + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int nEventsMB{}, nEventsJetJet{}; + float sumWeightsMB{}, sumWeightsJetJet{}; + int sumTracks{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + + // check subgenerator information and event weights + if (eventHeader->hasInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID)) { + bool isValid = false; + int subGeneratorId = eventHeader->getInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID, isValid); + if (eventHeader->hasInfo(o2::dataformats::MCInfoKeys::weight)) { + float weight = eventHeader->getInfo(o2::dataformats::MCInfoKeys::weight,isValid); + if (subGeneratorId == 0) { + nEventsMB++; + sumWeightsMB += weight; + } + else if (subGeneratorId == 1) { + nEventsJetJet++; + sumWeightsJetJet += weight; + } + } + } + sumTracks += tracks->size(); + } + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + std::cout << "# MB events: " << nEventsMB << "\n"; + std::cout << " sum of weights for MB events: " << sumWeightsMB << "\n"; + std::cout << "# Jet-jet events " << nEventsJetJet << "\n"; + std::cout << " sum of weights jet-jet events: " << sumWeightsJetJet << "\n"; + std::cout << "# tracks summed over all events (jet-jet + MB): " << sumTracks << "\n"; + + if (nEventsMB < nEvents * (1 - ratioTrigger) * 0.95 || nEventsMB > nEvents * (1 - ratioTrigger) * 1.05) { // we put some tolerance since the number of generated events is small + std::cerr << "Number of generated MB events different than expected\n"; + return 1; + } + if (nEventsJetJet < nEvents * ratioTrigger * 0.95 || nEventsJetJet > nEvents * ratioTrigger * 1.05) { + std::cerr << "Number of jet-jet generated events different than expected\n"; + return 1; + } + if(nEventsMB < sumWeightsMB * 0.95 || nEventsMB > sumWeightsMB * 1.05) { + std::cerr << "Weights of MB events do not = 1 as expected\n"; + return 1; + } + if(sumTracks < 1) { + std::cerr << "No tracks in simulated events\n"; + return 1; + } + return 0; +} diff --git a/MC/config/PWGGAJE/ini/tests/GeneratorJE_gapgen7_hook_pp13600GeV.C b/MC/config/PWGGAJE/ini/tests/GeneratorJE_gapgen7_hook_pp13600GeV.C new file mode 100644 index 000000000..3c3ebe1f3 --- /dev/null +++ b/MC/config/PWGGAJE/ini/tests/GeneratorJE_gapgen7_hook_pp13600GeV.C @@ -0,0 +1,71 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + + float ratioTrigger = 1./7; // one event triggered out of 7 + + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int nEventsMB{}, nEventsJetJet{}; + float sumWeightsMB{}, sumWeightsJetJet{}; + int sumTracks{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + + // check subgenerator information and event weights + if (eventHeader->hasInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID)) { + bool isValid = false; + int subGeneratorId = eventHeader->getInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID, isValid); + if (eventHeader->hasInfo(o2::dataformats::MCInfoKeys::weight)) { + float weight = eventHeader->getInfo(o2::dataformats::MCInfoKeys::weight,isValid); + if (subGeneratorId == 0) { + nEventsMB++; + sumWeightsMB += weight; + } + else if (subGeneratorId == 1) { + nEventsJetJet++; + sumWeightsJetJet += weight; + } + } + } + sumTracks += tracks->size(); + } + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + std::cout << "# MB events: " << nEventsMB << "\n"; + std::cout << " sum of weights for MB events: " << sumWeightsMB << "\n"; + std::cout << "# Jet-jet events " << nEventsJetJet << "\n"; + std::cout << " sum of weights jet-jet events: " << sumWeightsJetJet << "\n"; + std::cout << "# tracks summed over all events (jet-jet + MB): " << sumTracks << "\n"; + + if (nEventsMB < nEvents * (1 - ratioTrigger) * 0.95 || nEventsMB > nEvents * (1 - ratioTrigger) * 1.05) { // we put some tolerance since the number of generated events is small + std::cerr << "Number of generated MB events different than expected\n"; + return 1; + } + if (nEventsJetJet < nEvents * ratioTrigger * 0.95 || nEventsJetJet > nEvents * ratioTrigger * 1.05) { + std::cerr << "Number of jet-jet generated events different than expected\n"; + return 1; + } + if(nEventsMB < sumWeightsMB * 0.95 || nEventsMB > sumWeightsMB * 1.05) { + std::cerr << "Weights of MB events do not = 1 as expected\n"; + return 1; + } + if(sumTracks < 1) { + std::cerr << "No tracks in simulated events\n"; + return 1; + } + return 0; +} diff --git a/MC/config/PWGGAJE/ini/tests/GeneratorJE_gapgen9_hook_pp13600GeV.C b/MC/config/PWGGAJE/ini/tests/GeneratorJE_gapgen9_hook_pp13600GeV.C new file mode 100644 index 000000000..0c35fc634 --- /dev/null +++ b/MC/config/PWGGAJE/ini/tests/GeneratorJE_gapgen9_hook_pp13600GeV.C @@ -0,0 +1,71 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + + float ratioTrigger = 1./9; // one event triggered out of 9 + + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int nEventsMB{}, nEventsJetJet{}; + float sumWeightsMB{}, sumWeightsJetJet{}; + int sumTracks{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + + // check subgenerator information and event weights + if (eventHeader->hasInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID)) { + bool isValid = false; + int subGeneratorId = eventHeader->getInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID, isValid); + if (eventHeader->hasInfo(o2::dataformats::MCInfoKeys::weight)) { + float weight = eventHeader->getInfo(o2::dataformats::MCInfoKeys::weight,isValid); + if (subGeneratorId == 0) { + nEventsMB++; + sumWeightsMB += weight; + } + else if (subGeneratorId == 1) { + nEventsJetJet++; + sumWeightsJetJet += weight; + } + } + } + sumTracks += tracks->size(); + } + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + std::cout << "# MB events: " << nEventsMB << "\n"; + std::cout << " sum of weights for MB events: " << sumWeightsMB << "\n"; + std::cout << "# Jet-jet events " << nEventsJetJet << "\n"; + std::cout << " sum of weights jet-jet events: " << sumWeightsJetJet << "\n"; + std::cout << "# tracks summed over all events (jet-jet + MB): " << sumTracks << "\n"; + + if (nEventsMB < nEvents * (1 - ratioTrigger) * 0.90 || nEventsMB > nEvents * (1 - ratioTrigger) * 1.10) { // we put some tolerance since the number of generated events is small + std::cerr << "Number of generated MB events different than expected\n"; + return 1; + } + if (nEventsJetJet < nEvents * ratioTrigger * 0.90 || nEventsJetJet > nEvents * ratioTrigger * 1.10) { + std::cerr << "Number of jet-jet generated events different than expected\n"; + return 1; + } + if(nEventsMB < sumWeightsMB * 0.90 || nEventsMB > sumWeightsMB * 1.10) { + std::cerr << "Weights of MB events do not = 1 as expected\n"; + return 1; + } + if(sumTracks < 1) { + std::cerr << "No tracks in simulated events\n"; + return 1; + } + return 0; +} diff --git a/MC/config/PWGGAJE/ini/tests/GeneratorPythia8Box_parametrisedModel_pythia6Fragmentation_PbPb_cent0010_Bkg.C b/MC/config/PWGGAJE/ini/tests/GeneratorPythia8Box_parametrisedModel_pythia6Fragmentation_PbPb_cent0010_Bkg.C new file mode 100644 index 000000000..fddd963ae --- /dev/null +++ b/MC/config/PWGGAJE/ini/tests/GeneratorPythia8Box_parametrisedModel_pythia6Fragmentation_PbPb_cent0010_Bkg.C @@ -0,0 +1,34 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int sumTracks{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + sumTracks += tracks->size(); + } + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + std::cout << "# tracks summed over all events (jet-jet + MB): " << sumTracks + << "\n"; + + if (sumTracks < 1) { + std::cerr << "No tracks in simulated events\n"; + return 1; + } + return 0; +} \ No newline at end of file diff --git a/MC/config/PWGGAJE/ini/tests/GeneratorPythia8Box_parametrisedModel_pythia6Fragmentation_PbPb_cent0010_Sgl.C b/MC/config/PWGGAJE/ini/tests/GeneratorPythia8Box_parametrisedModel_pythia6Fragmentation_PbPb_cent0010_Sgl.C new file mode 100644 index 000000000..fddd963ae --- /dev/null +++ b/MC/config/PWGGAJE/ini/tests/GeneratorPythia8Box_parametrisedModel_pythia6Fragmentation_PbPb_cent0010_Sgl.C @@ -0,0 +1,34 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int sumTracks{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + sumTracks += tracks->size(); + } + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + std::cout << "# tracks summed over all events (jet-jet + MB): " << sumTracks + << "\n"; + + if (sumTracks < 1) { + std::cerr << "No tracks in simulated events\n"; + return 1; + } + return 0; +} \ No newline at end of file diff --git a/MC/config/PWGGAJE/ini/tests/GeneratorPythia8Box_parametrisedModel_pythia6Fragmentation_PbPb_cent0010_SglBkg.C b/MC/config/PWGGAJE/ini/tests/GeneratorPythia8Box_parametrisedModel_pythia6Fragmentation_PbPb_cent0010_SglBkg.C new file mode 100644 index 000000000..fddd963ae --- /dev/null +++ b/MC/config/PWGGAJE/ini/tests/GeneratorPythia8Box_parametrisedModel_pythia6Fragmentation_PbPb_cent0010_SglBkg.C @@ -0,0 +1,34 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int sumTracks{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + sumTracks += tracks->size(); + } + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + std::cout << "# tracks summed over all events (jet-jet + MB): " << sumTracks + << "\n"; + + if (sumTracks < 1) { + std::cerr << "No tracks in simulated events\n"; + return 1; + } + return 0; +} \ No newline at end of file diff --git a/MC/config/PWGGAJE/ini/tests/GeneratorPythia8Box_parametrisedModel_pythia6Fragmentation_PbPb_cent5080_Bkg.C b/MC/config/PWGGAJE/ini/tests/GeneratorPythia8Box_parametrisedModel_pythia6Fragmentation_PbPb_cent5080_Bkg.C new file mode 100644 index 000000000..fddd963ae --- /dev/null +++ b/MC/config/PWGGAJE/ini/tests/GeneratorPythia8Box_parametrisedModel_pythia6Fragmentation_PbPb_cent5080_Bkg.C @@ -0,0 +1,34 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int sumTracks{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + sumTracks += tracks->size(); + } + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + std::cout << "# tracks summed over all events (jet-jet + MB): " << sumTracks + << "\n"; + + if (sumTracks < 1) { + std::cerr << "No tracks in simulated events\n"; + return 1; + } + return 0; +} \ No newline at end of file diff --git a/MC/config/PWGGAJE/ini/tests/GeneratorPythia8Box_parametrisedModel_pythia6Fragmentation_PbPb_cent5080_Sgl.C b/MC/config/PWGGAJE/ini/tests/GeneratorPythia8Box_parametrisedModel_pythia6Fragmentation_PbPb_cent5080_Sgl.C new file mode 100644 index 000000000..fddd963ae --- /dev/null +++ b/MC/config/PWGGAJE/ini/tests/GeneratorPythia8Box_parametrisedModel_pythia6Fragmentation_PbPb_cent5080_Sgl.C @@ -0,0 +1,34 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int sumTracks{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + sumTracks += tracks->size(); + } + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + std::cout << "# tracks summed over all events (jet-jet + MB): " << sumTracks + << "\n"; + + if (sumTracks < 1) { + std::cerr << "No tracks in simulated events\n"; + return 1; + } + return 0; +} \ No newline at end of file diff --git a/MC/config/PWGGAJE/ini/tests/GeneratorPythia8Box_parametrisedModel_pythia6Fragmentation_PbPb_cent5080_SglBkg.C b/MC/config/PWGGAJE/ini/tests/GeneratorPythia8Box_parametrisedModel_pythia6Fragmentation_PbPb_cent5080_SglBkg.C new file mode 100644 index 000000000..fddd963ae --- /dev/null +++ b/MC/config/PWGGAJE/ini/tests/GeneratorPythia8Box_parametrisedModel_pythia6Fragmentation_PbPb_cent5080_SglBkg.C @@ -0,0 +1,34 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int sumTracks{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + sumTracks += tracks->size(); + } + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + std::cout << "# tracks summed over all events (jet-jet + MB): " << sumTracks + << "\n"; + + if (sumTracks < 1) { + std::cerr << "No tracks in simulated events\n"; + return 1; + } + return 0; +} \ No newline at end of file diff --git a/MC/config/PWGGAJE/ini/tests/GeneratorPythia8POWHEG.C b/MC/config/PWGGAJE/ini/tests/GeneratorPythia8POWHEG.C new file mode 100644 index 000000000..8c83ce1e3 --- /dev/null +++ b/MC/config/PWGGAJE/ini/tests/GeneratorPythia8POWHEG.C @@ -0,0 +1,46 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + std::ifstream powhegconf("powheg.input"); + if (!powhegconf) { + std::cerr << "POWHEG configuration file not found\n"; + return 1; + } + std::ifstream powhegout("pwgevents.lhe"); + if (!powhegout) { + std::cerr << "POWHEG output file not found\n"; + return 1; + } + powhegout.close(); + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + auto nEvents = tree->GetEntries(); + std::string line; + int nevpowheg = -1; + while (std::getline(powhegconf, line)) { + if (line.find("numevts") != std::string::npos) { + // Read the number right after numevts + auto pos = line.find("numevts"); + nevpowheg = std::stoi(line.substr(pos + 7)); + if (nevpowheg != nEvents) { + std::cerr << "Number of events in POWHEG configuration file " << nevpowheg + << " does not match the simulated number of events " + << nEvents << "\n"; + return 1; + } + } + } + if (nevpowheg == -1) { + std::cerr << "Number of events not found in POWHEG configuration file\n"; + return 1; + } + powhegconf.close(); + file.Close(); + + return 0; +} \ No newline at end of file diff --git a/MC/config/PWGGAJE/ini/tests/hook_prompt_gamma_gap.C b/MC/config/PWGGAJE/ini/tests/hook_prompt_gamma_gap.C new file mode 100644 index 000000000..69cd83f86 --- /dev/null +++ b/MC/config/PWGGAJE/ini/tests/hook_prompt_gamma_gap.C @@ -0,0 +1,71 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + + float ratioTrigger = 1./2; // one event triggered out of 5 + + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int nEventsMB{}, nEventsJetJet{}; + float sumWeightsMB{}, sumWeightsJetJet{}; + int sumTracks{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + + // check subgenerator information and event weights + if (eventHeader->hasInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID)) { + bool isValid = false; + int subGeneratorId = eventHeader->getInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID, isValid); + if (eventHeader->hasInfo(o2::dataformats::MCInfoKeys::weight)) { + float weight = eventHeader->getInfo(o2::dataformats::MCInfoKeys::weight,isValid); + if (subGeneratorId == 0) { + nEventsMB++; + sumWeightsMB += weight; + } + else if (subGeneratorId == 1) { + nEventsJetJet++; + sumWeightsJetJet += weight; + } + } + } + sumTracks += tracks->size(); + } + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + std::cout << "# MB events: " << nEventsMB << "\n"; + std::cout << " sum of weights for MB events: " << sumWeightsMB << "\n"; + std::cout << "# prompt_photon events " << nEventsJetJet << "\n"; + std::cout << " sum of weights prompt_photon events: " << sumWeightsJetJet << "\n"; + std::cout << "# tracks summed over all events (prompt_photon + MB): " << sumTracks << "\n"; + + if (nEventsMB < nEvents * (1 - ratioTrigger) * 0.95 || nEventsMB > nEvents * (1 - ratioTrigger) * 1.05) { // we put some tolerance since the number of generated events is small + std::cerr << "Number of generated MB events different than expected\n"; + return 1; + } + if (nEventsJetJet < nEvents * ratioTrigger * 0.95 || nEventsJetJet > nEvents * ratioTrigger * 1.05) { + std::cerr << "Number of prompt_photon generated events different than expected\n"; + return 1; + } + if(nEventsMB < sumWeightsMB * 0.95 || nEventsMB > sumWeightsMB * 1.05) { + std::cerr << "Weights of MB events do not = 1 as expected\n"; + return 1; + } + if(sumTracks < 1) { + std::cerr << "No tracks in simulated events\n"; + return 1; + } + return 0; +} diff --git a/MC/config/PWGGAJE/ini/tests/hook_prompt_gamma_gap3.C b/MC/config/PWGGAJE/ini/tests/hook_prompt_gamma_gap3.C new file mode 100644 index 000000000..a4ed3aa31 --- /dev/null +++ b/MC/config/PWGGAJE/ini/tests/hook_prompt_gamma_gap3.C @@ -0,0 +1,71 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + + float ratioTrigger = 1./3; // one event triggered out of 3 + + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int nEventsMB{}, nEventsJetJet{}; + float sumWeightsMB{}, sumWeightsJetJet{}; + int sumTracks{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + + // check subgenerator information and event weights + if (eventHeader->hasInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID)) { + bool isValid = false; + int subGeneratorId = eventHeader->getInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID, isValid); + if (eventHeader->hasInfo(o2::dataformats::MCInfoKeys::weight)) { + float weight = eventHeader->getInfo(o2::dataformats::MCInfoKeys::weight,isValid); + if (subGeneratorId == 0) { + nEventsMB++; + sumWeightsMB += weight; + } + else if (subGeneratorId == 1) { + nEventsJetJet++; + sumWeightsJetJet += weight; + } + } + } + sumTracks += tracks->size(); + } + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + std::cout << "# MB events: " << nEventsMB << "\n"; + std::cout << " sum of weights for MB events: " << sumWeightsMB << "\n"; + std::cout << "# prompt_photon events " << nEventsJetJet << "\n"; + std::cout << " sum of weights prompt_photon events: " << sumWeightsJetJet << "\n"; + std::cout << "# tracks summed over all events (prompt_photon + MB): " << sumTracks << "\n"; + + if (nEventsMB < nEvents * (1 - ratioTrigger) * 0.95 || nEventsMB > nEvents * (1 - ratioTrigger) * 1.05) { // we put some tolerance since the number of generated events is small + std::cerr << "Number of generated MB events different than expected\n"; + return 1; + } + if (nEventsJetJet < nEvents * ratioTrigger * 0.95 || nEventsJetJet > nEvents * ratioTrigger * 1.05) { + std::cerr << "Number of prompt_photon generated events different than expected\n"; + return 1; + } + if(nEventsMB < sumWeightsMB * 0.95 || nEventsMB > sumWeightsMB * 1.05) { + std::cerr << "Weights of MB events do not = 1 as expected\n"; + return 1; + } + if(sumTracks < 1) { + std::cerr << "No tracks in simulated events\n"; + return 1; + } + return 0; +} diff --git a/MC/config/PWGGAJE/ini/tests/hook_prompt_gamma_gap5.C b/MC/config/PWGGAJE/ini/tests/hook_prompt_gamma_gap5.C new file mode 100644 index 000000000..571ab5c6b --- /dev/null +++ b/MC/config/PWGGAJE/ini/tests/hook_prompt_gamma_gap5.C @@ -0,0 +1,71 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + + float ratioTrigger = 1./5; // one event triggered out of 5 + + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int nEventsMB{}, nEventsJetJet{}; + float sumWeightsMB{}, sumWeightsJetJet{}; + int sumTracks{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + + // check subgenerator information and event weights + if (eventHeader->hasInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID)) { + bool isValid = false; + int subGeneratorId = eventHeader->getInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID, isValid); + if (eventHeader->hasInfo(o2::dataformats::MCInfoKeys::weight)) { + float weight = eventHeader->getInfo(o2::dataformats::MCInfoKeys::weight,isValid); + if (subGeneratorId == 0) { + nEventsMB++; + sumWeightsMB += weight; + } + else if (subGeneratorId == 1) { + nEventsJetJet++; + sumWeightsJetJet += weight; + } + } + } + sumTracks += tracks->size(); + } + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + std::cout << "# MB events: " << nEventsMB << "\n"; + std::cout << " sum of weights for MB events: " << sumWeightsMB << "\n"; + std::cout << "# prompt_photon events " << nEventsJetJet << "\n"; + std::cout << " sum of weights prompt_photon events: " << sumWeightsJetJet << "\n"; + std::cout << "# tracks summed over all events (prompt_photon + MB): " << sumTracks << "\n"; + + if (nEventsMB < nEvents * (1 - ratioTrigger) * 0.95 || nEventsMB > nEvents * (1 - ratioTrigger) * 1.05) { // we put some tolerance since the number of generated events is small + std::cerr << "Number of generated MB events different than expected\n"; + return 1; + } + if (nEventsJetJet < nEvents * ratioTrigger * 0.95 || nEventsJetJet > nEvents * ratioTrigger * 1.05) { + std::cerr << "Number of prompt_photon generated events different than expected\n"; + return 1; + } + if(nEventsMB < sumWeightsMB * 0.95 || nEventsMB > sumWeightsMB * 1.05) { + std::cerr << "Weights of MB events do not = 1 as expected\n"; + return 1; + } + if(sumTracks < 1) { + std::cerr << "No tracks in simulated events\n"; + return 1; + } + return 0; +} diff --git a/MC/config/PWGGAJE/ini/tests/hook_prompt_gamma_gap7.C b/MC/config/PWGGAJE/ini/tests/hook_prompt_gamma_gap7.C new file mode 100644 index 000000000..22f252875 --- /dev/null +++ b/MC/config/PWGGAJE/ini/tests/hook_prompt_gamma_gap7.C @@ -0,0 +1,71 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + + float ratioTrigger = 1./7; // one event triggered out of 7 + + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int nEventsMB{}, nEventsJetJet{}; + float sumWeightsMB{}, sumWeightsJetJet{}; + int sumTracks{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + + // check subgenerator information and event weights + if (eventHeader->hasInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID)) { + bool isValid = false; + int subGeneratorId = eventHeader->getInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID, isValid); + if (eventHeader->hasInfo(o2::dataformats::MCInfoKeys::weight)) { + float weight = eventHeader->getInfo(o2::dataformats::MCInfoKeys::weight,isValid); + if (subGeneratorId == 0) { + nEventsMB++; + sumWeightsMB += weight; + } + else if (subGeneratorId == 1) { + nEventsJetJet++; + sumWeightsJetJet += weight; + } + } + } + sumTracks += tracks->size(); + } + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + std::cout << "# MB events: " << nEventsMB << "\n"; + std::cout << " sum of weights for MB events: " << sumWeightsMB << "\n"; + std::cout << "# prompt_photon events " << nEventsJetJet << "\n"; + std::cout << " sum of weights prompt_photon events: " << sumWeightsJetJet << "\n"; + std::cout << "# tracks summed over all events (prompt_photon + MB): " << sumTracks << "\n"; + + if (nEventsMB < nEvents * (1 - ratioTrigger) * 0.95 || nEventsMB > nEvents * (1 - ratioTrigger) * 1.05) { // we put some tolerance since the number of generated events is small + std::cerr << "Number of generated MB events different than expected\n"; + return 1; + } + if (nEventsJetJet < nEvents * ratioTrigger * 0.95 || nEventsJetJet > nEvents * ratioTrigger * 1.05) { + std::cerr << "Number of prompt_photon generated events different than expected\n"; + return 1; + } + if(nEventsMB < sumWeightsMB * 0.95 || nEventsMB > sumWeightsMB * 1.05) { + std::cerr << "Weights of MB events do not = 1 as expected\n"; + return 1; + } + if(sumTracks < 1) { + std::cerr << "No tracks in simulated events\n"; + return 1; + } + return 0; +} diff --git a/MC/config/PWGGAJE/ini/tests/hook_prompt_gamma_gap9.C b/MC/config/PWGGAJE/ini/tests/hook_prompt_gamma_gap9.C new file mode 100644 index 000000000..0a75b6d50 --- /dev/null +++ b/MC/config/PWGGAJE/ini/tests/hook_prompt_gamma_gap9.C @@ -0,0 +1,71 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + + float ratioTrigger = 1./9; // one event triggered out of 9 + + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int nEventsMB{}, nEventsJetJet{}; + float sumWeightsMB{}, sumWeightsJetJet{}; + int sumTracks{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + + // check subgenerator information and event weights + if (eventHeader->hasInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID)) { + bool isValid = false; + int subGeneratorId = eventHeader->getInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID, isValid); + if (eventHeader->hasInfo(o2::dataformats::MCInfoKeys::weight)) { + float weight = eventHeader->getInfo(o2::dataformats::MCInfoKeys::weight,isValid); + if (subGeneratorId == 0) { + nEventsMB++; + sumWeightsMB += weight; + } + else if (subGeneratorId == 1) { + nEventsJetJet++; + sumWeightsJetJet += weight; + } + } + } + sumTracks += tracks->size(); + } + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + std::cout << "# MB events: " << nEventsMB << "\n"; + std::cout << " sum of weights for MB events: " << sumWeightsMB << "\n"; + std::cout << "# prompt_photon events " << nEventsJetJet << "\n"; + std::cout << " sum of weights prompt_photon events: " << sumWeightsJetJet << "\n"; + std::cout << "# tracks summed over all events (prompt_photon + MB): " << sumTracks << "\n"; + + if (nEventsMB < nEvents * (1 - ratioTrigger) * 0.90 || nEventsMB > nEvents * (1 - ratioTrigger) * 1.10) { // we put some tolerance since the number of generated events is small + std::cerr << "Number of generated MB events different than expected\n"; + return 1; + } + if (nEventsJetJet < nEvents * ratioTrigger * 0.90 || nEventsJetJet > nEvents * ratioTrigger * 1.10) { + std::cerr << "Number of prompt_photon generated events different than expected\n"; + return 1; + } + if(nEventsMB < sumWeightsMB * 0.90 || nEventsMB > sumWeightsMB * 1.10) { + std::cerr << "Weights of MB events do not = 1 as expected\n"; + return 1; + } + if(sumTracks < 1) { + std::cerr << "No tracks in simulated events\n"; + return 1; + } + return 0; +} diff --git a/MC/config/PWGGAJE/ini/tests/prompt_gamma_gap.C b/MC/config/PWGGAJE/ini/tests/prompt_gamma_gap.C new file mode 100644 index 000000000..6f32003fe --- /dev/null +++ b/MC/config/PWGGAJE/ini/tests/prompt_gamma_gap.C @@ -0,0 +1,71 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + + float ratioTrigger = 1./2; // one event triggered out of 5 + + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int nEventsMB{}, nEventsJetJet{}; + float sumWeightsMB{}, sumWeightsJetJet{}; + int sumTracks{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + + // check subgenerator information and event weights + if (eventHeader->hasInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID)) { + bool isValid = false; + int subGeneratorId = eventHeader->getInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID, isValid); + if (eventHeader->hasInfo(o2::dataformats::MCInfoKeys::weight)) { + float weight = eventHeader->getInfo(o2::dataformats::MCInfoKeys::weight,isValid); + if (subGeneratorId == 0) { + nEventsMB++; + sumWeightsMB += weight; + } + else if (subGeneratorId == 1) { + nEventsJetJet++; + sumWeightsJetJet += weight; + } + } + } + sumTracks += tracks->size(); + } + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + std::cout << "# MB events: " << nEventsMB << "\n"; + std::cout << " sum of weights for MB events: " << sumWeightsMB << "\n"; + std::cout << "# Jet-jet events " << nEventsJetJet << "\n"; + std::cout << " sum of weights jet-jet events: " << sumWeightsJetJet << "\n"; + std::cout << "# tracks summed over all events (jet-jet + MB): " << sumTracks << "\n"; + + if (nEventsMB < nEvents * (1 - ratioTrigger) * 0.95 || nEventsMB > nEvents * (1 - ratioTrigger) * 1.05) { // we put some tolerance since the number of generated events is small + std::cerr << "Number of generated MB events different than expected\n"; + return 1; + } + if (nEventsJetJet < nEvents * ratioTrigger * 0.95 || nEventsJetJet > nEvents * ratioTrigger * 1.05) { + std::cerr << "Number of jet-jet generated events different than expected\n"; + return 1; + } + if(nEventsMB < sumWeightsMB * 0.95 || nEventsMB > sumWeightsMB * 1.05) { + std::cerr << "Weights of MB events do not = 1 as expected\n"; + return 1; + } + if(sumTracks < 1) { + std::cerr << "No tracks in simulated events\n"; + return 1; + } + return 0; +} diff --git a/MC/config/PWGGAJE/ini/trigger_decay_gamma.ini b/MC/config/PWGGAJE/ini/trigger_decay_gamma.ini index d235ed515..4179f3ab1 100644 --- a/MC/config/PWGGAJE/ini/trigger_decay_gamma.ini +++ b/MC/config/PWGGAJE/ini/trigger_decay_gamma.ini @@ -2,5 +2,5 @@ config=pythia8.cfg [TriggerExternal] -fileName=${O2DPG_ROOT}/MC/config/PWGGAJE/trigger/decay_gamma_jets.C +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/trigger/decay_gamma_jets.C funcName=decay_gamma_jets() diff --git a/MC/config/PWGGAJE/ini/trigger_decay_gamma_TrigPt3_5.ini b/MC/config/PWGGAJE/ini/trigger_decay_gamma_TrigPt3_5.ini index 33657e280..bb9bc0c26 100644 --- a/MC/config/PWGGAJE/ini/trigger_decay_gamma_TrigPt3_5.ini +++ b/MC/config/PWGGAJE/ini/trigger_decay_gamma_TrigPt3_5.ini @@ -2,5 +2,5 @@ config=pythia8.cfg [TriggerExternal] -fileName=${O2DPG_ROOT}/MC/config/PWGGAJE/trigger/decay_gamma_jets.C +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/trigger/decay_gamma_jets.C funcName=decay_gamma_jets(0,3.5) diff --git a/MC/config/PWGGAJE/ini/trigger_decay_gamma_TrigPt7.ini b/MC/config/PWGGAJE/ini/trigger_decay_gamma_TrigPt7.ini index 415793224..bcf25844b 100644 --- a/MC/config/PWGGAJE/ini/trigger_decay_gamma_TrigPt7.ini +++ b/MC/config/PWGGAJE/ini/trigger_decay_gamma_TrigPt7.ini @@ -2,5 +2,5 @@ config=pythia8.cfg [TriggerExternal] -fileName=${O2DPG_ROOT}/MC/config/PWGGAJE/trigger/decay_gamma_jets.C +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/trigger/decay_gamma_jets.C funcName=decay_gamma_jets(0,7) diff --git a/MC/config/PWGGAJE/ini/trigger_decay_gamma_allcalo_TrigPt3_5.ini b/MC/config/PWGGAJE/ini/trigger_decay_gamma_allcalo_TrigPt3_5.ini index afd08c89a..f281a41ec 100644 --- a/MC/config/PWGGAJE/ini/trigger_decay_gamma_allcalo_TrigPt3_5.ini +++ b/MC/config/PWGGAJE/ini/trigger_decay_gamma_allcalo_TrigPt3_5.ini @@ -2,5 +2,5 @@ config=pythia8.cfg [TriggerExternal] -fileName=${O2DPG_ROOT}/MC/config/PWGGAJE/trigger/decay_gamma_jets.C +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/trigger/decay_gamma_jets.C funcName=decay_gamma_jets(1,3.5) diff --git a/MC/config/PWGGAJE/ini/trigger_decay_gamma_allcalo_TrigPt7.ini b/MC/config/PWGGAJE/ini/trigger_decay_gamma_allcalo_TrigPt7.ini index d7036b913..a817a6ee1 100644 --- a/MC/config/PWGGAJE/ini/trigger_decay_gamma_allcalo_TrigPt7.ini +++ b/MC/config/PWGGAJE/ini/trigger_decay_gamma_allcalo_TrigPt7.ini @@ -2,5 +2,5 @@ config=pythia8.cfg [TriggerExternal] -fileName=${O2DPG_ROOT}/MC/config/PWGGAJE/trigger/decay_gamma_jets.C +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/trigger/decay_gamma_jets.C funcName=decay_gamma_jets(1,7) diff --git a/MC/config/PWGGAJE/ini/trigger_prompt_gamma.ini b/MC/config/PWGGAJE/ini/trigger_prompt_gamma.ini index 535a54283..e0ded08fa 100644 --- a/MC/config/PWGGAJE/ini/trigger_prompt_gamma.ini +++ b/MC/config/PWGGAJE/ini/trigger_prompt_gamma.ini @@ -2,5 +2,5 @@ config=pythia8.cfg [TriggerExternal] -fileName=${O2DPG_ROOT}/MC/config/PWGGAJE/trigger/prompt_gamma.C +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/trigger/prompt_gamma.C funcName=prompt_gamma() diff --git a/MC/config/PWGGAJE/ini/trigger_prompt_gamma_allcalo.ini b/MC/config/PWGGAJE/ini/trigger_prompt_gamma_allcalo.ini index cc8a04b3c..6dac48408 100644 --- a/MC/config/PWGGAJE/ini/trigger_prompt_gamma_allcalo.ini +++ b/MC/config/PWGGAJE/ini/trigger_prompt_gamma_allcalo.ini @@ -2,5 +2,5 @@ config=pythia8.cfg [TriggerExternal] -fileName=${O2DPG_ROOT}/MC/config/PWGGAJE/trigger/prompt_gamma.C +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/trigger/prompt_gamma.C funcName=prompt_gamma(1) diff --git a/MC/config/PWGGAJE/ini/trigger_prompt_gamma_focal.ini b/MC/config/PWGGAJE/ini/trigger_prompt_gamma_focal.ini new file mode 100644 index 000000000..c4f1d663b --- /dev/null +++ b/MC/config/PWGGAJE/ini/trigger_prompt_gamma_focal.ini @@ -0,0 +1,6 @@ +[GeneratorPythia8] +config=pythia8.cfg + +[TriggerExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/trigger/prompt_gamma.C +funcName=prompt_gamma(7) diff --git a/MC/config/PWGGAJE/pythia8/generator/pythia8_jet.cfg b/MC/config/PWGGAJE/pythia8/generator/pythia8_jet.cfg new file mode 100644 index 000000000..df6772816 --- /dev/null +++ b/MC/config/PWGGAJE/pythia8/generator/pythia8_jet.cfg @@ -0,0 +1,20 @@ +# 2 -> 2 jet production, oversampling by pThat^4 + +### beams +Beams:idA = 2212 # proton +Beams:idB = 2212 # proton +Beams:eCM = 13600. # GeV + +### processes +SoftQCD:inelastic = off +HardQCD:all = on + +### decays +ParticleDecays:limitTau0 = on +ParticleDecays:tau0Max = 10. + +### phase space cuts +PhaseSpace:pTHatMin = 5 +PhaseSpace:pTHatMax = 600 +PhaseSpace:bias2Selection = on +PhaseSpace:bias2SelectionPow = 4 diff --git a/MC/config/PWGGAJE/pythia8/generator/pythia8_jet_charmtriggers_with_decays.cfg b/MC/config/PWGGAJE/pythia8/generator/pythia8_jet_charmtriggers_with_decays.cfg new file mode 100644 index 000000000..9c0089c1c --- /dev/null +++ b/MC/config/PWGGAJE/pythia8/generator/pythia8_jet_charmtriggers_with_decays.cfg @@ -0,0 +1,117 @@ +### author: Aimeric Landou (aimeric.landou@cern.ch) +### based on Fabrizio Grosa's HF template (fabrizio.grosa@cern.ch) +### since: January 2023 + +### beams +Beams:idA 2212 # proton +Beams:idB 2212 # proton +Beams:eCM 13600. # GeV + +# ### processes +# SoftQCD:inelastic on # all inelastic processes + +### decays +ParticleDecays:limitTau0 on +ParticleDecays:tau0Max 10. + +### Force golden charm hadrons decay modes for trigger studies + +### add D+ decays absent in PYTHIA8 decay table and set BRs from PDG for other +411:oneChannel = 1 0.0752 0 -321 211 211 +411:addChannel = 1 0.0104 0 -313 211 +411:addChannel = 1 0.0156 0 311 211 +411:addChannel = 1 0.00276 0 333 211 +## add Lc decays absent in PYTHIA8 decay table and set BRs from PDG for other +4122:oneChannel = 1 0.0196 100 2212 -313 +4122:addChannel = 1 0.0108 100 2224 -321 +4122:addChannel = 1 0.022 100 3124 211 +4122:addChannel = 1 0.035 0 2212 -321 211 +### add Xic+ decays absent in PYTHIA8 decay table +4232:addChannel = 1 0.2 0 2212 -313 +4232:addChannel = 1 0.2 0 2212 -321 211 +4232:addChannel = 1 0.2 0 3324 211 +4232:addChannel = 1 0.2 0 3312 211 211 +### add Xic0 decays absent in PYTHIA8 decay table +4132:addChannel = 1 0.2 0 3312 211 + +### K* -> K pi +313:onMode = off +313:onIfAll = 321 211 +### for Ds -> Phi pi+ +333:onMode = off +333:onIfAll = 321 321 +### for D0 -> rho0 pi+ k- +113:onMode = off +113:onIfAll = 211 211 +### for Lambda_c -> Delta++ K- +2224:onMode = off +2224:onIfAll = 2212 211 +### for Lambda_c -> Lambda(1520) K- +102134:onMode = off +102134:onIfAll = 2212 321 + +### switch off all decay channels +411:onMode = off +421:onMode = off +431:onMode = off +4112:onMode = off +4122:onMode = off +4232:onMode = off +4132:onMode = off +443:onMode = off +4332:onMode = off + +### D0 -> K pi +421:onIfMatch = 321 211 + +### D+/- -> K pi pi +411:onIfMatch = 321 211 211 +### D+/- -> K* pi +411:onIfMatch = 313 211 +### D+/- -> phi pi +411:onIfMatch = 333 211 + +### D_s -> Phi pi +431:onIfMatch = 333 211 + +### Lambda_c -> p K* +4122:onIfMatch = 2212 313 +### Lambda_c -> Delta K +4122:onIfMatch = 2224 321 +### Lambda_c -> Lambda(1520) pi +4122:onIfMatch = 3124 211 +### Lambda_c -> p K pi +4122:onIfMatch = 2212 321 211 + +### Xic+ -> pK*0 +4232:onIfMatch = 2212 313 +### Xic+ -> p K- pi+ +4232:onIfMatch = 2212 321 211 +### Xic+ -> Xi*0 pi+, Xi*->Xi- pi+ +4232:onIfMatch = 3324 211 +### Xic+ -> Xi- pi+ pi+ +4232:onIfMatch = 3312 211 211 + +### Xic0 -> Xi- pi+ +4132:onIfMatch = 3312 211 + +### Omega_c -> Omega pi +4332:onIfMatch = 3334 211 + +### Jpsi -> ee +443:onIfMatch = 11 11 + +# Correct Lb decay length (wrong in PYTHIA8 decay table) +5122:tau0 = 4.41000e-01 + +# Correct OmegaC decay length (wrong in PYTHIA8 decay table) +4332:tau0 = 0.08000000000 + + +### Jets options +PhaseSpace:pTHatMin = 5.0 +PhaseSpace:pTHatMax = 300.0 +PhaseSpace:bias2Selection = on +PhaseSpace:bias2SelectionPow = 6.0 +SoftQCD:inelastic off # all inelastic processes +HardQCD:all on \ No newline at end of file diff --git a/MC/config/PWGGAJE/pythia8/generator/pythia8_jet_pp5360GeV.cfg b/MC/config/PWGGAJE/pythia8/generator/pythia8_jet_pp5360GeV.cfg new file mode 100644 index 000000000..59f936556 --- /dev/null +++ b/MC/config/PWGGAJE/pythia8/generator/pythia8_jet_pp5360GeV.cfg @@ -0,0 +1,25 @@ + +# ### random +# # if we want to input a spectific seed: + +# Random:setSeed = on +# Random:seed = seed + +### beams +Beams:idA = 2212 # proton +Beams:idB = 2212 # proton +Beams:eCM = 5360. # GeV + +### processes +SoftQCD:inelastic = off +HardQCD:all = on + +### decays +ParticleDecays:limitTau0 = on +ParticleDecays:tau0Max = 10. + +### phase space cuts +PhaseSpace:pTHatMin = 5 +PhaseSpace:pTHatMax = 600 +PhaseSpace:bias2Selection = on +PhaseSpace:bias2SelectionPow = 4 diff --git a/MC/config/PWGGAJE/pythia8/generator/pythia8_minbias.cfg b/MC/config/PWGGAJE/pythia8/generator/pythia8_minbias.cfg new file mode 100644 index 000000000..0b12248e9 --- /dev/null +++ b/MC/config/PWGGAJE/pythia8/generator/pythia8_minbias.cfg @@ -0,0 +1,13 @@ +# min. bias events + +### beams +Beams:idA = 2212 # proton +Beams:idB = 2212 # proton +Beams:eCM = 13600. # GeV + +### processes +SoftQCD:inelastic = on + +### decays +ParticleDecays:limitTau0 = on +ParticleDecays:tau0Max = 10. diff --git a/MC/config/PWGGAJE/pythia8/generator/pythia8_minbias_pp5360GeV.cfg b/MC/config/PWGGAJE/pythia8/generator/pythia8_minbias_pp5360GeV.cfg new file mode 100644 index 000000000..c0397d1d6 --- /dev/null +++ b/MC/config/PWGGAJE/pythia8/generator/pythia8_minbias_pp5360GeV.cfg @@ -0,0 +1,18 @@ + +# ### random +# # if we want to input a spectific seed: + +# Random:setSeed = on +# Random:seed = seed + +### beams +Beams:idA = 2212 # proton +Beams:idB = 2212 # proton +Beams:eCM = 5360. # GeV + +### processes +SoftQCD:inelastic = on + +### decays +ParticleDecays:limitTau0 = on +ParticleDecays:tau0Max = 10. diff --git a/MC/config/PWGGAJE/pythia8/generator/pythia8_powheg.cfg b/MC/config/PWGGAJE/pythia8/generator/pythia8_powheg.cfg new file mode 100644 index 000000000..e5a313535 --- /dev/null +++ b/MC/config/PWGGAJE/pythia8/generator/pythia8_powheg.cfg @@ -0,0 +1,79 @@ +Beams:eCM 13600. # GeV +### processes +Next:numberShowLHA = 1 +Next:numberShowInfo = 1 +Next:numberShowProcess = 1 +Next:numberShowEvent = 1 +Main:timesAllowErrors = 10 +! Read LHE file from POWHEG +Beams:frameType = 4 +Beams:LHEF = pwgevents.lhe + +! Number of outgoing particles of POWHEG Born level process +! (i.e. not counting additional POWHEG radiation) +POWHEG:nFinal = 2 + +! How vetoing is performed: +! 0 - No vetoing is performed (userhooks are not loaded) +! 1 - Showers are started at the kinematical limit. +! Emissions are vetoed if pTemt > pThard. +! See also POWHEG:vetoCount below +POWHEG:veto = 1 + +! After 'vetoCount' accepted emissions in a row, no more emissions +! are checked. 'vetoCount = 0' means that no emissions are checked. +! Use a very large value, e.g. 10000, to have all emissions checked. +POWHEG:vetoCount = 3 + +! Selection of pThard (note, for events where there is no +! radiation, pThard is always set to be SCALUP): +! 0 - pThard = SCALUP (of the LHA/LHEF standard) +! 1 - the pT of the POWHEG emission is tested against all other +! incoming and outgoing partons, with the minimal value chosen +! 2 - the pT of all final-state partons is tested against all other +! incoming and outgoing partons, with the minimal value chosen +POWHEG:pThard = 2 + +! Selection of pTemt: +! 0 - pTemt is pT of the emitted parton w.r.t. radiating parton +! 1 - pT of the emission is checked against all incoming and outgoing +! partons. pTemt is set to the minimum of these values +! 2 - the pT of all final-state partons is tested against all other +! incoming and outgoing partons, with the minimal value chosen +! WARNING: the choice here can give significant variations in the final +! distributions, notably in the tail to large pT values. +POWHEG:pTemt = 0 + +! Selection of emitted parton for FSR +! 0 - Pythia definition of emitted +! 1 - Pythia definition of radiator +! 2 - Random selection of emitted or radiator +! 3 - Both are emitted and radiator are tried +POWHEG:emitted = 0 + +! pT definitions +! 0 - POWHEG ISR pT definition is used for both ISR and FSR +! 1 - POWHEG ISR pT and FSR d_ij definitions +! 2 - Pythia definitions +POWHEG:pTdef = 1 + +! MPI vetoing +! 0 - No MPI vetoing is done +! 1 - When there is no radiation, MPIs with a scale above pT_1 are vetoed, +! else MPIs with a scale above (pT_1 + pT_2 + pT_3) / 2 are vetoed +POWHEG:MPIveto = 0 +! Note that POWHEG:MPIveto = 1 should be combined with +! MultipartonInteractions:pTmaxMatch = 2 +! which here is taken care of in main31.cc. + +! QED vetoing +! 0 - No QED vetoing is done for pTemt > 0. +! 1 - QED vetoing is done for pTemt > 0. +! 2 - QED vetoing is done for pTemt > 0. If a photon is found +! with pT>pThard from the Born level process, the event is accepted +! and no further veto of this event is allowed (for any pTemt). +POWHEG:QEDveto = 2 + +### decays +ParticleDecays:limitTau0 on +ParticleDecays:tau0Max 10. diff --git a/MC/config/PWGGAJE/pythia8/generator/pythia8_promptphoton.cfg b/MC/config/PWGGAJE/pythia8/generator/pythia8_promptphoton.cfg new file mode 100644 index 000000000..ff0258880 --- /dev/null +++ b/MC/config/PWGGAJE/pythia8/generator/pythia8_promptphoton.cfg @@ -0,0 +1,21 @@ +# 2 -> 2 prompt photon production, oversampling by pThat^4 + +### beams +Beams:idA = 2212 # proton +Beams:idB = 2212 # proton +Beams:eCM = 13600. # GeV + +### processes +SoftQCD:inelastic = off +HardQCD:all = off +PromptPhoton:all = on + +### decays +ParticleDecays:limitTau0 = on +ParticleDecays:tau0Max = 10. + +### phase space cuts +PhaseSpace:pTHatMin = 5 +PhaseSpace:pTHatMax = 600 +PhaseSpace:bias2Selection = on +PhaseSpace:bias2SelectionPow = 4 \ No newline at end of file diff --git a/MC/config/PWGGAJE/pythia8/generator/pythia8box_parametrisedModel.cfg b/MC/config/PWGGAJE/pythia8/generator/pythia8box_parametrisedModel.cfg new file mode 100644 index 000000000..e80199deb --- /dev/null +++ b/MC/config/PWGGAJE/pythia8/generator/pythia8box_parametrisedModel.cfg @@ -0,0 +1,16 @@ +# ### processes + +ProcessLevel:all = off +HadronLevel:all = on +Check:event = off +# ColourReconnection:reconnect = off +# PartonLevel:MPI = off + +### processes +### SoftQCD:inelastic = on + +### decays +ParticleDecays:limitTau0 = on +ParticleDecays:tau0Max = 0. + +### decays: no need to change anything as particles produced by external generator generator_pythia8_bkgBoltzmann are pions, which do not decay by default with PYTHIA8 diff --git a/MC/config/PWGGAJE/trigger/decay_gamma_jets.C b/MC/config/PWGGAJE/trigger/decay_gamma_jets.C index 791544241..2225c5dde 100644 --- a/MC/config/PWGGAJE/trigger/decay_gamma_jets.C +++ b/MC/config/PWGGAJE/trigger/decay_gamma_jets.C @@ -1,4 +1,4 @@ -R__ADD_INCLUDE_PATH($O2DPG_ROOT) +R__ADD_INCLUDE_PATH($O2DPG_MC_CONFIG_ROOT) #include "MC/run/common/detector_acceptance.C" #include #include "Generators/Trigger.h" diff --git a/MC/config/PWGGAJE/trigger/prompt_gamma.C b/MC/config/PWGGAJE/trigger/prompt_gamma.C index b6b51e3d3..3c019c809 100644 --- a/MC/config/PWGGAJE/trigger/prompt_gamma.C +++ b/MC/config/PWGGAJE/trigger/prompt_gamma.C @@ -1,4 +1,4 @@ -R__ADD_INCLUDE_PATH($O2DPG_ROOT) +R__ADD_INCLUDE_PATH($O2DPG_MC_CONFIG_ROOT) #include "MC/run/common/detector_acceptance.C" #include #include "Generators/Trigger.h" diff --git a/MC/config/PWGHF/external/generator/GeneratorHF.C b/MC/config/PWGHF/external/generator/GeneratorHF.C deleted file mode 100644 index ed56c438b..000000000 --- a/MC/config/PWGHF/external/generator/GeneratorHF.C +++ /dev/null @@ -1,160 +0,0 @@ -/// \author R+Preghenella - July 2020 - -// Example of an implementation of an event generator -// that provides HF signals for embedding in background - -R__ADD_INCLUDE_PATH($PYTHIA_ROOT/include) - -#include "Pythia8/Pythia.h" - -namespace o2 -{ -namespace eventgen -{ - -class GeneratorHF : public GeneratorPythia8 -{ - - public: - GeneratorHF() : GeneratorPythia8(){}; - ~GeneratorHF() = default; - - // We initialise the local Pythia8 event where we store the particles - // of the signal event that is the sum of multiple Pythia8 events - // generated according to the generateEvent() function below. - Bool_t Init() override - { - mOutputEvent.init("(GeneratorHF output event)", &mPythia.particleData); - return GeneratorPythia8::Init(); - } - - // This function is called by the primary generator - // for each event in case we are in embedding mode. - // We use it to setup the number of signal events - // to be generated and to be embedded on the background. - void notifyEmbedding(const o2::dataformats::MCEventHeader* bkgHeader) override - { - mEvents = mFormula.Eval(bkgHeader->GetB()); - std::cout << " --- notify embedding: impact parameter is " << bkgHeader->GetB() << ", generating " << mEvents << " signal events " << std::endl; - }; - - // We override this function to be able to generate multiple - // events and build an output event that is the sum of them - // where we have stripped out only the sub-event starting from - // the c-cbar ancestor particle - Bool_t generateEvent() override - { - - // reset counter and event - mOutputEvent.reset(); - - // loop over number of events to be generated - int nEvents = 0; - while (nEvents < mEvents) { - - // generate event - if (!GeneratorPythia8::generateEvent()) - return false; - - // find the c-cbar ancestor - auto ancestor = findAncestor(mPythia.event); - if (ancestor < 0) - continue; - - // append ancestor and its daughters to the output event - selectFromAncestor(ancestor, mPythia.event, mOutputEvent); - nEvents++; - } - - if (mVerbose) - mOutputEvent.list(); - - return true; - }; - - // We override this event to import the particles from the - // output event that we have constructed as the sum of multiple - // Pythia8 sub-events as generated above - Bool_t importParticles() override - { - return GeneratorPythia8::importParticles(mOutputEvent); - } - - // search for c-cbar mother with at least one c at midrapidity - int findAncestor(Pythia8::Event& event) - { - for (int ipa = 0; ipa < event.size(); ++ipa) { - auto daughterList = event[ipa].daughterList(); - bool hasq = false, hasqbar = false, atSelectedY = false; - for (auto ida : daughterList) { - if (event[ida].id() == mPDG) - hasq = true; - if (event[ida].id() == -mPDG) - hasqbar = true; - if ((event[ida].y() > mRapidityMin) && (event[ida].y() < mRapidityMax)) - atSelectedY = true; - } - if (hasq && hasqbar && atSelectedY) - return ipa; - } - return -1; - }; - - void setPDG(int val) { mPDG = val; }; - void setRapidity(double valMin, double valMax) - { - mRapidityMin = valMin; - mRapidityMax = valMax; - }; - void setVerbose(bool val) { mVerbose = val; }; - void setFormula(std::string val) { mFormula.Compile(val.c_str()); }; - - private: - TFormula mFormula; - int mEvents = 1; - Pythia8::Event mOutputEvent; - int mPDG = 4; - double mRapidityMin = -1.5; - double mRapidityMax = 1.5; - bool mVerbose = false; -}; - -} // namespace eventgen -} // namespace o2 - -/** generator instance and settings **/ - -FairGenerator* - GeneratorHF(double rapidityMin = -1.5, double rapidityMax = 1.5, bool verbose = false) -{ - auto gen = new o2::eventgen::GeneratorHF(); - gen->setRapidity(rapidityMin, rapidityMax); - gen->setVerbose(verbose); - gen->setFormula("max(1.,120.*(x<5.)+80.*(1.-x/20.)*(x>5.)*(x<11.)+240.*(1.-x/13.)*(x>11.))"); - - return gen; -} - -FairGenerator* - GeneratorHF_ccbar(double rapidityMin = -1.5, double rapidityMax = 1.5, bool verbose = false) -{ - auto gen = new o2::eventgen::GeneratorHF(); - gen->setPDG(4); - gen->setRapidity(rapidityMin, rapidityMax); - gen->setVerbose(verbose); - gen->setFormula("max(1.,120.*(x<5.)+80.*(1.-x/20.)*(x>5.)*(x<11.)+240.*(1.-x/13.)*(x>11.))"); - - return gen; -} - -FairGenerator* - GeneratorHF_bbbar(double rapidityMin = -1.5, double rapidityMax = 1.5, bool verbose = false) -{ - auto gen = new o2::eventgen::GeneratorHF(); - gen->setPDG(5); - gen->setRapidity(rapidityMin, rapidityMax); - gen->setVerbose(verbose); - gen->setFormula("max(1.,120.*(x<5.)+80.*(1.-x/20.)*(x>5.)*(x<11.)+240.*(1.-x/13.)*(x>11.))"); - - return gen; -} diff --git a/MC/config/PWGHF/external/generator/generator_pythia8_embed_charmnuclei.C b/MC/config/PWGHF/external/generator/generator_pythia8_embed_charmnuclei.C new file mode 100644 index 000000000..94c9c0b80 --- /dev/null +++ b/MC/config/PWGHF/external/generator/generator_pythia8_embed_charmnuclei.C @@ -0,0 +1,349 @@ +#include "FairGenerator.h" +#include "Generators/GeneratorPythia8.h" +#include "Pythia8/Pythia.h" +#include "TRandom.h" +#include "TF1.h" +#include "TMath.h" +#include +#include +#include +#include + +#include "Math/Vector3D.h" +#include "Math/Vector4D.h" + +R__ADD_INCLUDE_PATH($O2DPG_MC_CONFIG_ROOT) +#include "MC/config/common/external/generator/CoalescencePythia8.h" + +using namespace Pythia8; + +class GeneratorPythia8HFEmbedCharmNuclei : public o2::eventgen::GeneratorPythia8 +{ + public: + + /// constructor + GeneratorPythia8HFEmbedCharmNuclei(int pdgCode = 2010010020, float lifetime = 1.f, int nCharmNucleiPerEvent = 10, float yMin = -1.f, float yMax = 1.f, float ptMax = 25.f, bool trivialCoal = false, float coalMomentum = 0.2, float fracFromB = 0.f, unsigned int seed = 0u) + { + nNumberOfCharmNucleiPerEvent = nCharmNucleiPerEvent; + mRapidityMinCharmNuclei = yMin; + mRapidityMaxCharmNuclei = yMax; + mPtMaxCharmNuclei = ptMax; + mTrivialCoal = trivialCoal; + mCoalMomentum = coalMomentum; + mFractionFromBeauty = fracFromB; + mPdgCharmNucleus = pdgCode; + mSign = 1; + mUsedSeed = seed; + if (std::abs(mPdgCharmNucleus) == 2010010020) { + mMassCharmNucleus = 3.226f; + } else { + LOG(fatal) << "********** [GeneratorPythia8HFEmbedCharmNuclei] Only c-deuteron (pdg=2010010020) currently supported! Exit **********"; + } + mLifetimeCharmNucleus = lifetime; + mDecayDistr = new TF1("mDecayDistr", "TMath::Exp(-x * 1./[0])", 0., mLifetimeCharmNucleus * 100); + mDecayDistr->SetNpx(10000); + mDecayDistr->SetParameter(0, mLifetimeCharmNucleus); + mDecayDistrLb = new TF1("mDecayDistrLb", "TMath::Exp(-x * 1./[0])", 0., 44.f); + mDecayDistrLb->SetParameter(0, 0.440f); // lifetime of Lambda_b in mm/c + mDecayDistrLb->SetNpx(10000); + mPtDistrLb = new TF1("mPtDistrLb","[0]*x/TMath::Power((1+TMath::Power(x/[1],[3])),[2])",0.,100.); + mPtDistrLb->SetParameters(1000., 6.97355, 3.20721, 1.71678); + mPtDistrLb->SetNpx(10000); + + Print(); + + auto& param = o2::eventgen::GeneratorPythia8Param::Instance(); + LOG(info) << "Init \'GeneratorPythia8HFEmbedCharmNuclei\' with following parameters"; + LOG(info) << param; + if (param.config.empty()) { + LOG(fatal) << "Failed to init \'GeneratorPythia8\': problems with configuration file "; + } + std::string cfg = gSystem->ExpandPathName(param.config.c_str()); + LOG(info) << "GeneratorPythia8HFEmbedCharmNuclei Reading configuration from file: " << cfg; + if (!mPythiaGun.readFile(cfg, true)) { + LOG(fatal) << "Failed to init \'GeneratorPythia8\': problems with configuration file " << cfg; + } + + mPythiaGun.readString("Random:setSeed = on"); + mPythiaGun.readString("Random:seed = " + std::to_string(seed)); + + if (!mPythiaGun.init()) { + LOG(fatal) << "Failed to init \'GeneratorPythia8\': init returned with error"; + } + } + + /// Destructor + ~GeneratorPythia8HFEmbedCharmNuclei() = default; + + /// Print the input + void Print() + { + LOG(info) << "********** GeneratorPythia8HFEmbedCharmNuclei configuration dump **********"; + LOG(info) << Form("* PDG code of charm nuclei to be injected: %d", mPdgCharmNucleus); + LOG(info) << Form("* Mass of charm nuclei to be injected (GeV/c2): %f", mMassCharmNucleus); + LOG(info) << Form("* Lifetime of charm nuclei to be injected (mm): %f", mLifetimeCharmNucleus); + LOG(info) << Form("* Number of charm nuclei injected per event: %d", nNumberOfCharmNucleiPerEvent); + LOG(info) << Form("* Charmed nucleus rapidity: %f - %f", mRapidityMinCharmNuclei, mRapidityMaxCharmNuclei); + LOG(info) << Form("* Charmed nucleus pT max (prompt): %f", mPtMaxCharmNuclei); + LOG(info) << Form("* Trivial coalescence: %d", mTrivialCoal); + LOG(info) << Form("* Coalescence momentum: %f", mCoalMomentum); + LOG(info) << Form("* Fraction from beauty: %f", mFractionFromBeauty); + LOG(info) << "***********************************************************************"; + } + + void setHadronRapidity(float yMin, float yMax) + { + mRapidityMinCharmNuclei = yMin; + mRapidityMaxCharmNuclei = yMax; + }; + + void setUsedSeed(unsigned int seed) + { + mUsedSeed = seed; + }; + + unsigned int getUsedSeed() const + { + return mUsedSeed; + }; + + //__________________________________________________________________ + bool generateEvent() override + { + // we start from an empty event + mPythia.event.reset(); + + // we simulate c-deuteron decays + for (int iCharmNuclei{0}; iCharmNuclei 0) ? mSign = -1 : mSign = 1; + if (nNumberOfCharmNucleiPerEvent % 2 != 0 && iCharmNuclei == nNumberOfCharmNucleiPerEvent - 1) { + if (gRandom->Rndm() < 0.5) { + mSign = 1; + } + } + + int pdgToGen = mPdgCharmNucleus; + float massToGen = mMassCharmNucleus; + float lifetimeToGen = 0.f; + float minRapToGen = mRapidityMinCharmNuclei; + float maxRapToGen = mRapidityMaxCharmNuclei; + bool isFromB = gRandom->Rndm() < mFractionFromBeauty; + // we determine if it's prompt or non-prompt + if (isFromB) { + pdgToGen = 5122; // we generate a Lambda_b and we let it decay into the charmed nucleus, no other beauty hadrons are considered + massToGen = 5.61940f; // mass of Lambda_b (GeV/c2) + lifetimeToGen = mDecayDistrLb->GetRandom(); + minRapToGen *= 2; + maxRapToGen *= 2; + } else { + lifetimeToGen = mDecayDistr->GetRandom(); + } + + auto pt = (!isFromB) ? gRandom->Uniform(0., mPtMaxCharmNuclei) : mPtDistrLb->GetRandom(); + auto y = gRandom->Uniform(minRapToGen, maxRapToGen); + auto phi = gRandom->Uniform(0, TMath::TwoPi()); + auto px = pt * TMath::Cos(phi); + auto py = pt * TMath::Sin(phi); + auto mt = TMath::Sqrt(massToGen * massToGen + pt * pt); + auto pz = mt * TMath::SinH(y); + auto p = TMath::Sqrt(pt * pt + pz * pz); + auto e = TMath::Sqrt(massToGen * massToGen + p * p); + + Particle particle; + particle.id(mSign * pdgToGen); + particle.status(83); + particle.m(massToGen); + particle.px(px); + particle.py(py); + particle.pz(pz); + particle.e(e); + particle.xProd(0.f); + particle.yProd(0.f); + particle.zProd(0.f); + particle.tau(lifetimeToGen); + mPythiaGun.particleData.mayDecay(5122, true); // force decay + mPythiaGun.particleData.mayDecay(mPdgCharmNucleus, true); // force decay + + bool isCoalSuccess{false}; + int nTrials{0}; + while(!isCoalSuccess || nTrials > 1e4) { + mPythiaGun.event.reset(); + mPythiaGun.event.append(particle); + mPythiaGun.moreDecays(); + std::array dausToCoal = {-1, -1}; + std::vector pdgShortLivedResos = {313, 2224, 102134}; + std::map statusResoDecay = {{313, -85}, {2224, -86}, {102134, -87}}; // do not use 94, it is used by default for no resonances + std::map statusResoDecayFromB = {{313, -95}, {2224, -96}, {102134, -97}}; // do not use 94, it is used by default for no resonances + int whichReso{0}; + int idxCharmNucleus{-1}; + for (int iPart{0}; iPart= idxCharmNucleus) { + // we need to change the indices of the daughter particles to point to the charmed nucleus + auto dauList = part.daughterList(); + for (auto const& dau : dauList) { + mPythiaGun.event[dau].mother1(idxCharmNucleus); + } + mPythiaGun.event.remove(iPart, iPart, true); + whichReso=*resoIt; + } + } + if (whichReso > 0) { // we have to reset all the particles as daughters of the charm nucleus + std::vector idxDausCharmNucleus{}; + for (int iPart{0}; iPart newPartList{}; + std::vector idxToRemove{}; + for (int iPart{idxDausCharmNucleus[0]}; iPart updatedMothers{}; + for (int iPart{0}; iPart{1000010020}, mTrivialCoal, mCoalMomentum, dausToCoal[0], dausToCoal[1], 10.); + if (whichReso > 0) { + if (isFromB) { + mPythiaGun.event[idxCharmNucleus].status(statusResoDecayFromB[whichReso]); + } else { + mPythiaGun.event[idxCharmNucleus].status(statusResoDecay[whichReso]); + } + } + if (isCoalSuccess) { + restoreEnergyConservation(mPythiaGun.event, idxCharmNucleus); + int offset = mPythia.event.size(); // we need to rescale the indices of mothers and daughters, accounting for the particles that are already appended to the event + for (int iPart{0}; iPart 0) { + part.mother1(mother1 + offset - 1); + } + if (mother2 > 0) { + part.mother2(mother2 + offset - 1); + } + if (daughter1 > 0) { + part.daughter1(daughter1 + offset - 1); + } + if (daughter2 > 0) { + part.daughter2(daughter2 + offset - 1); + } + mPythia.event.append(part); + } + } + nTrials++; + } + } + + return true; + } + + +private: + + void restoreEnergyConservation(Pythia8::Event& event, int idxCharmNucleus, float targetMassTolerance = 1e-5) { + /// In the coalescence, the energy is not conserved, we rescale the momentum of the charmed nuclei daughters to restore it to avoid changes in the invariant mass of the charmed nucleus + + float scale = 1.f; + float invMass{0.f}; + while (abs(invMass - mMassCharmNucleus) > targetMassTolerance) { + ROOT::Math::PxPyPzMVector fourVecCharmNucleus; + for (int iDau{event[idxCharmNucleus].daughter1()}; iDau<=event[idxCharmNucleus].daughter2(); ++iDau) { + auto dau = event[iDau]; + fourVecCharmNucleus += ROOT::Math::PxPyPzMVector(dau.px() * scale, dau.py() * scale, dau.pz() * scale, dau.m()); + } + invMass = fourVecCharmNucleus.M(); + scale *= mMassCharmNucleus / invMass; + } + + for (int iDau{event[idxCharmNucleus].daughter1()}; iDau<=event[idxCharmNucleus].daughter2(); ++iDau) { + event[iDau].px(event[iDau].px() * scale); + event[iDau].py(event[iDau].py() * scale); + event[iDau].pz(event[iDau].pz() * scale); + } + event[idxCharmNucleus].px(event[idxCharmNucleus].px() * scale); + event[idxCharmNucleus].py(event[idxCharmNucleus].py() * scale); + event[idxCharmNucleus].pz(event[idxCharmNucleus].pz() * scale); + } + + // Properties of selection + float mMassCharmNucleus; /// mass of the charmed nucleus + int mPdgCharmNucleus; /// pdg code of the charmed nucleus + float mLifetimeCharmNucleus; /// lifetime of the charmed nucleus + int nNumberOfCharmNucleiPerEvent; /// number of charmed nuclei injected per event + float mRapidityMinCharmNuclei; /// rapidity min of the generated charmed nuclei + float mRapidityMaxCharmNuclei; /// rapidity max of the generated charmed nuclei + float mPtMaxCharmNuclei; /// pT max of the generated charmed nuclei + unsigned int mUsedSeed; /// seed + bool mTrivialCoal; /// if true, the coalescence is done without checking the distance in the phase space of the nucleons + float mCoalMomentum; /// coalescence momentum + Pythia8::Pythia mPythiaGun; /// Gun generator with decay support + TF1* mDecayDistr; /// Lifetime distribution + TF1* mDecayDistrLb; /// Lifetime distribution for Lb + TF1* mPtDistrLb; /// pt distribution for Lb (power-law fit to FONLL) + float mFractionFromBeauty; /// fraction of charmed nuclei coming from beauty hadrons + int mSign; /// sign of the charmed nuclei to be generated, if 0 they are generated with 50% of probability as particle or antiparticle +}; + + +///___________________________________________________________ +FairGenerator *GenerateHFEmbedCDeuteron(float lifetime = 1.f, int nCharmNucleiPerEvent = 10, float yMin = -1.f, float yMax = 1.f, float ptMax = 25.f, bool trivialCoal = false, float coalMomentum = 0.2f, float fracFromB = 0.25f) +{ + auto seed = (gRandom->TRandom::GetSeed() % 900000000); + auto myGen = new GeneratorPythia8HFEmbedCharmNuclei(2010010020, lifetime, nCharmNucleiPerEvent, yMin, yMax, ptMax, trivialCoal, coalMomentum, fracFromB, seed); + myGen->readString("Random:setSeed on"); + myGen->readString("Random:seed " + std::to_string(seed)); + return myGen; +} diff --git a/MC/config/PWGHF/external/generator/generator_pythia8_embed_hf.C b/MC/config/PWGHF/external/generator/generator_pythia8_embed_hf.C new file mode 100644 index 000000000..9d75081ea --- /dev/null +++ b/MC/config/PWGHF/external/generator/generator_pythia8_embed_hf.C @@ -0,0 +1,513 @@ +/////////////////////////////////////////////////////////////////////////////// +/// /// +/// HF MC generator for Pb-Pb /// +/// Option 1: generate N PYTHIA events triggered on ccbar and/or bbbar /// +/// to be embedded with a underlying Pb-Pb event /// +/// /// +/////////////////////////////////////////////////////////////////////////////// + +#include "generator_pythia8_gaptriggered_hf.C" + +using namespace Pythia8; + +#include +namespace hf_generators +{ + enum GenType : int { + GapTriggeredCharm = 0, // --> GeneratorPythia8GapTriggeredCharm: charm enriched + GapTriggeredBeauty, // --> GeneratorPythia8GapTriggeredBeauty: beauty enriched + GapTriggeredCharmAndBeauty, // --> GeneratorPythia8GapTriggeredCharmAndBeauty: charm and beauty enriched (with same ratio) + GapHF, // --> GeneratorPythia8GapHF + GapHFRatio, // --> GeneratorPythia8GapHF, quark list built from b/c ratio + NGenType + }; +} + +class GeneratorPythia8EmbedHF : public o2::eventgen::GeneratorPythia8 +{ +public: + + /// default constructor + GeneratorPythia8EmbedHF() = default; + + /// constructor + //GeneratorPythia8EmbedHF(int numSigEvs = 1) { + // mNumSigEvs = numSigEvs; + // //mGeneratedEvents = 0; + //} + + /// Destructor + ~GeneratorPythia8EmbedHF() { + // Clean up the internally created HF generator if any + if (mGeneratorEvHF) { + delete mGeneratorEvHF; + mGeneratorEvHF = nullptr; + } + } + + /// Init + bool Init() override + { + return o2::eventgen::GeneratorPythia8::Init(); + } + + /// @brief setup the event generator for HF signals + /// \param gentype generator type (only ccbar, only bbbar, both) + /// \param usePtHardBins flag to enable/disable pt-hard bins + /// \param yQuarkMin minimum quark rapidity + /// \param yQuarkMax maximum quark rapidity + /// \param yHadronMin minimum hadron rapidity + /// \param yHadronMax maximum hadron rapidity + /// \param hadronPdgList list of PDG codes for hadrons to be used in trigger + /// \param quarkPdgList list of PDG codes for quarks to be enriched in the trigger + void setupGeneratorEvHF(int genType, bool usePtHardBins, float yQuarkMin, float yQuarkMax, float yHadronMin, float yHadronMax, std::vector quarkPdgList = {}, std::vector hadronPdgList = {}, std::vector> partPdgToReplaceList = {}, std::vector freqReplaceList = {}) { + mGeneratorEvHF = nullptr; + switch (genType) + { + case hf_generators::GapTriggeredCharm: + LOG(info) << "********** [GeneratorPythia8EmbedHF] configuring GeneratorPythia8GapTriggeredCharm **********"; + LOG(info) << "********** Default number of HF signal events to be merged (updated by notifyEmbedding): " << mNumSigEvs; + mGeneratorEvHF = dynamic_cast(GeneratorPythia8GapTriggeredCharm(/*no gap trigger*/1, yQuarkMin, yQuarkMax, yHadronMin, yHadronMax, hadronPdgList, partPdgToReplaceList, freqReplaceList)); + break; + case hf_generators::GapTriggeredBeauty: + LOG(info) << "********** [GeneratorPythia8EmbedHF] configuring GeneratorPythia8GapTriggeredBeauty **********"; + LOG(info) << "********** Default number of HF signal events to be merged (updated by notifyEmbedding): " << mNumSigEvs; + mGeneratorEvHF = dynamic_cast(GeneratorPythia8GapTriggeredBeauty(/*no gap trigger*/1, yQuarkMin, yQuarkMax, yHadronMin, yHadronMax, hadronPdgList, partPdgToReplaceList, freqReplaceList)); + break; + case hf_generators::GapTriggeredCharmAndBeauty: + LOG(info) << "********** [GeneratorPythia8EmbedHF] configuring GeneratorPythia8GapTriggeredCharmAndBeauty **********"; + LOG(info) << "********** Default number of HF signal events to be merged (updated by notifyEmbedding): " << mNumSigEvs; + mGeneratorEvHF = dynamic_cast(GeneratorPythia8GapTriggeredCharmAndBeauty(/*no gap trigger*/1, yQuarkMin, yQuarkMax, yHadronMin, yHadronMax, hadronPdgList, partPdgToReplaceList, freqReplaceList)); + break; + case hf_generators::GapHF: + LOG(info) << "********** [GeneratorPythia8EmbedHF] configuring GeneratorPythia8GapHF **********"; + LOG(info) << "********** Default number of HF signal events to be merged (updated by notifyEmbedding): " << mNumSigEvs; + mGeneratorEvHF = dynamic_cast(GeneratorPythia8GapTriggeredBeauty(/*no gap trigger*/1, yQuarkMin, yQuarkMax, yHadronMin, yHadronMax, hadronPdgList, partPdgToReplaceList, freqReplaceList)); + break; + + case hf_generators::GapHFRatio: + LOG(info) << "********** [GeneratorPythia8EmbedHF] configuring GapHFRatio (custom b/c ratio) **********"; + LOG(info) << "********** Default number of HF signal events to be merged (updated by notifyEmbedding): " << mNumSigEvs; + mGeneratorEvHF = dynamic_cast(GeneratorPythia8GapHF(/*no gap trigger*/1, yQuarkMin, yQuarkMax, yHadronMin, yHadronMax, quarkPdgList, hadronPdgList, partPdgToReplaceList, freqReplaceList)); + break; + default: + LOG(fatal) << "********** [GeneratorPythia8EmbedHF] bad configuration, fix it! **********"; + break; + } + + // we set pT hard bins + if (usePtHardBins) { + auto seed = dynamic_cast(mGeneratorEvHF)->getUsedSeed(); + float ptHardBins[4] = {2.76, 20., 50., 1000.}; + int iPt{0}; + if (seed % 10 < 7) { + iPt = 0; + } else if (seed % 10 < 9) { + iPt = 1; + } else { + iPt = 2; + } + dynamic_cast(mGeneratorEvHF)->readString(Form("PhaseSpace:pTHatMin = %f", ptHardBins[iPt])); + dynamic_cast(mGeneratorEvHF)->readString(Form("PhaseSpace:pTHatMax = %f", ptHardBins[iPt+1])); + } + mGeneratorEvHF->Init(); + } + + // This function is called by the primary generator + // for each event in case we are in embedding mode. + // We use it to setup the number of signal events + // to be generated and to be embedded on the background. + void notifyEmbedding(const o2::dataformats::MCEventHeader* bkgHeader) override + { + LOG(info) << "[notifyEmbedding] ----- Function called"; + + /// Impact parameter between the two nuclei + const float x = bkgHeader->GetB(); + LOG(info) << "[notifyEmbedding] ----- Collision impact parameter: " << x; + + /// number of events to be embedded in a background event + mNumSigEvs = static_cast(std::lround(5.0 + 0.886202881 * std::pow(std::max(0.0f, 17.5f - x), 1.7))); + LOG(info) << "[notifyEmbedding] ----- generating " << mNumSigEvs << " signal events " << std::endl; + }; + +protected: + +/// @brief Main function for event generation +bool generateEvent() override +{ + /// Overriding that from GeneratorPythia8, to avoid the simulation of an untriggered event as first + return true; +} + +/// @brief Main function to find out whether the particle comes charm or beauty quark +/// @param partId is the index of the particle under study +/// @param particles are the particles of the full event +bool isFromCharmOrBeauty(const int partId, std::vector const& particles) { + + // Let's check wheter this is already a c or b quark? + const TParticle& part = particles.at(partId); + const int pdgAbs = std::abs(part.GetPdgCode()); + if(pdgAbs == 4 || pdgAbs == 5) { + return true; + } + + // Let's check the mother particles of the hadron at all stages + // and look for the charm or beauty quark + std::vector> arrayIds{}; + std::vector initVec{partId}; + arrayIds.push_back(initVec); // the first vector contains the index of the original particle + int stage = 0; + while(arrayIds[-stage].size() > 0) { + + //LOG(info) << "### stage " << stage << ", arrayIds[-stage].size() = " << arrayIds[-stage].size(); + + std::vector arrayIdsStage{}; + + for (auto& iPart : arrayIds[-stage]) { // check all the particles that were the mothers at the previous stage + const TParticle& partStage = particles.at(iPart); + + // check the first mother + const int firstMotherId = partStage.GetFirstMother(); + if( firstMotherId >= 0) { + const TParticle& firstMother = particles.at(firstMotherId); + const int pdgAbsFirstMother = std::abs(firstMother.GetPdgCode()); + if(pdgAbsFirstMother == 4 || pdgAbsFirstMother == 5) { + return true; + } + // the first mother is not a charm or beauty quark + arrayIdsStage.push_back(firstMotherId); + } + + // let's check all other mothers, if any + const int lastMotherId = partStage.GetSecondMother(); + if(lastMotherId >=0 && lastMotherId != firstMotherId) { + for(int motherId = firstMotherId+1 /*first mother already considered*/; motherId <= lastMotherId; motherId++) { + const TParticle& mother = particles.at(motherId); + const int pdgAbsMother = std::abs(mother.GetPdgCode()); + if(pdgAbsMother == 4 || pdgAbsMother == 5) { + return true; + } + // this mother is not a charm or beauty quark + arrayIdsStage.push_back(motherId); + } + } + + } + + /* + All light-flavour mothers are not considered with this approach + eg: D+ coming from c and uBar --> uBar lost + --> TODO: check if the current particle has a charm or beauty hadron as daughter. If yes, keep it + >>> we can ignore it! This might be useful only for jet analyses, however this approach of embedding N pp events into a Pb-Pb one might be not ideal for them + */ + + // none of the particle mothers is a charm or beauty quark, let's consider their indices for the next stage + arrayIds.push_back(arrayIdsStage); + stage--; // ready to go to next stage + + } /// end while(arrayIds[-stage].size() > 0) + + return false; +} + + +void printParticleVector(std::vector v) { + for(int id=0; id idFirstMother=" << idFirstMother << ", idLastMother=" << idLastMother << ", idFirstDaughter=" << idFirstDaughter << ", idLastDaughter=" << idLastDaughter; + } +} + + +int findKey(const std::map& m, int value) { + for(std::pair p : m) { + if(p.second /*index*/ == value) { + return p.first; // key --> it becomes the new index + } + } + return -1; +} + + +/// @brief Main function to copy the generated particles in mPythia.event into the stack (this.mParticles) +Bool_t importParticles() override +{ + /// Import particles from generated event + /// This should not do anything now, since we override generateEvent + GeneratorPythia8::importParticles(); + + LOG(info) << ""; + LOG(info) << "*************************************************************"; + LOG(info) << "************** New signal event considered **************"; + LOG(info) << "*************************************************************"; + LOG(info) << ""; + + /// Generate mNumSigEvs HF events to be merged in one + int nEvsHF = 0; + while(nEvsHF < mNumSigEvs) { + + /// generate the HF event + bool genOk = false; + while(!genOk) { + genOk = (mGeneratorEvHF->generateEvent() && mGeneratorEvHF->importParticles() /*copy particles from mGeneratorEvHF.mPythia.event to mGeneratorEvHF.mParticles*/ ); + } + + int originalSize = mParticles.size(); // stack of this event generator + + // for debug + // LOG(info) << ""; + // LOG(info) << "============ Before HF event " << nEvsHF; + // LOG(info) << "Full stack (size " << originalSize << "):"; + // printParticleVector(mParticles); + + /// copy the particles from the HF event in the particle stack + auto particlesHfEvent = mGeneratorEvHF->getParticles(); + std::map mapHfParticles = {}; + int counterHfParticles = 0; + + // for debug + // LOG(info) << "-----------------------------------------------"; + // LOG(info) << ">>> HF event " << nEvsHF; + // LOG(info) << " HF event stack:"; + // printParticleVector(particlesHfEvent); + + for(int iPart=0; iPart>>"; + // LOG(info) << " >>> printing mapHfParticles:"; + // for(auto& p : mapHfParticles) { + // const int pdgCodeFromMap = particlesHfEvent.at(p.second).GetPdgCode(); + // LOG(info) << " >>> entry " << p.first << ", original id = " << p.second << ", pdgCode=" << pdgCodeFromMap << " --> firstMotherId=" << particlesHfEvent.at(p.second).GetFirstMother() << ", lastMotherId=" << particlesHfEvent.at(p.second).GetSecondMother() << ", firstDaughterId=" << particlesHfEvent.at(p.second).GetFirstDaughter() << ", lastDaughterId=" << particlesHfEvent.at(p.second).GetLastDaughter(); + // } + + + // In the map we have only the particles from charm or beauty + // Let's readapt the mother/daughter indices accordingly + int offset = originalSize; + for(int iHfPart=0; iHfPart= 0) { + idFirstMother = findKey(mapHfParticles, idFirstMother); + /// If idFirstMother>=0, the 1st mother is from charm or beauty, i.e. is not a light-flavoured parton + /// Instead, if idFirstMother==-1 from findKey this means that the first mother was a light-flavoured parton --> not stored in the map + if(idFirstMother >= 0) { + /// the 1st mother is from charm or beauty, i.e. is not a light-flavoured parton + if(idLastMother != idFirstMotherOrig) { + idLastMother = findKey(mapHfParticles, idLastMother); + if(idLastMother < 0) { + // second mother not found, we set it to -1 + idLastMother = -1; + } + } else { + /// idLastMother is equal to idFirstMother + idLastMother = idFirstMother; + } + isFirstMotherOk = true; + } + } + if(!isFirstMotherOk) { + /// - If we are here, it means that the 1st mother was not from charm or beauty + /// - No need to check whether idLastMother>=0, + /// because this would mean that none of the mother is from charm or beauty and this was checked already in isFromCharmOrBeauty + /// - Need to loop between 1st and last mother, to treat cases like these + /// [11:52:13][INFO] id = 565, pdgCode = -2 --> idFirstMother=519, idLastMother=519 + /// [11:52:13][INFO] id = 566, pdgCode = -4 --> idFirstMother=520, idLastMother=520 + /// [11:52:13][INFO] id = 567, pdgCode = -1 --> idFirstMother=518, idLastMother=518 + /// [11:52:13][INFO] id = 568, pdgCode = -311 --> idFirstMother=565, idLastMother=567 + /// [11:52:13][INFO] id = 569, pdgCode = -4212 --> idFirstMother=565, idLastMother=567 + /// --> w/o loop between 1st and last mother, the mother Ids assigned to this Sc+ (4212) by findKey are -1, both first and last + bool foundAnyMother = false; + for(int idMotherOrig=(idFirstMotherOrig+1); idMotherOrig<=idLastMother; idMotherOrig++) { + const int idMother = findKey(mapHfParticles, idMotherOrig); + if(idMother >= 0) { + /// this should mean that the mother is from HF, i.e. that we found the correct one + if (idFirstMother < 0) { + idFirstMother = idMother; + } + idLastMother = idMother; + foundAnyMother = true; + } + } + // set last mother to -1 if no mother has been found so far + if (!foundAnyMother) { + idLastMother = -1; + idFirstMother = -1; + } + } + + /// fix daughter indices + idFirstDaughter = findKey(mapHfParticles, idFirstDaughter); + idLastDaughter = findKey(mapHfParticles, idLastDaughter); + + /// adjust the particle mother and daughter indices + particle.SetFirstMother((idFirstMother >= 0) ? idFirstMother + offset : idFirstMother); + particle.SetLastMother((idLastMother >= 0) ? idLastMother + offset : idLastMother); + particle.SetFirstDaughter((idFirstDaughter >= 0) ? idFirstDaughter + offset : idFirstDaughter); + particle.SetLastDaughter((idLastDaughter >= 0) ? idLastDaughter + offset : idLastDaughter); + + /// copy inside this.mParticles from mGeneratorEvHF.mParticles, i.e. the particles generated in mGeneratorEvHF + mParticles.push_back(particle); + + } + + // for debug + // LOG(info) << "-----------------------------------------------"; + // LOG(info) << "============ After HF event " << nEvsHF; + // LOG(info) << "Full stack:"; + // printParticleVector(mParticles); + + /// one more event generated, let's update the counter and clear it, to allow the next generation + nEvsHF++; + //mGeneratedEvents++; + mGeneratorEvHF->clearParticles(); + } + + return true; +} + +private: + + Generator* mGeneratorEvHF; // to generate HF signal events + + int mNumSigEvs{1}; // number of HF signal events to be merged in one Pythia event + //unsigned long long mGeneratedEvents; + +}; + +// Helper: build quarkPdgList from bOverCRatio (= N(beauty)/N(charm), default 1 = 1:1) +// Integer b/c: taken directly; fractional b/c: reduced to nearest nB:nC with nC+nB <= 20 +static std::vector BuildQuarkListFromBOverC(float bOverCRatio) +{ +const int iterNMax = 19; + if (bOverCRatio <= 0.f) { + LOG(fatal) << "bOverCRatio (b/c) must be > 0"; + } + if (bOverCRatio > iterNMax*1.f) { + bOverCRatio = iterNMax*1.f; + LOG(warn) << "bOverCRatio (b/c) too large, using 19:1"; + }else if (bOverCRatio < 1.f/iterNMax) { + bOverCRatio = 1.f/iterNMax; + LOG(warn) << "bOverCRatio (b/c) too small, using 1:19"; + } + + int nC = 1, nB = 1; + float bestErr = 1e9f; + for (int c = 1; c <= iterNMax; ++c) { + int b = static_cast(std::lround(bOverCRatio * c)); + if (b < 1 || c + b > 20) { + continue; + } + float err = std::fabs(static_cast(b) / c - bOverCRatio); + if (err < bestErr) { + /// This check here is needed in case bOverCRatio*c is not integer (it can happen with e.g. bOverCRatio=4./9.) + /// In this case, b is its truncation and the desired ratio is not obtained + /// It means that one needs to continue iterating until bOverCRatio*c is integer, i.e. b not truncated + /// + /// Possible cases: + /// 1. we want nB = nC*R, with R integer + /// -> we enter here in the first loop iteration + /// 2. we want more charm than beauty by an integer amount, i.e. nB = nC*R with R=1./N, with N integer + /// -> we enter here after N iterations, when c=N + /// 3. we want either more charm or beauty, but with a factor R that is not integer, as well as its inverse (e.g. bOverCRatio=4./9.) + /// -> In this case, bOverCRatio*c is not integer, namely b is its truncation and the desired ratio is not obtained + /// The code iterates at most until c becomes equal to the denominator of the fraction + /// 4. we want one of the previous cases, but we assign to bOverCRatio a value that is not rational + /// or such as we do not enter here within 19 iterations + /// -> nC and nB, are not touched, therefore we do not have the desired fraction. We'll need to throw a fatal (*) + /// + bestErr = err; + nC = c; + nB = b; + + /// If we are at this point, we reached already the desired ratio between b and c. + /// Let's break the loop + break; + } + } + + // (*) check if we have the desired fraction + bool isRatioUnity = std::fabs(bOverCRatio-1) < 1e-05; + if (!isRatioUnity && nC==1 && nB==1) { + LOG(fatal) << "nC=" << nC << ", nB=" << nB << " but you ask bOverCRatio to be " << bOverCRatio <<", which is different from nB/nC. It means either that bOverCRatio is not rational, or that you need more than " << iterNMax << " iterations. Change it!"; + } + + std::vector list; + // Bresenham interleaving + int len = nC + nB; + for (int k = 0; k < len; ++k) + list.push_back((((k + 1) * nC) / len > (k * nC) / len) ? 4 : 5); + return list; +} + +// Charm enriched +FairGenerator * GeneratorPythia8EmbedHFCharm(bool usePtHardBins = false, float yQuarkMin = -1.5, float yQuarkMax = 1.5, float yHadronMin = -1.5, float yHadronMax = 1.5, std::vector quarkPdgList = {}, std::vector hadronPdgList = {}, std::vector> partPdgToReplaceList = {}, std::vector freqReplaceList = {}) +{ + auto myGen = new GeneratorPythia8EmbedHF(); + + /// setup the internal generator for HF events + myGen->setupGeneratorEvHF(hf_generators::GapTriggeredCharm, usePtHardBins, yQuarkMin, yQuarkMax, yHadronMin, yHadronMax, quarkPdgList, hadronPdgList, partPdgToReplaceList, freqReplaceList); + + return myGen; +} + +// Beauty enriched +FairGenerator * GeneratorPythia8EmbedHFBeauty(bool usePtHardBins = false, float yQuarkMin = -1.5, float yQuarkMax = 1.5, float yHadronMin = -1.5, float yHadronMax = 1.5, std::vector quarkPdgList = {}, std::vector hadronPdgList = {}, std::vector> partPdgToReplaceList = {}, std::vector freqReplaceList = {}) +{ + auto myGen = new GeneratorPythia8EmbedHF(); + + /// setup the internal generator for HF events + myGen->setupGeneratorEvHF(hf_generators::GapTriggeredBeauty, usePtHardBins, yQuarkMin, yQuarkMax, yHadronMin, yHadronMax, quarkPdgList, hadronPdgList, partPdgToReplaceList, freqReplaceList); + + return myGen; +} + +// Charm and beauty enriched (with same ratio) +FairGenerator * GeneratorPythia8EmbedHFCharmAndBeauty(bool usePtHardBins = false, float yQuarkMin = -1.5, float yQuarkMax = 1.5, float yHadronMin = -1.5, float yHadronMax = 1.5, std::vector quarkPdgList = {}, std::vector hadronPdgList = {}, std::vector> partPdgToReplaceList = {}, std::vector freqReplaceList = {}) +{ + auto myGen = new GeneratorPythia8EmbedHF(); + + /// setup the internal generator for HF events + myGen->setupGeneratorEvHF(hf_generators::GapTriggeredCharmAndBeauty, usePtHardBins, yQuarkMin, yQuarkMax, yHadronMin, yHadronMax, quarkPdgList, hadronPdgList, partPdgToReplaceList, freqReplaceList); + + return myGen; +} + +// Charm and beauty enriched with tunable b/c ratio +FairGenerator * GeneratorPythia8EmbedHFRatio(float bOverCRatio = 1.f, bool usePtHardBins = false, float yQuarkMin = -1.5, float yQuarkMax = 1.5, float yHadronMin = -1.5, float yHadronMax = 1.5, std::vector hadronPdgList = {}, std::vector> partPdgToReplaceList = {}, std::vector freqReplaceList = {}) +{ + auto myGen = new GeneratorPythia8EmbedHF(); + + /// build the quark list from the b/c ratio + auto quarkPdgList = BuildQuarkListFromBOverC(bOverCRatio); + + /// setup the internal generator for HF events + myGen->setupGeneratorEvHF(hf_generators::GapHFRatio, + usePtHardBins, yQuarkMin, yQuarkMax, yHadronMin, yHadronMax, + quarkPdgList, hadronPdgList, partPdgToReplaceList, freqReplaceList); + + return myGen; +} diff --git a/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C b/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C index 75c2663b1..dc3407130 100644 --- a/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C +++ b/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C @@ -1,37 +1,134 @@ +R__LOAD_LIBRARY(EvtGen) +R__ADD_INCLUDE_PATH($EVTGEN_ROOT/include) + #include "FairGenerator.h" #include "Generators/GeneratorPythia8.h" +#include "Generators/GeneratorPythia8Param.h" +#include "EvtGen/EvtGen.hh" #include "Pythia8/Pythia.h" +#include "Pythia8Plugins/EvtGen.h" #include "TRandom.h" +#include "TDatabasePDG.h" +#include #include +#include using namespace Pythia8; -class GeneratorPythia8GapTriggeredHF : public o2::eventgen::GeneratorPythia8 { +class GeneratorPythia8GapTriggeredHF : public o2::eventgen::GeneratorPythia8 +{ public: - /// default constructor GeneratorPythia8GapTriggeredHF() = default; /// constructor - GeneratorPythia8GapTriggeredHF(int inputTriggerRatio = 5, int quarkPdg = 4) { + GeneratorPythia8GapTriggeredHF(int inputTriggerRatio = 5, std::vector quarkPdgList = {}, std::vector hadronPdgList = {}, std::vector> partPdgToReplaceList = {}, std::vector freqReplaceList = {}) + { mGeneratedEvents = 0; - mHadronPdg = 0; // unless differently set, we do not trigger on specific hadron species - mQuarkPdg = quarkPdg; mInverseTriggerRatio = inputTriggerRatio; mQuarkRapidityMin = -1.5; - mQuarkRapidityMax = -1.5; + mQuarkRapidityMax = 1.5; mHadRapidityMin = -1.5; - mHadRapidityMax = -1.5; - mDoNoQuarkTrigger = false; - } + mHadRapidityMax = 1.5; + mQuarkPdg = 0; + mHadronPdg = 0; + mQuarkPdgList = quarkPdgList; + mHadronPdgList = hadronPdgList; + mPartPdgToReplaceList = partPdgToReplaceList; + mFreqReplaceList = freqReplaceList; + mEvtGen = nullptr; + mUseEvtGen = false; + mEvtGenDecTable = ""; + // Ds1*(2700), Ds1*(2860), Ds3*(2860), Xic(3055)+, Xic(3080)+, Xic(3055)0, Xic(3080)0, LambdaC(2625), LambdaC(2595), LambdaC(2860), LambdaC(2880), LambdaC(2940), ThetaC(3100) + mCustomPartPdgs = {30433, 40433, 437, 4315, 4316, 4325, 4326, 4124, 14122, 24124, 24126, 4125, 9422111}; + mCustomPartMasses[30433] = 2.714f; + mCustomPartMasses[40433] = 2.859f; + mCustomPartMasses[437] = 2.860f; + mCustomPartMasses[4315] = 3.0590f; + mCustomPartMasses[4316] = 3.0799f; + mCustomPartMasses[4325] = 3.0559f; + mCustomPartMasses[4326] = 3.0772f; + mCustomPartMasses[4124] = 2.62810f; + mCustomPartMasses[14122] = 2.59225f; + mCustomPartMasses[24124] = 2.8561f; + mCustomPartMasses[24126] = 2.8816; + mCustomPartMasses[4125] = 2.9396f; + mCustomPartMasses[9422111] = 3.099f; + mCustomPartWidths[30433] = 0.122f; + mCustomPartWidths[40433] = 0.160f; + mCustomPartWidths[437] = 0.053f; + mCustomPartWidths[4315] = 0.0064f; + mCustomPartWidths[4316] = 0.0056f; + mCustomPartWidths[4325] = 0.0078f; + mCustomPartWidths[4326] = 0.0036f; + mCustomPartWidths[4124] = 0.00052f; + mCustomPartWidths[14122] = 0.0026f; + mCustomPartWidths[24124] = 0.0676f; + mCustomPartWidths[24126] = 0.0056f; + mCustomPartWidths[4125] = 0.017f; + mCustomPartWidths[9422111] = 0.0000083f; + Print(); + } /// Destructor ~GeneratorPythia8GapTriggeredHF() = default; - void addTriggerOnHadron(int hadPdg) { mHadronPdg = hadPdg; }; - void setQuarkTrigger (bool doNoQuarkTrigger) { mDoNoQuarkTrigger = doNoQuarkTrigger; }; + /// Print the input + void Print() + { + LOG(info) << "********** GeneratorPythia8GapTriggeredHF configuration dump **********"; + LOG(info)< {} (freq. {})", mPartPdgToReplaceList[iRepl].at(0), mPartPdgToReplaceList[iRepl].at(1), mFreqReplaceList[iRepl]); + } + LOG(info)<<"***********************************************************************"; + } + + bool Init() override + { + addSubGenerator(0, "Minimum bias"); + addSubGenerator(4, "Charm injected"); + addSubGenerator(5, "Beauty injected"); + + std::vector pdgToReplace{}; + for (auto iRepl{0u}; iRepl 1.f) { + LOGP(fatal, "Replacing more than 100% of a particle!"); + } + pdgToReplace.push_back(mPartPdgToReplaceList[iRepl].at(0)); + } + + if (mUseEvtGen) { + mEvtGen = new Pythia8::EvtGenDecays(&mPythia, mEvtGenDecTable.data(), gSystem->ExpandPathName("$EVTGEN_ROOT/share/EvtGen/evt.pdl")); + } + + return o2::eventgen::GeneratorPythia8::Init(); + } + void setQuarkRapidity(float yMin, float yMax) { mQuarkRapidityMin = yMin; @@ -42,26 +139,78 @@ public: mHadRapidityMin = yMin; mHadRapidityMax = yMax; }; + void setUsedSeed(unsigned int seed) + { + mUsedSeed = seed; + }; + unsigned int getUsedSeed() const + { + return mUsedSeed; + }; + void setUseEvtGenDecayer(std::string evtGenDecTable = "") { + mUseEvtGen = true; + if (evtGenDecTable.empty()) { + mEvtGenDecTable = gSystem->ExpandPathName("$EVTGEN_ROOT/share/EvtGen/DECAY.DEC"); + } else { + mEvtGenDecTable = gSystem->ExpandPathName(evtGenDecTable.data()); + } + } protected: //__________________________________________________________________ - bool generateEvent() override { + bool generateEvent() override + { + // Simple straightforward check to alternate generators - if (mGeneratedEvents % mInverseTriggerRatio == 0) { + if (mGeneratedEvents % mInverseTriggerRatio == 0) + { + int nInjectedEvents = mGeneratedEvents / mInverseTriggerRatio; + // Alternate quarks if enabled (with the same ratio) + if (mQuarkPdgList.size() >= 1) + { + int iQuark = nInjectedEvents % mQuarkPdgList.size(); + mQuarkPdg = mQuarkPdgList[iQuark]; + LOG(debug)<<"SELECTED quark: "<= 1) + { + int iHadron = (nInjectedEvents / std::max(mQuarkPdgList.size(), 1ul)) % mHadronPdgList.size(); + mHadronPdg = mHadronPdgList[iHadron]; + LOG(debug)<<"SELECTED hadron: "<decay(); + checkConsistency(); + } + genOk = selectEvent(); + } } - } else { + notifySubGenerator(mQuarkPdg); + } + else + { // Generate minimum-bias event bool genOk = false; - while (!genOk) { + while (!genOk) + { genOk = GeneratorPythia8::generateEvent(); + if (genOk) { + if (mUseEvtGen) { + mEvtGen->decay(); + checkConsistency(); + } + } } + notifySubGenerator(0); } mGeneratedEvents++; @@ -69,101 +218,313 @@ protected: return true; } - bool selectEvent(const Pythia8::Event& event) + bool selectEvent() { - bool isGoodAtPartonLevel; - bool isGoodAtHadronLevel; - if(mDoNoQuarkTrigger){ - isGoodAtPartonLevel = (mHadronPdg != 0) ? true : false; - isGoodAtHadronLevel = (mHadronPdg != 0) ? false : true; - } else { - isGoodAtPartonLevel = false; - isGoodAtHadronLevel = (mHadronPdg != 0) ? false : true; - } - for (auto iPart{0}; iPart < event.size(); ++iPart) { + bool isGoodAtPartonLevel{mQuarkPdgList.size() == 0}; + bool isGoodAtHadronLevel{mHadronPdgList.size() == 0}; + bool anyPartToReplace{mPartPdgToReplaceList.size() > 0}; + + for (auto iPart{0}; iPart < mPythia.event.size(); ++iPart) + { + // search for Q-Qbar mother with at least one Q in rapidity window - if (!isGoodAtPartonLevel) { - auto daughterList = event[iPart].daughterList(); + if (!isGoodAtPartonLevel) + { + auto daughterList = mPythia.event[iPart].daughterList(); bool hasQ = false, hasQbar = false, atSelectedY = false; - for (auto iDau : daughterList) { - if (event[iDau].id() == mQuarkPdg) + for (auto iDau : daughterList) + { + if (mPythia.event[iDau].id() == mQuarkPdg) + { hasQ = true; - if (event[iDau].id() == -mQuarkPdg) + if ((mPythia.event[iDau].y() > mQuarkRapidityMin) && (mPythia.event[iDau].y() < mQuarkRapidityMax)) + { + atSelectedY = true; + } + } + if (mPythia.event[iDau].id() == -mQuarkPdg) + { hasQbar = true; - if ((event[iDau].y() > mQuarkRapidityMin) && (event[iDau].y() < mQuarkRapidityMax)) - atSelectedY = true; + if ((mPythia.event[iDau].y() > mQuarkRapidityMin) && (mPythia.event[iDau].y() < mQuarkRapidityMax)) + { + atSelectedY = true; + } + } } - if (hasQ && hasQbar && atSelectedY) { + if (hasQ && hasQbar && atSelectedY) + { isGoodAtPartonLevel = true; } } // search for hadron in rapidity window - if (!isGoodAtHadronLevel) { - int id = std::abs(event[iPart].id()); - float rap = event[iPart].y(); - if (id == mHadronPdg && rap > mHadRapidityMin && rap < mHadRapidityMax) { + if (!isGoodAtHadronLevel) + { + int id = std::abs(mPythia.event[iPart].id()); + float rap = mPythia.event[iPart].y(); + if (id == mHadronPdg && rap > mHadRapidityMin && rap < mHadRapidityMax) + { isGoodAtHadronLevel = true; } } - // we send the trigger - if (isGoodAtPartonLevel && isGoodAtHadronLevel) { + // if requested, we replace the particle + const double pseudoRndm = mPythia.event[iPart].pT() * 1000. - (int64_t)(mPythia.event[iPart].pT() * 1000); + for (auto iPartToReplace{0u}; iPartToReplace= 91 && statusMother <= 99) { + return false; + } + + auto mothers = mPythia.event[iPartToReplace].motherList(); + + int charge = mPythia.event[iPartToReplace].id() / std::abs(mPythia.event[iPartToReplace].id()); + float px = mPythia.event[iPartToReplace].px(); + float py = mPythia.event[iPartToReplace].py(); + float pz = mPythia.event[iPartToReplace].pz(); + float mass = 0.f; + + if (std::find(mCustomPartPdgs.begin(), mCustomPartPdgs.end(), pdgCodeNew) == mCustomPartPdgs.end()) { // not a custom particle + float width = TDatabasePDG::Instance()->GetParticle(pdgCodeNew)->Width(); + float massRest = TDatabasePDG::Instance()->GetParticle(pdgCodeNew)->Mass(); + if (width > 0.f) { + mass = gRandom->BreitWigner(massRest, width); + } else { + mass = massRest; + } + } else { + if (mCustomPartWidths[pdgCodeNew] > 0.f) { + mass = gRandom->BreitWigner(mCustomPartMasses[pdgCodeNew], mCustomPartWidths[pdgCodeNew]); + } else { + mass = mCustomPartMasses[pdgCodeNew]; + } + } + float energy = std::sqrt(px*px + py*py + pz*pz + mass*mass); + + // buffer daughter indices of mothers + std::vector> dauOfMothers{}; + for (auto const& mother: mothers) { + dauOfMothers.push_back(mPythia.event[mother].daughterList()); + } + + // we remove particle to replace and its daughters + mPythia.event[iPartToReplace].undoDecay(); + int status = std::abs(mPythia.event[iPartToReplace].status()); // we must get it here otherwise it is negative (already decayed) + if (status < 81 || status > 89) { + status = 81; + } + mPythia.event.remove(iPartToReplace, iPartToReplace, true); // we remove the original particle + + // we restore the daughter indices of the mothers after the removal + int newPartIdx{0}; + std::array newMothers = {0, 0}; + if (o2::eventgen::GeneratorPythia8Param::Instance().includePartonEvent) { // only necessary in case of parton event, otherwise we keep no mother + newMothers[0] = mothers.front(); + newMothers[1] = mothers.back(); + newPartIdx = mPythia.event.size(); + } + for (auto iMom{0u}; iMom dau1) { + mPythia.event[mothers[iMom]].daughter1(dau1); + mPythia.event[mothers[iMom]].daughter2(dau2 - 1); + } else if (dau1 == dau2) { + if (dau1 == 0) { + mPythia.event[mothers[iMom]].daughter1(0); + mPythia.event[mothers[iMom]].daughter2(0); + } else { // in this case we set it equal to the new particle + mPythia.event[mothers[iMom]].daughter1(newPartIdx); + mPythia.event[mothers[iMom]].daughter2(newPartIdx); + } + } else if (dau2 < dau1) { // in this case we set it equal to the new particle + if (dau2 == 0) { + mPythia.event[mothers[iMom]].daughter1(newPartIdx); + } else { + if (dau1 == iPartToReplace) { + mPythia.event[mothers[iMom]].daughter1(newPartIdx); + } else { + mPythia.event[mothers[iMom]].daughter2(newPartIdx); + } + } + } + } + + mPythia.event.append(charge * pdgCodeNew, status, newMothers[0], newMothers[1], 0, 0, 0, 0, px, py, pz, energy, mass); + mPythia.moreDecays(); // we need to decay the new particle + + return true; + } + + void checkConsistency() { + for (int iPart{1}; iPartGetParticle(abs(mPythia.event[iPart].id()))) { + // std::cout << "Particle code non known in TDatabasePDG " << mPythia.event[iPart].id() << " - set pdg = 89" << std::endl; + mPythia.event[iPart].id(89); + } + } + } + + private: // Interface to override import particles Pythia8::Event mOutputEvent; - // Properties of selection +// Properties of selection int mQuarkPdg; float mQuarkRapidityMin; float mQuarkRapidityMax; int mHadronPdg; float mHadRapidityMin; float mHadRapidityMax; - bool mDoNoQuarkTrigger; + unsigned int mUsedSeed; + std::vector> mPartPdgToReplaceList; + std::vector mFreqReplaceList; + std::vector mCustomPartPdgs; + std::map mCustomPartMasses; + std::map mCustomPartWidths; // Control gap-triggering unsigned long long mGeneratedEvents; int mInverseTriggerRatio; + + // Control alternate trigger on charm and beauty quarks + std::vector mQuarkPdgList = {}; + + // Control alternate trigger on different hadrons + std::vector mHadronPdgList = {}; + + // EVTGEN decayer from PYTHIA8 plugins + Pythia8::EvtGenDecays *mEvtGen; + bool mUseEvtGen; + std::string mEvtGenDecTable; + }; // Predefined generators: - // Charm-enriched -FairGenerator *GeneratorPythia8GapTriggeredCharm(int inputTriggerRatio, float yQuarkMin=-1.5, float yQuarkMax=1.5, float yHadronMin=-1.5, float yHadronMax=1.5, int pdgCodeCharmHadron=0, bool doNoQuarkTrigger=false) { - auto myGen = new GeneratorPythia8GapTriggeredHF(inputTriggerRatio, 4); +FairGenerator *GeneratorPythia8GapTriggeredCharm(int inputTriggerRatio, float yQuarkMin = -1.5, float yQuarkMax = 1.5, float yHadronMin = -1.5, float yHadronMax = 1.5, std::vector hadronPdgList = {}, std::vector> partPdgToReplaceList = {}, std::vector freqReplaceList = {}) +{ + auto myGen = new GeneratorPythia8GapTriggeredHF(inputTriggerRatio, std::vector{4}, hadronPdgList, partPdgToReplaceList, freqReplaceList); auto seed = (gRandom->TRandom::GetSeed() % 900000000); + myGen->setUsedSeed(seed); myGen->readString("Random:setSeed on"); myGen->readString("Random:seed " + std::to_string(seed)); myGen->setQuarkRapidity(yQuarkMin, yQuarkMax); - if(pdgCodeCharmHadron != 0) { - myGen->setQuarkTrigger(doNoQuarkTrigger); - myGen->addTriggerOnHadron(pdgCodeCharmHadron); + if (hadronPdgList.size() != 0) + { myGen->setHadronRapidity(yHadronMin, yHadronMax); } return myGen; } // Beauty-enriched -FairGenerator *GeneratorPythia8GapTriggeredBeauty(int inputTriggerRatio, float yMin=-1.5, float yMax=1.5, float yHadronMin=-1.5, float yHadronMax=1.5, int pdgCodeCharmHadron=0, bool doNoQuarkTrigger=false) { - auto myGen = new GeneratorPythia8GapTriggeredHF(inputTriggerRatio, 5); +FairGenerator *GeneratorPythia8GapTriggeredBeauty(int inputTriggerRatio, float yQuarkMin = -1.5, float yQuarkMax = 1.5, float yHadronMin = -1.5, float yHadronMax = 1.5, std::vector hadronPdgList = {}, std::vector> partPdgToReplaceList = {}, std::vector freqReplaceList = {}) +{ + auto myGen = new GeneratorPythia8GapTriggeredHF(inputTriggerRatio, std::vector{5}, hadronPdgList, partPdgToReplaceList, freqReplaceList); + auto seed = (gRandom->TRandom::GetSeed() % 900000000); + myGen->setUsedSeed(seed); + myGen->readString("Random:setSeed on"); + myGen->readString("Random:seed " + std::to_string(seed)); + myGen->setQuarkRapidity(yQuarkMin, yQuarkMax); + if (hadronPdgList.size() != 0) + { + myGen->setHadronRapidity(yHadronMin, yHadronMax); + } + return myGen; +} + +// Charm and beauty enriched (with same ratio) +FairGenerator *GeneratorPythia8GapTriggeredCharmAndBeauty(int inputTriggerRatio, float yQuarkMin = -1.5, float yQuarkMax = 1.5, float yHadronMin = -1.5, float yHadronMax = 1.5, std::vector hadronPdgList = {}, std::vector> partPdgToReplaceList = {}, std::vector freqReplaceList = {}) +{ + auto myGen = new GeneratorPythia8GapTriggeredHF(inputTriggerRatio, std::vector{4, 5}, hadronPdgList, partPdgToReplaceList, freqReplaceList); + auto seed = (gRandom->TRandom::GetSeed() % 900000000); + myGen->setUsedSeed(seed); + myGen->readString("Random:setSeed on"); + myGen->readString("Random:seed " + std::to_string(seed)); + myGen->setQuarkRapidity(yQuarkMin, yQuarkMax); + if (hadronPdgList.size() != 0) + { + myGen->setHadronRapidity(yHadronMin, yHadronMax); + } + return myGen; +} + +// Charm and beauty enriched (2/3 charm 1/3 beauty) +FairGenerator *GeneratorPythia8GapTriggeredCharmBeautyCharm(int inputTriggerRatio, float yQuarkMin = -1.5, float yQuarkMax = 1.5, float yHadronMin = -1.5, float yHadronMax = 1.5, std::vector hadronPdgList = {}, std::vector> partPdgToReplaceList = {}, std::vector freqReplaceList = {}) +{ + auto myGen = new GeneratorPythia8GapTriggeredHF(inputTriggerRatio, std::vector{4, 5, 4}, hadronPdgList, partPdgToReplaceList, freqReplaceList); + auto seed = (gRandom->TRandom::GetSeed() % 900000000); + myGen->setUsedSeed(seed); + myGen->readString("Random:setSeed on"); + myGen->readString("Random:seed " + std::to_string(seed)); + myGen->setQuarkRapidity(yQuarkMin, yQuarkMax); + if (hadronPdgList.size() != 0) + { + myGen->setHadronRapidity(yHadronMin, yHadronMax); + } + return myGen; +} + +FairGenerator *GeneratorPythia8GapHF(int inputTriggerRatio, float yQuarkMin = -1.5, float yQuarkMax = 1.5, float yHadronMin = -1.5, float yHadronMax = 1.5, std::vector quarkPdgList = {}, std::vector hadronPdgList = {}, std::vector> partPdgToReplaceList = {}, std::vector freqReplaceList = {}) +{ + if (hadronPdgList.size() == 0 && quarkPdgList.size() == 0) + { + LOG(fatal) << "GeneratorPythia8GapHF: At least one quark or hadron PDG code must be specified"; + } + auto myGen = new GeneratorPythia8GapTriggeredHF(inputTriggerRatio, quarkPdgList, hadronPdgList, partPdgToReplaceList, freqReplaceList); auto seed = (gRandom->TRandom::GetSeed() % 900000000); + myGen->setUsedSeed(seed); myGen->readString("Random:setSeed on"); myGen->readString("Random:seed " + std::to_string(seed)); - myGen->setQuarkRapidity(yMin, yMax); - if(pdgCodeCharmHadron != 0) { - myGen->setQuarkTrigger(doNoQuarkTrigger); - myGen->addTriggerOnHadron(pdgCodeCharmHadron); + myGen->setQuarkRapidity(yQuarkMin, yQuarkMax); + myGen->setHadronRapidity(yHadronMin, yHadronMax); + + return myGen; +} + +// Beauty-enriched with EvtGen decayer +FairGenerator *GeneratorPythia8GapTriggeredBeautyWithEvtGen(int inputTriggerRatio, float yQuarkMin = -1.5, float yQuarkMax = 1.5, float yHadronMin = -1.5, float yHadronMax = 1.5, std::vector hadronPdgList = {}, std::vector> partPdgToReplaceList = {}, std::vector freqReplaceList = {}, std::string decayTable = "") +{ + auto myGen = new GeneratorPythia8GapTriggeredHF(inputTriggerRatio, std::vector{5}, hadronPdgList, partPdgToReplaceList, freqReplaceList); + auto seed = (gRandom->TRandom::GetSeed() % 900000000); + myGen->setUsedSeed(seed); + myGen->readString("Random:setSeed on"); + myGen->readString("Random:seed " + std::to_string(seed)); + myGen->setQuarkRapidity(yQuarkMin, yQuarkMax); + if (hadronPdgList.size() != 0) + { myGen->setHadronRapidity(yHadronMin, yHadronMax); } + myGen->setUseEvtGenDecayer(decayTable); return myGen; } diff --git a/MC/config/PWGHF/external/generator/generator_pythia8_hfhadron_to_nuclei.C b/MC/config/PWGHF/external/generator/generator_pythia8_hfhadron_to_nuclei.C new file mode 100644 index 000000000..339b79eb8 --- /dev/null +++ b/MC/config/PWGHF/external/generator/generator_pythia8_hfhadron_to_nuclei.C @@ -0,0 +1,218 @@ +#include "FairGenerator.h" +#include "Generators/GeneratorPythia8.h" +#include "Pythia8/Pythia.h" +#include "TRandom.h" +#include +#include +#include +#include + +R__ADD_INCLUDE_PATH($O2DPG_MC_CONFIG_ROOT) +#include "MC/config/common/external/generator/CoalescencePythia8.h" +#include "MC/config/PWGHF/pythia8/hooks/pythia8_userhooks_qqbar.C" + +using namespace Pythia8; + +class GeneratorPythia8HFHadToNuclei : public o2::eventgen::GeneratorPythia8 +{ + public: + /// default constructor + GeneratorPythia8HFHadToNuclei() = default; + + /// constructor + GeneratorPythia8HFHadToNuclei(int inputTriggerRatio = 5, std::vector hfHadronPdgList = {}, std::vector nucleiPdgList = {}, bool trivialCoal = false, float coalMomentum = 0.4) + { + + mGeneratedEvents = 0; + mInverseTriggerRatio = inputTriggerRatio; + mHadRapidityMin = -1.5; + mHadRapidityMax = 1.5; + mHadronPdg = 0; + mHFHadronPdgList = hfHadronPdgList; + mNucleiPdgList = nucleiPdgList; + mTrivialCoal = trivialCoal; + mCoalMomentum = coalMomentum; + Print(); + } + + /// Destructor + ~GeneratorPythia8HFHadToNuclei() = default; + + /// Print the input + void Print() + { + LOG(info) << "********** GeneratorPythia8HFHadToNuclei configuration dump **********"; + LOG(info) << Form("* Trigger ratio: %d", mInverseTriggerRatio); + LOG(info) << Form("* Hadron rapidity: %f - %f", mHadRapidityMin, mHadRapidityMax); + LOG(info) << Form("* Hadron pdg list: "); + for (auto pdg : mHFHadronPdgList) { + LOG(info) << Form("* %d ", pdg); + } + LOG(info) << Form("* Trivial coalescence: %d", mTrivialCoal); + LOG(info) << Form("* Coalescence momentum: %f", mCoalMomentum); + LOG(info) << Form("* Nuclei pdg list: "); + for (auto pdg : mNucleiPdgList) { + LOG(info) << Form("* %d ", pdg); + } + LOG(info) << "***********************************************************************"; + } + + bool Init() override + { + addSubGenerator(0, "Minimum bias"); + addSubGenerator(1, "HF + Coalescence"); + + // Bias parton-level generation towards bbbar production at midrapidity, but only + // while generating the triggered events. Without this, finding the requested b-hadron + // can stall the jobs for a very long time + mBbbarBiasHook = new UserHooks_qqbar(); + mBbbarBiasHook->setPDG(5); + mBbbarBiasHook->setRapidity(mHadRapidityMin, mHadRapidityMax); + mBbbarBiasHook->setActive(false); + setUserHooks(mBbbarBiasHook); + + return o2::eventgen::GeneratorPythia8::Init(); + } + + void setHadronRapidity(float yMin, float yMax) + { + mHadRapidityMin = yMin; + mHadRapidityMax = yMax; + }; + void setUsedSeed(unsigned int seed) + { + mUsedSeed = seed; + }; + unsigned int getUsedSeed() const + { + return mUsedSeed; + }; + + protected: + //__________________________________________________________________ + bool generateEvent() override + { + // Simple straightforward check to alternate generators + if (mGeneratedEvents % mInverseTriggerRatio == 0) { + int nInjectedEvents = mGeneratedEvents / mInverseTriggerRatio; + // Alternate hadrons if enabled (with the same ratio) + if (mHFHadronPdgList.size() >= 1) { + int iHadron = nInjectedEvents % mHFHadronPdgList.size(); + mHadronPdg = mHFHadronPdgList[iHadron]; + LOG(info) << "Selected hadron: " << mHFHadronPdgList[iHadron]; + } + + // Generate event of interest + bool genOk = false; + mBbbarBiasHook->setActive(true); + while (!genOk) { + if (GeneratorPythia8::generateEvent()) { + genOk = selectEvent(mPythia.event); + } + } + mBbbarBiasHook->setActive(false); + notifySubGenerator(1); + } else { + // Generate minimum-bias event + bool genOk = false; + while (!genOk) { + genOk = GeneratorPythia8::generateEvent(); + } + notifySubGenerator(0); + } + + mGeneratedEvents++; + + return true; + } + + bool selectEvent(Pythia8::Event& event) + { + for (auto iPart{0}; iPart < event.size(); ++iPart) { + // search for hadron in rapidity window + int id = std::abs(event[iPart].id()); + float rap = event[iPart].y(); + if (id == mHadronPdg && rap > mHadRapidityMin && rap < mHadRapidityMax) { + + int d1 = event[iPart].daughter1(); + int d2 = event[iPart].daughter2(); + + // Protection for invalid index of daughter 1 + if (d1 < 0 || d1 >= event.size()) { + LOG(info) << "Invalid daughter index 1! d1 = " << d1; + continue; + } + + // Protection for invalid index of daughter 2 + if (d2 < 0 || d2 >= event.size()) { + LOG(info) << "Invalid daughter index 2! d2 = " << d2; + continue; + } + + // Swap order + if (d1 > d2) { + std::swap(d1, d2); + } + + // No daughters + if (d1 == 0 && d2 == 0) { + LOG(info) << "No daughters (0,0). Skipping."; + continue; + } + + LOG(debug) << "-----------------------------------------------------"; + LOG(debug) << "Found hadron " << event[iPart].id() << " with rapidity " << rap << " and daughters " << d1 << " " << d2; + // print pdg code of daughters + LOG(debug) << "Daughters: "; + for (int iDau = d1; iDau <= d2; ++iDau) { + LOG(debug) << "Daughter " << iDau << ": " << event[iDau].id(); + } + bool isCoalDone = CoalescencePythia8(event, mNucleiPdgList, mTrivialCoal, mCoalMomentum, d1, d2); + if (isCoalDone) { + LOG(debug) << "Coalescence process found for hadron " << event[iPart].id() << " with daughters " << d1 << " " << d2; + LOG(debug) << "Check updated daughters: "; + for (int iDau = d1; iDau <= d2; ++iDau) { + LOG(debug) << "Daughter " << iDau << ": " << event[iDau].id(); + } + return true; + } + } + } + return false; + }; + + private: + // Interface to override import particles + Pythia8::Event mOutputEvent; + + // Properties of selection + int mHadronPdg; + float mHadRapidityMin; + float mHadRapidityMax; + unsigned int mUsedSeed; + + // Bias towards bbbar at midrapidity, active only while generating HF-triggered events + UserHooks_qqbar* mBbbarBiasHook{nullptr}; + + // Control gap-triggering + unsigned long long mGeneratedEvents; + int mInverseTriggerRatio; + + // Control alternate trigger on different hadrons + std::vector mHFHadronPdgList = {}; + std::vector mNucleiPdgList = {}; + + bool mTrivialCoal = false; /// if true, the coalescence is done without checking the distance in the phase space of the nucleons + float mCoalMomentum; /// coalescence momentum +}; + + +///___________________________________________________________ +FairGenerator *generateHFHadToNuclei(int input_trigger_ratio = 5, std::vector hf_hadron_pdg_list = {}, std::vector nuclei_pdg_list = {}, bool trivial_coal = false, float coal_momentum = 0.4) +{ + auto myGen = new GeneratorPythia8HFHadToNuclei(input_trigger_ratio, hf_hadron_pdg_list, nuclei_pdg_list, trivial_coal, coal_momentum); + auto seed = (gRandom->TRandom::GetSeed() % 900000000); + myGen->readString("Random:setSeed on"); + myGen->readString("Random:seed " + std::to_string(seed)); + return myGen; +} diff --git a/MC/config/PWGHF/external/generator/selectNonHfe.C b/MC/config/PWGHF/external/generator/selectNonHfe.C new file mode 100644 index 000000000..7dd325335 --- /dev/null +++ b/MC/config/PWGHF/external/generator/selectNonHfe.C @@ -0,0 +1,123 @@ + +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include "FairGenerator.h" +#include "FairPrimaryGenerator.h" +#include "Generators/GeneratorPythia8.h" +#include "TRandom3.h" +#include "TParticlePDG.h" +#include "TDatabasePDG.h" +#include "TMath.h" +#include +#include +#include +#endif + +#include "Pythia8/Pythia.h" + +class GeneratorPythia8Box : public o2::eventgen::GeneratorPythia8 +{ +public: + + GeneratorPythia8Box(std::vector pdgList, int nInject = 3, + float ptMin = 0.1, float ptMax = 50.0, + float etaMin = -0.8, float etaMax = 0.8) + : mPdgList(pdgList), nParticles(nInject), + genMinPt(ptMin), genMaxPt(ptMax), + genMinEta(etaMin), genMaxEta(etaMax) + { + // Initialize dedicated Pythia instance for injection/decay only + // Reuses same config as main Pythia; beam settings are ignored by moreDecays() + auto& param = o2::eventgen::GeneratorPythia8Param::Instance(); + std::string cfg = gSystem->ExpandPathName(param.config.c_str()); + if (!mPythiaGun.readFile(cfg, true)) { + LOG(fatal) << "Failed to read config for mPythiaGun: " << cfg; + } + if (!mPythiaGun.init()) { + LOG(fatal) << "Failed to init mPythiaGun"; + } + } + + ~GeneratorPythia8Box() = default; + + bool Init() override + { + return o2::eventgen::GeneratorPythia8::Init(); + } + +protected: + + // ── generateEvent: MB + injection ── + Bool_t generateEvent() override + { + bool hasElectron = false; + + while (!hasElectron) { + // ===== 1. Generate minimum-bias event ===== + if (!mPythia.next()) continue; + + // ===== 2. Inject π⁰/η into separate gun, decay only them ===== + mPythiaGun.event.reset(); + for (int i = 0; i < nParticles; ++i) { + int pdg = mPdgList[gRandom->Integer(mPdgList.size())]; + auto* pPDG = TDatabasePDG::Instance()->GetParticle(pdg); + if (!pPDG) continue; + double mass = pPDG->Mass(); + + double pt = gRandom->Uniform(genMinPt, genMaxPt); + double eta = gRandom->Uniform(genMinEta, genMaxEta); + double phi = gRandom->Uniform(0., TMath::TwoPi()); + double px = pt * cos(phi); + double py = pt * sin(phi); + double pz = pt * sinh(eta); + double e = sqrt(px * px + py * py + pz * pz + mass * mass); + + mPythiaGun.event.append(pdg, 11, 0, 0, px, py, pz, e, mass); + } + + mPythiaGun.moreDecays(); + + // ===== 3. Check for electron from injection in acceptance ===== + for (int i = 0; i < mPythiaGun.event.size(); ++i) { + auto& part = mPythiaGun.event[i]; + if (std::abs(part.id()) == 11 && part.isFinal()) { + if (part.pT() > 0.1 && std::abs(part.eta()) < 1.2) { + hasElectron = true; + break; + } + } + } + } + + // ===== 4. Copy injection products to MB event with index offset ===== + int offset = mPythia.event.size(); + + for (int i = 0; i < mPythiaGun.event.size(); ++i) { + auto part = mPythiaGun.event[i]; + if (part.id() == 90) continue; // skip system placeholder + if (!part.isFinal() && part.id() != 111 && part.id() != 221) continue; + + if (part.mother1() > 0) part.mother1(part.mother1() + offset - 1); + if (part.mother2() > 0) part.mother2(part.mother2() + offset - 1); + if (part.daughter1() > 0) part.daughter1(part.daughter1() + offset - 1); + if (part.daughter2() > 0) part.daughter2(part.daughter2() + offset - 1); + + mPythia.event.append(part); + } + + return true; + } + +private: + std::vector mPdgList; + double genMinPt, genMaxPt; + double genMinEta, genMaxEta; + int nParticles; + + Pythia8::Pythia mPythiaGun; +}; + +FairGenerator *generatePythia8Box(float ptMin = 0.1, float ptMax = 50.0) +{ + std::vector pdgList = {111, 221}; + return new GeneratorPythia8Box(pdgList, 3, ptMin, ptMax, -0.8, 0.8); +} \ No newline at end of file diff --git a/MC/config/PWGHF/hybrid/GeneratorHF_Non_Hfe.json b/MC/config/PWGHF/hybrid/GeneratorHF_Non_Hfe.json new file mode 100644 index 000000000..e69de29bb diff --git a/MC/config/PWGHF/ini/GeneratorHF.ini b/MC/config/PWGHF/ini/GeneratorHF.ini deleted file mode 100644 index 3c0ea694f..000000000 --- a/MC/config/PWGHF/ini/GeneratorHF.ini +++ /dev/null @@ -1,34 +0,0 @@ -### The setup uses an external event generator -### This part sets the path of the file and the function call to retrieve it - -[GeneratorExternal] -fileName = ${O2DPG_ROOT}/MC/config/PWGHF/external/generator/GeneratorHF.C -funcName = GeneratorHF() - -### The external generator derives from GeneratorPythia8. -### This part configures the bits of the interface: configuration and user hooks - -[GeneratorPythia8] -config = ${O2DPG_ROOT}/MC/config/common/pythia8/generator/pythia8_hf.cfg -hooksFileName = ${O2DPG_ROOT}/MC/config/PWGHF/pythia8/hooks/pythia8_userhooks_qqbar.C -hooksFuncName = pythia8_userhooks_ccbar(-1.5,1.5) - -### The setup uses the base configuration of the decayer which is loaded from the file specified by config[0]. -### On top of the base configuration, two more sets of settings are loaded sequentially from config[1] and [2]. - -[DecayerPythia8] -config[0] = ${O2DPG_ROOT}/MC/config/common/pythia8/decayer/base.cfg -config[1] = ${O2DPG_ROOT}/MC/config/PWGHF/pythia8/decayer/force_hadronic_D.cfg -config[2] = ${O2DPG_ROOT}/MC/config/PWGHF/pythia8/decayer/force_hadronic_D_use4bodies.cfg - -### The setup forces some particles to be decayed by the external decayer instead of Geant. -### The PDG list of the particles is specified below. - -[SimUserDecay] -pdglist = 411 421 431 4112 4122 4232 4132 - -### The setup inhibits transport of primary particles which are produce at forward rapidity. -### The settings below only transports particles in the barrel, which is currently defined as |eta| < 2 - -[Stack] -transportPrimary = barrel diff --git a/MC/config/PWGHF/ini/GeneratorHFOmegaCEmb.ini b/MC/config/PWGHF/ini/GeneratorHFOmegaCEmb.ini index 65351c553..1401a63ec 100644 --- a/MC/config/PWGHF/ini/GeneratorHFOmegaCEmb.ini +++ b/MC/config/PWGHF/ini/GeneratorHFOmegaCEmb.ini @@ -1,6 +1,7 @@ +#NEV_TEST> 10 [GeneratorExternal] -fileName=${O2DPG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_box.C +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_box.C funcName=generatePythia8Box(4332, 3) [GeneratorPythia8] -config=${O2DPG_ROOT}/MC/config/PWGHF/pythia8/generator/omega_c.cfg +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/generator/omega_c.cfg diff --git a/MC/config/PWGHF/ini/GeneratorHFOmegaCToXiPiEmb.ini b/MC/config/PWGHF/ini/GeneratorHFOmegaCToXiPiEmb.ini index e91bd5bcd..a781b81be 100644 --- a/MC/config/PWGHF/ini/GeneratorHFOmegaCToXiPiEmb.ini +++ b/MC/config/PWGHF/ini/GeneratorHFOmegaCToXiPiEmb.ini @@ -1,6 +1,7 @@ +#NEV_TEST> 10 [GeneratorExternal] -fileName=${O2DPG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_box.C +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_box.C funcName=generatePythia8Box(4332, 3) [GeneratorPythia8] -config=${O2DPG_ROOT}/MC/config/PWGHF/pythia8/generator/omega_c_toxipi.cfg +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/generator/omega_c_toxipi.cfg diff --git a/MC/config/PWGHF/ini/GeneratorHFTrigger_BToDeuteron.ini b/MC/config/PWGHF/ini/GeneratorHFTrigger_BToDeuteron.ini new file mode 100644 index 000000000..95bf68b19 --- /dev/null +++ b/MC/config/PWGHF/ini/GeneratorHFTrigger_BToDeuteron.ini @@ -0,0 +1,8 @@ +#NEV_TEST> 10 +#O2DPG_TEST> ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/ini/tests/GeneratorHFTrigger_BToDeuteron.C +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_hfhadron_to_nuclei.C +funcName=generateHFHadToNuclei(3, {521}, {1000010020}, true, 0.5) +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_bminus_CRmode2.cfg diff --git a/MC/config/PWGHF/ini/GeneratorHFTrigger_Bforced.ini b/MC/config/PWGHF/ini/GeneratorHFTrigger_Bforced.ini index dde63f456..282b34ea2 100755 --- a/MC/config/PWGHF/ini/GeneratorHFTrigger_Bforced.ini +++ b/MC/config/PWGHF/ini/GeneratorHFTrigger_Bforced.ini @@ -1,7 +1,7 @@ -### The external generator derives from GeneratorPythia8. +#NEV_TEST> 50 [GeneratorExternal] -fileName=${O2DPG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C funcName=GeneratorPythia8GapTriggeredBeauty(3, -5, 5) [GeneratorPythia8] -config=${O2DPG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_charmtriggers_with_Bforced_decay.cfg +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_charmtriggers_with_Bforced_decay.cfg diff --git a/MC/config/PWGHF/ini/GeneratorHFTrigger_LambdaBToDeuteron.ini b/MC/config/PWGHF/ini/GeneratorHFTrigger_LambdaBToDeuteron.ini new file mode 100644 index 000000000..fab061515 --- /dev/null +++ b/MC/config/PWGHF/ini/GeneratorHFTrigger_LambdaBToDeuteron.ini @@ -0,0 +1,7 @@ +#NEV_TEST> 10 +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_hfhadron_to_nuclei.C +funcName=generateHFHadToNuclei(3, {5122}, {1000010020}, true, 0.5) +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_lambdab_probQQtoQ.cfg diff --git a/MC/config/PWGHF/ini/GeneratorHFTrigger_LambdaBToNuclei.ini b/MC/config/PWGHF/ini/GeneratorHFTrigger_LambdaBToNuclei.ini new file mode 100644 index 000000000..5ef69c415 --- /dev/null +++ b/MC/config/PWGHF/ini/GeneratorHFTrigger_LambdaBToNuclei.ini @@ -0,0 +1,7 @@ +#NEV_TEST> 10 +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_hfhadron_to_nuclei.C +funcName=generateHFHadToNuclei(3, {5122}, {1000020030, 1000010030}, true, 0.5) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_lambdab_with_decays_Mode2.cfg diff --git a/MC/config/PWGHF/ini/GeneratorHFTrigger_OmegaCToXiPi.ini b/MC/config/PWGHF/ini/GeneratorHFTrigger_OmegaCToXiPi.ini index d38c3b4f3..80c27b5ba 100644 --- a/MC/config/PWGHF/ini/GeneratorHFTrigger_OmegaCToXiPi.ini +++ b/MC/config/PWGHF/ini/GeneratorHFTrigger_OmegaCToXiPi.ini @@ -1,7 +1,7 @@ -### The external generator derives from GeneratorPythia8. +#NEV_TEST> 10 [GeneratorExternal] -fileName=${O2DPG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C -funcName=GeneratorPythia8GapTriggeredCharm(5,-1.5,1.5,-1.5,1.5,4332,true) +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C +funcName=GeneratorPythia8GapTriggeredCharm(5,-1.5,1.5,-1.5,1.5,{4332}) [GeneratorPythia8] -config=${O2DPG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_charmtriggers_omegactoxipi.cfg +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_charmtriggers_omegactoxipi.cfg diff --git a/MC/config/PWGHF/ini/GeneratorHFTrigger_XiCToXiPi.ini b/MC/config/PWGHF/ini/GeneratorHFTrigger_XiCToXiPi.ini index 11c1ab205..5beb85b15 100644 --- a/MC/config/PWGHF/ini/GeneratorHFTrigger_XiCToXiPi.ini +++ b/MC/config/PWGHF/ini/GeneratorHFTrigger_XiCToXiPi.ini @@ -1,7 +1,7 @@ -### The external generator derives from GeneratorPythia8. +#NEV_TEST> 10 [GeneratorExternal] -fileName=${O2DPG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C -funcName=GeneratorPythia8GapTriggeredCharm(5,-1.5,1.5,-1.5,1.5,4132,true) +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C +funcName=GeneratorPythia8GapTriggeredCharm(5,-1.5,1.5,-1.5,1.5,{4132}) [GeneratorPythia8] -config=${O2DPG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_charmtriggers_xictoxipi.cfg +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_charmtriggers_xictoxipi.cfg diff --git a/MC/config/PWGHF/ini/GeneratorHFTrigger_Xi_Omega_C.ini b/MC/config/PWGHF/ini/GeneratorHFTrigger_Xi_Omega_C.ini new file mode 100644 index 000000000..98035dc7a --- /dev/null +++ b/MC/config/PWGHF/ini/GeneratorHFTrigger_Xi_Omega_C.ini @@ -0,0 +1,7 @@ +#NEV_TEST> 10 +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C +funcName=GeneratorPythia8GapHF(5,-1.5,1.5,-1.5,1.5,{4,5}, {4132, 4332}) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_charmtriggers_xicomegac.cfg \ No newline at end of file diff --git a/MC/config/PWGHF/ini/GeneratorHFTrigger_bbbar.ini b/MC/config/PWGHF/ini/GeneratorHFTrigger_bbbar.ini index 2f5041fcf..c79c39e3c 100755 --- a/MC/config/PWGHF/ini/GeneratorHFTrigger_bbbar.ini +++ b/MC/config/PWGHF/ini/GeneratorHFTrigger_bbbar.ini @@ -1,7 +1,7 @@ -### The external generator derives from GeneratorPythia8. +#NEV_TEST> 20 [GeneratorExternal] -fileName=${O2DPG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C funcName=GeneratorPythia8GapTriggeredBeauty(3, -5, 5) [GeneratorPythia8] -config=${O2DPG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_charmtriggers_with_decays.cfg +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_charmtriggers_with_decays.cfg diff --git a/MC/config/PWGHF/ini/GeneratorHFTrigger_ccbar.ini b/MC/config/PWGHF/ini/GeneratorHFTrigger_ccbar.ini index a5d6be786..4dc2594a0 100755 --- a/MC/config/PWGHF/ini/GeneratorHFTrigger_ccbar.ini +++ b/MC/config/PWGHF/ini/GeneratorHFTrigger_ccbar.ini @@ -1,7 +1,7 @@ -### The external generator derives from GeneratorPythia8. +#NEV_TEST> 20 [GeneratorExternal] -fileName=${O2DPG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C funcName=GeneratorPythia8GapTriggeredCharm(3, -5, 5) [GeneratorPythia8] -config=${O2DPG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_charmtriggers_with_decays.cfg +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_charmtriggers_with_decays.cfg diff --git a/MC/config/PWGHF/ini/GeneratorHFXiCToXiPiEmb.ini b/MC/config/PWGHF/ini/GeneratorHFXiCToXiPiEmb.ini index fb43800a4..4a234ed08 100644 --- a/MC/config/PWGHF/ini/GeneratorHFXiCToXiPiEmb.ini +++ b/MC/config/PWGHF/ini/GeneratorHFXiCToXiPiEmb.ini @@ -1,6 +1,7 @@ +#NEV_TEST> 10 [GeneratorExternal] -fileName=${O2DPG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_box.C +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_box.C funcName=generatePythia8Box(4132, 3) [GeneratorPythia8] -config=${O2DPG_ROOT}/MC/config/PWGHF/pythia8/generator/xi_c_toxipi.cfg +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/generator/xi_c_toxipi.cfg diff --git a/MC/config/PWGHF/ini/GeneratorHF_CDeuteron_Injected.ini b/MC/config/PWGHF/ini/GeneratorHF_CDeuteron_Injected.ini new file mode 100644 index 000000000..2a574f340 --- /dev/null +++ b/MC/config/PWGHF/ini/GeneratorHF_CDeuteron_Injected.ini @@ -0,0 +1,10 @@ +#NEV_TEST> 100 +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_embed_charmnuclei.C +funcName=GenerateHFEmbedCDeuteron(0.5, 10, -1., 1., 25., 0, 0.4, 0.25) + +### includePartonEvent is needed to keep the c-deuteron in the event record, even if there are no partons in the event +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_cdeuteron_to_dkpi.cfg +includePartonEvent=true diff --git a/MC/config/PWGHF/ini/GeneratorHF_D2H_bbbar_B2JPsi_gap5_Mode2.ini b/MC/config/PWGHF/ini/GeneratorHF_D2H_bbbar_B2JPsi_gap5_Mode2.ini new file mode 100755 index 000000000..e2e4a5acd --- /dev/null +++ b/MC/config/PWGHF/ini/GeneratorHF_D2H_bbbar_B2JPsi_gap5_Mode2.ini @@ -0,0 +1,9 @@ +#NEV_TEST> 20 +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C +funcName=GeneratorPythia8GapTriggeredBeautyWithEvtGen(5, -1.5, 1.5, -1.5, 1.5, {}, {}, {}, "${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/decayer/EVTGEN_DECAY_B2JPSI.DEC") + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_Mode2_noforcedecays.cfg +includePartonEvent=false diff --git a/MC/config/PWGHF/ini/GeneratorHF_D2H_bbbar_Bforced_gap5_Mode2.ini b/MC/config/PWGHF/ini/GeneratorHF_D2H_bbbar_Bforced_gap5_Mode2.ini new file mode 100755 index 000000000..52ddaf828 --- /dev/null +++ b/MC/config/PWGHF/ini/GeneratorHF_D2H_bbbar_Bforced_gap5_Mode2.ini @@ -0,0 +1,8 @@ +#NEV_TEST> 20 +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C +funcName=GeneratorPythia8GapTriggeredBeauty(5, -1.5, 1.5) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_beautyhadronic_with_decays_Mode2.cfg +includePartonEvent=true diff --git a/MC/config/PWGHF/ini/GeneratorHF_D2H_bbbar_BtoDK_gap5_Mode2.ini b/MC/config/PWGHF/ini/GeneratorHF_D2H_bbbar_BtoDK_gap5_Mode2.ini new file mode 100755 index 000000000..25d30964c --- /dev/null +++ b/MC/config/PWGHF/ini/GeneratorHF_D2H_bbbar_BtoDK_gap5_Mode2.ini @@ -0,0 +1,8 @@ +#NEV_TEST> 20 +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C +funcName=GeneratorPythia8GapTriggeredBeauty(5, -1.5, 1.5) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_beautyhadronic_with_BtoDK_Mode2.cfg +includePartonEvent=true diff --git a/MC/config/PWGHF/ini/GeneratorHF_D2H_bbbar_Mode2_OmegaC.ini b/MC/config/PWGHF/ini/GeneratorHF_D2H_bbbar_Mode2_OmegaC.ini new file mode 100755 index 000000000..fc956774f --- /dev/null +++ b/MC/config/PWGHF/ini/GeneratorHF_D2H_bbbar_Mode2_OmegaC.ini @@ -0,0 +1,11 @@ +#NEV_TEST> 5 +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C +funcName=GeneratorPythia8GapTriggeredBeauty(1, -1.5, 1.5, -1.5, 1.5, {4332}) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_with_decays_Mode2.cfg +hooksFileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/hooks/pythia8_userhooks_qqbar.C +hooksFuncName=pythia8_userhooks_bbbar(-1.5,1.5) +includePartonEvent=true diff --git a/MC/config/PWGHF/ini/GeneratorHF_D2H_bbbar_Mode2_OmegaC_NoDecay.ini b/MC/config/PWGHF/ini/GeneratorHF_D2H_bbbar_Mode2_OmegaC_NoDecay.ini new file mode 100644 index 000000000..508b76dec --- /dev/null +++ b/MC/config/PWGHF/ini/GeneratorHF_D2H_bbbar_Mode2_OmegaC_NoDecay.ini @@ -0,0 +1,11 @@ +#NEV_TEST> 5 +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C +funcName=GeneratorPythia8GapTriggeredBeauty(1, -1.5, 1.5, -1.5, 1.5, {4332}) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_OmegaC_NoDecay.cfg +hooksFileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/hooks/pythia8_userhooks_qqbar.C +hooksFuncName=pythia8_userhooks_bbbar(-1.5,1.5) +includePartonEvent=true diff --git a/MC/config/PWGHF/ini/GeneratorHF_D2H_bbbar_Mode2_OmegaC_NoDecay_pp_ref.ini b/MC/config/PWGHF/ini/GeneratorHF_D2H_bbbar_Mode2_OmegaC_NoDecay_pp_ref.ini new file mode 100644 index 000000000..5a9b792e5 --- /dev/null +++ b/MC/config/PWGHF/ini/GeneratorHF_D2H_bbbar_Mode2_OmegaC_NoDecay_pp_ref.ini @@ -0,0 +1,11 @@ +#NEV_TEST> 5 +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C +funcName=GeneratorPythia8GapTriggeredBeauty(1, -1.5, 1.5, -1.5, 1.5, {4332}) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_OmegaC_NoDecay_Mode2_pp_ref.cfg +hooksFileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/hooks/pythia8_userhooks_qqbar.C +hooksFuncName=pythia8_userhooks_bbbar(-1.5,1.5) +includePartonEvent=true \ No newline at end of file diff --git a/MC/config/PWGHF/ini/GeneratorHF_D2H_bbbar_Mode2_XiC0_XiCplus.ini b/MC/config/PWGHF/ini/GeneratorHF_D2H_bbbar_Mode2_XiC0_XiCplus.ini new file mode 100755 index 000000000..e16fbfb70 --- /dev/null +++ b/MC/config/PWGHF/ini/GeneratorHF_D2H_bbbar_Mode2_XiC0_XiCplus.ini @@ -0,0 +1,11 @@ +#NEV_TEST> 10 +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C +funcName=GeneratorPythia8GapTriggeredBeauty(1, -1.5, 1.5, -1.5, 1.5, {4132, 4232}) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_with_decays_Mode2.cfg +hooksFileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/hooks/pythia8_userhooks_qqbar.C +hooksFuncName=pythia8_userhooks_bbbar(-1.5,1.5) +includePartonEvent=true diff --git a/MC/config/PWGHF/ini/GeneratorHF_D2H_bbbar_Mode2_XiC_NoDecay.ini b/MC/config/PWGHF/ini/GeneratorHF_D2H_bbbar_Mode2_XiC_NoDecay.ini new file mode 100644 index 000000000..d1fd4223f --- /dev/null +++ b/MC/config/PWGHF/ini/GeneratorHF_D2H_bbbar_Mode2_XiC_NoDecay.ini @@ -0,0 +1,11 @@ +#NEV_TEST> 10 +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C +funcName=GeneratorPythia8GapTriggeredBeauty(1, -1.5, 1.5, -1.5, 1.5, {4132}) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_XiC_NoDecay.cfg +hooksFileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/hooks/pythia8_userhooks_qqbar.C +hooksFuncName=pythia8_userhooks_bbbar(-1.5,1.5) +includePartonEvent=true diff --git a/MC/config/PWGHF/ini/GeneratorHF_D2H_bbbar_Mode2_XiC_NoDecay_pp_ref.ini b/MC/config/PWGHF/ini/GeneratorHF_D2H_bbbar_Mode2_XiC_NoDecay_pp_ref.ini new file mode 100644 index 000000000..cee03f3bc --- /dev/null +++ b/MC/config/PWGHF/ini/GeneratorHF_D2H_bbbar_Mode2_XiC_NoDecay_pp_ref.ini @@ -0,0 +1,11 @@ +#NEV_TEST> 10 +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C +funcName=GeneratorPythia8GapTriggeredBeauty(1, -1.5, 1.5, -1.5, 1.5, {4132}) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_XiC_NoDecay_Mode2_pp_ref.cfg +hooksFileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/hooks/pythia8_userhooks_qqbar.C +hooksFuncName=pythia8_userhooks_bbbar(-1.5,1.5) +includePartonEvent=true \ No newline at end of file diff --git a/MC/config/PWGHF/ini/GeneratorHF_D2H_bbbar_Mode2_XiCplus_NoDecay.ini b/MC/config/PWGHF/ini/GeneratorHF_D2H_bbbar_Mode2_XiCplus_NoDecay.ini new file mode 100644 index 000000000..8bc123304 --- /dev/null +++ b/MC/config/PWGHF/ini/GeneratorHF_D2H_bbbar_Mode2_XiCplus_NoDecay.ini @@ -0,0 +1,11 @@ +#NEV_TEST> 10 +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C +funcName=GeneratorPythia8GapTriggeredBeauty(1, -1.5, 1.5, -1.5, 1.5, {4232}) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_XiCplus_NoDecay.cfg +hooksFileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/hooks/pythia8_userhooks_qqbar.C +hooksFuncName=pythia8_userhooks_bbbar(-1.5,1.5) +includePartonEvent=true diff --git a/MC/config/PWGHF/ini/GeneratorHF_D2H_bbbar_Mode2_XiCplus_NoDecay_pp_ref.ini b/MC/config/PWGHF/ini/GeneratorHF_D2H_bbbar_Mode2_XiCplus_NoDecay_pp_ref.ini new file mode 100644 index 000000000..8b5935473 --- /dev/null +++ b/MC/config/PWGHF/ini/GeneratorHF_D2H_bbbar_Mode2_XiCplus_NoDecay_pp_ref.ini @@ -0,0 +1,11 @@ +#NEV_TEST> 10 +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C +funcName=GeneratorPythia8GapTriggeredBeauty(1, -1.5, 1.5, -1.5, 1.5, {4232}) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_XiCplus_NoDecay_Mode2_pp_ref.cfg +hooksFileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/hooks/pythia8_userhooks_qqbar.C +hooksFuncName=pythia8_userhooks_bbbar(-1.5,1.5) +includePartonEvent=true \ No newline at end of file diff --git a/MC/config/PWGHF/ini/GeneratorHF_D2H_bbbar_gap5.ini b/MC/config/PWGHF/ini/GeneratorHF_D2H_bbbar_gap5.ini new file mode 100755 index 000000000..4b65f23e8 --- /dev/null +++ b/MC/config/PWGHF/ini/GeneratorHF_D2H_bbbar_gap5.ini @@ -0,0 +1,9 @@ +#NEV_TEST> 20 +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C +funcName=GeneratorPythia8GapTriggeredBeauty(5, -1.5, 1.5) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_with_decays.cfg +includePartonEvent=true diff --git a/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar3_and_bbbar_PbPb.ini b/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar3_and_bbbar_PbPb.ini new file mode 100644 index 000000000..37b778d4a --- /dev/null +++ b/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar3_and_bbbar_PbPb.ini @@ -0,0 +1,11 @@ +#NEV_TEST> 10 +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_embed_hf.C +funcName=GeneratorPythia8EmbedHFRatio(0.2) +### bOverCRatio = 0.2 generates quarkDpgList = [5,4,4,4] +### because the loop stops at the first valid iteration (c=3, b=1) due to the early break, rather than finding the true closest ratio + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_with_decays_Mode2_hardQCD_5TeV.cfg +includePartonEvent=true diff --git a/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_Mode2_OmegaC.ini b/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_Mode2_OmegaC.ini new file mode 100755 index 000000000..95c99d275 --- /dev/null +++ b/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_Mode2_OmegaC.ini @@ -0,0 +1,11 @@ +#NEV_TEST> 10 +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C +funcName=GeneratorPythia8GapTriggeredCharm(1, -1.5, 1.5, -1.5, 1.5, {4332}) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_with_decays_Mode2.cfg +hooksFileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/hooks/pythia8_userhooks_qqbar.C +hooksFuncName=pythia8_userhooks_ccbar(-1.5,1.5) +includePartonEvent=true diff --git a/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_Mode2_OmegaC_NoDecay.ini b/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_Mode2_OmegaC_NoDecay.ini new file mode 100644 index 000000000..fedb78299 --- /dev/null +++ b/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_Mode2_OmegaC_NoDecay.ini @@ -0,0 +1,11 @@ +#NEV_TEST> 10 +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C +funcName=GeneratorPythia8GapTriggeredCharm(1, -1.5, 1.5, -1.5, 1.5, {4332}) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_OmegaC_NoDecay.cfg +hooksFileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/hooks/pythia8_userhooks_qqbar.C +hooksFuncName=pythia8_userhooks_ccbar(-1.5,1.5) +includePartonEvent=true diff --git a/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_Mode2_OmegaC_NoDecay_pp_ref.ini b/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_Mode2_OmegaC_NoDecay_pp_ref.ini new file mode 100644 index 000000000..68348cbbd --- /dev/null +++ b/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_Mode2_OmegaC_NoDecay_pp_ref.ini @@ -0,0 +1,11 @@ +#NEV_TEST> 10 +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C +funcName=GeneratorPythia8GapTriggeredCharm(1, -1.5, 1.5, -1.5, 1.5, {4332}) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_OmegaC_NoDecay_Mode2_pp_ref.cfg +hooksFileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/hooks/pythia8_userhooks_qqbar.C +hooksFuncName=pythia8_userhooks_ccbar(-1.5,1.5) +includePartonEvent=true \ No newline at end of file diff --git a/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_Mode2_XiC0_XiCplus.ini b/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_Mode2_XiC0_XiCplus.ini new file mode 100755 index 000000000..5e301a4cf --- /dev/null +++ b/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_Mode2_XiC0_XiCplus.ini @@ -0,0 +1,11 @@ +#NEV_TEST> 10 +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C +funcName=GeneratorPythia8GapTriggeredCharm(1, -1.5, 1.5, -1.5, 1.5, {4132, 4232}) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_with_decays_Mode2.cfg +hooksFileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/hooks/pythia8_userhooks_qqbar.C +hooksFuncName=pythia8_userhooks_ccbar(-1.5,1.5) +includePartonEvent=true diff --git a/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_Mode2_XiC_NoDecay.ini b/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_Mode2_XiC_NoDecay.ini new file mode 100644 index 000000000..2b23edbf2 --- /dev/null +++ b/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_Mode2_XiC_NoDecay.ini @@ -0,0 +1,11 @@ +#NEV_TEST> 10 +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C +funcName=GeneratorPythia8GapTriggeredCharm(1, -1.5, 1.5, -1.5, 1.5, {4132}) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_XiC_NoDecay.cfg +hooksFileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/hooks/pythia8_userhooks_qqbar.C +hooksFuncName=pythia8_userhooks_ccbar(-1.5,1.5) +includePartonEvent=true diff --git a/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_Mode2_XiC_NoDecay_pp_ref.ini b/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_Mode2_XiC_NoDecay_pp_ref.ini new file mode 100644 index 000000000..650eb8a5f --- /dev/null +++ b/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_Mode2_XiC_NoDecay_pp_ref.ini @@ -0,0 +1,11 @@ +#NEV_TEST> 10 +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C +funcName=GeneratorPythia8GapTriggeredCharm(1, -1.5, 1.5, -1.5, 1.5, {4132}) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_XiC_NoDecay_Mode2_pp_ref.cfg +hooksFileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/hooks/pythia8_userhooks_qqbar.C +hooksFuncName=pythia8_userhooks_ccbar(-1.5,1.5) +includePartonEvent=true \ No newline at end of file diff --git a/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_Mode2_XiCplus_NoDecay.ini b/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_Mode2_XiCplus_NoDecay.ini new file mode 100644 index 000000000..c308bfeda --- /dev/null +++ b/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_Mode2_XiCplus_NoDecay.ini @@ -0,0 +1,11 @@ +#NEV_TEST> 10 +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C +funcName=GeneratorPythia8GapTriggeredCharm(1, -1.5, 1.5, -1.5, 1.5, {4232}) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_XiCplus_NoDecay.cfg +hooksFileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/hooks/pythia8_userhooks_qqbar.C +hooksFuncName=pythia8_userhooks_ccbar(-1.5,1.5) +includePartonEvent=true diff --git a/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_Mode2_XiCplus_NoDecay_pp_ref.ini b/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_Mode2_XiCplus_NoDecay_pp_ref.ini new file mode 100644 index 000000000..849c5f4fa --- /dev/null +++ b/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_Mode2_XiCplus_NoDecay_pp_ref.ini @@ -0,0 +1,11 @@ +#NEV_TEST> 10 +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C +funcName=GeneratorPythia8GapTriggeredCharm(1, -1.5, 1.5, -1.5, 1.5, {4232}) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_XiCplus_NoDecay_Mode2_pp_ref.cfg +hooksFileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/hooks/pythia8_userhooks_qqbar.C +hooksFuncName=pythia8_userhooks_ccbar(-1.5,1.5) +includePartonEvent=true \ No newline at end of file diff --git a/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_and_bbbar_Mode2_ptHardBins.ini b/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_and_bbbar_Mode2_ptHardBins.ini new file mode 100644 index 000000000..ed7202eb2 --- /dev/null +++ b/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_and_bbbar_Mode2_ptHardBins.ini @@ -0,0 +1,9 @@ +#NEV_TEST> 20 +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C +funcName=GeneratorPythia8GapTriggeredCharmAndBeauty(1, -1.5, 1.5) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_with_decays_Mode2_ptHardBins.cfg +includePartonEvent=true diff --git a/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_and_bbbar_PbPb.ini b/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_and_bbbar_PbPb.ini new file mode 100644 index 000000000..03b15ce2c --- /dev/null +++ b/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_and_bbbar_PbPb.ini @@ -0,0 +1,9 @@ +#NEV_TEST> 10 +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_embed_hf.C +funcName=GeneratorPythia8EmbedHFCharmAndBeauty() + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_with_decays_Mode2_hardQCD_5TeV.cfg +includePartonEvent=true diff --git a/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_and_bbbar_PbPb_corrBkg.ini b/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_and_bbbar_PbPb_corrBkg.ini new file mode 100644 index 000000000..57715e41c --- /dev/null +++ b/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_and_bbbar_PbPb_corrBkg.ini @@ -0,0 +1,9 @@ +#NEV_TEST> 10 +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_embed_hf.C +funcName=GeneratorPythia8EmbedHFCharmAndBeauty() + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_with_decays_Mode2_hardQCD_5TeV_CorrBkg.cfg +includePartonEvent=true diff --git a/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_and_bbbar_PbPb_ptHardBins.ini b/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_and_bbbar_PbPb_ptHardBins.ini new file mode 100644 index 000000000..2273c8205 --- /dev/null +++ b/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_and_bbbar_PbPb_ptHardBins.ini @@ -0,0 +1,9 @@ +#NEV_TEST> 10 +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_embed_hf.C +funcName=GeneratorPythia8EmbedHFCharmAndBeauty(true) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_with_decays_Mode2_hardQCD_5TeV.cfg +includePartonEvent=true diff --git a/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_and_bbbar_PbPb_replaced.ini b/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_and_bbbar_PbPb_replaced.ini new file mode 100644 index 000000000..e0cd149eb --- /dev/null +++ b/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_and_bbbar_PbPb_replaced.ini @@ -0,0 +1,9 @@ +#NEV_TEST> 10 +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_embed_hf.C +funcName=GeneratorPythia8EmbedHFCharmAndBeauty(false, -1.5, 1.5, -1.5, 1.5, std::vector{}, std::vector{}, std::vector>{{423,4132},{423,4232},{4212,4332}}, std::vector{0.5,0.5,1}) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_decays_Mode2_hardQCD_5TeV_XicOmegaC.cfg +includePartonEvent=true \ No newline at end of file diff --git a/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_and_bbbar_gap2_QCDCR_NeNe.ini b/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_and_bbbar_gap2_QCDCR_NeNe.ini new file mode 100755 index 000000000..1e313ae7a --- /dev/null +++ b/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_and_bbbar_gap2_QCDCR_NeNe.ini @@ -0,0 +1,9 @@ +#NEV_TEST> 16 +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C +funcName=GeneratorPythia8GapTriggeredCharmAndBeauty(2, -1.5, 1.5) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_with_decays_QCDCR_NeNe.cfg +includePartonEvent=true diff --git a/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_and_bbbar_gap2_QCDCR_OO.ini b/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_and_bbbar_gap2_QCDCR_OO.ini new file mode 100755 index 000000000..854f810a1 --- /dev/null +++ b/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_and_bbbar_gap2_QCDCR_OO.ini @@ -0,0 +1,9 @@ +#NEV_TEST> 16 +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C +funcName=GeneratorPythia8GapTriggeredCharmAndBeauty(2, -1.5, 1.5) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_with_decays_QCDCR_OO.cfg +includePartonEvent=true diff --git a/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_and_bbbar_gap2_QCDCR_pO.ini b/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_and_bbbar_gap2_QCDCR_pO.ini new file mode 100755 index 000000000..838a4a4f4 --- /dev/null +++ b/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_and_bbbar_gap2_QCDCR_pO.ini @@ -0,0 +1,8 @@ +#NEV_TEST> 12 +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C +funcName=GeneratorPythia8GapTriggeredCharmAndBeauty(2, -1.5, 1.5) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_with_decays_QCDCR_pO.cfg +includePartonEvent=true diff --git a/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_and_bbbar_gap3.ini b/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_and_bbbar_gap3.ini new file mode 100755 index 000000000..f7fefbb20 --- /dev/null +++ b/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_and_bbbar_gap3.ini @@ -0,0 +1,9 @@ +#NEV_TEST> 24 +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C +funcName=GeneratorPythia8GapTriggeredCharmAndBeauty(3, -1.5, 1.5) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_with_decays.cfg +includePartonEvent=true diff --git a/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_and_bbbar_gap3_Mode2.ini b/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_and_bbbar_gap3_Mode2.ini new file mode 100755 index 000000000..8b0d2fb2f --- /dev/null +++ b/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_and_bbbar_gap3_Mode2.ini @@ -0,0 +1,9 @@ +#NEV_TEST> 12 +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C +funcName=GeneratorPythia8GapTriggeredCharmAndBeauty(3, -1.5, 1.5) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_with_decays_Mode2.cfg +includePartonEvent=true diff --git a/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_and_bbbar_gap3_Mode2_XiC.ini b/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_and_bbbar_gap3_Mode2_XiC.ini new file mode 100755 index 000000000..bfa5df7b9 --- /dev/null +++ b/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_and_bbbar_gap3_Mode2_XiC.ini @@ -0,0 +1,9 @@ +#NEV_TEST> 12 +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C +funcName=GeneratorPythia8GapTriggeredCharmAndBeauty(3, -1.5, 1.5, -1.5, 1.5, {4132}) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_with_decays_Mode2.cfg +includePartonEvent=true diff --git a/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_and_bbbar_gap5.ini b/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_and_bbbar_gap5.ini new file mode 100755 index 000000000..429f59170 --- /dev/null +++ b/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_and_bbbar_gap5.ini @@ -0,0 +1,9 @@ +#NEV_TEST> 20 +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C +funcName=GeneratorPythia8GapTriggeredCharmAndBeauty(5, -1.5, 1.5) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_with_decays.cfg +includePartonEvent=true diff --git a/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_and_bbbar_gap5_DReso.ini b/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_and_bbbar_gap5_DReso.ini new file mode 100644 index 000000000..713b79c1c --- /dev/null +++ b/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_and_bbbar_gap5_DReso.ini @@ -0,0 +1,9 @@ +#NEV_TEST> 20 +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C +funcName=GeneratorPythia8GapTriggeredCharmAndBeauty(5, -1.5, 1.5) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_with_decays_DReso.cfg +includePartonEvent=true diff --git a/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_and_bbbar_gap5_DResoTrigger.ini b/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_and_bbbar_gap5_DResoTrigger.ini new file mode 100644 index 000000000..4ec024367 --- /dev/null +++ b/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_and_bbbar_gap5_DResoTrigger.ini @@ -0,0 +1,10 @@ +#NEV_TEST> 40 +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C +funcName=GeneratorPythia8GapTriggeredCharmAndBeauty(5, -1.5, 1.5, -1.5, 1.5, {10433, 435}, {{10433, 30433}, {10433, 437}, {435, 4325}, {435, 4326}, {425, 4315}, {425, 4316}}, {0.1, 0.1, 0.1, 0.1, 0.5, 0.5}) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_with_decays_DResoTrigger.cfg +includePartonEvent=false +### not needed for jet studies, hence no need to keep parton event diff --git a/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_and_bbbar_gap5_LcResoTrigger.ini b/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_and_bbbar_gap5_LcResoTrigger.ini new file mode 100644 index 000000000..cbd913e01 --- /dev/null +++ b/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_and_bbbar_gap5_LcResoTrigger.ini @@ -0,0 +1,10 @@ +#NEV_TEST> 20 +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C +funcName=GeneratorPythia8GapTriggeredCharmAndBeauty(5, -1.5, 1.5, -1.5, 1.5, {24124, 24126, 4125, 9422111}, {{4122, 24124}, {4122, 24126}, {4122, 4125}, {4122, 9422111}}, {0.1, 0.1, 0.1, 0.1}) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_with_decays_LcResoTrigger.cfg +includePartonEvent=false +### not needed for jet studies, hence no need to keep parton event diff --git a/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_and_bbbar_gap5_Mode2.ini b/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_and_bbbar_gap5_Mode2.ini new file mode 100755 index 000000000..2f4a41721 --- /dev/null +++ b/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_and_bbbar_gap5_Mode2.ini @@ -0,0 +1,9 @@ +#NEV_TEST> 20 +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C +funcName=GeneratorPythia8GapTriggeredCharmAndBeauty(5, -1.5, 1.5) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_with_decays_Mode2.cfg +includePartonEvent=true diff --git a/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_and_bbbar_gap5_Mode2_CharmBaryons.ini b/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_and_bbbar_gap5_Mode2_CharmBaryons.ini new file mode 100755 index 000000000..20fd5ab60 --- /dev/null +++ b/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_and_bbbar_gap5_Mode2_CharmBaryons.ini @@ -0,0 +1,9 @@ +#NEV_TEST> 10 +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C +funcName=GeneratorPythia8GapTriggeredCharmAndBeauty(5, -1.5, 1.5, -1.5, 1.5, {4132, 4332, 4232}) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_with_decays_Mode2.cfg +includePartonEvent=true diff --git a/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_and_bbbar_gap5_Mode2_CharmBaryons_pp_ref.ini b/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_and_bbbar_gap5_Mode2_CharmBaryons_pp_ref.ini new file mode 100644 index 000000000..569fee4c5 --- /dev/null +++ b/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_and_bbbar_gap5_Mode2_CharmBaryons_pp_ref.ini @@ -0,0 +1,9 @@ +#NEV_TEST> 10 +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C +funcName=GeneratorPythia8GapTriggeredCharmAndBeauty(5, -1.5, 1.5, -1.5, 1.5, {4132, 4332, 4232}) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_with_decays_Mode2_pp_ref.cfg +includePartonEvent=true \ No newline at end of file diff --git a/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_and_bbbar_gap5_Mode2_XiC.ini b/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_and_bbbar_gap5_Mode2_XiC.ini new file mode 100755 index 000000000..ad1aa5422 --- /dev/null +++ b/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_and_bbbar_gap5_Mode2_XiC.ini @@ -0,0 +1,9 @@ +#NEV_TEST> 10 +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C +funcName=GeneratorPythia8GapTriggeredCharmAndBeauty(5, -1.5, 1.5, -1.5, 1.5, {4132}) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_with_decays_Mode2.cfg +includePartonEvent=true diff --git a/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_and_bbbar_gap5_Mode2_XiC_OmegaC.ini b/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_and_bbbar_gap5_Mode2_XiC_OmegaC.ini new file mode 100755 index 000000000..4d2e31712 --- /dev/null +++ b/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_and_bbbar_gap5_Mode2_XiC_OmegaC.ini @@ -0,0 +1,9 @@ +#NEV_TEST> 10 +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C +funcName=GeneratorPythia8GapTriggeredCharmAndBeauty(5, -1.5, 1.5, -1.5, 1.5, {4132, 4332}) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_with_decays_Mode2.cfg +includePartonEvent=true diff --git a/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_and_bbbar_gap5_Mode2_corrBkg.ini b/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_and_bbbar_gap5_Mode2_corrBkg.ini new file mode 100644 index 000000000..840f0d14b --- /dev/null +++ b/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_and_bbbar_gap5_Mode2_corrBkg.ini @@ -0,0 +1,9 @@ +#NEV_TEST> 20 +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C +funcName=GeneratorPythia8GapTriggeredCharmAndBeauty(5, -1.5, 1.5) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_with_decays_Mode2_CorrBkg.cfg +includePartonEvent=true diff --git a/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_and_bbbar_gap5_Mode2_corrBkgSigmaC.ini b/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_and_bbbar_gap5_Mode2_corrBkgSigmaC.ini new file mode 100644 index 000000000..ddb61c610 --- /dev/null +++ b/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_and_bbbar_gap5_Mode2_corrBkgSigmaC.ini @@ -0,0 +1,10 @@ +#NEV_TEST> 150 +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C +funcName=GeneratorPythia8GapTriggeredCharmAndBeauty(5, -1.5, 1.5, -1.5, 1.5, {413}, {{413, 14122}, {413, 4124}}, {0.5, 0.5}) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_with_decays_Mode2_CorrBkgSigmaC.cfg +includePartonEvent=false +### not needed for jet studies, hence no need to keep parton event \ No newline at end of file diff --git a/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_and_bbbar_gap5_Mode2_pp_ref.ini b/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_and_bbbar_gap5_Mode2_pp_ref.ini new file mode 100644 index 000000000..a25d587cc --- /dev/null +++ b/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_and_bbbar_gap5_Mode2_pp_ref.ini @@ -0,0 +1,9 @@ +#NEV_TEST> 20 +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C +funcName=GeneratorPythia8GapTriggeredCharmAndBeauty(5, -1.5, 1.5) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_with_decays_Mode2_pp_ref.cfg +includePartonEvent=true diff --git a/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_and_bbbar_gap8.ini b/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_and_bbbar_gap8.ini new file mode 100755 index 000000000..47b370b8c --- /dev/null +++ b/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_and_bbbar_gap8.ini @@ -0,0 +1,9 @@ +#NEV_TEST> 32 +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C +funcName=GeneratorPythia8GapTriggeredCharmAndBeauty(8, -1.5, 1.5) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_with_decays.cfg +includePartonEvent=true diff --git a/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_and_bbbar_gap8_Mode2_XiC.ini b/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_and_bbbar_gap8_Mode2_XiC.ini new file mode 100755 index 000000000..97795a224 --- /dev/null +++ b/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_and_bbbar_gap8_Mode2_XiC.ini @@ -0,0 +1,9 @@ +#NEV_TEST> 16 +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C +funcName=GeneratorPythia8GapTriggeredCharmAndBeauty(8, -1.5, 1.5, -1.5, 1.5, {4132}) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_with_decays_Mode2.cfg +includePartonEvent=true diff --git a/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_gap5.ini b/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_gap5.ini new file mode 100755 index 000000000..8c598e7e1 --- /dev/null +++ b/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_gap5.ini @@ -0,0 +1,9 @@ +#NEV_TEST> 20 +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C +funcName=GeneratorPythia8GapTriggeredCharm(5, -1.5, 1.5) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_with_decays.cfg +includePartonEvent=true diff --git a/MC/config/PWGHF/ini/GeneratorHF_HFe_ccbar_and_bbar_gap5_Mode2.ini b/MC/config/PWGHF/ini/GeneratorHF_HFe_ccbar_and_bbar_gap5_Mode2.ini new file mode 100644 index 000000000..564cb14f7 --- /dev/null +++ b/MC/config/PWGHF/ini/GeneratorHF_HFe_ccbar_and_bbar_gap5_Mode2.ini @@ -0,0 +1,9 @@ +#NEV_TEST> 20 +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C +funcName=GeneratorPythia8GapTriggeredCharmAndBeauty(5, -1.5, 1.5, -1.0, 1.0, {11}) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_HFe_Mode2.cfg +includePartonEvent=true diff --git a/MC/config/PWGHF/ini/GeneratorHF_HFe_ccbar_and_bbar_gap5_Mode2_pp_ref.ini b/MC/config/PWGHF/ini/GeneratorHF_HFe_ccbar_and_bbar_gap5_Mode2_pp_ref.ini new file mode 100644 index 000000000..202b484b5 --- /dev/null +++ b/MC/config/PWGHF/ini/GeneratorHF_HFe_ccbar_and_bbar_gap5_Mode2_pp_ref.ini @@ -0,0 +1,9 @@ +#NEV_TEST> 20 +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C +funcName=GeneratorPythia8GapTriggeredCharmAndBeauty(5, -1.5, 1.5, -1.0, 1.0, {11}) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_HFe_Mode2_pp_ref.cfg +includePartonEvent=true diff --git a/MC/config/PWGHF/ini/GeneratorHF_Non_Hfe.ini b/MC/config/PWGHF/ini/GeneratorHF_Non_Hfe.ini new file mode 100644 index 000000000..7ace23bea --- /dev/null +++ b/MC/config/PWGHF/ini/GeneratorHF_Non_Hfe.ini @@ -0,0 +1,7 @@ +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/external/generator/selectNonHfe.C +funcName=generatePythia8Box(0.1,50.0) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_NonHfe.cfg +includePartonEvent=true \ No newline at end of file diff --git a/MC/config/PWGHF/ini/GeneratorHF_bbbar.ini b/MC/config/PWGHF/ini/GeneratorHF_bbbar.ini deleted file mode 100644 index 4d4244f62..000000000 --- a/MC/config/PWGHF/ini/GeneratorHF_bbbar.ini +++ /dev/null @@ -1,34 +0,0 @@ -### The setup uses an external event generator -### This part sets the path of the file and the function call to retrieve it - -[GeneratorExternal] -fileName = ${O2DPG_ROOT}/MC/config/PWGHF/external/generator/GeneratorHF.C -funcName = GeneratorHF_bbbar() - -### The external generator derives from GeneratorPythia8. -### This part configures the bits of the interface: configuration and user hooks - -[GeneratorPythia8] -config = ${O2DPG_ROOT}/MC/config/common/pythia8/generator/pythia8_hf.cfg -hooksFileName = ${O2DPG_ROOT}/MC/config/PWGHF/pythia8/hooks/pythia8_userhooks_qqbar.C -hooksFuncName = pythia8_userhooks_bbbar(-1.5,1.5) - -### The setup uses the base configuration of the decayer which is loaded from the file specified by config[0]. -### On top of the base configuration, two more sets of settings are loaded sequentially from config[1] and [2]. - -[DecayerPythia8] -config[0] = ${O2DPG_ROOT}/MC/config/common/pythia8/decayer/base.cfg -config[1] = ${O2DPG_ROOT}/MC/config/PWGHF/pythia8/decayer/force_hadronic_D.cfg -config[2] = ${O2DPG_ROOT}/MC/config/PWGHF/pythia8/decayer/force_hadronic_D_use4bodies.cfg - -### The setup forces some particles to be decayed by the external decayer instead of Geant. -### The PDG list of the particles is specified below. - -[SimUserDecay] -pdglist = 411 421 431 4112 4122 4232 4132 - -### The setup inhibits transport of primary particles which are produce at forward rapidity. -### The settings below only transports particles in the barrel, which is currently defined as |eta| < 2 - -[Stack] -transportPrimary = barrel diff --git a/MC/config/PWGHF/ini/GeneratorHF_bbbar_fwd_gap4_natural_inel_Mode2.ini b/MC/config/PWGHF/ini/GeneratorHF_bbbar_fwd_gap4_natural_inel_Mode2.ini new file mode 100644 index 000000000..a2b849bc3 --- /dev/null +++ b/MC/config/PWGHF/ini/GeneratorHF_bbbar_fwd_gap4_natural_inel_Mode2.ini @@ -0,0 +1,9 @@ +#NEV_TEST> 20 +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C +funcName=GeneratorPythia8GapTriggeredBeauty(4, -4.3, -2.2) +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_inel_Mode2.cfg +includePartonEvent=true + \ No newline at end of file diff --git a/MC/config/PWGHF/ini/GeneratorHF_bbbar_fwd_gap4_natural_inel_Mode2_muTrig.ini b/MC/config/PWGHF/ini/GeneratorHF_bbbar_fwd_gap4_natural_inel_Mode2_muTrig.ini new file mode 100644 index 000000000..b126fceed --- /dev/null +++ b/MC/config/PWGHF/ini/GeneratorHF_bbbar_fwd_gap4_natural_inel_Mode2_muTrig.ini @@ -0,0 +1,8 @@ +#NEV_TEST> 20 +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C +funcName=GeneratorPythia8GapTriggeredBeauty(4, -5, -1.5, -4.3, -2.2, {13}) +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_inel_Mode2.cfg +includePartonEvent=true \ No newline at end of file diff --git a/MC/config/PWGHF/ini/GeneratorHF_ccbar.ini b/MC/config/PWGHF/ini/GeneratorHF_ccbar.ini deleted file mode 100644 index 1d7e9df77..000000000 --- a/MC/config/PWGHF/ini/GeneratorHF_ccbar.ini +++ /dev/null @@ -1,34 +0,0 @@ -### The setup uses an external event generator -### This part sets the path of the file and the function call to retrieve it - -[GeneratorExternal] -fileName = ${O2DPG_ROOT}/MC/config/PWGHF/external/generator/GeneratorHF.C -funcName = GeneratorHF_ccbar() - -### The external generator derives from GeneratorPythia8. -### This part configures the bits of the interface: configuration and user hooks - -[GeneratorPythia8] -config = ${O2DPG_ROOT}/MC/config/common/pythia8/generator/pythia8_hf.cfg -hooksFileName = ${O2DPG_ROOT}/MC/config/PWGHF/pythia8/hooks/pythia8_userhooks_qqbar.C -hooksFuncName = pythia8_userhooks_ccbar(-1.5,1.5) - -### The setup uses the base configuration of the decayer which is loaded from the file specified by config[0]. -### On top of the base configuration, two more sets of settings are loaded sequentially from config[1] and [2]. - -[DecayerPythia8] -config[0] = ${O2DPG_ROOT}/MC/config/common/pythia8/decayer/base.cfg -config[1] = ${O2DPG_ROOT}/MC/config/PWGHF/pythia8/decayer/force_hadronic_D.cfg -config[2] = ${O2DPG_ROOT}/MC/config/PWGHF/pythia8/decayer/force_hadronic_D_use4bodies.cfg - -### The setup forces some particles to be decayed by the external decayer instead of Geant. -### The PDG list of the particles is specified below. - -[SimUserDecay] -pdglist = 411 421 431 4112 443 4122 4232 4132 4332 - -### The setup inhibits transport of primary particles which are produce at forward rapidity. -### The settings below only transports particles in the barrel, which is currently defined as |eta| < 2 - -#[Stack] -#transportPrimary = barrel diff --git a/MC/config/PWGHF/ini/GeneratorHF_decay.ini b/MC/config/PWGHF/ini/GeneratorHF_decay.ini deleted file mode 100644 index fe13bb851..000000000 --- a/MC/config/PWGHF/ini/GeneratorHF_decay.ini +++ /dev/null @@ -1,18 +0,0 @@ -### The setup uses an external event generator -### This part sets the path of the file and the function call to retrieve it - - -### The setup uses the base configuration of the decayer which is loaded from the file specified by config[0]. -### On top of the base configuration, two more sets of settings are loaded sequentially from config[1] and [2]. - -[DecayerPythia8] -config[0] = ${O2DPG_ROOT}/MC/config/common/pythia8/decayer/base.cfg -config[1] = ${O2DPG_ROOT}/MC/config/PWGHF/pythia8/decayer/force_hadronic_D.cfg -config[2] = ${O2DPG_ROOT}/MC/config/PWGHF/pythia8/decayer/force_hadronic_D_use4bodies.cfg -config[3] = $O2DPG_ROOT/MC/config/PWGHF/pythia8/decayer/force_hadronic_D_forceLcChannel1.cfg -### The setup forces some particles to be decayed by the external decayer instead of Geant. -### The PDG list of the particles is specified below. - - -[Stack] -transportPrimary = barrel diff --git a/MC/config/PWGHF/ini/GeneratorHF_mu_bbbar_gap3_Mode2_accSmall.ini b/MC/config/PWGHF/ini/GeneratorHF_mu_bbbar_gap3_Mode2_accSmall.ini new file mode 100644 index 000000000..73dd727e7 --- /dev/null +++ b/MC/config/PWGHF/ini/GeneratorHF_mu_bbbar_gap3_Mode2_accSmall.ini @@ -0,0 +1,8 @@ +#NEV_TEST> 24 +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C +funcName=GeneratorPythia8GapTriggeredBeauty(3, -4.3, -2.2) +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_beauty_with_mudecays_Mode2.cfg +includePartonEvent=true diff --git a/MC/config/PWGHF/ini/GeneratorHF_mu_bbbar_gap5_Mode2_accInt_muTrig.ini b/MC/config/PWGHF/ini/GeneratorHF_mu_bbbar_gap5_Mode2_accInt_muTrig.ini new file mode 100644 index 000000000..582ed736b --- /dev/null +++ b/MC/config/PWGHF/ini/GeneratorHF_mu_bbbar_gap5_Mode2_accInt_muTrig.ini @@ -0,0 +1,8 @@ +#NEV_TEST> 20 +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C +funcName=GeneratorPythia8GapTriggeredBeauty(5, -5, -1.5, -4.3, -2.2, {13}) +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_beauty_with_mudecays_Mode2.cfg +includePartonEvent=true diff --git a/MC/config/PWGHF/ini/GeneratorHF_mu_bbbar_gap5_Mode2_accLarge.ini b/MC/config/PWGHF/ini/GeneratorHF_mu_bbbar_gap5_Mode2_accLarge.ini new file mode 100644 index 000000000..8db0ed60b --- /dev/null +++ b/MC/config/PWGHF/ini/GeneratorHF_mu_bbbar_gap5_Mode2_accLarge.ini @@ -0,0 +1,8 @@ +#NEV_TEST> 20 +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C +funcName=GeneratorPythia8GapTriggeredBeauty(5, -5.0, 5.0) +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_beauty_with_mudecays_Mode2.cfg +includePartonEvent=true diff --git a/MC/config/PWGHF/ini/GeneratorHF_mu_bbbar_gap5_Mode2_accLarge_muTrig.ini b/MC/config/PWGHF/ini/GeneratorHF_mu_bbbar_gap5_Mode2_accLarge_muTrig.ini new file mode 100644 index 000000000..972c075b2 --- /dev/null +++ b/MC/config/PWGHF/ini/GeneratorHF_mu_bbbar_gap5_Mode2_accLarge_muTrig.ini @@ -0,0 +1,8 @@ +#NEV_TEST> 20 +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C +funcName=GeneratorPythia8GapTriggeredBeauty(5, -5.0, 5.0, -4.3, -2.2, {13}) +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_beauty_with_mudecays_Mode2.cfg +includePartonEvent=true diff --git a/MC/config/PWGHF/ini/GeneratorHF_mu_bbbar_gap5_Mode2_accSmall.ini b/MC/config/PWGHF/ini/GeneratorHF_mu_bbbar_gap5_Mode2_accSmall.ini new file mode 100644 index 000000000..923394b48 --- /dev/null +++ b/MC/config/PWGHF/ini/GeneratorHF_mu_bbbar_gap5_Mode2_accSmall.ini @@ -0,0 +1,8 @@ +#NEV_TEST> 20 +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C +funcName=GeneratorPythia8GapTriggeredBeauty(5, -4.3, -2.2) +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_beauty_with_mudecays_Mode2.cfg +includePartonEvent=true diff --git a/MC/config/PWGHF/ini/GeneratorHF_mu_ccbar_bbbar_charmdecays_gap3_Mode2_accSmall.ini b/MC/config/PWGHF/ini/GeneratorHF_mu_ccbar_bbbar_charmdecays_gap3_Mode2_accSmall.ini new file mode 100644 index 000000000..3cdcaa54a --- /dev/null +++ b/MC/config/PWGHF/ini/GeneratorHF_mu_ccbar_bbbar_charmdecays_gap3_Mode2_accSmall.ini @@ -0,0 +1,8 @@ +#NEV_TEST> 150 +### The external generator derives from GeneratorPythia8 +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C +funcName=GeneratorPythia8GapTriggeredCharmBeautyCharm(3, -4.3, -2.2) +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_charm_with_mudecays_Mode2.cfg +includePartonEvent=true diff --git a/MC/config/PWGHF/ini/GeneratorHF_mu_ccbar_gap3_Mode2_accSmall.ini b/MC/config/PWGHF/ini/GeneratorHF_mu_ccbar_gap3_Mode2_accSmall.ini new file mode 100644 index 000000000..bc2c06c55 --- /dev/null +++ b/MC/config/PWGHF/ini/GeneratorHF_mu_ccbar_gap3_Mode2_accSmall.ini @@ -0,0 +1,8 @@ +#NEV_TEST> 24 +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C +funcName=GeneratorPythia8GapTriggeredCharm(3, -4.3, -2.2) +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_charm_with_mudecays_Mode2.cfg +includePartonEvent=true diff --git a/MC/config/PWGHF/ini/GeneratorHF_mu_ccbar_gap5_Mode2_accInt_muTrig.ini b/MC/config/PWGHF/ini/GeneratorHF_mu_ccbar_gap5_Mode2_accInt_muTrig.ini new file mode 100644 index 000000000..149728000 --- /dev/null +++ b/MC/config/PWGHF/ini/GeneratorHF_mu_ccbar_gap5_Mode2_accInt_muTrig.ini @@ -0,0 +1,8 @@ +#NEV_TEST> 20 + ### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C +funcName=GeneratorPythia8GapTriggeredCharm(5, -5, -1.5, -4.3, -2.2, {13}) +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_charm_with_mudecays_Mode2.cfg +includePartonEvent=true diff --git a/MC/config/PWGHF/ini/GeneratorHF_mu_ccbar_gap5_Mode2_accLarge.ini b/MC/config/PWGHF/ini/GeneratorHF_mu_ccbar_gap5_Mode2_accLarge.ini new file mode 100644 index 000000000..6c3904834 --- /dev/null +++ b/MC/config/PWGHF/ini/GeneratorHF_mu_ccbar_gap5_Mode2_accLarge.ini @@ -0,0 +1,8 @@ +#NEV_TEST> 20 +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C +funcName=GeneratorPythia8GapTriggeredCharm(5, -5.0, 5.0) +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_charm_with_mudecays_Mode2.cfg +includePartonEvent=true diff --git a/MC/config/PWGHF/ini/GeneratorHF_mu_ccbar_gap5_Mode2_accLarge_muTrig.ini b/MC/config/PWGHF/ini/GeneratorHF_mu_ccbar_gap5_Mode2_accLarge_muTrig.ini new file mode 100644 index 000000000..970a08807 --- /dev/null +++ b/MC/config/PWGHF/ini/GeneratorHF_mu_ccbar_gap5_Mode2_accLarge_muTrig.ini @@ -0,0 +1,8 @@ +#NEV_TEST> 20 +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C +funcName=GeneratorPythia8GapTriggeredCharm(5, -5.0, 5.0, -4.3, -2.2, {13}) +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_charm_with_mudecays_Mode2.cfg +includePartonEvent=true diff --git a/MC/config/PWGHF/ini/GeneratorHF_mu_ccbar_gap5_Mode2_accSmall.ini b/MC/config/PWGHF/ini/GeneratorHF_mu_ccbar_gap5_Mode2_accSmall.ini new file mode 100644 index 000000000..43e1b79fa --- /dev/null +++ b/MC/config/PWGHF/ini/GeneratorHF_mu_ccbar_gap5_Mode2_accSmall.ini @@ -0,0 +1,8 @@ +#NEV_TEST> 20 +### The external generator derives from GeneratorPythia8. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C +funcName=GeneratorPythia8GapTriggeredCharm(5, -4.3, -2.2) +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_charm_with_mudecays_Mode2.cfg +includePartonEvent=true diff --git a/MC/config/PWGHF/ini/tests/GeneratorHFTrigger_BToDeuteron.C b/MC/config/PWGHF/ini/tests/GeneratorHFTrigger_BToDeuteron.C new file mode 100644 index 000000000..bd56f98e6 --- /dev/null +++ b/MC/config/PWGHF/ini/tests/GeneratorHFTrigger_BToDeuteron.C @@ -0,0 +1,48 @@ +int External() +{ + std::string path{"o2sim_Kine.root"}; + std::vector checkPdgHadron{521}; + std::vector nucleiDauPdg{1000010020}; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) + { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int nSignals{}, nSignalGoodDecay{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) + { + tree->GetEntry(i); + for (auto &track : *tracks) + { + auto pdg = track.GetPdgCode(); + if (std::find(checkPdgHadron.begin(), checkPdgHadron.end(), std::abs(pdg)) != checkPdgHadron.end()) + { + if(std::abs(track.GetRapidity()) > 1.5) continue; + nSignals++; + for (int j{track.getFirstDaughterTrackId()}; j <= track.getLastDaughterTrackId(); ++j) + { + auto pdgDau = tracks->at(j).GetPdgCode(); + if (std::find(nucleiDauPdg.begin(), nucleiDauPdg.end(), std::abs(pdgDau)) != nucleiDauPdg.end()) + { + nSignalGoodDecay++; + } + } + } + } + } + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + std::cout << "# signal hadrons: " << nSignals << "\n"; + std::cout << "# signal hadrons decaying into nuclei: " << nSignalGoodDecay << "\n"; + + return 0; +} \ No newline at end of file diff --git a/MC/config/PWGHF/ini/tests/GeneratorHFTrigger_Bforced.C b/MC/config/PWGHF/ini/tests/GeneratorHFTrigger_Bforced.C index 979334f30..7f0eca77f 100644 --- a/MC/config/PWGHF/ini/tests/GeneratorHFTrigger_Bforced.C +++ b/MC/config/PWGHF/ini/tests/GeneratorHFTrigger_Bforced.C @@ -9,7 +9,7 @@ int External() {421, {{-321, 211}, {-321, 111, 211}}}, // D0 {431, {{211, 333}}}, // Ds+ {443, {{-11, 11}}}, // Jpsi - {4122, {{-313, 2212}, {-321, 2224}, {211, 3124}, {-321, 211, 2212}}}, // Lc+ + {4122, {{-313, 2212}, {-321, 2224}, {211, 102134}, {-321, 211, 2212}}}, // Lc+ {4132, {{211, 3312}}}, // Xic0 {4232, {{-313, 2212}, {-321, 3324}, {211, 211, 3312}, {-321, 211, 2212}}}, // Xic+ {4332, {{211, 3334}}}, // Omegac+ @@ -90,8 +90,9 @@ int External() return 1; } - float fracForcedDecays = float(nSignalGoodDecay) / nSignals; - if (fracForcedDecays < 0.75) // we put some tolerance (e.g. due to oscillations which might change the final state) + float fracForcedDecays = nSignals ? float(nSignalGoodDecay) / nSignals : 0.0f; + float uncFracForcedDecays = nSignals ? std::sqrt(fracForcedDecays * (1 - fracForcedDecays) / nSignals) : 1.0f; + if (1 - fracForcedDecays > 0.3 + uncFracForcedDecays) // we put some tolerance (e.g. due to oscillations which might change the final state) { std::cerr << "Fraction of signals decaying into the correct channel " << fracForcedDecays << " lower than expected\n"; return 1; diff --git a/MC/config/PWGHF/ini/tests/GeneratorHFTrigger_LambdaBToDeuteron.C b/MC/config/PWGHF/ini/tests/GeneratorHFTrigger_LambdaBToDeuteron.C new file mode 100644 index 000000000..3df607574 --- /dev/null +++ b/MC/config/PWGHF/ini/tests/GeneratorHFTrigger_LambdaBToDeuteron.C @@ -0,0 +1,57 @@ +int External() +{ + std::string path{"o2sim_Kine.root"}; + std::vector checkPdgHadron{5122}; // Lambda_b + std::vector nucleiDauPdg{1000010020}; // d, 3He, 3H + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) + { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int nSignals{}, nSignalGoodDecay{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) + { + tree->GetEntry(i); + for (auto &track : *tracks) + { + auto pdg = track.GetPdgCode(); + if (std::find(checkPdgHadron.begin(), checkPdgHadron.end(), std::abs(pdg)) != checkPdgHadron.end()) // found signal + { + // count signal PDG + if(std::abs(track.GetRapidity()) > 1.5) continue; // skip if outside rapidity window + nSignals++; + for (int j{track.getFirstDaughterTrackId()}; j <= track.getLastDaughterTrackId(); ++j) + { + auto pdgDau = tracks->at(j).GetPdgCode(); + if (std::find(nucleiDauPdg.begin(), nucleiDauPdg.end(), std::abs(pdgDau)) != nucleiDauPdg.end()) + { + nSignalGoodDecay++; + } + } + } + } + } + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + std::cout <<"# signal hadrons: " << nSignals << "\n"; + std::cout <<"# signal hadrons decaying into nuclei: " << nSignalGoodDecay << "\n"; + + float fracForcedDecays = nSignals ? float(nSignalGoodDecay) / nSignals : 0.0f; + float uncFracForcedDecays = nSignals ? std::sqrt(fracForcedDecays * (1 - fracForcedDecays) / nSignals) : 1.0f; + if (1 - fracForcedDecays > 0.2 + uncFracForcedDecays) // we put some tolerance (lambdaB in MB events do not coalesce) + { + std::cerr << "Fraction of signals decaying into nuclei: " << fracForcedDecays << ", lower than expected\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGHF/ini/tests/GeneratorHFTrigger_LambdaBToNuclei.C b/MC/config/PWGHF/ini/tests/GeneratorHFTrigger_LambdaBToNuclei.C new file mode 100644 index 000000000..be1ef1b56 --- /dev/null +++ b/MC/config/PWGHF/ini/tests/GeneratorHFTrigger_LambdaBToNuclei.C @@ -0,0 +1,57 @@ +int External() +{ + std::string path{"o2sim_Kine.root"}; + std::vector checkPdgHadron{5122}; // Lambda_b + std::vector nucleiDauPdg{1000020030, 1000010030}; // 3He, 3H + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) + { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int nSignals{}, nSignalGoodDecay{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) + { + tree->GetEntry(i); + for (auto &track : *tracks) + { + auto pdg = track.GetPdgCode(); + if (std::find(checkPdgHadron.begin(), checkPdgHadron.end(), std::abs(pdg)) != checkPdgHadron.end()) // found signal + { + // count signal PDG + if(std::abs(track.GetRapidity()) > 1.5) continue; // skip if outside rapidity window + nSignals++; + for (int j{track.getFirstDaughterTrackId()}; j <= track.getLastDaughterTrackId(); ++j) + { + auto pdgDau = tracks->at(j).GetPdgCode(); + if (std::find(nucleiDauPdg.begin(), nucleiDauPdg.end(), std::abs(pdgDau)) != nucleiDauPdg.end()) + { + nSignalGoodDecay++; + } + } + } + } + } + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + std::cout <<"# signal hadrons: " << nSignals << "\n"; + std::cout <<"# signal hadrons decaying into nuclei: " << nSignalGoodDecay << "\n"; + + float fracForcedDecays = nSignals ? float(nSignalGoodDecay) / nSignals : 0.0f; + float uncFracForcedDecays = nSignals ? std::sqrt(fracForcedDecays * (1 - fracForcedDecays) / nSignals) : 1.0f; + if (1 - fracForcedDecays > 0.2 + uncFracForcedDecays) // we put some tolerance (lambdaB in MB events do not coalesce) + { + std::cerr << "Fraction of signals decaying into nuclei: " << fracForcedDecays << ", lower than expected\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGHF/ini/tests/GeneratorHFTrigger_Xi_Omega_C.C b/MC/config/PWGHF/ini/tests/GeneratorHFTrigger_Xi_Omega_C.C new file mode 100644 index 000000000..1e0a637a9 --- /dev/null +++ b/MC/config/PWGHF/ini/tests/GeneratorHFTrigger_Xi_Omega_C.C @@ -0,0 +1,155 @@ +int External() +{ + int checkPdgSignal = 4132; + int checkPdgDecayPi = 211; + int checkPdgDecayXi = 3312; + int checkPdgQuark = 4; + float ratioTrigger = 1./3; // one event triggered out of 3 + + std::string path{"o2sim_Kine.root"}; + std::cout << "Check for\nsignal PDG " << checkPdgSignal << "\ndecay PDG " << checkPdgDecayXi << " and " << checkPdgDecayPi << "\n"; + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) + { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int nQuark{}; // charm quark + int nXi{}; // xi- + int nAntiXi{}; // xi+ + int nPi{}; // pi+ + int nAntiPi{}; // pi- + + int nDauPairs{}; + + int nSignalTot{}; + int nSignalPart{}; + int nSignalAntiPart{}; + + int nDecayXic{}; + int nFullDecayChain{}; + + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) + { + tree->GetEntry(i); + for (auto &track : *tracks) + { + auto pdg = track.GetPdgCode(); + if (std::abs(pdg) == checkPdgQuark) { + nQuark++; + continue; + } + if (pdg == checkPdgDecayXi) + { + nXi++; + } + else if (pdg == -checkPdgDecayXi) + { + nAntiXi++; + } + else if (pdg == checkPdgDecayPi) + { + nPi++; + } + else if (pdg == -checkPdgDecayPi) + { + nAntiPi++; + } + else if (std::abs(pdg) == checkPdgSignal) + { + nSignalTot++; + + if(pdg == checkPdgSignal){ + nSignalPart++; + } else if (pdg == -checkPdgSignal) { + nSignalAntiPart++; + } + + auto child0 = o2::mcutils::MCTrackNavigator::getDaughter0(track, *tracks); + auto child1 = o2::mcutils::MCTrackNavigator::getDaughter1(track, *tracks); + if (child0 != nullptr && child1 != nullptr) + { + nDauPairs++; + + // check for parent-child relations + auto pdg0 = child0->GetPdgCode(); + auto pdg1 = child1->GetPdgCode(); + std::cout << "First and last children of parent " << pdg << " are PDG0: " << pdg0 << " PDG1: " << pdg1 << "\n"; + if ((std::abs(pdg0) == checkPdgDecayXi && std::abs(pdg1) == checkPdgDecayPi) || (std::abs(pdg1) == checkPdgDecayXi && std::abs(pdg0) == checkPdgDecayPi)) + { // 211 pi+ and 3312 xi- from MC numbering scheme + nDecayXic++; + // ------------- cascade is child0 ------------- + if (std::abs(pdg0) == checkPdgDecayXi) + { + auto childCasc0 = o2::mcutils::MCTrackNavigator::getDaughter0(*child0, *tracks); + auto childCasc1 = o2::mcutils::MCTrackNavigator::getDaughter1(*child0, *tracks); + nFullDecayChain++; + } + + else if (std::abs(pdg1) == checkPdgDecayXi) + { // ------------- cascade is child1 ------------- + auto childCasc0 = o2::mcutils::MCTrackNavigator::getDaughter0(*child1, *tracks); + auto childCasc1 = o2::mcutils::MCTrackNavigator::getDaughter1(*child1, *tracks); + nFullDecayChain++; + } + } + } + } + } + } + + + std::cout << "#events: " << nEvents << "\n" + <<"#charm quark: " << nQuark << "\n" + << "#xi: " << nXi << "\n" + << "#antixi: " << nAntiXi << "\n" + << "#pi: " << nPi << "\n" + << "#antipi: " << nAntiPi << "\n" + << "#signal tot: " << nSignalTot << "\n" + << "#signal particles: " << nSignalPart << "\n" + << "#signal anti-particles: " << nSignalAntiPart << "\n" + << "#Daughter pairs: " << nDauPairs << "\n" + << "#Correct Xic decays: " << nDecayXic << "\n" + << "#Correct full decay chain: " << nFullDecayChain << "\n"; + + if (nDauPairs == 0) + { + std::cerr << "Number of daughter pairs should be greater than 0.\n"; + return 1; + } + if (nSignalTot == 0) + { + std::cerr << "Number of Xic + Anti-Xic should be greater than 0.\n"; + return 1; + } + if (nXi == 0 && nAntiXi == 0) + { + std::cerr << "At least one among number of xi and number of anti-xi should be greater than 1.\n"; + return 1; + } + if (nPi == 0 && nAntiPi == 0) + { + std::cerr << "At least one among number of pi and number of anti-pi should be greater than 1.\n"; + return 1; + } + if (nDecayXic != nFullDecayChain) + { + std::cerr << "The full OmegaC decay chain is not the expected one (Xic -> Xi pi).\n"; + return 1; + } + if (nQuark < 2 * nEvents * ratioTrigger) // we expect anyway more because the same quark is repeated several time, after each gluon radiation + { + std::cerr << "Number of generated (anti)quarks " << checkPdgQuark << " lower than expected\n"; + return 1; + } + + return 0; + +} diff --git a/MC/config/PWGHF/ini/tests/GeneratorHFTrigger_bbbar.C b/MC/config/PWGHF/ini/tests/GeneratorHFTrigger_bbbar.C index 2e1ae3e6e..05a465e6d 100644 --- a/MC/config/PWGHF/ini/tests/GeneratorHFTrigger_bbbar.C +++ b/MC/config/PWGHF/ini/tests/GeneratorHFTrigger_bbbar.C @@ -9,7 +9,7 @@ int External() {421, {{-321, 211}, {-321, 111, 211}}}, // D0 {431, {{211, 333}}}, // Ds+ {443, {{-11, 11}}}, // Jpsi - {4122, {{-313, 2212}, {-321, 2224}, {211, 3124}, {-321, 211, 2212}}}, // Lc+ + {4122, {{-313, 2212}, {-321, 2224}, {211, 102134}, {-321, 211, 2212}}}, // Lc+ {4132, {{211, 3312}}}, // Xic0 {4232, {{-313, 2212}, {-321, 3324}, {211, 211, 3312}, {-321, 211, 2212}}}, // Xic+ {4332, {{211, 3334}}} // Omegac+ @@ -82,8 +82,9 @@ int External() return 1; } - float fracForcedDecays = float(nSignalGoodDecay) / nSignals; - if (fracForcedDecays < 0.9) // we put some tolerance (e.g. due to oscillations which might change the final state) + float fracForcedDecays = nSignals ? float(nSignalGoodDecay) / nSignals : 0.0f; + float uncFracForcedDecays = nSignals ? std::sqrt(fracForcedDecays * (1 - fracForcedDecays) / nSignals) : 1.0f; + if (1 - fracForcedDecays > 0.15 + uncFracForcedDecays) // we put some tolerance (e.g. due to oscillations which might change the final state) { std::cerr << "Fraction of signals decaying into the correct channel " << fracForcedDecays << " lower than expected\n"; return 1; diff --git a/MC/config/PWGHF/ini/tests/GeneratorHFTrigger_ccbar.C b/MC/config/PWGHF/ini/tests/GeneratorHFTrigger_ccbar.C index e4433e23f..22863be41 100644 --- a/MC/config/PWGHF/ini/tests/GeneratorHFTrigger_ccbar.C +++ b/MC/config/PWGHF/ini/tests/GeneratorHFTrigger_ccbar.C @@ -9,7 +9,7 @@ int External() {421, {{-321, 211}, {-321, 111, 211}}}, // D0 {431, {{211, 333}}}, // Ds+ {443, {{-11, 11}}}, // Jpsi - {4122, {{-313, 2212}, {-321, 2224}, {211, 3124}, {-321, 211, 2212}}}, // Lc+ + {4122, {{-313, 2212}, {-321, 2224}, {211, 102134}, {-321, 211, 2212}}}, // Lc+ {4132, {{211, 3312}}}, // Xic0 {4232, {{-313, 2212}, {-321, 3324}, {211, 211, 3312}, {-321, 211, 2212}}}, // Xic+ {4332, {{211, 3334}}} // Omegac+ @@ -82,8 +82,9 @@ int External() return 1; } - float fracForcedDecays = float(nSignalGoodDecay) / nSignals; - if (fracForcedDecays < 0.9) // we put some tolerance (e.g. due to oscillations which might change the final state) + float fracForcedDecays = nSignals ? float(nSignalGoodDecay) / nSignals : 0.0f; + float uncFracForcedDecays = nSignals ? std::sqrt(fracForcedDecays * (1 - fracForcedDecays) / nSignals) : 1.0f; + if (1 - fracForcedDecays > 0.15 + uncFracForcedDecays) // we put some tolerance (e.g. due to oscillations which might change the final state) { std::cerr << "Fraction of signals decaying into the correct channel " << fracForcedDecays << " lower than expected\n"; return 1; diff --git a/MC/config/PWGHF/ini/tests/GeneratorHF_CDeuteron_Injected.C b/MC/config/PWGHF/ini/tests/GeneratorHF_CDeuteron_Injected.C new file mode 100644 index 000000000..56d22c72a --- /dev/null +++ b/MC/config/PWGHF/ini/tests/GeneratorHF_CDeuteron_Injected.C @@ -0,0 +1,127 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + + int pdgCDeuteron{2010010020}; + int checkNumberOfCDeuteronPerEvent{10}; + float checkLifetimeCDeuteron{0.05f}; + std::map>> checkDecays{ + {2010010020, {{-321, 211, 1000010020}}} // c-deuteron -> K- + pi+ + deuteron + }; + float checkFracCDeuteronFromBeauty{0.25}; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int nCDeuteron{}, nCDeuteronGoodDecay{}, nCDeuteronFromBeauty{}; + std::array averageLifetimeCDeuteron{0.f, 0.f}; // prompt and non-prompt + float massCDeuteron = 3.226f; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + + for (auto &track : *tracks) { + auto pdg = track.GetPdgCode(); + auto absPdg = std::abs(pdg); + // std::cout << "Event " << i << ": found particle with PDG " << pdg << std::endl; + + if (absPdg == pdgCDeuteron) { // found signal + nCDeuteron++; // count signal PDG + + // std::cout << "Event " << i << ": found c-deuteron with PDG " << pdg << std::endl; + + std::vector pdgsDecay{}; + std::vector pdgsDecayAntiPart{}; + if (track.getFirstDaughterTrackId() >= 0 && track.getLastDaughterTrackId() >= 0) { + for (int j{track.getFirstDaughterTrackId()}; j <= track.getLastDaughterTrackId(); ++j) { + // std::cout << "Fetching daughter track with ID " << j << std::endl; + auto pdgDau = tracks->at(j).GetPdgCode(); + // std::cout << "PDG of daughter track " << j << ": " << pdgDau << std::endl; + pdgsDecay.push_back(pdgDau); + if (pdgDau != 333 && pdgDau != 111) { // phi and pi0 are antiparticles of themselves + pdgsDecayAntiPart.push_back(-pdgDau); + } else { + pdgsDecayAntiPart.push_back(pdgDau); + } + } + } + // std::cout << "Daughters fetched" << std::endl; + + auto mother = track.getMotherTrackId(); + bool isFromBeauty{false}; + if (mother >= 0 && std::abs(tracks->at(mother).GetPdgCode()) == 5122) { // check if c-deuteron comes from Lb + nCDeuteronFromBeauty++; + isFromBeauty = true; + } + + auto dauTrack = tracks->at(track.getFirstDaughterTrackId()); + float decayLength = std::sqrt((track.GetStartVertexCoordinatesX() - dauTrack.GetStartVertexCoordinatesX()) * (track.GetStartVertexCoordinatesX() - dauTrack.GetStartVertexCoordinatesX()) + (track.GetStartVertexCoordinatesY() - dauTrack.GetStartVertexCoordinatesY()) * (track.GetStartVertexCoordinatesY() - dauTrack.GetStartVertexCoordinatesY()) + (track.GetStartVertexCoordinatesZ() - dauTrack.GetStartVertexCoordinatesZ()) * (track.GetStartVertexCoordinatesZ() - dauTrack.GetStartVertexCoordinatesZ())); + if (!isFromBeauty) { + averageLifetimeCDeuteron[0] += decayLength * massCDeuteron / track.GetP(); + } else { + averageLifetimeCDeuteron[1] += decayLength * massCDeuteron / track.GetP(); + } + + std::sort(pdgsDecay.begin(), pdgsDecay.end()); + std::sort(pdgsDecayAntiPart.begin(), pdgsDecayAntiPart.end()); + + for (auto &decay : checkDecays[std::abs(pdg)]) { + if (pdgsDecay == decay || pdgsDecayAntiPart == decay) { + nCDeuteronGoodDecay++; + break; + } + } + // std::cout << "Daughters checked " << std::endl; + } + } + } + + averageLifetimeCDeuteron[0] /= nCDeuteron - nCDeuteronFromBeauty; + averageLifetimeCDeuteron[1] /= nCDeuteronFromBeauty; + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + std::cout <<"# signal c-deuteron: " << nCDeuteron << "\n"; + std::cout <<"# signal c-deuteron decaying in the correct channel: " << nCDeuteronGoodDecay << "\n"; + std::cout <<"# signal c-deuteron from beauty: " << nCDeuteronFromBeauty << "\n"; + std::cout <<"Average lifetime of c-deuteron (prompt): " << averageLifetimeCDeuteron[0] << " (cm) \n"; + std::cout <<"Average lifetime of c-deuteron (non-prompt): " << averageLifetimeCDeuteron[1] << " (cm) \n"; + + float numberOfCDeuteronPerEvent = float(nCDeuteron) / nEvents; + float fracCDeuteronGoodDecay = float(nCDeuteronGoodDecay) / nCDeuteron; + float fracCDeuteronFromBeauty = float(nCDeuteronFromBeauty) / nCDeuteron; + + if (std::abs(numberOfCDeuteronPerEvent - checkNumberOfCDeuteronPerEvent) / numberOfCDeuteronPerEvent > 0.05) { // we put some tolerance since the number of generated events is small + std::cerr << "Number of C-deuterons per event " << numberOfCDeuteronPerEvent << " different than expected " << checkNumberOfCDeuteronPerEvent << "\n"; + return 1; + } + + if (fracCDeuteronGoodDecay < 0.95) { // we put some tolerance since the number of generated events is small + std::cerr << "Fraction of signals decaying into the correct channel " << fracCDeuteronGoodDecay << " lower than expected\n"; + return 1; + } + + if (std::abs(fracCDeuteronFromBeauty - checkFracCDeuteronFromBeauty) / checkFracCDeuteronFromBeauty > 0.10) { // we put some tolerance since the number of generated events is small + std::cerr << "Fraction of signals from beauty " << fracCDeuteronFromBeauty << " different than expected " << checkFracCDeuteronFromBeauty << "\n"; + return 1; + } + + if (std::abs(averageLifetimeCDeuteron[0] - checkLifetimeCDeuteron) / checkLifetimeCDeuteron > 0.10) { // we put some tolerance since the number of generated events is small + std::cerr << "Lifetime for prompt c-deuteron " << averageLifetimeCDeuteron[0] << " different than expected " << checkLifetimeCDeuteron << "\n"; + return 1; + } + + if (std::abs(averageLifetimeCDeuteron[1] - checkLifetimeCDeuteron) / checkLifetimeCDeuteron > 0.10) { // we put some tolerance since the number of generated events is small + std::cerr << "Lifetime for non-prompt c-deuteron " << averageLifetimeCDeuteron[1] << " different than expected " << checkLifetimeCDeuteron << "\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_bbbar_B2JPsi_gap5_Mode2.C b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_bbbar_B2JPsi_gap5_Mode2.C new file mode 100644 index 000000000..e18dcadde --- /dev/null +++ b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_bbbar_B2JPsi_gap5_Mode2.C @@ -0,0 +1,113 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + + int checkPdgQuark{5}; + float ratioTrigger = 1./5; // one event triggered out of 5 + + std::vector checkPdgHadron{443, 511, 521, 531, 5122}; + std::map>> checkHadronDecays{ // sorted pdg of daughters + {443, {{-11, 11}, {-13, 13}}}, // J/psi + {511, {{313, 443}}}, // B0 + {521, {{321, 443}}}, // B+ + {531, {{333, 443}}}, // Bs0 + {5122, {{321, 443, 2212}}} // Lb0 + }; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int nEventsMB{}, nEventsInj{}; + int nSignals{}, nSignalGoodDecay{}; + int nJPsiToEE{}, nJPsiToMuMu{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + + // check subgenerator information + if (eventHeader->hasInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID)) { + bool isValid = false; + int subGeneratorId = eventHeader->getInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID, isValid); + if (subGeneratorId == 0) { + nEventsMB++; + } else if (subGeneratorId == checkPdgQuark) { + nEventsInj++; + } + } + + for (auto &track : *tracks) { + auto pdg = track.GetPdgCode(); + if (std::find(checkPdgHadron.begin(), checkPdgHadron.end(), std::abs(pdg)) != checkPdgHadron.end()) { // found signal + nSignals++; // count signal PDG + + std::vector pdgsDecay{}; + std::vector pdgsDecayAntiPart{}; + for (int j{track.getFirstDaughterTrackId()}; j <= track.getLastDaughterTrackId(); ++j) { + auto pdgDau = tracks->at(j).GetPdgCode(); + if (pdg == 443 && pdgDau == 22) { + continue; + } + pdgsDecay.push_back(pdgDau); + if (pdgDau != 333) { // phi is antiparticle of itself + pdgsDecayAntiPart.push_back(-pdgDau); + } else { + pdgsDecayAntiPart.push_back(pdgDau); + } + } + + std::sort(pdgsDecay.begin(), pdgsDecay.end()); + std::sort(pdgsDecayAntiPart.begin(), pdgsDecayAntiPart.end()); + + for (auto &decay : checkHadronDecays[std::abs(pdg)]) { + if (pdgsDecay == decay || pdgsDecayAntiPart == decay) { + nSignalGoodDecay++; + if (pdg == 443) { + if (decay == std::vector{-11, 11}) { + nJPsiToEE++; + } else if (decay == std::vector{-13, 13}) { + nJPsiToMuMu++; + } + } + break; + } + } + } + } + } + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + std::cout << "# MB events: " << nEventsMB << "\n"; + std::cout << Form("# events injected with %d quark pair: ", checkPdgQuark) << nEventsInj << "\n"; + std::cout <<"# signal hadrons: " << nSignals << "\n"; + std::cout <<"# signal hadrons decaying in the correct channel: " << nSignalGoodDecay << "\n"; + std::cout <<"# J/Psi decaying to e+e-: " << nJPsiToEE << "\n"; + std::cout <<"# J/Psi decaying to mu+mu-: " << nJPsiToMuMu << "\n"; + + if (nEventsMB < nEvents * (1 - ratioTrigger) * 0.95 || nEventsMB > nEvents * (1 - ratioTrigger) * 1.05) { // we put some tolerance since the number of generated events is small + std::cerr << "Number of generated MB events different than expected\n"; + return 1; + } + if (nEventsInj < nEvents * ratioTrigger * 0.95 || nEventsInj > nEvents * ratioTrigger * 1.05) { + std::cerr << "Number of generated events injected with " << checkPdgQuark << " different than expected\n"; + return 1; + } + + float fracForcedDecays = nSignals ? float(nSignalGoodDecay) / nSignals : 0.0f; + float uncFracForcedDecays = nSignals ? std::sqrt(fracForcedDecays * (1 - fracForcedDecays) / nSignals) : 1.0f; + if (1 - fracForcedDecays > 0.5 + uncFracForcedDecays) { // at least 50% in the main decay channels (we also have correlated backgrounds, mostly from other B decays) + std::cerr << "Fraction of signals decaying into the correct channel " << fracForcedDecays << " lower than expected\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_bbbar_Bforced_gap5_Mode2.C b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_bbbar_Bforced_gap5_Mode2.C new file mode 100644 index 000000000..7a5fdab70 --- /dev/null +++ b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_bbbar_Bforced_gap5_Mode2.C @@ -0,0 +1,113 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + + int checkPdgQuark{5}; + float ratioTrigger = 1./5; // one event triggered out of 5 + + std::vector checkPdgHadron{411, 421, 431, 4122, 4132, 4232, 4332, 511, 521, 531, 5122}; + std::map>> checkHadronDecays{ // sorted pdg of daughters + {411, {{-321, 211, 211}, {-313, 211}, {211, 333}}}, // D+ + {421, {{-321, 211}, {-321, 111, 211}}}, // D0 + {431, {{211, 333}, {-313, 321}}}, // Ds+ + {4122, {{-313, 2212}, {-321, 2224}, {211, 102134}, {-321, 211, 2212}}}, // Lc+ + {511, {{-411, 211}, {-413, 211}, {-411, 213}, {-411, 321}, {-413, 213}, {-413, 321}}}, // B0 + {521, {{-421, 211}, {-423, 211}, {-421, 213}, {-421, 321}}}, // B+ + {531, {{-431, 211}, {-433, 211}, {-431, 213}}}, // Bs0 + {5122, {{4122, -211}, {4122, -213}, {14122, -211}, {4124, -211}, {4212, -211}}} // Lb0 + }; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int nEventsMB{}, nEventsInj{}; + int nQuarks{}, nSignals{}, nSignalGoodDecay{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + + // check subgenerator information + if (eventHeader->hasInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID)) { + bool isValid = false; + int subGeneratorId = eventHeader->getInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID, isValid); + if (subGeneratorId == 0) { + nEventsMB++; + } else if (subGeneratorId == checkPdgQuark) { + nEventsInj++; + } + } + + for (auto &track : *tracks) { + auto pdg = track.GetPdgCode(); + if (std::abs(pdg) == checkPdgQuark) { + nQuarks++; + continue; + } + if (std::find(checkPdgHadron.begin(), checkPdgHadron.end(), std::abs(pdg)) != checkPdgHadron.end()) { // found signal + nSignals++; // count signal PDG + + std::vector pdgsDecay{}; + std::vector pdgsDecayAntiPart{}; + for (int j{track.getFirstDaughterTrackId()}; j <= track.getLastDaughterTrackId(); ++j) { + auto pdgDau = tracks->at(j).GetPdgCode(); + pdgsDecay.push_back(pdgDau); + if (pdgDau != 333) { // phi is antiparticle of itself + pdgsDecayAntiPart.push_back(-pdgDau); + } else { + pdgsDecayAntiPart.push_back(pdgDau); + } + } + + std::sort(pdgsDecay.begin(), pdgsDecay.end()); + std::sort(pdgsDecayAntiPart.begin(), pdgsDecayAntiPart.end()); + + for (auto &decay : checkHadronDecays[std::abs(pdg)]) { + if (pdgsDecay == decay || pdgsDecayAntiPart == decay) { + nSignalGoodDecay++; + break; + } + } + } + } + } + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + std::cout << "# MB events: " << nEventsMB << "\n"; + std::cout << Form("# events injected with %d quark pair: ", checkPdgQuark) << nEventsInj << "\n"; + std::cout << Form("# %d (anti)quarks: ", checkPdgQuark) << nQuarks << "\n"; + std::cout <<"# signal hadrons: " << nSignals << "\n"; + std::cout <<"# signal hadrons decaying in the correct channel: " << nSignalGoodDecay << "\n"; + + if (nEventsMB < nEvents * (1 - ratioTrigger) * 0.95 || nEventsMB > nEvents * (1 - ratioTrigger) * 1.05) { // we put some tolerance since the number of generated events is small + std::cerr << "Number of generated MB events different than expected\n"; + return 1; + } + if (nEventsInj < nEvents * ratioTrigger * 0.95 || nEventsInj > nEvents * ratioTrigger * 1.05) { + std::cerr << "Number of generated events injected with " << checkPdgQuark << " different than expected\n"; + return 1; + } + + if (nQuarks < nEvents * ratioTrigger) { // we expect anyway more because the same quark is repeated several time, after each gluon radiation + std::cerr << "Number of generated (anti)quarks " << checkPdgQuark << " lower than expected\n"; + return 1; + } + + float fracForcedDecays = nSignals ? float(nSignalGoodDecay) / nSignals : 0.0f; + float uncFracForcedDecays = nSignals ? std::sqrt(fracForcedDecays * (1 - fracForcedDecays) / nSignals) : 1.0f; + if (1 - fracForcedDecays > 0.3 + uncFracForcedDecays) { // we put some tolerance (e.g. due to oscillations which might change the final state) + std::cerr << "Fraction of signals decaying into the correct channel " << fracForcedDecays << " lower than expected\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_bbbar_BtoDK_gap5_Mode2.C b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_bbbar_BtoDK_gap5_Mode2.C new file mode 100644 index 000000000..07f70bfd7 --- /dev/null +++ b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_bbbar_BtoDK_gap5_Mode2.C @@ -0,0 +1,116 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + + int checkPdgQuark{5}; + float ratioTrigger = 1./5; // one event triggered out of 5 + + std::vector checkPdgHadron{411, 421, 431, 4122, 4132, 4232, 4332, 511, 521, 531, 5122}; + std::map>> checkHadronDecays{ // sorted pdg of daughters + {411, {{-321, 211, 211}, {-313, 211}, {211, 311}, {211, 333}}}, // D+ + {421, {{-321, 211}, {-321, 111, 211}}}, // D0 + {431, {{211, 333}, {-313, 321}}}, // Ds+ + {4122, {{-313, 2212}, {-321, 2224}, {211, 102134}, {-321, 211, 2212}, {311, 2212}}}, // Lc+ + {4132, {{211, 3312}}}, // Xic0 + {4232, {{-313, 2212}, {-321, 3324}, {211, 211, 3312}, {-321, 211, 2212}}}, // Xic+ + {4332, {{211, 3334}}}, // Omegac+ + {511, {{-411, 211}, {-411, 321}}}, // B0 + {521, {{-421, 211}, {-421, 321}}}, // B+ + {531, {{-431, 211}, {-431, 321}}}, // Bs0 + {5122, {{4122, -211}, {4122, -321}}} // Lb0 + }; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int nEventsMB{}, nEventsInj{}; + int nQuarks{}, nSignals{}, nSignalGoodDecay{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + + // check subgenerator information + if (eventHeader->hasInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID)) { + bool isValid = false; + int subGeneratorId = eventHeader->getInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID, isValid); + if (subGeneratorId == 0) { + nEventsMB++; + } else if (subGeneratorId == checkPdgQuark) { + nEventsInj++; + } + } + + for (auto &track : *tracks) { + auto pdg = track.GetPdgCode(); + if (std::abs(pdg) == checkPdgQuark) { + nQuarks++; + continue; + } + if (std::find(checkPdgHadron.begin(), checkPdgHadron.end(), std::abs(pdg)) != checkPdgHadron.end()) { // found signal + nSignals++; // count signal PDG + + std::vector pdgsDecay{}; + std::vector pdgsDecayAntiPart{}; + for (int j{track.getFirstDaughterTrackId()}; j <= track.getLastDaughterTrackId(); ++j) { + auto pdgDau = tracks->at(j).GetPdgCode(); + pdgsDecay.push_back(pdgDau); + if (pdgDau != 333) { // phi is antiparticle of itself + pdgsDecayAntiPart.push_back(-pdgDau); + } else { + pdgsDecayAntiPart.push_back(pdgDau); + } + } + + std::sort(pdgsDecay.begin(), pdgsDecay.end()); + std::sort(pdgsDecayAntiPart.begin(), pdgsDecayAntiPart.end()); + + for (auto &decay : checkHadronDecays[std::abs(pdg)]) { + if (pdgsDecay == decay || pdgsDecayAntiPart == decay) { + nSignalGoodDecay++; + break; + } + } + } + } + } + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + std::cout << "# MB events: " << nEventsMB << "\n"; + std::cout << Form("# events injected with %d quark pair: ", checkPdgQuark) << nEventsInj << "\n"; + std::cout << Form("# %d (anti)quarks: ", checkPdgQuark) << nQuarks << "\n"; + std::cout <<"# signal hadrons: " << nSignals << "\n"; + std::cout <<"# signal hadrons decaying in the correct channel: " << nSignalGoodDecay << "\n"; + + if (nEventsMB < nEvents * (1 - ratioTrigger) * 0.95 || nEventsMB > nEvents * (1 - ratioTrigger) * 1.05) { // we put some tolerance since the number of generated events is small + std::cerr << "Number of generated MB events different than expected\n"; + return 1; + } + if (nEventsInj < nEvents * ratioTrigger * 0.95 || nEventsInj > nEvents * ratioTrigger * 1.05) { + std::cerr << "Number of generated events injected with " << checkPdgQuark << " different than expected\n"; + return 1; + } + + if (nQuarks < nEvents * ratioTrigger) { // we expect anyway more because the same quark is repeated several time, after each gluon radiation + std::cerr << "Number of generated (anti)quarks " << checkPdgQuark << " lower than expected\n"; + return 1; + } + + float fracForcedDecays = nSignals ? float(nSignalGoodDecay) / nSignals : 0.0f; + float uncFracForcedDecays = nSignals ? std::sqrt(fracForcedDecays * (1 - fracForcedDecays) / nSignals) : 1.0f; + if (1 - fracForcedDecays > 0.15 + uncFracForcedDecays) { // we put some tolerance (e.g. due to oscillations which might change the final state) + std::cerr << "Fraction of signals decaying into the correct channel " << fracForcedDecays << " lower than expected\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_bbbar_Mode2_OmegaC.C b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_bbbar_Mode2_OmegaC.C new file mode 100644 index 000000000..d4c8fdf56 --- /dev/null +++ b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_bbbar_Mode2_OmegaC.C @@ -0,0 +1,106 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + + int checkPdgQuark{5}; + float ratioTrigger = 1.; // only enriched events + + std::vector checkPdgHadron{4332}; + std::map>> checkHadronDecays{ // sorted pdg of daughters + {4332, {{211, 3334}, {211, 3312}}}, // Omegac0 + }; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int nEventsMB{}, nEventsInj{}; + int nQuarks{}, nSignals{}, nSignalGoodDecay{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + + // check subgenerator information + if (eventHeader->hasInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID)) { + bool isValid = false; + int subGeneratorId = eventHeader->getInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID, isValid); + if (subGeneratorId == 0) { + nEventsMB++; + } else if (subGeneratorId == checkPdgQuark) { + nEventsInj++; + } + } + + for (auto &track : *tracks) { + auto pdg = track.GetPdgCode(); + if (std::abs(pdg) == checkPdgQuark) { + nQuarks++; + continue; + } + if (std::find(checkPdgHadron.begin(), checkPdgHadron.end(), std::abs(pdg)) != checkPdgHadron.end()) { // found signal + nSignals++; // count signal PDG + + std::vector pdgsDecay{}; + std::vector pdgsDecayAntiPart{}; + for (int j{track.getFirstDaughterTrackId()}; j <= track.getLastDaughterTrackId(); ++j) { + auto pdgDau = tracks->at(j).GetPdgCode(); + pdgsDecay.push_back(pdgDau); + if (pdgDau != 333) { // phi is antiparticle of itself + pdgsDecayAntiPart.push_back(-pdgDau); + } else { + pdgsDecayAntiPart.push_back(pdgDau); + } + } + + std::sort(pdgsDecay.begin(), pdgsDecay.end()); + std::sort(pdgsDecayAntiPart.begin(), pdgsDecayAntiPart.end()); + + for (auto &decay : checkHadronDecays[std::abs(pdg)]) { + if (pdgsDecay == decay || pdgsDecayAntiPart == decay) { + nSignalGoodDecay++; + break; + } + } + } + } + } + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + std::cout << "# MB events: " << nEventsMB << "\n"; + std::cout << Form("# events injected with %d quark pair: ", checkPdgQuark) << nEventsInj << "\n"; + std::cout << Form("# %d (anti)quarks: ", checkPdgQuark) << nQuarks << "\n"; + std::cout <<"# signal hadrons: " << nSignals << "\n"; + std::cout <<"# signal hadrons decaying in the correct channel: " << nSignalGoodDecay << "\n"; + + if (nEventsMB < nEvents * (1 - ratioTrigger) * 0.95 || nEventsMB > nEvents * (1 - ratioTrigger) * 1.05) { // we put some tolerance since the number of generated events is small + std::cerr << "Number of generated MB events different than expected\n"; + return 1; + } + if (nEventsInj < nEvents * ratioTrigger * 0.95 || nEventsInj > nEvents * ratioTrigger * 1.05) { + std::cerr << "Number of generated events injected with " << checkPdgQuark << " different than expected\n"; + return 1; + } + + if (nQuarks < 2 * nEvents * ratioTrigger) { // we expect anyway more because the same quark is repeated several time, after each gluon radiation + std::cerr << "Number of generated (anti)quarks " << checkPdgQuark << " lower than expected\n"; + return 1; + } + + float fracForcedDecays = nSignals ? float(nSignalGoodDecay) / nSignals : 0.0f; + float uncFracForcedDecays = nSignals ? std::sqrt(fracForcedDecays * (1 - fracForcedDecays) / nSignals) : 1.0f; + if (1 - fracForcedDecays > 0.15 + uncFracForcedDecays) { // we put some tolerance (e.g. due to oscillations which might change the final state) + std::cerr << "Fraction of signals decaying into the correct channel " << fracForcedDecays << " lower than expected\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_bbbar_Mode2_OmegaC_NoDecay.C b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_bbbar_Mode2_OmegaC_NoDecay.C new file mode 100644 index 000000000..70fecc536 --- /dev/null +++ b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_bbbar_Mode2_OmegaC_NoDecay.C @@ -0,0 +1,83 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + + int checkPdgQuark{5}; + + int checkPdgHadron{4332}; + int checkHadronDecays{0}; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int nEventsInj{}; + int nQuarks{}, nSignals{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + + // check subgenerator information + if (eventHeader->hasInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID)) { + bool isValid = false; + int subGeneratorId = eventHeader->getInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID, isValid); + if (subGeneratorId == checkPdgQuark) { + nEventsInj++; + } + } + + for (auto &track : *tracks) { + auto pdg = track.GetPdgCode(); + if (std::abs(pdg) == checkPdgQuark) { + nQuarks++; + continue; + } + if (std::abs(pdg) == checkPdgHadron) { // found signal + nSignals++; // count signal PDG + + for (int j{track.getFirstDaughterTrackId()}; j <= track.getLastDaughterTrackId(); ++j) { + if (j >= 0) { + checkHadronDecays += 1; + } + } + } + } + } + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + std::cout << Form("# events injected with %d quark pair: ", checkPdgQuark) << nEventsInj << "\n"; + std::cout << Form("# %d (anti)quarks: ", checkPdgQuark) << nQuarks << "\n"; + std::cout <<"# signal hadrons: " << nSignals << "\n"; + std::cout <<"# signal hadrons decaying : " << checkHadronDecays << "\n"; + + if (nEventsInj < nEvents) { + std::cerr << "Number of generated events with triggered events different than expected\n"; + return 1; + } + + if (nQuarks < nEvents) { // we expect anyway more because the same quark is repeated several time, after each gluon radiation + std::cerr << "Number of generated (anti)quarks " << checkPdgQuark << " lower than expected\n"; + return 1; + } + + if (nSignals < nEvents) { + std::cerr << "Number of generated signals lower than expected\n"; + return 1; + } + + if (checkHadronDecays > 0) { + std::cerr << "Decayed OmegaC, it should never decay\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_bbbar_Mode2_OmegaC_NoDecay_pp_ref.C b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_bbbar_Mode2_OmegaC_NoDecay_pp_ref.C new file mode 100644 index 000000000..70fecc536 --- /dev/null +++ b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_bbbar_Mode2_OmegaC_NoDecay_pp_ref.C @@ -0,0 +1,83 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + + int checkPdgQuark{5}; + + int checkPdgHadron{4332}; + int checkHadronDecays{0}; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int nEventsInj{}; + int nQuarks{}, nSignals{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + + // check subgenerator information + if (eventHeader->hasInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID)) { + bool isValid = false; + int subGeneratorId = eventHeader->getInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID, isValid); + if (subGeneratorId == checkPdgQuark) { + nEventsInj++; + } + } + + for (auto &track : *tracks) { + auto pdg = track.GetPdgCode(); + if (std::abs(pdg) == checkPdgQuark) { + nQuarks++; + continue; + } + if (std::abs(pdg) == checkPdgHadron) { // found signal + nSignals++; // count signal PDG + + for (int j{track.getFirstDaughterTrackId()}; j <= track.getLastDaughterTrackId(); ++j) { + if (j >= 0) { + checkHadronDecays += 1; + } + } + } + } + } + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + std::cout << Form("# events injected with %d quark pair: ", checkPdgQuark) << nEventsInj << "\n"; + std::cout << Form("# %d (anti)quarks: ", checkPdgQuark) << nQuarks << "\n"; + std::cout <<"# signal hadrons: " << nSignals << "\n"; + std::cout <<"# signal hadrons decaying : " << checkHadronDecays << "\n"; + + if (nEventsInj < nEvents) { + std::cerr << "Number of generated events with triggered events different than expected\n"; + return 1; + } + + if (nQuarks < nEvents) { // we expect anyway more because the same quark is repeated several time, after each gluon radiation + std::cerr << "Number of generated (anti)quarks " << checkPdgQuark << " lower than expected\n"; + return 1; + } + + if (nSignals < nEvents) { + std::cerr << "Number of generated signals lower than expected\n"; + return 1; + } + + if (checkHadronDecays > 0) { + std::cerr << "Decayed OmegaC, it should never decay\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_bbbar_Mode2_XiC0_XiCplus.C b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_bbbar_Mode2_XiC0_XiCplus.C new file mode 100644 index 000000000..27591c842 --- /dev/null +++ b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_bbbar_Mode2_XiC0_XiCplus.C @@ -0,0 +1,117 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + + int checkPdgQuark{5}; + float ratioTrigger = 1.; // only enriched events + + std::vector checkPdgHadron{4132, 4232}; + std::map>> checkHadronDecays{ // sorted pdg of daughters + {4132, { + {3312, 211}, // Xic0 -> Xi-, pi+ + }}, + {4232, { + {2212, -321, 211}, // Xic+ -> p, K-, pi+ + {2212, -313}, // Xic+ -> p, Kbar^*(892)0 + {3312, 211, 211}, // Xic+ -> Xi-, pi+, pi+ + {2212, 333}, // Xic+ -> p, phi(1020)0 + {3222, -211, 211}, // Xic+ -> Sigma+, pi-, pi+ + {3324, 211}, // Xic+ -> Xi(1530)0, pi+ + }}, + }; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int nEventsMB{}, nEventsInj{}; + int nQuarks{}, nSignals{}, nSignalGoodDecay{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + + // check subgenerator information + if (eventHeader->hasInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID)) { + bool isValid = false; + int subGeneratorId = eventHeader->getInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID, isValid); + if (subGeneratorId == 0) { + nEventsMB++; + } else if (subGeneratorId == checkPdgQuark) { + nEventsInj++; + } + } + + for (auto &track : *tracks) { + auto pdg = track.GetPdgCode(); + if (std::abs(pdg) == checkPdgQuark) { + nQuarks++; + continue; + } + if (std::find(checkPdgHadron.begin(), checkPdgHadron.end(), std::abs(pdg)) != checkPdgHadron.end()) { // found signal + nSignals++; // count signal PDG + + std::vector pdgsDecay{}; + std::vector pdgsDecayAntiPart{}; + for (int j{track.getFirstDaughterTrackId()}; j <= track.getLastDaughterTrackId(); ++j) { + auto pdgDau = tracks->at(j).GetPdgCode(); + pdgsDecay.push_back(pdgDau); + if (pdgDau != 333) { // phi is antiparticle of itself + pdgsDecayAntiPart.push_back(-pdgDau); + } else { + pdgsDecayAntiPart.push_back(pdgDau); + } + } + + std::sort(pdgsDecay.begin(), pdgsDecay.end()); + std::sort(pdgsDecayAntiPart.begin(), pdgsDecayAntiPart.end()); + + for (auto &decay : checkHadronDecays[std::abs(pdg)]) { + std::sort(decay.begin(), decay.end()); + if (pdgsDecay == decay || pdgsDecayAntiPart == decay) { + nSignalGoodDecay++; + break; + } + } + } + } + } + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + std::cout << "# MB events: " << nEventsMB << "\n"; + std::cout << Form("# events injected with %d quark pair: ", checkPdgQuark) << nEventsInj << "\n"; + std::cout << Form("# %d (anti)quarks: ", checkPdgQuark) << nQuarks << "\n"; + std::cout <<"# signal hadrons: " << nSignals << "\n"; + std::cout <<"# signal hadrons decaying in the correct channel: " << nSignalGoodDecay << "\n"; + + if (nEventsMB < nEvents * (1 - ratioTrigger) * 0.95 || nEventsMB > nEvents * (1 - ratioTrigger) * 1.05) { // we put some tolerance since the number of generated events is small + std::cerr << "Number of generated MB events different than expected\n"; + return 1; + } + if (nEventsInj < nEvents * ratioTrigger * 0.95 || nEventsInj > nEvents * ratioTrigger * 1.05) { + std::cerr << "Number of generated events injected with " << checkPdgQuark << " different than expected\n"; + return 1; + } + + if (nQuarks < 2 * nEvents * ratioTrigger) { // we expect anyway more because the same quark is repeated several time, after each gluon radiation + std::cerr << "Number of generated (anti)quarks " << checkPdgQuark << " lower than expected\n"; + return 1; + } + + float fracForcedDecays = nSignals ? float(nSignalGoodDecay) / nSignals : 0.0f; + float uncFracForcedDecays = nSignals ? std::sqrt(fracForcedDecays * (1 - fracForcedDecays) / nSignals) : 1.0f; + if (1 - fracForcedDecays > 0.15 + uncFracForcedDecays) { // we put some tolerance (e.g. due to oscillations which might change the final state) + std::cerr << "Fraction of signals decaying into the correct channel " << fracForcedDecays << " lower than expected\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_bbbar_Mode2_XiC_NoDecay.C b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_bbbar_Mode2_XiC_NoDecay.C new file mode 100644 index 000000000..5f406097e --- /dev/null +++ b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_bbbar_Mode2_XiC_NoDecay.C @@ -0,0 +1,83 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + + int checkPdgQuark{5}; + + int checkPdgHadron{4132}; + int checkHadronDecays{0}; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int nEventsInj{}; + int nQuarks{}, nSignals{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + + // check subgenerator information + if (eventHeader->hasInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID)) { + bool isValid = false; + int subGeneratorId = eventHeader->getInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID, isValid); + if (subGeneratorId == checkPdgQuark) { + nEventsInj++; + } + } + + for (auto &track : *tracks) { + auto pdg = track.GetPdgCode(); + if (std::abs(pdg) == checkPdgQuark) { + nQuarks++; + continue; + } + if (std::abs(pdg) == checkPdgHadron) { // found signal + nSignals++; // count signal PDG + + for (int j{track.getFirstDaughterTrackId()}; j <= track.getLastDaughterTrackId(); ++j) { + if (j >= 0) { + checkHadronDecays += 1; + } + } + } + } + } + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + std::cout << Form("# events injected with %d quark pair: ", checkPdgQuark) << nEventsInj << "\n"; + std::cout << Form("# %d (anti)quarks: ", checkPdgQuark) << nQuarks << "\n"; + std::cout <<"# signal hadrons: " << nSignals << "\n"; + std::cout <<"# signal hadrons decaying : " << checkHadronDecays << "\n"; + + if (nEventsInj < nEvents) { + std::cerr << "Number of generated events with triggered events different than expected\n"; + return 1; + } + + if (nQuarks < nEvents) { // we expect anyway more because the same quark is repeated several time, after each gluon radiation + std::cerr << "Number of generated (anti)quarks " << checkPdgQuark << " lower than expected\n"; + return 1; + } + + if (nSignals < nEvents) { + std::cerr << "Number of generated signals lower than expected\n"; + return 1; + } + + if (checkHadronDecays > 0) { + std::cerr << "Decayed XiC, it should never decay\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_bbbar_Mode2_XiC_NoDecay_pp_ref.C b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_bbbar_Mode2_XiC_NoDecay_pp_ref.C new file mode 100644 index 000000000..5f406097e --- /dev/null +++ b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_bbbar_Mode2_XiC_NoDecay_pp_ref.C @@ -0,0 +1,83 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + + int checkPdgQuark{5}; + + int checkPdgHadron{4132}; + int checkHadronDecays{0}; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int nEventsInj{}; + int nQuarks{}, nSignals{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + + // check subgenerator information + if (eventHeader->hasInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID)) { + bool isValid = false; + int subGeneratorId = eventHeader->getInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID, isValid); + if (subGeneratorId == checkPdgQuark) { + nEventsInj++; + } + } + + for (auto &track : *tracks) { + auto pdg = track.GetPdgCode(); + if (std::abs(pdg) == checkPdgQuark) { + nQuarks++; + continue; + } + if (std::abs(pdg) == checkPdgHadron) { // found signal + nSignals++; // count signal PDG + + for (int j{track.getFirstDaughterTrackId()}; j <= track.getLastDaughterTrackId(); ++j) { + if (j >= 0) { + checkHadronDecays += 1; + } + } + } + } + } + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + std::cout << Form("# events injected with %d quark pair: ", checkPdgQuark) << nEventsInj << "\n"; + std::cout << Form("# %d (anti)quarks: ", checkPdgQuark) << nQuarks << "\n"; + std::cout <<"# signal hadrons: " << nSignals << "\n"; + std::cout <<"# signal hadrons decaying : " << checkHadronDecays << "\n"; + + if (nEventsInj < nEvents) { + std::cerr << "Number of generated events with triggered events different than expected\n"; + return 1; + } + + if (nQuarks < nEvents) { // we expect anyway more because the same quark is repeated several time, after each gluon radiation + std::cerr << "Number of generated (anti)quarks " << checkPdgQuark << " lower than expected\n"; + return 1; + } + + if (nSignals < nEvents) { + std::cerr << "Number of generated signals lower than expected\n"; + return 1; + } + + if (checkHadronDecays > 0) { + std::cerr << "Decayed XiC, it should never decay\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_bbbar_Mode2_XiCplus_NoDecay.C b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_bbbar_Mode2_XiCplus_NoDecay.C new file mode 100644 index 000000000..3e245d287 --- /dev/null +++ b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_bbbar_Mode2_XiCplus_NoDecay.C @@ -0,0 +1,83 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + + int checkPdgQuark = 5; + + int checkPdgHadron{4232}; + int checkHadronDecays{0}; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int nEventsInj{}; + int nQuarks{}, nSignals{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + + // check subgenerator information + if (eventHeader->hasInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID)) { + bool isValid = false; + int subGeneratorId = eventHeader->getInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID, isValid); + if (subGeneratorId == checkPdgQuark) { + nEventsInj++; + } + } + + for (auto &track : *tracks) { + auto pdg = track.GetPdgCode(); + if (std::abs(pdg) == checkPdgQuark) { + nQuarks++; + continue; + } + if (std::abs(pdg) == checkPdgHadron) { // found signal + nSignals++; // count signal PDG + + for (int j{track.getFirstDaughterTrackId()}; j <= track.getLastDaughterTrackId(); ++j) { + if (j >= 0) { + checkHadronDecays += 1; + } + } + } + } + } + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + std::cout << Form("# events injected with %d quark pair: ", checkPdgQuark) << nEventsInj << "\n"; + std::cout << Form("# %d (anti)quarks: ", checkPdgQuark) << nQuarks << "\n"; + std::cout <<"# signal hadrons: " << nSignals << "\n"; + std::cout <<"# signal hadrons decaying : " << checkHadronDecays << "\n"; + + if (nEventsInj < nEvents) { + std::cerr << "Number of generated events with triggered events different than expected\n"; + return 1; + } + + if (nQuarks < nEvents) { // we expect anyway more because the same quark is repeated several time, after each gluon radiation + std::cerr << "Number of generated (anti)quarks " << checkPdgQuark << " lower than expected\n"; + return 1; + } + + if (nSignals < nEvents) { + std::cerr << "Number of generated signals lower than expected\n"; + return 1; + } + + if (checkHadronDecays > 0) { + std::cerr << "Decayed XiC, it should never decay\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_bbbar_Mode2_XiCplus_NoDecay_pp_ref.C b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_bbbar_Mode2_XiCplus_NoDecay_pp_ref.C new file mode 100644 index 000000000..4ec473f52 --- /dev/null +++ b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_bbbar_Mode2_XiCplus_NoDecay_pp_ref.C @@ -0,0 +1,83 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + + int checkPdgQuark{5}; + + int checkPdgHadron{4232}; + int checkHadronDecays{0}; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int nEventsInj{}; + int nQuarks{}, nSignals{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + + // check subgenerator information + if (eventHeader->hasInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID)) { + bool isValid = false; + int subGeneratorId = eventHeader->getInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID, isValid); + if (subGeneratorId == checkPdgQuark) { + nEventsInj++; + } + } + + for (auto &track : *tracks) { + auto pdg = track.GetPdgCode(); + if (std::abs(pdg) == checkPdgQuark) { + nQuarks++; + continue; + } + if (std::abs(pdg) == checkPdgHadron) { // found signal + nSignals++; // count signal PDG + + for (int j{track.getFirstDaughterTrackId()}; j <= track.getLastDaughterTrackId(); ++j) { + if (j >= 0) { + checkHadronDecays += 1; + } + } + } + } + } + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + std::cout << Form("# events injected with %d quark pair: ", checkPdgQuark) << nEventsInj << "\n"; + std::cout << Form("# %d (anti)quarks: ", checkPdgQuark) << nQuarks << "\n"; + std::cout <<"# signal hadrons: " << nSignals << "\n"; + std::cout <<"# signal hadrons decaying : " << checkHadronDecays << "\n"; + + if (nEventsInj < nEvents) { + std::cerr << "Number of generated events with triggered events different than expected\n"; + return 1; + } + + if (nQuarks < nEvents) { // we expect anyway more because the same quark is repeated several time, after each gluon radiation + std::cerr << "Number of generated (anti)quarks " << checkPdgQuark << " lower than expected\n"; + return 1; + } + + if (nSignals < nEvents) { + std::cerr << "Number of generated signals lower than expected\n"; + return 1; + } + + if (checkHadronDecays > 0) { + std::cerr << "Decayed XiCplus, it should never decay\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_bbbar_gap5.C b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_bbbar_gap5.C new file mode 100644 index 000000000..9d4e1574e --- /dev/null +++ b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_bbbar_gap5.C @@ -0,0 +1,113 @@ +int External() +{ + std::string path{"o2sim_Kine.root"}; + + int checkPdgQuark{5}; + float ratioTrigger = 1./5; // one event triggered out of 5 + + std::vector checkPdgHadron{411, 421, 431, 4122, 4132, 4232, 4332}; + std::map>> checkHadronDecays{ // sorted pdg of daughters + {411, {{-321, 211, 211}, {-313, 211}, {211, 311}, {211, 333}}}, // D+ + {421, {{-321, 211}, {-321, 111, 211}}}, // D0 + {431, {{211, 333}, {-313, 321}}}, // Ds+ + {4122, {{-313, 2212}, {-321, 2224}, {211, 102134}, {-321, 211, 2212}, {311, 2212}}}, // Lc+ + {4132, {{211, 3312}}}, // Xic0 + {4232, {{-313, 2212}, {-321, 3324}, {211, 211, 3312}, {-321, 211, 2212}}}, // Xic+ + {4332, {{211, 3334}}} // Omegac+ + }; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int nEventsMB{}, nEventsInj{}; + int nQuarks{}, nSignals{}, nSignalGoodDecay{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + + // check subgenerator information + if (eventHeader->hasInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID)) { + bool isValid = false; + int subGeneratorId = eventHeader->getInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID, isValid); + if (subGeneratorId == 0) { + nEventsMB++; + } else if (subGeneratorId == checkPdgQuark) { + nEventsInj++; + } + } + + for (auto &track : *tracks) { + auto pdg = track.GetPdgCode(); + if (std::abs(pdg) == checkPdgQuark) { + nQuarks++; + continue; + } + if (std::find(checkPdgHadron.begin(), checkPdgHadron.end(), std::abs(pdg)) != checkPdgHadron.end()) { // found signal + nSignals++; // count signal PDG + + std::vector pdgsDecay{}; + std::vector pdgsDecayAntiPart{}; + for (int j{track.getFirstDaughterTrackId()}; j <= track.getLastDaughterTrackId(); ++j) { + auto pdgDau = tracks->at(j).GetPdgCode(); + pdgsDecay.push_back(pdgDau); + if (pdgDau != 333) { // phi is antiparticle of itself + pdgsDecayAntiPart.push_back(-pdgDau); + } else { + pdgsDecayAntiPart.push_back(pdgDau); + } + } + + std::sort(pdgsDecay.begin(), pdgsDecay.end()); + std::sort(pdgsDecayAntiPart.begin(), pdgsDecayAntiPart.end()); + + for (auto &decay : checkHadronDecays[std::abs(pdg)]) { + if (pdgsDecay == decay || pdgsDecayAntiPart == decay) { + nSignalGoodDecay++; + break; + } + } + } + } + } + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + std::cout << "# MB events: " << nEventsMB << "\n"; + std::cout << Form("# events injected with %d quark pair: ", checkPdgQuark) << nEventsInj << "\n"; + std::cout << Form("# %d (anti)quarks: ", checkPdgQuark) << nQuarks << "\n"; + std::cout <<"# signal hadrons: " << nSignals << "\n"; + std::cout <<"# signal hadrons decaying in the correct channel: " << nSignalGoodDecay << "\n"; + + if (nEventsMB < nEvents * (1 - ratioTrigger) * 0.95 || nEventsMB > nEvents * (1 - ratioTrigger) * 1.05) { // we put some tolerance since the number of generated events is small + std::cerr << "Number of generated MB events different than expected\n"; + return 1; + } + if (nEventsInj < nEvents * ratioTrigger * 0.95 || nEventsInj > nEvents * ratioTrigger * 1.05) { + std::cerr << "Number of generated events injected with " << checkPdgQuark << " different than expected\n"; + return 1; + } + + if (nQuarks < 2 * nEvents * ratioTrigger) { // we expect anyway more because the same quark is repeated several time, after each gluon radiation + std::cerr << "Number of generated (anti)quarks " << checkPdgQuark << " lower than expected\n"; + return 1; + } + + float fracForcedDecays = nSignals ? float(nSignalGoodDecay) / nSignals : 0.0f; + float uncFracForcedDecays = nSignals ? std::sqrt(fracForcedDecays * (1 - fracForcedDecays) / nSignals) : 1.0f; + if (1 - fracForcedDecays > 0.15 + uncFracForcedDecays) { // we put some tolerance (e.g. due to oscillations which might change the final state) + std::cerr << "Fraction of signals decaying into the correct channel " << fracForcedDecays << " lower than expected\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar3_and_bbbar_PbPb.C b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar3_and_bbbar_PbPb.C new file mode 100644 index 000000000..c73693d87 --- /dev/null +++ b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar3_and_bbbar_PbPb.C @@ -0,0 +1,192 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + //std::string path{"tf1/sgn_1_Kine.root"}; + + int checkPdgQuarkOne{4}; + int checkPdgQuarkTwo{5}; + float ratioTrigger = 1.; // one event triggered out of 1 + + std::vector checkPdgHadron{411, 421, 431, 4122, 4132, 4232, 4332}; + std::map>> checkHadronDecays{ // sorted pdg of daughters + {421, { + {-321, 211}, // D0 -> K-, pi+ + {-321, 211, 111}, // D0 -> K-, pi+, pi0 + {213, -321}, // D0 -> rho(770)+, K- + {-313, 111}, // D0 -> Kbar^*(892)0, pi0 + {-323, 211}, // D0 -> K^*(892)-, pi+ + {-211, 211}, // D0 -> pi-, pi+ + {213, -211}, // D0 -> rho(770)+, pi- + {-211, 211, 111}, // D0 -> pi-, pi+, pi0 + {-321, 321}, // D0 -> K-, K+ + }}, + + {411, { + {-321, 211, 211}, // D+ -> K-, pi+, pi+ + {-10311, 211}, // D+ -> Kbar0^*(1430)0, pi+ + {-313, 211}, // D+ -> Kbar^*(892)0, pi+ + {-321, 211, 211, 111}, // D+ -> K-, pi+, pi+, pi0 + {333, 211}, // D+ -> phi(1020)0, pi+ + {-313, 321}, // D+ -> Kbar^*(892)0, K+ + {-10311, 321}, // D+ -> Kbar0^*(1430)0, K+ + {-321, 321, 211}, // D+ -> K-, K+, pi+ + {113, 211}, // D+ -> rho(770)0, pi+ + {225, 211}, // D+ -> f2(1270)0, pi+ + {-211, 211, 211}, // D+ -> pi-, pi+, pi+ + }}, + + {431, { + {333, 211}, // Ds+ -> phi(1020)0, pi+ + {-313, 321}, // Ds+ -> Kbar^*(892)0, K+ + {333, 213}, // Ds+ -> phi(1020)0, rho(770)+ + {113, 211}, // Ds+ -> rho(770)0, pi+ + {225, 211}, // Ds+ -> f2(1270)0, pi+ + {-211, 211, 211}, // Ds+ -> pi-, pi+, pi+ + {313, 211}, // Ds+ -> K^*(892)0, pi+ + {10221, 321}, // Ds+ -> f0(1370)0, K+ + {113, 321}, // Ds+ -> rho(770)0, K+ + {-211, 321, 211}, // Ds+ -> pi-, K+, pi+ + {221, 211}, // Ds+ -> eta, pi+ + }}, + + {4122, { + {2212, -321, 211}, // Lambdac+ -> p, K-, pi+ + {2212, -313}, // Lambdac+ -> p, Kbar^*(892)0 + {2224, -321}, // Lambdac+ -> Delta(1232)++, K- + {102134, 211}, // Lambdac+ -> 102134, pi+ + {2212, 310}, // Lambdac+ -> p, K0s + {2212, -321, 211, 111}, // Lambdac+ -> p, K-, pi+, pi0 + {2212, -211, 211}, // Lambdac+ -> p, pi-, pi+ + {2212, 333}, // Lambdac+ -> p, phi(1020)0 + }}, + + {4232, { + {2212, -321, 211}, // Xic+ -> p, K-, pi+ + {2212, -313}, // Xic+ -> p, Kbar^*(892)0 + {3312, 211, 211}, // Xic+ -> Xi-, pi+, pi+ + {2212, 333}, // Xic+ -> p, phi(1020)0 + {3222, -211, 211}, // Xic+ -> Sigma+, pi-, pi+ + {3324, 211}, // Xic+ -> Xi(1530)0, pi+ + }}, + + {4132, { + {3312, 211}, // Xic0 -> Xi-, pi+ + }}, + + {4332, { + {3334, 211}, // Omegac0 -> Omega-, pi+ + {3312, 211}, // Omegac0 -> Xi-, pi+ + }}, + }; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int nEventsMB{}, nEventsInjOne{}, nEventsInjTwo{}; + int nQuarksOne{}, nQuarksTwo{}, nSignals{}, nSignalGoodDecay{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + + // check subgenerator information + //if (eventHeader->hasInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID)) { + // bool isValid = false; + // int subGeneratorId = eventHeader->getInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID, isValid); + // if (subGeneratorId == 0) { + // nEventsMB++; + // } else if (subGeneratorId == checkPdgQuarkOne) { + // nEventsInjOne++; + // } else if (subGeneratorId == checkPdgQuarkTwo) { + // nEventsInjTwo++; + // } + //} + + for (auto &track : *tracks) { + auto pdg = track.GetPdgCode(); + if (std::abs(pdg) == checkPdgQuarkOne) { + nQuarksOne++; + continue; + } + if (std::abs(pdg) == checkPdgQuarkTwo) { + nQuarksTwo++; + continue; + } + if (std::find(checkPdgHadron.begin(), checkPdgHadron.end(), std::abs(pdg)) != checkPdgHadron.end()) { // found signal + nSignals++; // count signal PDG + + std::vector pdgsDecay{}; + std::vector pdgsDecayAntiPart{}; + for (int j{track.getFirstDaughterTrackId()}; j <= track.getLastDaughterTrackId(); ++j) { + auto pdgDau = tracks->at(j).GetPdgCode(); + pdgsDecay.push_back(pdgDau); + if (pdgDau != 333 && pdgDau != 111 && pdgDau != 221 && pdgDau != 113 && pdgDau != 225) { // phi is antiparticle of itself + pdgsDecayAntiPart.push_back(-pdgDau); + } else { + pdgsDecayAntiPart.push_back(pdgDau); + } + } + + std::sort(pdgsDecay.begin(), pdgsDecay.end()); + std::sort(pdgsDecayAntiPart.begin(), pdgsDecayAntiPart.end()); + + for (auto &decay : checkHadronDecays[std::abs(pdg)]) { + std::sort(decay.begin(), decay.end()); + if (pdgsDecay == decay || pdgsDecayAntiPart == decay) { + nSignalGoodDecay++; + break; + } + } + } + } + } + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + //std::cout << "# MB events: " << nEventsMB << "\n"; + //std::cout << Form("# events injected with %d quark pair: ", checkPdgQuarkOne) << nEventsInjOne << "\n"; + //std::cout << Form("# events injected with %d quark pair: ", checkPdgQuarkTwo) << nEventsInjTwo << "\n"; + std::cout << Form("# %d (anti)quarks: ", checkPdgQuarkOne) << nQuarksOne << "\n"; + std::cout << Form("# %d (anti)quarks: ", checkPdgQuarkTwo) << nQuarksTwo << "\n"; + std::cout <<"# signal hadrons: " << nSignals << "\n"; + std::cout <<"# signal hadrons decaying in the correct channel: " << nSignalGoodDecay << "\n"; + + //if (nEventsMB < nEvents * (1 - ratioTrigger) * 0.95 || nEventsMB > nEvents * (1 - ratioTrigger) * 1.05) { // we put some tolerance since the number of generated events is small + // std::cerr << "Number of generated MB events different than expected\n"; + // return 1; + //} + //if (nEventsInjOne < nEvents * ratioTrigger * 0.5 * 0.95 || nEventsInjOne > nEvents * ratioTrigger * 0.5 * 1.05) { + // std::cerr << "Number of generated events injected with " << checkPdgQuarkOne << " different than expected\n"; + // return 1; + //} + //if (nEventsInjTwo < nEvents * ratioTrigger * 0.5 * 0.95 || nEventsInjTwo > nEvents * ratioTrigger * 0.5 * 1.05) { + // std::cerr << "Number of generated events injected with " << checkPdgQuarkTwo << " different than expected\n"; + // return 1; + //} + + if (nQuarksOne < nEvents * ratioTrigger) { // we expect anyway more because the same quark is repeated several time, after each gluon radiation + std::cerr << "Number of generated (anti)quarks " << checkPdgQuarkOne << " lower than expected\n"; + return 1; + } + if (nQuarksTwo < nEvents * ratioTrigger) { // we expect anyway more because the same quark is repeated several time, after each gluon radiation + std::cerr << "Number of generated (anti)quarks " << checkPdgQuarkTwo << " lower than expected\n"; + return 1; + } + + float fracForcedDecays = nSignals ? float(nSignalGoodDecay) / nSignals : 0.0f; + float uncFracForcedDecays = nSignals ? std::sqrt(fracForcedDecays * (1 - fracForcedDecays) / nSignals) : 1.0f; + if (1 - fracForcedDecays > 0.15 + uncFracForcedDecays) { // we put some tolerance (e.g. due to oscillations which might change the final state) + std::cerr << "Fraction of signals decaying into the correct channel " << fracForcedDecays << " lower than expected\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_Mode2_OmegaC.C b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_Mode2_OmegaC.C new file mode 100644 index 000000000..5734e02da --- /dev/null +++ b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_Mode2_OmegaC.C @@ -0,0 +1,106 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + + int checkPdgQuark{4}; + float ratioTrigger = 1.; // only enriched events + + std::vector checkPdgHadron{4332}; + std::map>> checkHadronDecays{ // sorted pdg of daughters + {4332, {{211, 3334}, {211, 3312}}}, // Omegac0 + }; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int nEventsMB{}, nEventsInj{}; + int nQuarks{}, nSignals{}, nSignalGoodDecay{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + + // check subgenerator information + if (eventHeader->hasInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID)) { + bool isValid = false; + int subGeneratorId = eventHeader->getInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID, isValid); + if (subGeneratorId == 0) { + nEventsMB++; + } else if (subGeneratorId == checkPdgQuark) { + nEventsInj++; + } + } + + for (auto &track : *tracks) { + auto pdg = track.GetPdgCode(); + if (std::abs(pdg) == checkPdgQuark) { + nQuarks++; + continue; + } + if (std::find(checkPdgHadron.begin(), checkPdgHadron.end(), std::abs(pdg)) != checkPdgHadron.end()) { // found signal + nSignals++; // count signal PDG + + std::vector pdgsDecay{}; + std::vector pdgsDecayAntiPart{}; + for (int j{track.getFirstDaughterTrackId()}; j <= track.getLastDaughterTrackId(); ++j) { + auto pdgDau = tracks->at(j).GetPdgCode(); + pdgsDecay.push_back(pdgDau); + if (pdgDau != 333) { // phi is antiparticle of itself + pdgsDecayAntiPart.push_back(-pdgDau); + } else { + pdgsDecayAntiPart.push_back(pdgDau); + } + } + + std::sort(pdgsDecay.begin(), pdgsDecay.end()); + std::sort(pdgsDecayAntiPart.begin(), pdgsDecayAntiPart.end()); + + for (auto &decay : checkHadronDecays[std::abs(pdg)]) { + if (pdgsDecay == decay || pdgsDecayAntiPart == decay) { + nSignalGoodDecay++; + break; + } + } + } + } + } + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + std::cout << "# MB events: " << nEventsMB << "\n"; + std::cout << Form("# events injected with %d quark pair: ", checkPdgQuark) << nEventsInj << "\n"; + std::cout << Form("# %d (anti)quarks: ", checkPdgQuark) << nQuarks << "\n"; + std::cout <<"# signal hadrons: " << nSignals << "\n"; + std::cout <<"# signal hadrons decaying in the correct channel: " << nSignalGoodDecay << "\n"; + + if (nEventsMB < nEvents * (1 - ratioTrigger) * 0.95 || nEventsMB > nEvents * (1 - ratioTrigger) * 1.05) { // we put some tolerance since the number of generated events is small + std::cerr << "Number of generated MB events different than expected\n"; + return 1; + } + if (nEventsInj < nEvents * ratioTrigger * 0.95 || nEventsInj > nEvents * ratioTrigger * 1.05) { + std::cerr << "Number of generated events injected with " << checkPdgQuark << " different than expected\n"; + return 1; + } + + if (nQuarks < 2 * nEvents * ratioTrigger) { // we expect anyway more because the same quark is repeated several time, after each gluon radiation + std::cerr << "Number of generated (anti)quarks " << checkPdgQuark << " lower than expected\n"; + return 1; + } + + float fracForcedDecays = nSignals ? float(nSignalGoodDecay) / nSignals : 0.0f; + float uncFracForcedDecays = nSignals ? std::sqrt(fracForcedDecays * (1 - fracForcedDecays) / nSignals) : 1.0f; + if (1 - fracForcedDecays > 0.15 + uncFracForcedDecays) { // we put some tolerance (e.g. due to oscillations which might change the final state) + std::cerr << "Fraction of signals decaying into the correct channel " << fracForcedDecays << " lower than expected\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_Mode2_OmegaC_NoDecay.C b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_Mode2_OmegaC_NoDecay.C new file mode 100644 index 000000000..58ef8cbf1 --- /dev/null +++ b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_Mode2_OmegaC_NoDecay.C @@ -0,0 +1,83 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + + int checkPdgQuark{4}; + + int checkPdgHadron{4332}; + int checkHadronDecays{0}; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int nEventsInj{}; + int nQuarks{}, nSignals{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + + // check subgenerator information + if (eventHeader->hasInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID)) { + bool isValid = false; + int subGeneratorId = eventHeader->getInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID, isValid); + if (subGeneratorId == checkPdgQuark) { + nEventsInj++; + } + } + + for (auto &track : *tracks) { + auto pdg = track.GetPdgCode(); + if (std::abs(pdg) == checkPdgQuark) { + nQuarks++; + continue; + } + if (std::abs(pdg) == checkPdgHadron) { // found signal + nSignals++; // count signal PDG + + for (int j{track.getFirstDaughterTrackId()}; j <= track.getLastDaughterTrackId(); ++j) { + if (j >= 0) { + checkHadronDecays += 1; + } + } + } + } + } + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + std::cout << Form("# events injected with %d quark pair: ", checkPdgQuark) << nEventsInj << "\n"; + std::cout << Form("# %d (anti)quarks: ", checkPdgQuark) << nQuarks << "\n"; + std::cout <<"# signal hadrons: " << nSignals << "\n"; + std::cout <<"# signal hadrons decaying : " << checkHadronDecays << "\n"; + + if (nEventsInj < nEvents) { + std::cerr << "Number of generated events with triggered events different than expected\n"; + return 1; + } + + if (nQuarks < nEvents) { // we expect anyway more because the same quark is repeated several time, after each gluon radiation + std::cerr << "Number of generated (anti)quarks " << checkPdgQuark << " lower than expected\n"; + return 1; + } + + if (nSignals < nEvents) { + std::cerr << "Number of generated signals lower than expected\n"; + return 1; + } + + if (checkHadronDecays > 0) { + std::cerr << "Decayed OmegaC, it should never decay\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_Mode2_OmegaC_NoDecay_pp_ref.C b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_Mode2_OmegaC_NoDecay_pp_ref.C new file mode 100644 index 000000000..81ea759b2 --- /dev/null +++ b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_Mode2_OmegaC_NoDecay_pp_ref.C @@ -0,0 +1,84 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + + int checkPdgQuark{4}; + + int checkPdgHadron{4332}; + int checkHadronDecays{0}; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int nEventsInj{}; + int nQuarks{}, nSignals{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + + // check subgenerator information + if (eventHeader->hasInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID)) { + bool isValid = false; + int subGeneratorId = eventHeader->getInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID, isValid); + if (subGeneratorId == checkPdgQuark) { + nEventsInj++; + } + } + + for (auto &track : *tracks) { + auto pdg = track.GetPdgCode(); + if (std::abs(pdg) == checkPdgQuark) { + nQuarks++; + continue; + } + if (std::abs(pdg) == checkPdgHadron) { // found signal + nSignals++; // count signal PDG + + for (int j{track.getFirstDaughterTrackId()}; j <= track.getLastDaughterTrackId(); ++j) { + if (j >= 0) { + checkHadronDecays += 1; + } + } + } + } + } + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + std::cout << Form("# events injected with %d quark pair: ", checkPdgQuark) << nEventsInj << "\n"; + std::cout << Form("# %d (anti)quarks: ", checkPdgQuark) << nQuarks << "\n"; + std::cout <<"# signal hadrons: " << nSignals << "\n"; + std::cout <<"# signal hadrons decaying : " << checkHadronDecays << "\n"; + + if (nEventsInj < nEvents) { + std::cerr << "Number of generated events with triggered events different than expected\n"; + return 1; + } + + if (nQuarks < nEvents) { // we expect anyway more because the same quark is repeated several time, after each gluon radiation + std::cerr << "Number of generated (anti)quarks " << checkPdgQuark << " lower than expected\n"; + return 1; + } + + if (nSignals < nEvents) { + std::cerr << "Number of generated signals lower than expected\n"; + return 1; + } + + if (checkHadronDecays > 0) { + std::cerr << "Decayed OmegaC, it should never decay\n"; + return 1; + } + + return 0; +} + diff --git a/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_Mode2_XiC0_XiCplus.C b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_Mode2_XiC0_XiCplus.C new file mode 100644 index 000000000..17987c9a0 --- /dev/null +++ b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_Mode2_XiC0_XiCplus.C @@ -0,0 +1,117 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + + int checkPdgQuark{4}; + float ratioTrigger = 1.; // only enriched events + + std::vector checkPdgHadron{4132, 4232}; + std::map>> checkHadronDecays{ // sorted pdg of daughters + {4132, { + {3312, 211}, // Xic0 -> Xi-, pi+ + }}, + {4232, { + {2212, -321, 211}, // Xic+ -> p, K-, pi+ + {2212, -313}, // Xic+ -> p, Kbar^*(892)0 + {3312, 211, 211}, // Xic+ -> Xi-, pi+, pi+ + {2212, 333}, // Xic+ -> p, phi(1020)0 + {3222, -211, 211}, // Xic+ -> Sigma+, pi-, pi+ + {3324, 211}, // Xic+ -> Xi(1530)0, pi+ + }}, + }; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int nEventsMB{}, nEventsInj{}; + int nQuarks{}, nSignals{}, nSignalGoodDecay{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + + // check subgenerator information + if (eventHeader->hasInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID)) { + bool isValid = false; + int subGeneratorId = eventHeader->getInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID, isValid); + if (subGeneratorId == 0) { + nEventsMB++; + } else if (subGeneratorId == checkPdgQuark) { + nEventsInj++; + } + } + + for (auto &track : *tracks) { + auto pdg = track.GetPdgCode(); + if (std::abs(pdg) == checkPdgQuark) { + nQuarks++; + continue; + } + if (std::find(checkPdgHadron.begin(), checkPdgHadron.end(), std::abs(pdg)) != checkPdgHadron.end()) { // found signal + nSignals++; // count signal PDG + + std::vector pdgsDecay{}; + std::vector pdgsDecayAntiPart{}; + for (int j{track.getFirstDaughterTrackId()}; j <= track.getLastDaughterTrackId(); ++j) { + auto pdgDau = tracks->at(j).GetPdgCode(); + pdgsDecay.push_back(pdgDau); + if (pdgDau != 333) { // phi is antiparticle of itself + pdgsDecayAntiPart.push_back(-pdgDau); + } else { + pdgsDecayAntiPart.push_back(pdgDau); + } + } + + std::sort(pdgsDecay.begin(), pdgsDecay.end()); + std::sort(pdgsDecayAntiPart.begin(), pdgsDecayAntiPart.end()); + + for (auto &decay : checkHadronDecays[std::abs(pdg)]) { + std::sort(decay.begin(), decay.end()); + if (pdgsDecay == decay || pdgsDecayAntiPart == decay) { + nSignalGoodDecay++; + break; + } + } + } + } + } + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + std::cout << "# MB events: " << nEventsMB << "\n"; + std::cout << Form("# events injected with %d quark pair: ", checkPdgQuark) << nEventsInj << "\n"; + std::cout << Form("# %d (anti)quarks: ", checkPdgQuark) << nQuarks << "\n"; + std::cout <<"# signal hadrons: " << nSignals << "\n"; + std::cout <<"# signal hadrons decaying in the correct channel: " << nSignalGoodDecay << "\n"; + + if (nEventsMB < nEvents * (1 - ratioTrigger) * 0.95 || nEventsMB > nEvents * (1 - ratioTrigger) * 1.05) { // we put some tolerance since the number of generated events is small + std::cerr << "Number of generated MB events different than expected\n"; + return 1; + } + if (nEventsInj < nEvents * ratioTrigger * 0.95 || nEventsInj > nEvents * ratioTrigger * 1.05) { + std::cerr << "Number of generated events injected with " << checkPdgQuark << " different than expected\n"; + return 1; + } + + if (nQuarks < 2 * nEvents * ratioTrigger) { // we expect anyway more because the same quark is repeated several time, after each gluon radiation + std::cerr << "Number of generated (anti)quarks " << checkPdgQuark << " lower than expected\n"; + return 1; + } + + float fracForcedDecays = nSignals ? float(nSignalGoodDecay) / nSignals : 0.0f; + float uncFracForcedDecays = nSignals ? std::sqrt(fracForcedDecays * (1 - fracForcedDecays) / nSignals) : 1.0f; + if (1 - fracForcedDecays > 0.15 + uncFracForcedDecays) { // we put some tolerance (e.g. due to oscillations which might change the final state) + std::cerr << "Fraction of signals decaying into the correct channel " << fracForcedDecays << " lower than expected\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_Mode2_XiC_NoDecay.C b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_Mode2_XiC_NoDecay.C new file mode 100644 index 000000000..b05f61cda --- /dev/null +++ b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_Mode2_XiC_NoDecay.C @@ -0,0 +1,84 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + + int checkPdgQuark{4}; + + int checkPdgHadron{4132}; + int checkHadronDecays{0}; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int nEventsInj{}; + int nQuarks{}, nSignals{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + + // check subgenerator information + if (eventHeader->hasInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID)) { + bool isValid = false; + int subGeneratorId = eventHeader->getInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID, isValid); + if (subGeneratorId == checkPdgQuark) { + nEventsInj++; + } + } + + for (auto &track : *tracks) { + auto pdg = track.GetPdgCode(); + if (std::abs(pdg) == checkPdgQuark) { + nQuarks++; + continue; + } + if (std::abs(pdg) == checkPdgHadron) { // found signal + nSignals++; // count signal PDG + + for (int j{track.getFirstDaughterTrackId()}; j <= track.getLastDaughterTrackId(); ++j) { + if (j >= 0) { + checkHadronDecays += 1; + } + } + } + } + } + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + std::cout << Form("# events injected with %d quark pair: ", checkPdgQuark) << nEventsInj << "\n"; + std::cout << Form("# %d (anti)quarks: ", checkPdgQuark) << nQuarks << "\n"; + std::cout <<"# signal hadrons: " << nSignals << "\n"; + std::cout <<"# signal hadrons decaying : " << checkHadronDecays << "\n"; + + if (nEventsInj < nEvents) { + std::cerr << "Number of generated events with triggered events different than expected\n"; + return 1; + } + + if (nQuarks < nEvents) { // we expect anyway more because the same quark is repeated several time, after each gluon radiation + std::cerr << "Number of generated (anti)quarks " << checkPdgQuark << " lower than expected\n"; + return 1; + } + + if (nSignals < nEvents) { + std::cerr << "Number of generated signals lower than expected\n"; + return 1; + } + + if (checkHadronDecays > 0) { + std::cerr << "Decayed XiC, it should never decay\n"; + return 1; + } + + return 0; +} + diff --git a/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_Mode2_XiC_NoDecay_pp_ref.C b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_Mode2_XiC_NoDecay_pp_ref.C new file mode 100644 index 000000000..b05f61cda --- /dev/null +++ b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_Mode2_XiC_NoDecay_pp_ref.C @@ -0,0 +1,84 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + + int checkPdgQuark{4}; + + int checkPdgHadron{4132}; + int checkHadronDecays{0}; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int nEventsInj{}; + int nQuarks{}, nSignals{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + + // check subgenerator information + if (eventHeader->hasInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID)) { + bool isValid = false; + int subGeneratorId = eventHeader->getInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID, isValid); + if (subGeneratorId == checkPdgQuark) { + nEventsInj++; + } + } + + for (auto &track : *tracks) { + auto pdg = track.GetPdgCode(); + if (std::abs(pdg) == checkPdgQuark) { + nQuarks++; + continue; + } + if (std::abs(pdg) == checkPdgHadron) { // found signal + nSignals++; // count signal PDG + + for (int j{track.getFirstDaughterTrackId()}; j <= track.getLastDaughterTrackId(); ++j) { + if (j >= 0) { + checkHadronDecays += 1; + } + } + } + } + } + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + std::cout << Form("# events injected with %d quark pair: ", checkPdgQuark) << nEventsInj << "\n"; + std::cout << Form("# %d (anti)quarks: ", checkPdgQuark) << nQuarks << "\n"; + std::cout <<"# signal hadrons: " << nSignals << "\n"; + std::cout <<"# signal hadrons decaying : " << checkHadronDecays << "\n"; + + if (nEventsInj < nEvents) { + std::cerr << "Number of generated events with triggered events different than expected\n"; + return 1; + } + + if (nQuarks < nEvents) { // we expect anyway more because the same quark is repeated several time, after each gluon radiation + std::cerr << "Number of generated (anti)quarks " << checkPdgQuark << " lower than expected\n"; + return 1; + } + + if (nSignals < nEvents) { + std::cerr << "Number of generated signals lower than expected\n"; + return 1; + } + + if (checkHadronDecays > 0) { + std::cerr << "Decayed XiC, it should never decay\n"; + return 1; + } + + return 0; +} + diff --git a/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_Mode2_XiCplus_NoDecay.C b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_Mode2_XiCplus_NoDecay.C new file mode 100644 index 000000000..a2b4d3499 --- /dev/null +++ b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_Mode2_XiCplus_NoDecay.C @@ -0,0 +1,84 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + + int checkPdgQuark = 4; + + int checkPdgHadron{4232}; + int checkHadronDecays{0}; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int nEventsInj{}; + int nQuarks{}, nSignals{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + + // check subgenerator information + if (eventHeader->hasInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID)) { + bool isValid = false; + int subGeneratorId = eventHeader->getInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID, isValid); + if (subGeneratorId == checkPdgQuark) { + nEventsInj++; + } + } + + for (auto &track : *tracks) { + auto pdg = track.GetPdgCode(); + if (std::abs(pdg) == checkPdgQuark) { + nQuarks++; + continue; + } + if (std::abs(pdg) == checkPdgHadron) { // found signal + nSignals++; // count signal PDG + + for (int j{track.getFirstDaughterTrackId()}; j <= track.getLastDaughterTrackId(); ++j) { + if (j >= 0) { + checkHadronDecays += 1; + } + } + } + } + } + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + std::cout << Form("# events injected with %d quark pair: ", checkPdgQuark) << nEventsInj << "\n"; + std::cout << Form("# %d (anti)quarks: ", checkPdgQuark) << nQuarks << "\n"; + std::cout <<"# signal hadrons: " << nSignals << "\n"; + std::cout <<"# signal hadrons decaying : " << checkHadronDecays << "\n"; + + if (nEventsInj < nEvents) { + std::cerr << "Number of generated events with triggered events different than expected\n"; + return 1; + } + + if (nQuarks < nEvents) { // we expect anyway more because the same quark is repeated several time, after each gluon radiation + std::cerr << "Number of generated (anti)quarks " << checkPdgQuark << " lower than expected\n"; + return 1; + } + + if (nSignals < nEvents) { + std::cerr << "Number of generated signals lower than expected\n"; + return 1; + } + + if (checkHadronDecays > 0) { + std::cerr << "Decayed XiC, it should never decay\n"; + return 1; + } + + return 0; +} + diff --git a/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_Mode2_XiCplus_NoDecay_pp_ref.C b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_Mode2_XiCplus_NoDecay_pp_ref.C new file mode 100644 index 000000000..f595aadd8 --- /dev/null +++ b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_Mode2_XiCplus_NoDecay_pp_ref.C @@ -0,0 +1,84 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + + int checkPdgQuark{4}; + + int checkPdgHadron{4232}; + int checkHadronDecays{0}; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int nEventsInj{}; + int nQuarks{}, nSignals{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + + // check subgenerator information + if (eventHeader->hasInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID)) { + bool isValid = false; + int subGeneratorId = eventHeader->getInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID, isValid); + if (subGeneratorId == checkPdgQuark) { + nEventsInj++; + } + } + + for (auto &track : *tracks) { + auto pdg = track.GetPdgCode(); + if (std::abs(pdg) == checkPdgQuark) { + nQuarks++; + continue; + } + if (std::abs(pdg) == checkPdgHadron) { // found signal + nSignals++; // count signal PDG + + for (int j{track.getFirstDaughterTrackId()}; j <= track.getLastDaughterTrackId(); ++j) { + if (j >= 0) { + checkHadronDecays += 1; + } + } + } + } + } + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + std::cout << Form("# events injected with %d quark pair: ", checkPdgQuark) << nEventsInj << "\n"; + std::cout << Form("# %d (anti)quarks: ", checkPdgQuark) << nQuarks << "\n"; + std::cout <<"# signal hadrons: " << nSignals << "\n"; + std::cout <<"# signal hadrons decaying : " << checkHadronDecays << "\n"; + + if (nEventsInj < nEvents) { + std::cerr << "Number of generated events with triggered events different than expected\n"; + return 1; + } + + if (nQuarks < nEvents) { // we expect anyway more because the same quark is repeated several time, after each gluon radiation + std::cerr << "Number of generated (anti)quarks " << checkPdgQuark << " lower than expected\n"; + return 1; + } + + if (nSignals < nEvents) { + std::cerr << "Number of generated signals lower than expected\n"; + return 1; + } + + if (checkHadronDecays > 0) { + std::cerr << "Decayed XiCplus, it should never decay\n"; + return 1; + } + + return 0; +} + diff --git a/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_and_bbbar_Mode2_ptHardBins.C b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_and_bbbar_Mode2_ptHardBins.C new file mode 100644 index 000000000..4a3fba732 --- /dev/null +++ b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_and_bbbar_Mode2_ptHardBins.C @@ -0,0 +1,139 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + + int checkPdgQuarkOne{4}; + int checkPdgQuarkTwo{5}; + float ratioTrigger = 1.; // each event triggered + float averagePt = 0.; + + std::vector checkPdgHadron{411, 421, 431, 4122, 4132, 4232, 4332}; + std::map>> checkHadronDecays{ // sorted pdg of daughters + {411, {{-321, 211, 211}, {-313, 211}, {211, 311}, {211, 333}}}, // D+ + {421, {{-321, 211}, {-321, 111, 211}}}, // D0 + {431, {{211, 333}, {-313, 321}}}, // Ds+ + {4122, {{-313, 2212}, {-321, 2224}, {211, 102134}, {-321, 211, 2212}, {311, 2212}}}, // Lc+ + {4132, {{211, 3312}}}, // Xic0 + {4232, {{-313, 2212}, {-321, 3324}, {211, 211, 3312}, {-321, 211, 2212}}}, // Xic+ + {4332, {{211, 3334}}} // Omegac+ + }; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int nEventsMB{}, nEventsInjOne{}, nEventsInjTwo{}; + int nQuarksOne{}, nQuarksTwo{}, nSignals{}, nSignalGoodDecay{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + + // check subgenerator information + if (eventHeader->hasInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID)) { + bool isValid = false; + int subGeneratorId = eventHeader->getInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID, isValid); + if (subGeneratorId == 0) { + nEventsMB++; + } else if (subGeneratorId == checkPdgQuarkOne) { + nEventsInjOne++; + } else if (subGeneratorId == checkPdgQuarkTwo) { + nEventsInjTwo++; + } + } + + for (auto &track : *tracks) { + auto pdg = track.GetPdgCode(); + if (std::abs(pdg) == checkPdgQuarkOne) { + nQuarksOne++; + continue; + } + if (std::abs(pdg) == checkPdgQuarkTwo) { + nQuarksTwo++; + continue; + } + if (std::find(checkPdgHadron.begin(), checkPdgHadron.end(), std::abs(pdg)) != checkPdgHadron.end()) { // found signal + nSignals++; // count signal PDG + averagePt += track.GetPt(); + + std::vector pdgsDecay{}; + std::vector pdgsDecayAntiPart{}; + for (int j{track.getFirstDaughterTrackId()}; j <= track.getLastDaughterTrackId(); ++j) { + auto pdgDau = tracks->at(j).GetPdgCode(); + pdgsDecay.push_back(pdgDau); + if (pdgDau != 333 && pdgDau != 111) { // phi is antiparticle of itself + pdgsDecayAntiPart.push_back(-pdgDau); + } else { + pdgsDecayAntiPart.push_back(pdgDau); + } + } + + std::sort(pdgsDecay.begin(), pdgsDecay.end()); + std::sort(pdgsDecayAntiPart.begin(), pdgsDecayAntiPart.end()); + + for (auto &decay : checkHadronDecays[std::abs(pdg)]) { + if (pdgsDecay == decay || pdgsDecayAntiPart == decay) { + nSignalGoodDecay++; + break; + } + } + } + } + } + + averagePt /= nSignals; + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + std::cout << "# MB events: " << nEventsMB << "\n"; + std::cout << Form("# events injected with %d quark pair: ", checkPdgQuarkOne) << nEventsInjOne << "\n"; + std::cout << Form("# events injected with %d quark pair: ", checkPdgQuarkTwo) << nEventsInjTwo << "\n"; + std::cout << Form("# %d (anti)quarks: ", checkPdgQuarkOne) << nQuarksOne << "\n"; + std::cout << Form("# %d (anti)quarks: ", checkPdgQuarkTwo) << nQuarksTwo << "\n"; + std::cout <<"# signal hadrons: " << nSignals << "\n"; + std::cout <<"# signal hadrons decaying in the correct channel: " << nSignalGoodDecay << "\n"; + std::cout <<"average pT of signal hadrons: " << averagePt << "\n"; + + if (nEventsMB < nEvents * (1 - ratioTrigger) * 0.95 || nEventsMB > nEvents * (1 - ratioTrigger) * 1.05) { // we put some tolerance since the number of generated events is small + std::cerr << "Number of generated MB events different than expected\n"; + return 1; + } + if (nEventsInjOne < nEvents * ratioTrigger * 0.5 * 0.95 || nEventsInjOne > nEvents * ratioTrigger * 0.5 * 1.05) { + std::cerr << "Number of generated events injected with " << checkPdgQuarkOne << " different than expected\n"; + return 1; + } + if (nEventsInjTwo < nEvents * ratioTrigger * 0.5 * 0.95 || nEventsInjTwo > nEvents * ratioTrigger * 0.5 * 1.05) { + std::cerr << "Number of generated events injected with " << checkPdgQuarkTwo << " different than expected\n"; + return 1; + } + + if (nQuarksOne < nEvents * ratioTrigger) { // we expect anyway more because the same quark is repeated several time, after each gluon radiation + std::cerr << "Number of generated (anti)quarks " << checkPdgQuarkOne << " lower than expected\n"; + return 1; + } + if (nQuarksTwo < nEvents * ratioTrigger) { // we expect anyway more because the same quark is repeated several time, after each gluon radiation + std::cerr << "Number of generated (anti)quarks " << checkPdgQuarkTwo << " lower than expected\n"; + return 1; + } + + float fracForcedDecays = nSignals ? float(nSignalGoodDecay) / nSignals : 0.0f; + float uncFracForcedDecays = nSignals ? std::sqrt(fracForcedDecays * (1 - fracForcedDecays) / nSignals) : 1.0f; + if (1 - fracForcedDecays > 0.15 + uncFracForcedDecays) { // we put some tolerance (e.g. due to oscillations which might change the final state) + std::cerr << "Fraction of signals decaying into the correct channel " << fracForcedDecays << " lower than expected\n"; + return 1; + } + + if (averagePt < 6) { // by testing locally it should be around 8.5 GeV/c with pthard bin 20-200 (contrary to 2-2.5 GeV/c of SoftQCD) + std::cerr << "Average pT of charmed hadrons " << averagePt << " lower than expected\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_and_bbbar_PbPb.C b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_and_bbbar_PbPb.C new file mode 100644 index 000000000..0edd85513 --- /dev/null +++ b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_and_bbbar_PbPb.C @@ -0,0 +1,192 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + //std::string path{"tf1/sgn_1_Kine.root"}; + + int checkPdgQuarkOne{4}; + int checkPdgQuarkTwo{5}; + float ratioTrigger = 1.; // one event triggered out of 1 + + std::vector checkPdgHadron{411, 421, 431, 4122, 4132, 4232, 4332}; + std::map>> checkHadronDecays{ // sorted pdg of daughters + {421, { + {-321, 211}, // D0 -> K-, pi+ + {-321, 211, 111}, // D0 -> K-, pi+, pi0 + {213, -321}, // D0 -> rho(770)+, K- + {-313, 111}, // D0 -> Kbar^*(892)0, pi0 + {-323, 211}, // D0 -> K^*(892)-, pi+ + {-211, 211}, // D0 -> pi-, pi+ + {213, -211}, // D0 -> rho(770)+, pi- + {-211, 211, 111}, // D0 -> pi-, pi+, pi0 + {-321, 321}, // D0 -> K-, K+ + }}, + + {411, { + {-321, 211, 211}, // D+ -> K-, pi+, pi+ + {-10311, 211}, // D+ -> Kbar0^*(1430)0, pi+ + {-313, 211}, // D+ -> Kbar^*(892)0, pi+ + {-321, 211, 211, 111}, // D+ -> K-, pi+, pi+, pi0 + {333, 211}, // D+ -> phi(1020)0, pi+ + {-313, 321}, // D+ -> Kbar^*(892)0, K+ + {-10311, 321}, // D+ -> Kbar0^*(1430)0, K+ + {-321, 321, 211}, // D+ -> K-, K+, pi+ + {113, 211}, // D+ -> rho(770)0, pi+ + {225, 211}, // D+ -> f2(1270)0, pi+ + {-211, 211, 211}, // D+ -> pi-, pi+, pi+ + }}, + + {431, { + {333, 211}, // Ds+ -> phi(1020)0, pi+ + {-313, 321}, // Ds+ -> Kbar^*(892)0, K+ + {333, 213}, // Ds+ -> phi(1020)0, rho(770)+ + {113, 211}, // Ds+ -> rho(770)0, pi+ + {225, 211}, // Ds+ -> f2(1270)0, pi+ + {-211, 211, 211}, // Ds+ -> pi-, pi+, pi+ + {313, 211}, // Ds+ -> K^*(892)0, pi+ + {10221, 321}, // Ds+ -> f0(1370)0, K+ + {113, 321}, // Ds+ -> rho(770)0, K+ + {-211, 321, 211}, // Ds+ -> pi-, K+, pi+ + {221, 211}, // Ds+ -> eta, pi+ + }}, + + {4122, { + {2212, -321, 211}, // Lambdac+ -> p, K-, pi+ + {2212, -313}, // Lambdac+ -> p, Kbar^*(892)0 + {2224, -321}, // Lambdac+ -> Delta(1232)++, K- + {102134, 211}, // Lambdac+ -> 102134, pi+ + {2212, 311}, // Lambdac+ -> p, K0 + {2212, -321, 211, 111}, // Lambdac+ -> p, K-, pi+, pi0 + {2212, -211, 211}, // Lambdac+ -> p, pi-, pi+ + {2212, 333}, // Lambdac+ -> p, phi(1020)0 + }}, + + {4232, { + {2212, -321, 211}, // Xic+ -> p, K-, pi+ + {2212, -313}, // Xic+ -> p, Kbar^*(892)0 + {3312, 211, 211}, // Xic+ -> Xi-, pi+, pi+ + {2212, 333}, // Xic+ -> p, phi(1020)0 + {3222, -211, 211}, // Xic+ -> Sigma+, pi-, pi+ + {3324, 211}, // Xic+ -> Xi(1530)0, pi+ + }}, + + {4132, { + {3312, 211}, // Xic0 -> Xi-, pi+ + }}, + + {4332, { + {3334, 211}, // Omegac0 -> Omega-, pi+ + {3312, 211}, // Omegac0 -> Xi-, pi+ + }}, + }; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int nEventsMB{}, nEventsInjOne{}, nEventsInjTwo{}; + int nQuarksOne{}, nQuarksTwo{}, nSignals{}, nSignalGoodDecay{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + + // check subgenerator information + //if (eventHeader->hasInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID)) { + // bool isValid = false; + // int subGeneratorId = eventHeader->getInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID, isValid); + // if (subGeneratorId == 0) { + // nEventsMB++; + // } else if (subGeneratorId == checkPdgQuarkOne) { + // nEventsInjOne++; + // } else if (subGeneratorId == checkPdgQuarkTwo) { + // nEventsInjTwo++; + // } + //} + + for (auto &track : *tracks) { + auto pdg = track.GetPdgCode(); + if (std::abs(pdg) == checkPdgQuarkOne) { + nQuarksOne++; + continue; + } + if (std::abs(pdg) == checkPdgQuarkTwo) { + nQuarksTwo++; + continue; + } + if (std::find(checkPdgHadron.begin(), checkPdgHadron.end(), std::abs(pdg)) != checkPdgHadron.end()) { // found signal + nSignals++; // count signal PDG + + std::vector pdgsDecay{}; + std::vector pdgsDecayAntiPart{}; + for (int j{track.getFirstDaughterTrackId()}; j <= track.getLastDaughterTrackId(); ++j) { + auto pdgDau = tracks->at(j).GetPdgCode(); + pdgsDecay.push_back(pdgDau); + if (pdgDau != 333 && pdgDau != 111 && pdgDau != 221 && pdgDau != 113 && pdgDau != 225) { // phi is antiparticle of itself + pdgsDecayAntiPart.push_back(-pdgDau); + } else { + pdgsDecayAntiPart.push_back(pdgDau); + } + } + + std::sort(pdgsDecay.begin(), pdgsDecay.end()); + std::sort(pdgsDecayAntiPart.begin(), pdgsDecayAntiPart.end()); + + for (auto &decay : checkHadronDecays[std::abs(pdg)]) { + std::sort(decay.begin(), decay.end()); + if (pdgsDecay == decay || pdgsDecayAntiPart == decay) { + nSignalGoodDecay++; + break; + } + } + } + } + } + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + //std::cout << "# MB events: " << nEventsMB << "\n"; + //std::cout << Form("# events injected with %d quark pair: ", checkPdgQuarkOne) << nEventsInjOne << "\n"; + //std::cout << Form("# events injected with %d quark pair: ", checkPdgQuarkTwo) << nEventsInjTwo << "\n"; + std::cout << Form("# %d (anti)quarks: ", checkPdgQuarkOne) << nQuarksOne << "\n"; + std::cout << Form("# %d (anti)quarks: ", checkPdgQuarkTwo) << nQuarksTwo << "\n"; + std::cout <<"# signal hadrons: " << nSignals << "\n"; + std::cout <<"# signal hadrons decaying in the correct channel: " << nSignalGoodDecay << "\n"; + + //if (nEventsMB < nEvents * (1 - ratioTrigger) * 0.95 || nEventsMB > nEvents * (1 - ratioTrigger) * 1.05) { // we put some tolerance since the number of generated events is small + // std::cerr << "Number of generated MB events different than expected\n"; + // return 1; + //} + //if (nEventsInjOne < nEvents * ratioTrigger * 0.5 * 0.95 || nEventsInjOne > nEvents * ratioTrigger * 0.5 * 1.05) { + // std::cerr << "Number of generated events injected with " << checkPdgQuarkOne << " different than expected\n"; + // return 1; + //} + //if (nEventsInjTwo < nEvents * ratioTrigger * 0.5 * 0.95 || nEventsInjTwo > nEvents * ratioTrigger * 0.5 * 1.05) { + // std::cerr << "Number of generated events injected with " << checkPdgQuarkTwo << " different than expected\n"; + // return 1; + //} + + if (nQuarksOne < nEvents * ratioTrigger) { // we expect anyway more because the same quark is repeated several time, after each gluon radiation + std::cerr << "Number of generated (anti)quarks " << checkPdgQuarkOne << " lower than expected\n"; + return 1; + } + if (nQuarksTwo < nEvents * ratioTrigger) { // we expect anyway more because the same quark is repeated several time, after each gluon radiation + std::cerr << "Number of generated (anti)quarks " << checkPdgQuarkTwo << " lower than expected\n"; + return 1; + } + + float fracForcedDecays = nSignals ? float(nSignalGoodDecay) / nSignals : 0.0f; + float uncFracForcedDecays = nSignals ? std::sqrt(fracForcedDecays * (1 - fracForcedDecays) / nSignals) : 1.0f; + if (1 - fracForcedDecays > 0.15 + uncFracForcedDecays) { // we put some tolerance (e.g. due to oscillations which might change the final state) + std::cerr << "Fraction of signals decaying into the correct channel " << fracForcedDecays << " lower than expected\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_and_bbbar_PbPb_corrBkg.C b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_and_bbbar_PbPb_corrBkg.C new file mode 100644 index 000000000..be57a1439 --- /dev/null +++ b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_and_bbbar_PbPb_corrBkg.C @@ -0,0 +1,183 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + //std::string path{"tf1/sgn_1_Kine.root"}; + + int checkPdgQuarkOne{4}; + int checkPdgQuarkTwo{5}; + float ratioTrigger = 1.; // one event triggered out of 1 + + std::vector checkPdgHadron{411, 421, 431, 4122, 4232}; + std::map>> checkHadronDecays{ // sorted pdg of daughters + {421, { + {-321, 211}, // D0 -> K-, pi+ + {-321, 211, 111}, // D0 -> K-, pi+, pi0 + {213, -321}, // D0 -> rho(770)+, K- + {-313, 111}, // D0 -> Kbar^*(892)0, pi0 + {-323, 211}, // D0 -> K^*(892)-, pi+ + {-211, 211}, // D0 -> pi-, pi+ + {213, -211}, // D0 -> rho(770)+, pi- + {-211, 211, 111}, // D0 -> pi-, pi+, pi0 + {-321, 321}, // D0 -> K-, K+ + }}, + + {411, { + {-321, 211, 211}, // D+ -> K-, pi+, pi+ + {-10311, 211}, // D+ -> Kbar0^*(1430)0, pi+ + {-313, 211}, // D+ -> Kbar^*(892)0, pi+ + {-321, 211, 211, 111}, // D+ -> K-, pi+, pi+, pi0 + {333, 211}, // D+ -> phi(1020)0, pi+ + {-313, 321}, // D+ -> Kbar^*(892)0, K+ + {-10311, 321}, // D+ -> Kbar0^*(1430)0, K+ + {-321, 321, 211}, // D+ -> K-, K+, pi+ + {113, 211}, // D+ -> rho(770)0, pi+ + {225, 211}, // D+ -> f2(1270)0, pi+ + {-211, 211, 211}, // D+ -> pi-, pi+, pi+ + }}, + + {431, { + {333, 211}, // Ds+ -> phi(1020)0, pi+ + {-313, 321}, // Ds+ -> Kbar^*(892)0, K+ + {333, 213}, // Ds+ -> phi(1020)0, rho(770)+ + {113, 211}, // Ds+ -> rho(770)0, pi+ + {225, 211}, // Ds+ -> f2(1270)0, pi+ + {-211, 211, 211}, // Ds+ -> pi-, pi+, pi+ + {313, 211}, // Ds+ -> K^*(892)0, pi+ + {10221, 321}, // Ds+ -> f0(1370)0, K+ + {113, 321}, // Ds+ -> rho(770)0, K+ + {-211, 321, 211}, // Ds+ -> pi-, K+, pi+ + {221, 211}, // Ds+ -> eta, pi+ + }}, + + {4122, { + {2212, -321, 211}, // Lambdac+ -> p, K-, pi+ + {2212, -313}, // Lambdac+ -> p, Kbar^*(892)0 + {2224, -321}, // Lambdac+ -> Delta(1232)++, K- + {102134, 211}, // Lambdac+ -> 102134, pi+ + {2212, 311}, // Lambdac+ -> p, K0 + {2212, -321, 211, 111}, // Lambdac+ -> p, K-, pi+, pi0 + {2212, -211, 211}, // Lambdac+ -> p, pi-, pi+ + {2212, 333}, // Lambdac+ -> p, phi(1020)0 + }}, + + {4232, { + {2212, -321, 211}, // Xic+ -> p, K-, pi+ + {2212, -313}, // Xic+ -> p, Kbar^*(892)0 + {3312, 211, 211}, // Xic+ -> Xi-, pi+, pi+ + {2212, 333}, // Xic+ -> p, phi(1020)0 + {3222, -211, 211}, // Xic+ -> Sigma+, pi-, pi+ + {3324, 211}, // Xic+ -> Xi(1530)0, pi+ + }}, + }; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int nEventsMB{}, nEventsInjOne{}, nEventsInjTwo{}; + int nQuarksOne{}, nQuarksTwo{}, nSignals{}, nSignalGoodDecay{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + + // check subgenerator information + //if (eventHeader->hasInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID)) { + // bool isValid = false; + // int subGeneratorId = eventHeader->getInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID, isValid); + // if (subGeneratorId == 0) { + // nEventsMB++; + // } else if (subGeneratorId == checkPdgQuarkOne) { + // nEventsInjOne++; + // } else if (subGeneratorId == checkPdgQuarkTwo) { + // nEventsInjTwo++; + // } + //} + + for (auto &track : *tracks) { + auto pdg = track.GetPdgCode(); + if (std::abs(pdg) == checkPdgQuarkOne) { + nQuarksOne++; + continue; + } + if (std::abs(pdg) == checkPdgQuarkTwo) { + nQuarksTwo++; + continue; + } + if (std::find(checkPdgHadron.begin(), checkPdgHadron.end(), std::abs(pdg)) != checkPdgHadron.end()) { // found signal + nSignals++; // count signal PDG + + std::vector pdgsDecay{}; + std::vector pdgsDecayAntiPart{}; + for (int j{track.getFirstDaughterTrackId()}; j <= track.getLastDaughterTrackId(); ++j) { + auto pdgDau = tracks->at(j).GetPdgCode(); + pdgsDecay.push_back(pdgDau); + if (pdgDau != 333 && pdgDau != 111 && pdgDau != 221 && pdgDau != 113 && pdgDau != 225) { // phi is antiparticle of itself + pdgsDecayAntiPart.push_back(-pdgDau); + } else { + pdgsDecayAntiPart.push_back(pdgDau); + } + } + + std::sort(pdgsDecay.begin(), pdgsDecay.end()); + std::sort(pdgsDecayAntiPart.begin(), pdgsDecayAntiPart.end()); + + for (auto &decay : checkHadronDecays[std::abs(pdg)]) { + std::sort(decay.begin(), decay.end()); + if (pdgsDecay == decay || pdgsDecayAntiPart == decay) { + nSignalGoodDecay++; + break; + } + } + } + } + } + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + //std::cout << "# MB events: " << nEventsMB << "\n"; + //std::cout << Form("# events injected with %d quark pair: ", checkPdgQuarkOne) << nEventsInjOne << "\n"; + //std::cout << Form("# events injected with %d quark pair: ", checkPdgQuarkTwo) << nEventsInjTwo << "\n"; + std::cout << Form("# %d (anti)quarks: ", checkPdgQuarkOne) << nQuarksOne << "\n"; + std::cout << Form("# %d (anti)quarks: ", checkPdgQuarkTwo) << nQuarksTwo << "\n"; + std::cout <<"# signal hadrons: " << nSignals << "\n"; + std::cout <<"# signal hadrons decaying in the correct channel: " << nSignalGoodDecay << "\n"; + + //if (nEventsMB < nEvents * (1 - ratioTrigger) * 0.95 || nEventsMB > nEvents * (1 - ratioTrigger) * 1.05) { // we put some tolerance since the number of generated events is small + // std::cerr << "Number of generated MB events different than expected\n"; + // return 1; + //} + //if (nEventsInjOne < nEvents * ratioTrigger * 0.5 * 0.95 || nEventsInjOne > nEvents * ratioTrigger * 0.5 * 1.05) { + // std::cerr << "Number of generated events injected with " << checkPdgQuarkOne << " different than expected\n"; + // return 1; + //} + //if (nEventsInjTwo < nEvents * ratioTrigger * 0.5 * 0.95 || nEventsInjTwo > nEvents * ratioTrigger * 0.5 * 1.05) { + // std::cerr << "Number of generated events injected with " << checkPdgQuarkTwo << " different than expected\n"; + // return 1; + //} + + if (nQuarksOne < nEvents * ratioTrigger) { // we expect anyway more because the same quark is repeated several time, after each gluon radiation + std::cerr << "Number of generated (anti)quarks " << checkPdgQuarkOne << " lower than expected\n"; + return 1; + } + if (nQuarksTwo < nEvents * ratioTrigger) { // we expect anyway more because the same quark is repeated several time, after each gluon radiation + std::cerr << "Number of generated (anti)quarks " << checkPdgQuarkTwo << " lower than expected\n"; + return 1; + } + + float fracForcedDecays = nSignals ? float(nSignalGoodDecay) / nSignals : 0.0f; + float uncFracForcedDecays = nSignals ? std::sqrt(fracForcedDecays * (1 - fracForcedDecays) / nSignals) : 1.0f; + if (1 - fracForcedDecays > 0.15 + uncFracForcedDecays) { // we put some tolerance (e.g. due to oscillations which might change the final state) + std::cerr << "Fraction of signals decaying into the correct channel " << fracForcedDecays << " lower than expected\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_and_bbbar_PbPb_ptHardBins.C b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_and_bbbar_PbPb_ptHardBins.C new file mode 100644 index 000000000..0edd85513 --- /dev/null +++ b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_and_bbbar_PbPb_ptHardBins.C @@ -0,0 +1,192 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + //std::string path{"tf1/sgn_1_Kine.root"}; + + int checkPdgQuarkOne{4}; + int checkPdgQuarkTwo{5}; + float ratioTrigger = 1.; // one event triggered out of 1 + + std::vector checkPdgHadron{411, 421, 431, 4122, 4132, 4232, 4332}; + std::map>> checkHadronDecays{ // sorted pdg of daughters + {421, { + {-321, 211}, // D0 -> K-, pi+ + {-321, 211, 111}, // D0 -> K-, pi+, pi0 + {213, -321}, // D0 -> rho(770)+, K- + {-313, 111}, // D0 -> Kbar^*(892)0, pi0 + {-323, 211}, // D0 -> K^*(892)-, pi+ + {-211, 211}, // D0 -> pi-, pi+ + {213, -211}, // D0 -> rho(770)+, pi- + {-211, 211, 111}, // D0 -> pi-, pi+, pi0 + {-321, 321}, // D0 -> K-, K+ + }}, + + {411, { + {-321, 211, 211}, // D+ -> K-, pi+, pi+ + {-10311, 211}, // D+ -> Kbar0^*(1430)0, pi+ + {-313, 211}, // D+ -> Kbar^*(892)0, pi+ + {-321, 211, 211, 111}, // D+ -> K-, pi+, pi+, pi0 + {333, 211}, // D+ -> phi(1020)0, pi+ + {-313, 321}, // D+ -> Kbar^*(892)0, K+ + {-10311, 321}, // D+ -> Kbar0^*(1430)0, K+ + {-321, 321, 211}, // D+ -> K-, K+, pi+ + {113, 211}, // D+ -> rho(770)0, pi+ + {225, 211}, // D+ -> f2(1270)0, pi+ + {-211, 211, 211}, // D+ -> pi-, pi+, pi+ + }}, + + {431, { + {333, 211}, // Ds+ -> phi(1020)0, pi+ + {-313, 321}, // Ds+ -> Kbar^*(892)0, K+ + {333, 213}, // Ds+ -> phi(1020)0, rho(770)+ + {113, 211}, // Ds+ -> rho(770)0, pi+ + {225, 211}, // Ds+ -> f2(1270)0, pi+ + {-211, 211, 211}, // Ds+ -> pi-, pi+, pi+ + {313, 211}, // Ds+ -> K^*(892)0, pi+ + {10221, 321}, // Ds+ -> f0(1370)0, K+ + {113, 321}, // Ds+ -> rho(770)0, K+ + {-211, 321, 211}, // Ds+ -> pi-, K+, pi+ + {221, 211}, // Ds+ -> eta, pi+ + }}, + + {4122, { + {2212, -321, 211}, // Lambdac+ -> p, K-, pi+ + {2212, -313}, // Lambdac+ -> p, Kbar^*(892)0 + {2224, -321}, // Lambdac+ -> Delta(1232)++, K- + {102134, 211}, // Lambdac+ -> 102134, pi+ + {2212, 311}, // Lambdac+ -> p, K0 + {2212, -321, 211, 111}, // Lambdac+ -> p, K-, pi+, pi0 + {2212, -211, 211}, // Lambdac+ -> p, pi-, pi+ + {2212, 333}, // Lambdac+ -> p, phi(1020)0 + }}, + + {4232, { + {2212, -321, 211}, // Xic+ -> p, K-, pi+ + {2212, -313}, // Xic+ -> p, Kbar^*(892)0 + {3312, 211, 211}, // Xic+ -> Xi-, pi+, pi+ + {2212, 333}, // Xic+ -> p, phi(1020)0 + {3222, -211, 211}, // Xic+ -> Sigma+, pi-, pi+ + {3324, 211}, // Xic+ -> Xi(1530)0, pi+ + }}, + + {4132, { + {3312, 211}, // Xic0 -> Xi-, pi+ + }}, + + {4332, { + {3334, 211}, // Omegac0 -> Omega-, pi+ + {3312, 211}, // Omegac0 -> Xi-, pi+ + }}, + }; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int nEventsMB{}, nEventsInjOne{}, nEventsInjTwo{}; + int nQuarksOne{}, nQuarksTwo{}, nSignals{}, nSignalGoodDecay{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + + // check subgenerator information + //if (eventHeader->hasInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID)) { + // bool isValid = false; + // int subGeneratorId = eventHeader->getInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID, isValid); + // if (subGeneratorId == 0) { + // nEventsMB++; + // } else if (subGeneratorId == checkPdgQuarkOne) { + // nEventsInjOne++; + // } else if (subGeneratorId == checkPdgQuarkTwo) { + // nEventsInjTwo++; + // } + //} + + for (auto &track : *tracks) { + auto pdg = track.GetPdgCode(); + if (std::abs(pdg) == checkPdgQuarkOne) { + nQuarksOne++; + continue; + } + if (std::abs(pdg) == checkPdgQuarkTwo) { + nQuarksTwo++; + continue; + } + if (std::find(checkPdgHadron.begin(), checkPdgHadron.end(), std::abs(pdg)) != checkPdgHadron.end()) { // found signal + nSignals++; // count signal PDG + + std::vector pdgsDecay{}; + std::vector pdgsDecayAntiPart{}; + for (int j{track.getFirstDaughterTrackId()}; j <= track.getLastDaughterTrackId(); ++j) { + auto pdgDau = tracks->at(j).GetPdgCode(); + pdgsDecay.push_back(pdgDau); + if (pdgDau != 333 && pdgDau != 111 && pdgDau != 221 && pdgDau != 113 && pdgDau != 225) { // phi is antiparticle of itself + pdgsDecayAntiPart.push_back(-pdgDau); + } else { + pdgsDecayAntiPart.push_back(pdgDau); + } + } + + std::sort(pdgsDecay.begin(), pdgsDecay.end()); + std::sort(pdgsDecayAntiPart.begin(), pdgsDecayAntiPart.end()); + + for (auto &decay : checkHadronDecays[std::abs(pdg)]) { + std::sort(decay.begin(), decay.end()); + if (pdgsDecay == decay || pdgsDecayAntiPart == decay) { + nSignalGoodDecay++; + break; + } + } + } + } + } + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + //std::cout << "# MB events: " << nEventsMB << "\n"; + //std::cout << Form("# events injected with %d quark pair: ", checkPdgQuarkOne) << nEventsInjOne << "\n"; + //std::cout << Form("# events injected with %d quark pair: ", checkPdgQuarkTwo) << nEventsInjTwo << "\n"; + std::cout << Form("# %d (anti)quarks: ", checkPdgQuarkOne) << nQuarksOne << "\n"; + std::cout << Form("# %d (anti)quarks: ", checkPdgQuarkTwo) << nQuarksTwo << "\n"; + std::cout <<"# signal hadrons: " << nSignals << "\n"; + std::cout <<"# signal hadrons decaying in the correct channel: " << nSignalGoodDecay << "\n"; + + //if (nEventsMB < nEvents * (1 - ratioTrigger) * 0.95 || nEventsMB > nEvents * (1 - ratioTrigger) * 1.05) { // we put some tolerance since the number of generated events is small + // std::cerr << "Number of generated MB events different than expected\n"; + // return 1; + //} + //if (nEventsInjOne < nEvents * ratioTrigger * 0.5 * 0.95 || nEventsInjOne > nEvents * ratioTrigger * 0.5 * 1.05) { + // std::cerr << "Number of generated events injected with " << checkPdgQuarkOne << " different than expected\n"; + // return 1; + //} + //if (nEventsInjTwo < nEvents * ratioTrigger * 0.5 * 0.95 || nEventsInjTwo > nEvents * ratioTrigger * 0.5 * 1.05) { + // std::cerr << "Number of generated events injected with " << checkPdgQuarkTwo << " different than expected\n"; + // return 1; + //} + + if (nQuarksOne < nEvents * ratioTrigger) { // we expect anyway more because the same quark is repeated several time, after each gluon radiation + std::cerr << "Number of generated (anti)quarks " << checkPdgQuarkOne << " lower than expected\n"; + return 1; + } + if (nQuarksTwo < nEvents * ratioTrigger) { // we expect anyway more because the same quark is repeated several time, after each gluon radiation + std::cerr << "Number of generated (anti)quarks " << checkPdgQuarkTwo << " lower than expected\n"; + return 1; + } + + float fracForcedDecays = nSignals ? float(nSignalGoodDecay) / nSignals : 0.0f; + float uncFracForcedDecays = nSignals ? std::sqrt(fracForcedDecays * (1 - fracForcedDecays) / nSignals) : 1.0f; + if (1 - fracForcedDecays > 0.15 + uncFracForcedDecays) { // we put some tolerance (e.g. due to oscillations which might change the final state) + std::cerr << "Fraction of signals decaying into the correct channel " << fracForcedDecays << " lower than expected\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_and_bbbar_PbPb_replaced.C b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_and_bbbar_PbPb_replaced.C new file mode 100644 index 000000000..ac01f2afc --- /dev/null +++ b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_and_bbbar_PbPb_replaced.C @@ -0,0 +1,210 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + //std::string path{"tf1/sgn_Kine.root"}; + + // Particle replacement configuration: {original, replacement}, frequencies + std::array, 3> pdgReplParticles = { + std::array{423, 4132}, // D*0 -> Xic0 + std::array{423, 4232}, // D*0 -> Xic+ + std::array{4212, 4332} // Sigmac+ -> Omegac0 + }; + std::array, 3> pdgReplPartCounters = { + std::array{0, 0}, + std::array{0, 0}, + std::array{0, 0} + }; + std::array freqRepl = {0.5, 0.5, 1.0}; + std::map sumOrigReplacedParticles = {{423, 0}, {4212, 0}}; + + // Signal hadrons to check (only final charm baryons after replacement) + std::array checkPdgHadron{4122, 4132, 4232, 4332}; + + // Expected decay channels - will be sorted automatically + // Define both particle and antiparticle versions + std::map>> checkHadronDecaysRaw{ + // Λc+ decays (from cfg: 4122:addChannel + resonance decays) + {4122, { + {2212, 311}, {-2212, -311}, // p K0s + {2212, -321, 211}, {-2212, 321, -211}, // p K- π+ (non-resonant) + {2212, 313}, {-2212, -313}, // p K*0 (not decayed) + {2212, 321, 211}, {-2212, -321, -211}, // p K*0 -> p (K- π+) [K*0 decayed] + {2224, -321}, {-2224, 321}, // Delta++ K- (not decayed) + {2212, 211, -321}, {-2212, -211, 321}, // Delta++ K- -> (p π+) K- [Delta decayed] + {102134, 211}, {-102134, -211}, // Lambda(1520) π+ (not decayed) + {2212, 321, 211}, {-2212, -321, -211}, // Lambda(1520) π+ -> (p K-) π+ [Lambda* decayed] + {2212, -321, 211, 111}, {-2212, 321, -211, 111}, // p K- π+ π0 + {2212, -211, 211}, {-2212, 211, -211}, // p π- π+ (cfg line 61: 2212 -211 211) + {2212, 333}, {-2212, 333}, // p φ (not decayed) + {2212, 321, -321}, {-2212, -321, 321} // p φ -> p (K+ K-) [φ decayed] + }}, + // Ξc0 decays (from cfg: 4132:onIfMatch) + {4132, { + {3312, 211}, {-3312, -211}, // Ξ- π+ + {3334, 321}, {-3334, -321} // Ω- K+ + }}, + // Ξc+ decays (from cfg: 4232:onIfMatch + resonance decays) + {4232, { + {2212, -321, 211}, {-2212, 321, -211}, // p K- π+ + {2212, -313}, {-2212, 313}, // p K̄*0 (not decayed) + {2212, -321, 211}, {-2212, 321, -211}, // p K̄*0 -> p (K+ π-) [K*0 decayed] + {2212, 333}, {-2212, 333}, // p φ (not decayed) + {2212, 321, -321}, {-2212, -321, 321}, // p φ -> p (K+ K-) [φ decayed] + {3222, -211, 211}, {-3222, 211, -211}, // Σ+ π- π+ + {3324, 211}, {-3324, -211}, // Ξ*0 π+ + {3312, 211, 211}, {-3312, -211, -211} // Ξ- π+ π+ + }}, + // Ωc0 decays (from cfg: 4332:onIfMatch) + {4332, { + {3334, 211}, {-3334, -211}, // Ω- π+ + {3312, 211}, {-3312, -211}, // Ξ- π+ + {3334, 321}, {-3334, -321} // Ω- K+ + }} + }; + + // Sort all decay channels + std::map>> checkHadronDecays; + for (auto &[pdg, decays] : checkHadronDecaysRaw) { + for (auto decay : decays) { + std::sort(decay.begin(), decay.end()); + checkHadronDecays[pdg].push_back(decay); + } + } + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) + { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int nSignals{}, nSignalGoodDecay{}; + std::map failedDecayCount{{4122, 0}, {4132, 0}, {4232, 0}, {4332, 0}}; + std::map>> unknownDecays; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) + { + tree->GetEntry(i); + for (auto &track : *tracks) + { + auto pdg = track.GetPdgCode(); + auto absPdg = std::abs(pdg); + + if (std::find(checkPdgHadron.begin(), checkPdgHadron.end(), absPdg) != checkPdgHadron.end()) + { + nSignals++; // count signal PDG + + // Count replacement particles (single-match per track) + int matchedIdx = -1; + bool matchedIsReplacement = false; + for (int iRepl{0}; iRepl < 3; ++iRepl) { + if (absPdg == pdgReplParticles[iRepl][0]) { + matchedIdx = iRepl; + matchedIsReplacement = false; + break; + } + if (absPdg == pdgReplParticles[iRepl][1]) { + matchedIdx = iRepl; + matchedIsReplacement = true; + break; + } + } + if (matchedIdx >= 0) { + if (matchedIsReplacement) { + pdgReplPartCounters[matchedIdx][1]++; + } else { + pdgReplPartCounters[matchedIdx][0]++; + } + // Count the original-particle population once for this matched group + sumOrigReplacedParticles[pdgReplParticles[matchedIdx][0]]++; + } + + // Collect decay products + std::vector pdgsDecay{}; + if (track.getFirstDaughterTrackId() >= 0 && track.getLastDaughterTrackId() >= 0) { + for (int j{track.getFirstDaughterTrackId()}; j <= track.getLastDaughterTrackId(); ++j) { + auto pdgDau = tracks->at(j).GetPdgCode(); + pdgsDecay.push_back(pdgDau); + } + } + + std::sort(pdgsDecay.begin(), pdgsDecay.end()); + + // Check if decay matches expected channels + bool foundMatch = false; + for (auto &decay : checkHadronDecays[absPdg]) { + if (pdgsDecay == decay) { + nSignalGoodDecay++; + foundMatch = true; + break; + } + } + + // Record failed decays for debugging + if (!foundMatch && pdgsDecay.size() > 0) { + failedDecayCount[absPdg]++; + unknownDecays[absPdg].insert(pdgsDecay); + } + } + } + } + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + std::cout << "# signal charm baryons: " << nSignals << "\n"; + std::cout << "# signal charm baryons decaying in the correct channel: " << nSignalGoodDecay << "\n"; + + // Print failed decay statistics + std::cout << "\nFailed decay counts:\n"; + for (auto &[pdg, count] : failedDecayCount) { + if (count > 0) { + std::cout << "PDG " << pdg << ": " << count << " failed decays\n"; + std::cout << " Unknown decay channels (first 5):\n"; + int printed = 0; + for (auto &decay : unknownDecays[pdg]) { + if (printed++ >= 5) break; + std::cout << " ["; + for (size_t i = 0; i < decay.size(); ++i) { + std::cout << decay[i]; + if (i < decay.size()-1) std::cout << ", "; + } + std::cout << "]\n"; + } + } + } + std::cout << "\n"; + + std::cout << "# D*0 (original): " << pdgReplPartCounters[0][0] << "\n"; + std::cout << "# Xic0 (replaced from D*0): " << pdgReplPartCounters[0][1] << "\n"; + std::cout << "# Xic+ (replaced from D*0): " << pdgReplPartCounters[1][1] << "\n"; + std::cout << "# Sigmac+ (original): " << pdgReplPartCounters[2][0] << "\n"; + std::cout << "# Omegac0 (replaced from Sigmac+): " << pdgReplPartCounters[2][1] << "\n"; + + // Check forced decay fraction + float fracForcedDecays = nSignals ? float(nSignalGoodDecay) / nSignals : 0.0f; + float uncFracForcedDecays = nSignals ? std::sqrt(fracForcedDecays * (1 - fracForcedDecays) / nSignals) : 1.0f; + std::cout << "# fraction of signals decaying into the correct channel: " << fracForcedDecays + << " (" << fracForcedDecays * 100.0f << "%)\n"; + if (1 - fracForcedDecays > 0.15 + uncFracForcedDecays) { // 85% threshold with tolerance + std::cerr << "Fraction of signals decaying into the correct channel " << fracForcedDecays << " lower than expected\n"; + return 1; + } + + // Check particle replacement ratios (2-sigma statistical compatibility) + for (int iRepl{0}; iRepl<6; ++iRepl) { + float numPart = sumOrigReplacedParticles[pdgReplParticles[iRepl][0]]; + float fracMeas = numPart ? float(pdgReplPartCounters[iRepl][1]) / numPart : 0.0f; + float fracMeasUnc = fracMeas ? std::sqrt(pdgReplPartCounters[iRepl][1]) / numPart : 1.0f; + + if (std::abs(fracMeas - freqRepl[iRepl]) > fracMeasUnc) { + std::cerr << "Fraction of replaced " << pdgReplParticles[iRepl][0] << " into " << pdgReplParticles[iRepl][1] << " is " << fracMeas <<" (expected "<< freqRepl[iRepl] << ")\n"; + return 1; + } + } + + return 0; +} \ No newline at end of file diff --git a/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_and_bbbar_gap2_QCDCR_NeNe.C b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_and_bbbar_gap2_QCDCR_NeNe.C new file mode 100644 index 000000000..07c5f8837 --- /dev/null +++ b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_and_bbbar_gap2_QCDCR_NeNe.C @@ -0,0 +1,190 @@ +int External(std::string path = "o2sim_Kine.root") { + + int checkPdgQuarkOne{4}; + int checkPdgQuarkTwo{5}; + float ratioTrigger = 1./2; // one event triggered out of 2 + + std::vector checkPdgHadron{411, 421, 431, 4122, 4132, 4232, 4332}; + std::map>> checkHadronDecays{ // sorted pdg of daughters + {421, { + {-321, 211}, // D0 -> K-, pi+ + {-321, 211, 111}, // D0 -> K-, pi+, pi0 + {213, -321}, // D0 -> rho(770)+, K- + {-313, 111}, // D0 -> Kbar^*(892)0, pi0 + {-323, 211}, // D0 -> K^*(892)-, pi+ + {-211, 211}, // D0 -> pi-, pi+ + {213, -211}, // D0 -> rho(770)+, pi- + {-211, 211, 111}, // D0 -> pi-, pi+, pi0 + {-321, 321}, // D0 -> K-, K+ + }}, + + {411, { + {-321, 211, 211}, // D+ -> K-, pi+, pi+ + {-10311, 211}, // D+ -> Kbar0^*(1430)0, pi+ + {-313, 211}, // D+ -> Kbar^*(892)0, pi+ + {-321, 211, 211, 111}, // D+ -> K-, pi+, pi+, pi0 + {333, 211}, // D+ -> phi(1020)0, pi+ + {-313, 321}, // D+ -> Kbar^*(892)0, K+ + {-10311, 321}, // D+ -> Kbar0^*(1430)0, K+ + {-321, 321, 211}, // D+ -> K-, K+, pi+ + {113, 211}, // D+ -> rho(770)0, pi+ + {225, 211}, // D+ -> f2(1270)0, pi+ + {-211, 211, 211}, // D+ -> pi-, pi+, pi+ + }}, + + {431, { + {333, 211}, // Ds+ -> phi(1020)0, pi+ + {-313, 321}, // Ds+ -> Kbar^*(892)0, K+ + {333, 213}, // Ds+ -> phi(1020)0, rho(770)+ + {113, 211}, // Ds+ -> rho(770)0, pi+ + {225, 211}, // Ds+ -> f2(1270)0, pi+ + {-211, 211, 211}, // Ds+ -> pi-, pi+, pi+ + {313, 211}, // Ds+ -> K^*(892)0, pi+ + {10221, 321}, // Ds+ -> f0(1370)0, K+ + {113, 321}, // Ds+ -> rho(770)0, K+ + {-211, 321, 211}, // Ds+ -> pi-, K+, pi+ + {221, 211}, // Ds+ -> eta, pi+ + }}, + + {4122, { + {2212, -321, 211}, // Lambdac+ -> p, K-, pi+ + {2212, -313}, // Lambdac+ -> p, Kbar^*(892)0 + {2224, -321}, // Lambdac+ -> Delta(1232)++, K- + {102134, 211}, // Lambdac+ -> 102134, pi+ + {2212, 311}, // Lambdac+ -> p, K0 + {2212, -321, 211, 111}, // Lambdac+ -> p, K-, pi+, pi0 + {2212, -211, 211}, // Lambdac+ -> p, pi-, pi+ + {2212, 333}, // Lambdac+ -> p, phi(1020)0 + }}, + + {4232, { + {2212, -321, 211}, // Xic+ -> p, K-, pi+ + {2212, -313}, // Xic+ -> p, Kbar^*(892)0 + {3312, 211, 211}, // Xic+ -> Xi-, pi+, pi+ + {2212, 333}, // Xic+ -> p, phi(1020)0 + {3222, -211, 211}, // Xic+ -> Sigma+, pi-, pi+ + {3324, 211}, // Xic+ -> Xi(1530)0, pi+ + }}, + + {4132, { + {3312, 211}, // Xic0 -> Xi-, pi+ + }}, + + {4332, { + {3334, 211}, // Omegac0 -> Omega-, pi+ + {3312, 211}, // Omegac0 -> Xi-, pi+ + }}, + }; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int nEventsMB{}, nEventsInjOne{}, nEventsInjTwo{}; + int nQuarksOne{}, nQuarksTwo{}, nSignals{}, nSignalGoodDecay{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + + // check subgenerator information + if (eventHeader->hasInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID)) { + bool isValid = false; + int subGeneratorId = eventHeader->getInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID, isValid); + if (subGeneratorId == 0) { + nEventsMB++; + } else if (subGeneratorId == checkPdgQuarkOne) { + nEventsInjOne++; + } else if (subGeneratorId == checkPdgQuarkTwo) { + nEventsInjTwo++; + } + } + + for (auto &track : *tracks) { + auto pdg = track.GetPdgCode(); + if (std::abs(pdg) == checkPdgQuarkOne) { + nQuarksOne++; + continue; + } + if (std::abs(pdg) == checkPdgQuarkTwo) { + nQuarksTwo++; + continue; + } + if (std::find(checkPdgHadron.begin(), checkPdgHadron.end(), std::abs(pdg)) != checkPdgHadron.end()) { // found signal + nSignals++; // count signal PDG + + std::vector pdgsDecay{}; + std::vector pdgsDecayAntiPart{}; + for (int j{track.getFirstDaughterTrackId()}; j <= track.getLastDaughterTrackId(); ++j) { + auto pdgDau = tracks->at(j).GetPdgCode(); + pdgsDecay.push_back(pdgDau); + if (pdgDau != 333 && pdgDau != 111 && pdgDau != 221 && pdgDau != 113 && pdgDau != 225) { // phi is antiparticle of itself + pdgsDecayAntiPart.push_back(-pdgDau); + } else { + pdgsDecayAntiPart.push_back(pdgDau); + } + } + + std::sort(pdgsDecay.begin(), pdgsDecay.end()); + std::sort(pdgsDecayAntiPart.begin(), pdgsDecayAntiPart.end()); + + for (auto &decay : checkHadronDecays[std::abs(pdg)]) { + std::sort(decay.begin(), decay.end()); + if (pdgsDecay == decay || pdgsDecayAntiPart == decay) { + nSignalGoodDecay++; + break; + } + } + } + } + } + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + std::cout << "# MB events: " << nEventsMB << "\n"; + std::cout << Form("# events injected with %d quark pair: ", checkPdgQuarkOne) << nEventsInjOne << "\n"; + std::cout << Form("# events injected with %d quark pair: ", checkPdgQuarkTwo) << nEventsInjTwo << "\n"; + std::cout << Form("# %d (anti)quarks: ", checkPdgQuarkOne) << nQuarksOne << "\n"; + std::cout << Form("# %d (anti)quarks: ", checkPdgQuarkTwo) << nQuarksTwo << "\n"; + std::cout <<"# signal hadrons: " << nSignals << "\n"; + std::cout <<"# signal hadrons decaying in the correct channel: " << nSignalGoodDecay << "\n"; + + if (nEventsMB < nEvents * (1 - ratioTrigger) * 0.95 || nEventsMB > nEvents * (1 - ratioTrigger) * 1.05) { // we put some tolerance since the number of generated events is small + std::cerr << "Number of generated MB events different than expected\n"; + return 1; + } + if (nEventsInjOne < nEvents * ratioTrigger * 0.5 * 0.95 || nEventsInjOne > nEvents * ratioTrigger * 0.5 * 1.05) { + std::cerr << "Number of generated events injected with " << checkPdgQuarkOne << " different than expected\n"; + return 1; + } + if (nEventsInjTwo < nEvents * ratioTrigger * 0.5 * 0.95 || nEventsInjTwo > nEvents * ratioTrigger * 0.5 * 1.05) { + std::cerr << "Number of generated events injected with " << checkPdgQuarkTwo << " different than expected\n"; + return 1; + } + + if (nQuarksOne < nEvents * ratioTrigger) { // we expect anyway more because the same quark is repeated several time, after each gluon radiation + std::cerr << "Number of generated (anti)quarks " << checkPdgQuarkOne << " lower than expected\n"; + return 1; + } + if (nQuarksTwo < nEvents * ratioTrigger) { // we expect anyway more because the same quark is repeated several time, after each gluon radiation + std::cerr << "Number of generated (anti)quarks " << checkPdgQuarkTwo << " lower than expected\n"; + return 1; + } + + float fracForcedDecays = nSignals ? float(nSignalGoodDecay) / nSignals : 0.0f; + float uncFracForcedDecays = nSignals ? std::sqrt(fracForcedDecays * (1 - fracForcedDecays) / nSignals) : 1.0f; + if (1 - fracForcedDecays > 0.15 + uncFracForcedDecays) { // we put some tolerance (e.g. due to oscillations which might change the final state) + std::cerr << "Fraction of signals decaying into the correct channel " << fracForcedDecays << " lower than expected\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_and_bbbar_gap2_QCDCR_OO.C b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_and_bbbar_gap2_QCDCR_OO.C new file mode 100644 index 000000000..07c5f8837 --- /dev/null +++ b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_and_bbbar_gap2_QCDCR_OO.C @@ -0,0 +1,190 @@ +int External(std::string path = "o2sim_Kine.root") { + + int checkPdgQuarkOne{4}; + int checkPdgQuarkTwo{5}; + float ratioTrigger = 1./2; // one event triggered out of 2 + + std::vector checkPdgHadron{411, 421, 431, 4122, 4132, 4232, 4332}; + std::map>> checkHadronDecays{ // sorted pdg of daughters + {421, { + {-321, 211}, // D0 -> K-, pi+ + {-321, 211, 111}, // D0 -> K-, pi+, pi0 + {213, -321}, // D0 -> rho(770)+, K- + {-313, 111}, // D0 -> Kbar^*(892)0, pi0 + {-323, 211}, // D0 -> K^*(892)-, pi+ + {-211, 211}, // D0 -> pi-, pi+ + {213, -211}, // D0 -> rho(770)+, pi- + {-211, 211, 111}, // D0 -> pi-, pi+, pi0 + {-321, 321}, // D0 -> K-, K+ + }}, + + {411, { + {-321, 211, 211}, // D+ -> K-, pi+, pi+ + {-10311, 211}, // D+ -> Kbar0^*(1430)0, pi+ + {-313, 211}, // D+ -> Kbar^*(892)0, pi+ + {-321, 211, 211, 111}, // D+ -> K-, pi+, pi+, pi0 + {333, 211}, // D+ -> phi(1020)0, pi+ + {-313, 321}, // D+ -> Kbar^*(892)0, K+ + {-10311, 321}, // D+ -> Kbar0^*(1430)0, K+ + {-321, 321, 211}, // D+ -> K-, K+, pi+ + {113, 211}, // D+ -> rho(770)0, pi+ + {225, 211}, // D+ -> f2(1270)0, pi+ + {-211, 211, 211}, // D+ -> pi-, pi+, pi+ + }}, + + {431, { + {333, 211}, // Ds+ -> phi(1020)0, pi+ + {-313, 321}, // Ds+ -> Kbar^*(892)0, K+ + {333, 213}, // Ds+ -> phi(1020)0, rho(770)+ + {113, 211}, // Ds+ -> rho(770)0, pi+ + {225, 211}, // Ds+ -> f2(1270)0, pi+ + {-211, 211, 211}, // Ds+ -> pi-, pi+, pi+ + {313, 211}, // Ds+ -> K^*(892)0, pi+ + {10221, 321}, // Ds+ -> f0(1370)0, K+ + {113, 321}, // Ds+ -> rho(770)0, K+ + {-211, 321, 211}, // Ds+ -> pi-, K+, pi+ + {221, 211}, // Ds+ -> eta, pi+ + }}, + + {4122, { + {2212, -321, 211}, // Lambdac+ -> p, K-, pi+ + {2212, -313}, // Lambdac+ -> p, Kbar^*(892)0 + {2224, -321}, // Lambdac+ -> Delta(1232)++, K- + {102134, 211}, // Lambdac+ -> 102134, pi+ + {2212, 311}, // Lambdac+ -> p, K0 + {2212, -321, 211, 111}, // Lambdac+ -> p, K-, pi+, pi0 + {2212, -211, 211}, // Lambdac+ -> p, pi-, pi+ + {2212, 333}, // Lambdac+ -> p, phi(1020)0 + }}, + + {4232, { + {2212, -321, 211}, // Xic+ -> p, K-, pi+ + {2212, -313}, // Xic+ -> p, Kbar^*(892)0 + {3312, 211, 211}, // Xic+ -> Xi-, pi+, pi+ + {2212, 333}, // Xic+ -> p, phi(1020)0 + {3222, -211, 211}, // Xic+ -> Sigma+, pi-, pi+ + {3324, 211}, // Xic+ -> Xi(1530)0, pi+ + }}, + + {4132, { + {3312, 211}, // Xic0 -> Xi-, pi+ + }}, + + {4332, { + {3334, 211}, // Omegac0 -> Omega-, pi+ + {3312, 211}, // Omegac0 -> Xi-, pi+ + }}, + }; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int nEventsMB{}, nEventsInjOne{}, nEventsInjTwo{}; + int nQuarksOne{}, nQuarksTwo{}, nSignals{}, nSignalGoodDecay{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + + // check subgenerator information + if (eventHeader->hasInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID)) { + bool isValid = false; + int subGeneratorId = eventHeader->getInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID, isValid); + if (subGeneratorId == 0) { + nEventsMB++; + } else if (subGeneratorId == checkPdgQuarkOne) { + nEventsInjOne++; + } else if (subGeneratorId == checkPdgQuarkTwo) { + nEventsInjTwo++; + } + } + + for (auto &track : *tracks) { + auto pdg = track.GetPdgCode(); + if (std::abs(pdg) == checkPdgQuarkOne) { + nQuarksOne++; + continue; + } + if (std::abs(pdg) == checkPdgQuarkTwo) { + nQuarksTwo++; + continue; + } + if (std::find(checkPdgHadron.begin(), checkPdgHadron.end(), std::abs(pdg)) != checkPdgHadron.end()) { // found signal + nSignals++; // count signal PDG + + std::vector pdgsDecay{}; + std::vector pdgsDecayAntiPart{}; + for (int j{track.getFirstDaughterTrackId()}; j <= track.getLastDaughterTrackId(); ++j) { + auto pdgDau = tracks->at(j).GetPdgCode(); + pdgsDecay.push_back(pdgDau); + if (pdgDau != 333 && pdgDau != 111 && pdgDau != 221 && pdgDau != 113 && pdgDau != 225) { // phi is antiparticle of itself + pdgsDecayAntiPart.push_back(-pdgDau); + } else { + pdgsDecayAntiPart.push_back(pdgDau); + } + } + + std::sort(pdgsDecay.begin(), pdgsDecay.end()); + std::sort(pdgsDecayAntiPart.begin(), pdgsDecayAntiPart.end()); + + for (auto &decay : checkHadronDecays[std::abs(pdg)]) { + std::sort(decay.begin(), decay.end()); + if (pdgsDecay == decay || pdgsDecayAntiPart == decay) { + nSignalGoodDecay++; + break; + } + } + } + } + } + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + std::cout << "# MB events: " << nEventsMB << "\n"; + std::cout << Form("# events injected with %d quark pair: ", checkPdgQuarkOne) << nEventsInjOne << "\n"; + std::cout << Form("# events injected with %d quark pair: ", checkPdgQuarkTwo) << nEventsInjTwo << "\n"; + std::cout << Form("# %d (anti)quarks: ", checkPdgQuarkOne) << nQuarksOne << "\n"; + std::cout << Form("# %d (anti)quarks: ", checkPdgQuarkTwo) << nQuarksTwo << "\n"; + std::cout <<"# signal hadrons: " << nSignals << "\n"; + std::cout <<"# signal hadrons decaying in the correct channel: " << nSignalGoodDecay << "\n"; + + if (nEventsMB < nEvents * (1 - ratioTrigger) * 0.95 || nEventsMB > nEvents * (1 - ratioTrigger) * 1.05) { // we put some tolerance since the number of generated events is small + std::cerr << "Number of generated MB events different than expected\n"; + return 1; + } + if (nEventsInjOne < nEvents * ratioTrigger * 0.5 * 0.95 || nEventsInjOne > nEvents * ratioTrigger * 0.5 * 1.05) { + std::cerr << "Number of generated events injected with " << checkPdgQuarkOne << " different than expected\n"; + return 1; + } + if (nEventsInjTwo < nEvents * ratioTrigger * 0.5 * 0.95 || nEventsInjTwo > nEvents * ratioTrigger * 0.5 * 1.05) { + std::cerr << "Number of generated events injected with " << checkPdgQuarkTwo << " different than expected\n"; + return 1; + } + + if (nQuarksOne < nEvents * ratioTrigger) { // we expect anyway more because the same quark is repeated several time, after each gluon radiation + std::cerr << "Number of generated (anti)quarks " << checkPdgQuarkOne << " lower than expected\n"; + return 1; + } + if (nQuarksTwo < nEvents * ratioTrigger) { // we expect anyway more because the same quark is repeated several time, after each gluon radiation + std::cerr << "Number of generated (anti)quarks " << checkPdgQuarkTwo << " lower than expected\n"; + return 1; + } + + float fracForcedDecays = nSignals ? float(nSignalGoodDecay) / nSignals : 0.0f; + float uncFracForcedDecays = nSignals ? std::sqrt(fracForcedDecays * (1 - fracForcedDecays) / nSignals) : 1.0f; + if (1 - fracForcedDecays > 0.15 + uncFracForcedDecays) { // we put some tolerance (e.g. due to oscillations which might change the final state) + std::cerr << "Fraction of signals decaying into the correct channel " << fracForcedDecays << " lower than expected\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_and_bbbar_gap2_QCDCR_pO.C b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_and_bbbar_gap2_QCDCR_pO.C new file mode 100644 index 000000000..07c5f8837 --- /dev/null +++ b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_and_bbbar_gap2_QCDCR_pO.C @@ -0,0 +1,190 @@ +int External(std::string path = "o2sim_Kine.root") { + + int checkPdgQuarkOne{4}; + int checkPdgQuarkTwo{5}; + float ratioTrigger = 1./2; // one event triggered out of 2 + + std::vector checkPdgHadron{411, 421, 431, 4122, 4132, 4232, 4332}; + std::map>> checkHadronDecays{ // sorted pdg of daughters + {421, { + {-321, 211}, // D0 -> K-, pi+ + {-321, 211, 111}, // D0 -> K-, pi+, pi0 + {213, -321}, // D0 -> rho(770)+, K- + {-313, 111}, // D0 -> Kbar^*(892)0, pi0 + {-323, 211}, // D0 -> K^*(892)-, pi+ + {-211, 211}, // D0 -> pi-, pi+ + {213, -211}, // D0 -> rho(770)+, pi- + {-211, 211, 111}, // D0 -> pi-, pi+, pi0 + {-321, 321}, // D0 -> K-, K+ + }}, + + {411, { + {-321, 211, 211}, // D+ -> K-, pi+, pi+ + {-10311, 211}, // D+ -> Kbar0^*(1430)0, pi+ + {-313, 211}, // D+ -> Kbar^*(892)0, pi+ + {-321, 211, 211, 111}, // D+ -> K-, pi+, pi+, pi0 + {333, 211}, // D+ -> phi(1020)0, pi+ + {-313, 321}, // D+ -> Kbar^*(892)0, K+ + {-10311, 321}, // D+ -> Kbar0^*(1430)0, K+ + {-321, 321, 211}, // D+ -> K-, K+, pi+ + {113, 211}, // D+ -> rho(770)0, pi+ + {225, 211}, // D+ -> f2(1270)0, pi+ + {-211, 211, 211}, // D+ -> pi-, pi+, pi+ + }}, + + {431, { + {333, 211}, // Ds+ -> phi(1020)0, pi+ + {-313, 321}, // Ds+ -> Kbar^*(892)0, K+ + {333, 213}, // Ds+ -> phi(1020)0, rho(770)+ + {113, 211}, // Ds+ -> rho(770)0, pi+ + {225, 211}, // Ds+ -> f2(1270)0, pi+ + {-211, 211, 211}, // Ds+ -> pi-, pi+, pi+ + {313, 211}, // Ds+ -> K^*(892)0, pi+ + {10221, 321}, // Ds+ -> f0(1370)0, K+ + {113, 321}, // Ds+ -> rho(770)0, K+ + {-211, 321, 211}, // Ds+ -> pi-, K+, pi+ + {221, 211}, // Ds+ -> eta, pi+ + }}, + + {4122, { + {2212, -321, 211}, // Lambdac+ -> p, K-, pi+ + {2212, -313}, // Lambdac+ -> p, Kbar^*(892)0 + {2224, -321}, // Lambdac+ -> Delta(1232)++, K- + {102134, 211}, // Lambdac+ -> 102134, pi+ + {2212, 311}, // Lambdac+ -> p, K0 + {2212, -321, 211, 111}, // Lambdac+ -> p, K-, pi+, pi0 + {2212, -211, 211}, // Lambdac+ -> p, pi-, pi+ + {2212, 333}, // Lambdac+ -> p, phi(1020)0 + }}, + + {4232, { + {2212, -321, 211}, // Xic+ -> p, K-, pi+ + {2212, -313}, // Xic+ -> p, Kbar^*(892)0 + {3312, 211, 211}, // Xic+ -> Xi-, pi+, pi+ + {2212, 333}, // Xic+ -> p, phi(1020)0 + {3222, -211, 211}, // Xic+ -> Sigma+, pi-, pi+ + {3324, 211}, // Xic+ -> Xi(1530)0, pi+ + }}, + + {4132, { + {3312, 211}, // Xic0 -> Xi-, pi+ + }}, + + {4332, { + {3334, 211}, // Omegac0 -> Omega-, pi+ + {3312, 211}, // Omegac0 -> Xi-, pi+ + }}, + }; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int nEventsMB{}, nEventsInjOne{}, nEventsInjTwo{}; + int nQuarksOne{}, nQuarksTwo{}, nSignals{}, nSignalGoodDecay{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + + // check subgenerator information + if (eventHeader->hasInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID)) { + bool isValid = false; + int subGeneratorId = eventHeader->getInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID, isValid); + if (subGeneratorId == 0) { + nEventsMB++; + } else if (subGeneratorId == checkPdgQuarkOne) { + nEventsInjOne++; + } else if (subGeneratorId == checkPdgQuarkTwo) { + nEventsInjTwo++; + } + } + + for (auto &track : *tracks) { + auto pdg = track.GetPdgCode(); + if (std::abs(pdg) == checkPdgQuarkOne) { + nQuarksOne++; + continue; + } + if (std::abs(pdg) == checkPdgQuarkTwo) { + nQuarksTwo++; + continue; + } + if (std::find(checkPdgHadron.begin(), checkPdgHadron.end(), std::abs(pdg)) != checkPdgHadron.end()) { // found signal + nSignals++; // count signal PDG + + std::vector pdgsDecay{}; + std::vector pdgsDecayAntiPart{}; + for (int j{track.getFirstDaughterTrackId()}; j <= track.getLastDaughterTrackId(); ++j) { + auto pdgDau = tracks->at(j).GetPdgCode(); + pdgsDecay.push_back(pdgDau); + if (pdgDau != 333 && pdgDau != 111 && pdgDau != 221 && pdgDau != 113 && pdgDau != 225) { // phi is antiparticle of itself + pdgsDecayAntiPart.push_back(-pdgDau); + } else { + pdgsDecayAntiPart.push_back(pdgDau); + } + } + + std::sort(pdgsDecay.begin(), pdgsDecay.end()); + std::sort(pdgsDecayAntiPart.begin(), pdgsDecayAntiPart.end()); + + for (auto &decay : checkHadronDecays[std::abs(pdg)]) { + std::sort(decay.begin(), decay.end()); + if (pdgsDecay == decay || pdgsDecayAntiPart == decay) { + nSignalGoodDecay++; + break; + } + } + } + } + } + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + std::cout << "# MB events: " << nEventsMB << "\n"; + std::cout << Form("# events injected with %d quark pair: ", checkPdgQuarkOne) << nEventsInjOne << "\n"; + std::cout << Form("# events injected with %d quark pair: ", checkPdgQuarkTwo) << nEventsInjTwo << "\n"; + std::cout << Form("# %d (anti)quarks: ", checkPdgQuarkOne) << nQuarksOne << "\n"; + std::cout << Form("# %d (anti)quarks: ", checkPdgQuarkTwo) << nQuarksTwo << "\n"; + std::cout <<"# signal hadrons: " << nSignals << "\n"; + std::cout <<"# signal hadrons decaying in the correct channel: " << nSignalGoodDecay << "\n"; + + if (nEventsMB < nEvents * (1 - ratioTrigger) * 0.95 || nEventsMB > nEvents * (1 - ratioTrigger) * 1.05) { // we put some tolerance since the number of generated events is small + std::cerr << "Number of generated MB events different than expected\n"; + return 1; + } + if (nEventsInjOne < nEvents * ratioTrigger * 0.5 * 0.95 || nEventsInjOne > nEvents * ratioTrigger * 0.5 * 1.05) { + std::cerr << "Number of generated events injected with " << checkPdgQuarkOne << " different than expected\n"; + return 1; + } + if (nEventsInjTwo < nEvents * ratioTrigger * 0.5 * 0.95 || nEventsInjTwo > nEvents * ratioTrigger * 0.5 * 1.05) { + std::cerr << "Number of generated events injected with " << checkPdgQuarkTwo << " different than expected\n"; + return 1; + } + + if (nQuarksOne < nEvents * ratioTrigger) { // we expect anyway more because the same quark is repeated several time, after each gluon radiation + std::cerr << "Number of generated (anti)quarks " << checkPdgQuarkOne << " lower than expected\n"; + return 1; + } + if (nQuarksTwo < nEvents * ratioTrigger) { // we expect anyway more because the same quark is repeated several time, after each gluon radiation + std::cerr << "Number of generated (anti)quarks " << checkPdgQuarkTwo << " lower than expected\n"; + return 1; + } + + float fracForcedDecays = nSignals ? float(nSignalGoodDecay) / nSignals : 0.0f; + float uncFracForcedDecays = nSignals ? std::sqrt(fracForcedDecays * (1 - fracForcedDecays) / nSignals) : 1.0f; + if (1 - fracForcedDecays > 0.15 + uncFracForcedDecays) { // we put some tolerance (e.g. due to oscillations which might change the final state) + std::cerr << "Fraction of signals decaying into the correct channel " << fracForcedDecays << " lower than expected\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_and_bbbar_gap3.C b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_and_bbbar_gap3.C new file mode 100644 index 000000000..10f9b8ccb --- /dev/null +++ b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_and_bbbar_gap3.C @@ -0,0 +1,129 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + + int checkPdgQuarkOne{4}; + int checkPdgQuarkTwo{5}; + float ratioTrigger = 1./3; // one event triggered out of 3 + + std::vector checkPdgHadron{411, 421, 431, 4122, 4132, 4232, 4332}; + std::map>> checkHadronDecays{ // sorted pdg of daughters + {411, {{-321, 211, 211}, {-313, 211}, {211, 311}, {211, 333}}}, // D+ + {421, {{-321, 211}, {-321, 111, 211}}}, // D0 + {431, {{211, 333}, {-313, 321}}}, // Ds+ + {4122, {{-313, 2212}, {-321, 2224}, {211, 102134}, {-321, 211, 2212}, {311, 2212}}}, // Lc+ + {4132, {{211, 3312}}}, // Xic0 + {4232, {{-313, 2212}, {-321, 3324}, {211, 211, 3312}, {-321, 211, 2212}}}, // Xic+ + {4332, {{211, 3334}}} // Omegac+ + }; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int nEventsMB{}, nEventsInjOne{}, nEventsInjTwo{}; + int nQuarksOne{}, nQuarksTwo{}, nSignals{}, nSignalGoodDecay{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + + // check subgenerator information + if (eventHeader->hasInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID)) { + bool isValid = false; + int subGeneratorId = eventHeader->getInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID, isValid); + if (subGeneratorId == 0) { + nEventsMB++; + } else if (subGeneratorId == checkPdgQuarkOne) { + nEventsInjOne++; + } else if (subGeneratorId == checkPdgQuarkTwo) { + nEventsInjTwo++; + } + } + + for (auto &track : *tracks) { + auto pdg = track.GetPdgCode(); + if (std::abs(pdg) == checkPdgQuarkOne) { + nQuarksOne++; + continue; + } + if (std::abs(pdg) == checkPdgQuarkTwo) { + nQuarksTwo++; + continue; + } + if (std::find(checkPdgHadron.begin(), checkPdgHadron.end(), std::abs(pdg)) != checkPdgHadron.end()) { // found signal + nSignals++; // count signal PDG + + std::vector pdgsDecay{}; + std::vector pdgsDecayAntiPart{}; + for (int j{track.getFirstDaughterTrackId()}; j <= track.getLastDaughterTrackId(); ++j) { + auto pdgDau = tracks->at(j).GetPdgCode(); + pdgsDecay.push_back(pdgDau); + if (pdgDau != 333) { // phi is antiparticle of itself + pdgsDecayAntiPart.push_back(-pdgDau); + } else { + pdgsDecayAntiPart.push_back(pdgDau); + } + } + + std::sort(pdgsDecay.begin(), pdgsDecay.end()); + std::sort(pdgsDecayAntiPart.begin(), pdgsDecayAntiPart.end()); + + for (auto &decay : checkHadronDecays[std::abs(pdg)]) { + if (pdgsDecay == decay || pdgsDecayAntiPart == decay) { + nSignalGoodDecay++; + break; + } + } + } + } + } + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + std::cout << "# MB events: " << nEventsMB << "\n"; + std::cout << Form("# events injected with %d quark pair: ", checkPdgQuarkOne) << nEventsInjOne << "\n"; + std::cout << Form("# events injected with %d quark pair: ", checkPdgQuarkTwo) << nEventsInjTwo << "\n"; + std::cout << Form("# %d (anti)quarks: ", checkPdgQuarkOne) << nQuarksOne << "\n"; + std::cout << Form("# %d (anti)quarks: ", checkPdgQuarkTwo) << nQuarksTwo << "\n"; + std::cout <<"# signal hadrons: " << nSignals << "\n"; + std::cout <<"# signal hadrons decaying in the correct channel: " << nSignalGoodDecay << "\n"; + + if (nEventsMB < nEvents * (1 - ratioTrigger) * 0.95 || nEventsMB > nEvents * (1 - ratioTrigger) * 1.05) { // we put some tolerance since the number of generated events is small + std::cerr << "Number of generated MB events different than expected\n"; + return 1; + } + if (nEventsInjOne < nEvents * ratioTrigger * 0.5 * 0.95 || nEventsInjOne > nEvents * ratioTrigger * 0.5 * 1.05) { + std::cerr << "Number of generated events injected with " << checkPdgQuarkOne << " different than expected\n"; + return 1; + } + if (nEventsInjTwo < nEvents * ratioTrigger * 0.5 * 0.95 || nEventsInjTwo > nEvents * ratioTrigger * 0.5 * 1.05) { + std::cerr << "Number of generated events injected with " << checkPdgQuarkTwo << " different than expected\n"; + return 1; + } + + if (nQuarksOne < nEvents * ratioTrigger) { // we expect anyway more because the same quark is repeated several time, after each gluon radiation + std::cerr << "Number of generated (anti)quarks " << checkPdgQuarkOne << " lower than expected\n"; + return 1; + } + if (nQuarksTwo < nEvents * ratioTrigger) { // we expect anyway more because the same quark is repeated several time, after each gluon radiation + std::cerr << "Number of generated (anti)quarks " << checkPdgQuarkTwo << " lower than expected\n"; + return 1; + } + + float fracForcedDecays = nSignals ? float(nSignalGoodDecay) / nSignals : 0.0f; + float uncFracForcedDecays = nSignals ? std::sqrt(fracForcedDecays * (1 - fracForcedDecays) / nSignals) : 1.0f; + if (1 - fracForcedDecays > 0.15 + uncFracForcedDecays) { // we put some tolerance (e.g. due to oscillations which might change the final state) + std::cerr << "Fraction of signals decaying into the correct channel " << fracForcedDecays << " lower than expected\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_and_bbbar_gap3_Mode2.C b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_and_bbbar_gap3_Mode2.C new file mode 100644 index 000000000..3d5a21e0b --- /dev/null +++ b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_and_bbbar_gap3_Mode2.C @@ -0,0 +1,181 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + + int checkPdgQuarkOne{4}; + int checkPdgQuarkTwo{5}; + float ratioTrigger = 1./3; // one event triggered out of 3 + + std::vector checkPdgHadron{411, 421, 431, 4122, 4132, 4232, 4332}; + std::map>> checkHadronDecays{ // sorted pdg of daughters + {411, { + {-321, 211, 211}, // K- π+ π+ (non-resonant) + {-321, 111, 211, 211}, // K- π+ π+ π0 (non-resonant) + {-313, 321}, // K*0(892) K+ + {-10311, 321}, // K*0(1430) K+ + {211, 333}, // φ π+ + {-321, 211, 321}, // K- K+ π+ (non-resonant) + {113, 211}, // ρ0 π+ + {211, 225}, // f2(1270) π+ + {-211, 211, 211} // π- π+ π+ (non-resonant) + }}, + {421, { + {-321, 211}, // K- π+ (non-resonant) + {-321, 111, 211}, // K- π+ π0 + {-321, 213}, // ρ+ K- + {-313, 111}, // antiK*0(892) π0 + {-323, 211}, // K*-(892) π+ + {-211, 211}, // π- π+ + {-211, 213}, // ρ+ π- + {-211, 111, 211}, // π- π+ π0 + {-321, 321} // K- K+ + }}, + {431, { + {211, 333}, // φ π+ + {-313, 321}, // antiK*(892) K+ + {213, 333}, // φ ρ + {113, 211}, // ρ π+ + {211, 225}, // f2(1270) π+ + {-211, 211, 211}, // π- π+ π+ (s-wave) + {211, 313}, // K*(892)0 π+ + {321, 10221}, // f0(1370) K+ + {113, 321}, // ρ0 K+ + {-211, 211, 321}, // π- K+ π+ (non-resonant) + {211, 221} // η π+ + }}, + {4122, { + {-321, 211, 2212}, // p K- π+ (non-resonant) + {-313, 2212}, // p K*0(892) + {-321, 2224}, // Δ++ K- + {211, 102134}, // Λ(1520) π+ + {-321, 111, 211, 2212}, // p K- π+ π0 + {-211, 211, 2212}, // p π- π+ + {333, 2212} // p φ + }}, + {4232, { + {-321, 211, 2212}, // Xic+ -> p, K-, pi+ + {-313, 2212}, // Xic+ -> p, Kbar^*(892)0 + {211, 211, 3312}, // Xic+ -> Xi-, pi+, pi+ + {333, 2212}, // Xic+ -> p, phi(1020)0 + {-211, 211, 3222}, // Xic+ -> Sigma+, pi-, pi+ + {211, 3324} // Xic+ -> Xi(1530)0, pi+ + }}, + {4132, { + {211, 3312}, // Xic0 -> Xi-, pi+ + }}, + {4332, { + {211, 3334}, // Omegac0 -> Omega-, pi+ + {211, 3312} // Omegac0 -> Xi-, pi+ + }} + }; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int nEventsMB{}, nEventsInjOne{}, nEventsInjTwo{}; + int nQuarksOne{}, nQuarksTwo{}, nSignals{}, nSignalGoodDecay{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + + // check subgenerator information + if (eventHeader->hasInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID)) { + bool isValid = false; + int subGeneratorId = eventHeader->getInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID, isValid); + if (subGeneratorId == 0) { + nEventsMB++; + } else if (subGeneratorId == checkPdgQuarkOne) { + nEventsInjOne++; + } else if (subGeneratorId == checkPdgQuarkTwo) { + nEventsInjTwo++; + } + } + + for (auto &track : *tracks) { + auto pdg = track.GetPdgCode(); + if (std::abs(pdg) == checkPdgQuarkOne) { + nQuarksOne++; + continue; + } + if (std::abs(pdg) == checkPdgQuarkTwo) { + nQuarksTwo++; + continue; + } + if (std::find(checkPdgHadron.begin(), checkPdgHadron.end(), std::abs(pdg)) != checkPdgHadron.end()) { // found signal + nSignals++; // count signal PDG + + std::vector pdgsDecay{}; + std::vector pdgsDecayAntiPart{}; + for (int j{track.getFirstDaughterTrackId()}; j <= track.getLastDaughterTrackId(); ++j) { + auto pdgDau = tracks->at(j).GetPdgCode(); + pdgsDecay.push_back(pdgDau); + if (pdgDau != 333 && pdgDau != 111 && pdgDau != 221 && pdgDau != 113 && pdgDau != 225) { // phi is antiparticle of itself + pdgsDecayAntiPart.push_back(-pdgDau); + } else { + pdgsDecayAntiPart.push_back(pdgDau); + } + } + + std::sort(pdgsDecay.begin(), pdgsDecay.end()); + std::sort(pdgsDecayAntiPart.begin(), pdgsDecayAntiPart.end()); + + for (auto &decay : checkHadronDecays[std::abs(pdg)]) { + if (pdgsDecay == decay || pdgsDecayAntiPart == decay) { + nSignalGoodDecay++; + break; + } + } + } + } + } + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + std::cout << "# MB events: " << nEventsMB << "\n"; + std::cout << Form("# events injected with %d quark pair: ", checkPdgQuarkOne) << nEventsInjOne << "\n"; + std::cout << Form("# events injected with %d quark pair: ", checkPdgQuarkTwo) << nEventsInjTwo << "\n"; + std::cout << Form("# %d (anti)quarks: ", checkPdgQuarkOne) << nQuarksOne << "\n"; + std::cout << Form("# %d (anti)quarks: ", checkPdgQuarkTwo) << nQuarksTwo << "\n"; + std::cout <<"# signal hadrons: " << nSignals << "\n"; + std::cout <<"# signal hadrons decaying in the correct channel: " << nSignalGoodDecay << "\n"; + + if (nEventsMB < nEvents * (1 - ratioTrigger) * 0.95 || nEventsMB > nEvents * (1 - ratioTrigger) * 1.05) { // we put some tolerance since the number of generated events is small + std::cerr << "Number of generated MB events different than expected\n"; + return 1; + } + if (nEventsInjOne < nEvents * ratioTrigger * 0.5 * 0.95 || nEventsInjOne > nEvents * ratioTrigger * 0.5 * 1.05) { + std::cerr << "Number of generated events injected with " << checkPdgQuarkOne << " different than expected\n"; + return 1; + } + if (nEventsInjTwo < nEvents * ratioTrigger * 0.5 * 0.95 || nEventsInjTwo > nEvents * ratioTrigger * 0.5 * 1.05) { + std::cerr << "Number of generated events injected with " << checkPdgQuarkTwo << " different than expected\n"; + return 1; + } + + if (nQuarksOne < nEvents * ratioTrigger) { // we expect anyway more because the same quark is repeated several time, after each gluon radiation + std::cerr << "Number of generated (anti)quarks " << checkPdgQuarkOne << " lower than expected\n"; + return 1; + } + if (nQuarksTwo < nEvents * ratioTrigger) { // we expect anyway more because the same quark is repeated several time, after each gluon radiation + std::cerr << "Number of generated (anti)quarks " << checkPdgQuarkTwo << " lower than expected\n"; + return 1; + } + + float fracForcedDecays = nSignals ? float(nSignalGoodDecay) / nSignals : 0.0f; + float uncFracForcedDecays = nSignals ? std::sqrt(fracForcedDecays * (1 - fracForcedDecays) / nSignals) : 1.0f; + if (1 - fracForcedDecays > 0.15 + uncFracForcedDecays) { // we put some tolerance (e.g. due to oscillations which might change the final state) + std::cerr << "Fraction of signals decaying into the correct channel " << fracForcedDecays << " lower than expected\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_and_bbbar_gap3_Mode2_XiC.C b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_and_bbbar_gap3_Mode2_XiC.C new file mode 100644 index 000000000..9d6cbac69 --- /dev/null +++ b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_and_bbbar_gap3_Mode2_XiC.C @@ -0,0 +1,123 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + + int checkPdgQuarkOne{4}; + int checkPdgQuarkTwo{5}; + float ratioTrigger = 1./3; // one event triggered out of 3 + + std::vector checkPdgHadron{4132}; + std::map>> checkHadronDecays{ // sorted pdg of daughters + {4132, {{211, 3312}}} // Xic0 + }; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int nEventsMB{}, nEventsInjOne{}, nEventsInjTwo{}; + int nQuarksOne{}, nQuarksTwo{}, nSignals{}, nSignalGoodDecay{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + + // check subgenerator information + if (eventHeader->hasInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID)) { + bool isValid = false; + int subGeneratorId = eventHeader->getInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID, isValid); + if (subGeneratorId == 0) { + nEventsMB++; + } else if (subGeneratorId == checkPdgQuarkOne) { + nEventsInjOne++; + } else if (subGeneratorId == checkPdgQuarkTwo) { + nEventsInjTwo++; + } + } + + for (auto &track : *tracks) { + auto pdg = track.GetPdgCode(); + if (std::abs(pdg) == checkPdgQuarkOne) { + nQuarksOne++; + continue; + } + if (std::abs(pdg) == checkPdgQuarkTwo) { + nQuarksTwo++; + continue; + } + if (std::find(checkPdgHadron.begin(), checkPdgHadron.end(), std::abs(pdg)) != checkPdgHadron.end()) { // found signal + nSignals++; // count signal PDG + + std::vector pdgsDecay{}; + std::vector pdgsDecayAntiPart{}; + for (int j{track.getFirstDaughterTrackId()}; j <= track.getLastDaughterTrackId(); ++j) { + auto pdgDau = tracks->at(j).GetPdgCode(); + pdgsDecay.push_back(pdgDau); + if (pdgDau != 333) { // phi is antiparticle of itself + pdgsDecayAntiPart.push_back(-pdgDau); + } else { + pdgsDecayAntiPart.push_back(pdgDau); + } + } + + std::sort(pdgsDecay.begin(), pdgsDecay.end()); + std::sort(pdgsDecayAntiPart.begin(), pdgsDecayAntiPart.end()); + + for (auto &decay : checkHadronDecays[std::abs(pdg)]) { + if (pdgsDecay == decay || pdgsDecayAntiPart == decay) { + nSignalGoodDecay++; + break; + } + } + } + } + } + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + std::cout << "# MB events: " << nEventsMB << "\n"; + std::cout << Form("# events injected with %d quark pair: ", checkPdgQuarkOne) << nEventsInjOne << "\n"; + std::cout << Form("# events injected with %d quark pair: ", checkPdgQuarkTwo) << nEventsInjTwo << "\n"; + std::cout << Form("# %d (anti)quarks: ", checkPdgQuarkOne) << nQuarksOne << "\n"; + std::cout << Form("# %d (anti)quarks: ", checkPdgQuarkTwo) << nQuarksTwo << "\n"; + std::cout <<"# signal hadrons: " << nSignals << "\n"; + std::cout <<"# signal hadrons decaying in the correct channel: " << nSignalGoodDecay << "\n"; + + if (nEventsMB < nEvents * (1 - ratioTrigger) * 0.95 || nEventsMB > nEvents * (1 - ratioTrigger) * 1.05) { // we put some tolerance since the number of generated events is small + std::cerr << "Number of generated MB events different than expected\n"; + return 1; + } + if (nEventsInjOne < nEvents * ratioTrigger * 0.5 * 0.95 || nEventsInjOne > nEvents * ratioTrigger * 0.5 * 1.05) { + std::cerr << "Number of generated events injected with " << checkPdgQuarkOne << " different than expected\n"; + return 1; + } + if (nEventsInjTwo < nEvents * ratioTrigger * 0.5 * 0.95 || nEventsInjTwo > nEvents * ratioTrigger * 0.5 * 1.05) { + std::cerr << "Number of generated events injected with " << checkPdgQuarkTwo << " different than expected\n"; + return 1; + } + + if (nQuarksOne < nEvents * ratioTrigger) { // we expect anyway more because the same quark is repeated several time, after each gluon radiation + std::cerr << "Number of generated (anti)quarks " << checkPdgQuarkOne << " lower than expected\n"; + return 1; + } + if (nQuarksTwo < nEvents * ratioTrigger) { // we expect anyway more because the same quark is repeated several time, after each gluon radiation + std::cerr << "Number of generated (anti)quarks " << checkPdgQuarkTwo << " lower than expected\n"; + return 1; + } + + float fracForcedDecays = nSignals ? float(nSignalGoodDecay) / nSignals : 0.0f; + float uncFracForcedDecays = nSignals ? std::sqrt(fracForcedDecays * (1 - fracForcedDecays) / nSignals) : 1.0f; + if (1 - fracForcedDecays > 0.15 + uncFracForcedDecays) { // we put some tolerance (e.g. due to oscillations which might change the final state) + std::cerr << "Fraction of signals decaying into the correct channel " << fracForcedDecays << " lower than expected\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_and_bbbar_gap5.C b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_and_bbbar_gap5.C new file mode 100644 index 000000000..f36c8a02f --- /dev/null +++ b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_and_bbbar_gap5.C @@ -0,0 +1,129 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + + int checkPdgQuarkOne{4}; + int checkPdgQuarkTwo{5}; + float ratioTrigger = 1./5; // one event triggered out of 5 + + std::vector checkPdgHadron{411, 421, 431, 4122, 4132, 4232, 4332}; + std::map>> checkHadronDecays{ // sorted pdg of daughters + {411, {{-321, 211, 211}, {-313, 211}, {211, 311}, {211, 333}}}, // D+ + {421, {{-321, 211}, {-321, 111, 211}}}, // D0 + {431, {{211, 333}, {-313, 321}}}, // Ds+ + {4122, {{-313, 2212}, {-321, 2224}, {211, 102134}, {-321, 211, 2212}, {311, 2212}}}, // Lc+ + {4132, {{211, 3312}}}, // Xic0 + {4232, {{-313, 2212}, {-321, 3324}, {211, 211, 3312}, {-321, 211, 2212}}}, // Xic+ + {4332, {{211, 3334}}} // Omegac+ + }; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int nEventsMB{}, nEventsInjOne{}, nEventsInjTwo{}; + int nQuarksOne{}, nQuarksTwo{}, nSignals{}, nSignalGoodDecay{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + + // check subgenerator information + if (eventHeader->hasInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID)) { + bool isValid = false; + int subGeneratorId = eventHeader->getInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID, isValid); + if (subGeneratorId == 0) { + nEventsMB++; + } else if (subGeneratorId == checkPdgQuarkOne) { + nEventsInjOne++; + } else if (subGeneratorId == checkPdgQuarkTwo) { + nEventsInjTwo++; + } + } + + for (auto &track : *tracks) { + auto pdg = track.GetPdgCode(); + if (std::abs(pdg) == checkPdgQuarkOne) { + nQuarksOne++; + continue; + } + if (std::abs(pdg) == checkPdgQuarkTwo) { + nQuarksTwo++; + continue; + } + if (std::find(checkPdgHadron.begin(), checkPdgHadron.end(), std::abs(pdg)) != checkPdgHadron.end()) { // found signal + nSignals++; // count signal PDG + + std::vector pdgsDecay{}; + std::vector pdgsDecayAntiPart{}; + for (int j{track.getFirstDaughterTrackId()}; j <= track.getLastDaughterTrackId(); ++j) { + auto pdgDau = tracks->at(j).GetPdgCode(); + pdgsDecay.push_back(pdgDau); + if (pdgDau != 333) { // phi is antiparticle of itself + pdgsDecayAntiPart.push_back(-pdgDau); + } else { + pdgsDecayAntiPart.push_back(pdgDau); + } + } + + std::sort(pdgsDecay.begin(), pdgsDecay.end()); + std::sort(pdgsDecayAntiPart.begin(), pdgsDecayAntiPart.end()); + + for (auto &decay : checkHadronDecays[std::abs(pdg)]) { + if (pdgsDecay == decay || pdgsDecayAntiPart == decay) { + nSignalGoodDecay++; + break; + } + } + } + } + } + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + std::cout << "# MB events: " << nEventsMB << "\n"; + std::cout << Form("# events injected with %d quark pair: ", checkPdgQuarkOne) << nEventsInjOne << "\n"; + std::cout << Form("# events injected with %d quark pair: ", checkPdgQuarkTwo) << nEventsInjTwo << "\n"; + std::cout << Form("# %d (anti)quarks: ", checkPdgQuarkOne) << nQuarksOne << "\n"; + std::cout << Form("# %d (anti)quarks: ", checkPdgQuarkTwo) << nQuarksTwo << "\n"; + std::cout <<"# signal hadrons: " << nSignals << "\n"; + std::cout <<"# signal hadrons decaying in the correct channel: " << nSignalGoodDecay << "\n"; + + if (nEventsMB < nEvents * (1 - ratioTrigger) * 0.95 || nEventsMB > nEvents * (1 - ratioTrigger) * 1.05) { // we put some tolerance since the number of generated events is small + std::cerr << "Number of generated MB events different than expected\n"; + return 1; + } + if (nEventsInjOne < nEvents * ratioTrigger * 0.5 * 0.95 || nEventsInjOne > nEvents * ratioTrigger * 0.5 * 1.05) { + std::cerr << "Number of generated events injected with " << checkPdgQuarkOne << " different than expected\n"; + return 1; + } + if (nEventsInjTwo < nEvents * ratioTrigger * 0.5 * 0.95 || nEventsInjTwo > nEvents * ratioTrigger * 0.5 * 1.05) { + std::cerr << "Number of generated events injected with " << checkPdgQuarkTwo << " different than expected\n"; + return 1; + } + + if (nQuarksOne < nEvents * ratioTrigger) { // we expect anyway more because the same quark is repeated several time, after each gluon radiation + std::cerr << "Number of generated (anti)quarks " << checkPdgQuarkOne << " lower than expected\n"; + return 1; + } + if (nQuarksTwo < nEvents * ratioTrigger) { // we expect anyway more because the same quark is repeated several time, after each gluon radiation + std::cerr << "Number of generated (anti)quarks " << checkPdgQuarkTwo << " lower than expected\n"; + return 1; + } + + float fracForcedDecays = nSignals ? float(nSignalGoodDecay) / nSignals : 0.0f; + float uncFracForcedDecays = nSignals ? std::sqrt(fracForcedDecays * (1 - fracForcedDecays) / nSignals) : 1.0f; + if (1 - fracForcedDecays > 0.15 + uncFracForcedDecays) { // we put some tolerance (e.g. due to oscillations which might change the final state) + std::cerr << "Fraction of signals decaying into the correct channel " << fracForcedDecays << " lower than expected\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_and_bbbar_gap5_DReso.C b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_and_bbbar_gap5_DReso.C new file mode 100644 index 000000000..ed1021a5d --- /dev/null +++ b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_and_bbbar_gap5_DReso.C @@ -0,0 +1,141 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + + int checkPdgQuarkOne{4}; + int checkPdgQuarkTwo{5}; + float ratioTrigger = 1./5.; // one event triggered out of 5 + + std::vector checkPdgHadron{411, 415, 421, 425, 431, 435, 511, 521, 531, 4122, 10411, 10421, 10433, 20423, 20433}; + std::map>> checkHadronDecays{ // sorted pdg of daughters + {411, {{-321, 211, 211}, {-313, 211}, {211, 311}, {211, 333}}}, // D+ + {415, {{211, 421}}}, // D2*(2460)+ + {421, {{-321, 211}, {-321, 111, 211}}}, // D0 + {425, {{-211, 413},{-211, 411}}}, // D2*(2460)0 + {431, {{211, 333}, {-313, 321}}}, // Ds+ + {435, {{311, 413}, {311, 411}, {321,421}}}, // Ds2*(2573) + {511, {{-415, -11, 12}, {-10411, -11, 12}, {-415, -13, 14}, {-10411, -13, 14}, {-415, -15, 16}, {-10411, -15, 16}, + {-10411, 211}, {-10421, 211}, {-415, 433}, { -415, 431}, {-415, 211}, {-415, 213} }}, // B0 + {521, {{-20423, -11, 12}, {-425, -11, 12}, {-10421, -11, 12}, {-20423, -13, 14}, {-425, -13, 14}, {-10421, -13, 14}, + {-20423, -15, 16}, {-425, -15, 16}, {-10421, -15, 16}, {-20423, 211}, {-20423, 213}, {-20423, 431}, {-20423, 433}, + {-425, 211}, {-425, 213}, {-425, 431}, {-425, 433}}}, // B+ + {531, {{-435, -11, 12}, {-10433, -11, 12}, {-435, -13, 14}, {-10433, -13, 14}, {-435, -15, 16}, {-10433, -15, 16}, + {-435, 211}, {-20433, 211}, {-20433, 213}}},// Bs0 + {4122, {{-313, 2212}, {-321, 2224}, {211, 3124}, {-321, 211, 2212}, {311, 2212}}}, // Lc+ + {10411, {{211, 421}}}, // D0*+ + {10421, {{-211, 411}}}, // D0*0 + {10433, {{311, 413}}}, // Ds1(2536) + {20423, {{-211, 413}}}, // D1(2430)0 + {20433, {{22, 431}, {-211, 211, 431} }} // Ds1 (2460) + }; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int nEventsMB{}, nEventsInjOne{}, nEventsInjTwo{}; + int nQuarksOne{}, nQuarksTwo{}, nSignals{}, nSignalGoodDecay{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + + // check subgenerator information + if (eventHeader->hasInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID)) { + bool isValid = false; + int subGeneratorId = eventHeader->getInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID, isValid); + if (subGeneratorId == 0) { + nEventsMB++; + } else if (subGeneratorId == checkPdgQuarkOne) { + nEventsInjOne++; + } else if (subGeneratorId == checkPdgQuarkTwo) { + nEventsInjTwo++; + } + } + + for (auto &track : *tracks) { + auto pdg = track.GetPdgCode(); + if (std::abs(pdg) == checkPdgQuarkOne) { + nQuarksOne++; + continue; + } + if (std::abs(pdg) == checkPdgQuarkTwo) { + nQuarksTwo++; + continue; + } + if (std::find(checkPdgHadron.begin(), checkPdgHadron.end(), std::abs(pdg)) != checkPdgHadron.end()) { // found signal + nSignals++; // count signal PDG + + std::vector pdgsDecay{}; + std::vector pdgsDecayAntiPart{}; + for (int j{track.getFirstDaughterTrackId()}; j <= track.getLastDaughterTrackId(); ++j) { + auto pdgDau = tracks->at(j).GetPdgCode(); + pdgsDecay.push_back(pdgDau); + if (pdgDau != 333) { // phi is antiparticle of itself + pdgsDecayAntiPart.push_back(-pdgDau); + } else { + pdgsDecayAntiPart.push_back(pdgDau); + } + } + + std::sort(pdgsDecay.begin(), pdgsDecay.end()); + std::sort(pdgsDecayAntiPart.begin(), pdgsDecayAntiPart.end()); + + for (auto &decay : checkHadronDecays[std::abs(pdg)]) { + if (pdgsDecay == decay || pdgsDecayAntiPart == decay) { + nSignalGoodDecay++; + break; + } + } + } + } + } + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + std::cout << "# MB events: " << nEventsMB << "\n"; + std::cout << Form("# events injected with %d quark pair: ", checkPdgQuarkOne) << nEventsInjOne << "\n"; + std::cout << Form("# events injected with %d quark pair: ", checkPdgQuarkTwo) << nEventsInjTwo << "\n"; + std::cout << Form("# %d (anti)quarks: ", checkPdgQuarkOne) << nQuarksOne << "\n"; + std::cout << Form("# %d (anti)quarks: ", checkPdgQuarkTwo) << nQuarksTwo << "\n"; + std::cout <<"# signal hadrons: " << nSignals << "\n"; + std::cout <<"# signal hadrons decaying in the correct channel: " << nSignalGoodDecay << "\n"; + + if (nEventsMB < nEvents * (1 - ratioTrigger) * 0.95 || nEventsMB > nEvents * (1 - ratioTrigger) * 1.05) { // we put some tolerance since the number of generated events is small + std::cerr << "Number of generated MB events different than expected\n"; + return 1; + } + if (nEventsInjOne < nEvents * ratioTrigger * 0.5 * 0.95 || nEventsInjOne > nEvents * ratioTrigger * 0.5 * 1.05) { + std::cerr << "Number of generated events injected with " << checkPdgQuarkOne << " different than expected\n"; + return 1; + } + if (nEventsInjTwo < nEvents * ratioTrigger * 0.5 * 0.95 || nEventsInjTwo > nEvents * ratioTrigger * 0.5 * 1.05) { + std::cerr << "Number of generated events injected with " << checkPdgQuarkTwo << " different than expected\n"; + return 1; + } + + if (nQuarksOne < nEvents * ratioTrigger) { // we expect anyway more because the same quark is repeated several time, after each gluon radiation + std::cerr << "Number of generated (anti)quarks " << checkPdgQuarkOne << " lower than expected\n"; + return 1; + } + if (nQuarksTwo < nEvents * ratioTrigger) { // we expect anyway more because the same quark is repeated several time, after each gluon radiation + std::cerr << "Number of generated (anti)quarks " << checkPdgQuarkTwo << " lower than expected\n"; + return 1; + } + + float fracForcedDecays = nSignals ? float(nSignalGoodDecay) / nSignals : 0.0f; + float uncFracForcedDecays = nSignals ? std::sqrt(fracForcedDecays * (1 - fracForcedDecays) / nSignals) : 1.0f; + if (1 - fracForcedDecays > 0.15 + uncFracForcedDecays) { // we put some tolerance (e.g. due to oscillations which might change the final state) + std::cerr << "Fraction of signals decaying into the correct channel " << fracForcedDecays << " lower than expected\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_and_bbbar_gap5_DResoTrigger.C b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_and_bbbar_gap5_DResoTrigger.C new file mode 100644 index 000000000..702c1ddad --- /dev/null +++ b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_and_bbbar_gap5_DResoTrigger.C @@ -0,0 +1,146 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + + int checkPdgQuarkOne{4}; + int checkPdgQuarkTwo{5}; + float ratioTrigger = 1./5.; // one event triggered out of 5 + std::array, 6> pdgReplParticles = {std::array{10433, 30433}, std::array{10433, 437}, std::array{435, 4325}, std::array{435, 4326}, std::array{425, 4315}, std::array{425, 4316}}; + std::array, 6> pdgReplPartCounters = {std::array{0, 0}, std::array{0, 0}, std::array{0, 0}, std::array{0, 0}, std::array{0, 0}, std::array{0, 0}}; + std::array freqRepl = {0.1, 0.1, 0.1, 0.1, 0.5, 0.5}; + std::map sumOrigReplacedParticles = {{10433, 0}, {435, 0}, {425, 0}}; + + std::array checkPdgHadron{411, 421, 10433, 30433, 435, 437, 4325, 4326, 4315, 4316, 531, 425}; + std::map>> checkHadronDecays{ // sorted pdg of daughters + {411, {{-321, 211, 211}, {-313, 211}, {211, 311}, {211, 333}}}, // D+ + {421, {{-321, 211}, {-321, 111, 211}}}, // D0 + {435, {{311, 413}, {311, 411}}}, // Ds2*(2573) + {10433, {{311, 413}}}, // Ds1(2536) + {30433, {{311, 413}}}, // Ds1*(2700) + {437, {{311, 413}}}, // Ds3*(2860) + {4325, {{411, 3122}}}, // Xic(3055)+ + {4326, {{411, 3122}}}, // Xic(3080)+ + {4315, {{421, 3122}}}, // Xic(3055)+ + {4316, {{421, 3122}}}, // Xic(3080)+ + {531, {{-435, -11, 12}, {-10433, -11, 12}, {-435, -13, 14}, {-10433, -13, 14}, {-435, -15, 16}, {-10433, -15, 16}, {-435, 211}}}, // Bs0 + {425, {{-211, 413}, {111, 423}, {-211, 411}, {111, 421}, {-211, 111, 413}, {-211, 211, 423}}} + }; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int nEventsMB{}, nEventsInjOne{}, nEventsInjTwo{}; + int nSignals{}, nSignalGoodDecay{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + + // check subgenerator information + int subGeneratorId{-1}; + if (eventHeader->hasInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID)) { + bool isValid = false; + subGeneratorId = eventHeader->getInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID, isValid); + if (subGeneratorId == 0) { + nEventsMB++; + } else if (subGeneratorId == checkPdgQuarkOne) { + nEventsInjOne++; + } else if (subGeneratorId == checkPdgQuarkTwo) { + nEventsInjTwo++; + } + } + + for (auto &track : *tracks) { + auto pdg = track.GetPdgCode(); + auto absPdg = std::abs(pdg); + if (std::find(checkPdgHadron.begin(), checkPdgHadron.end(), absPdg) != checkPdgHadron.end()) { // found signal + nSignals++; // count signal PDG + + if (subGeneratorId == checkPdgQuarkOne) { // replacement only for prompt + for (int iRepl{0}; iRepl<6; ++iRepl) { + if (absPdg == pdgReplParticles[iRepl][0]) { + pdgReplPartCounters[iRepl][0]++; + sumOrigReplacedParticles[pdgReplParticles[iRepl][0]]++; + } else if (absPdg == pdgReplParticles[iRepl][1]) { + pdgReplPartCounters[iRepl][1]++; + sumOrigReplacedParticles[pdgReplParticles[iRepl][0]]++; + } + } + } + + std::vector pdgsDecay{}; + std::vector pdgsDecayAntiPart{}; + if (track.getFirstDaughterTrackId() >= 0 && track.getLastDaughterTrackId() >= 0) { + for (int j{track.getFirstDaughterTrackId()}; j <= track.getLastDaughterTrackId(); ++j) { + auto pdgDau = tracks->at(j).GetPdgCode(); + pdgsDecay.push_back(pdgDau); + if (pdgDau != 333 && pdgDau != 111) { // phi and pi0 are antiparticles of themselves + pdgsDecayAntiPart.push_back(-pdgDau); + } else { + pdgsDecayAntiPart.push_back(pdgDau); + } + } + } + + std::sort(pdgsDecay.begin(), pdgsDecay.end()); + std::sort(pdgsDecayAntiPart.begin(), pdgsDecayAntiPart.end()); + + for (auto &decay : checkHadronDecays[std::abs(pdg)]) { + if (pdgsDecay == decay || pdgsDecayAntiPart == decay) { + nSignalGoodDecay++; + break; + } + } + } + } + } + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + std::cout << "# MB events: " << nEventsMB << "\n"; + std::cout << Form("# events injected with %d quark pair: ", checkPdgQuarkOne) << nEventsInjOne << "\n"; + std::cout << Form("# events injected with %d quark pair: ", checkPdgQuarkTwo) << nEventsInjTwo << "\n"; + std::cout <<"# signal hadrons: " << nSignals << "\n"; + std::cout <<"# signal hadrons decaying in the correct channel: " << nSignalGoodDecay << "\n"; + + if (nEventsMB < nEvents * (1 - ratioTrigger) * 0.95 || nEventsMB > nEvents * (1 - ratioTrigger) * 1.05) { // we put some tolerance since the number of generated events is small + std::cerr << "Number of generated MB events different than expected\n"; + return 1; + } + if (nEventsInjOne < nEvents * ratioTrigger * 0.5 * 0.95 || nEventsInjOne > nEvents * ratioTrigger * 0.5 * 1.05) { + std::cerr << "Number of generated events injected with " << checkPdgQuarkOne << " different than expected\n"; + return 1; + } + if (nEventsInjTwo < nEvents * ratioTrigger * 0.5 * 0.95 || nEventsInjTwo > nEvents * ratioTrigger * 0.5 * 1.05) { + std::cerr << "Number of generated events injected with " << checkPdgQuarkTwo << " different than expected\n"; + return 1; + } + + float fracForcedDecays = nSignals ? float(nSignalGoodDecay) / nSignals : 0.0f; + float uncFracForcedDecays = nSignals ? std::sqrt(fracForcedDecays * (1 - fracForcedDecays) / nSignals) : 1.0f; + if (1 - fracForcedDecays > 0.15 + uncFracForcedDecays) { // we put some tolerance (e.g. due to oscillations which might change the final state) + std::cerr << "Fraction of signals decaying into the correct channel " << fracForcedDecays << " lower than expected\n"; + return 1; + } + + for (int iRepl{0}; iRepl<6; ++iRepl) { + float numPart = sumOrigReplacedParticles[pdgReplParticles[iRepl][0]]; + float fracMeas = numPart ? float(pdgReplPartCounters[iRepl][1]) / numPart : 0.0f; + float fracMeasUnc = (fracMeas && numPart != 1) ? std::sqrt(pdgReplPartCounters[iRepl][1]) / numPart : 1.0f; + + if (std::abs(fracMeas - freqRepl[iRepl]) > fracMeasUnc) { + std::cerr << "Fraction of replaced " << pdgReplParticles[iRepl][0] << " into " << pdgReplParticles[iRepl][1] << " is " << fracMeas <<" (expected "<< freqRepl[iRepl] << ")\n"; + return 1; + } + } + + return 0; +} diff --git a/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_and_bbbar_gap5_LcResoTrigger.C b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_and_bbbar_gap5_LcResoTrigger.C new file mode 100644 index 000000000..a2c5fa546 --- /dev/null +++ b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_and_bbbar_gap5_LcResoTrigger.C @@ -0,0 +1,131 @@ +int ExternalLc() { + std::string path{"o2sim_Kine.root"}; + + int checkPdgQuarkOne{4}; // c quark injection + int checkPdgQuarkTwo{5}; // b quark injection + float ratioTrigger = 1./5.; // one event triggered out of 5 + + // PDG replacements: Λc(4122) -> resonances + std::array, 4> pdgReplParticles = { + std::array{4122, 24124}, // Λc -> Λc(2860) + std::array{4122, 24126}, // Λc -> Λc(2880) + std::array{4122, 4125}, // Λc -> Λc(2940) + std::array{4122, 9422111} // Λc -> Tc(3100) + }; + + // Counters for replacements + std::array, 4> pdgReplPartCounters = { + std::array{0,0}, std::array{0,0}, std::array{0,0}, std::array{0,0} + }; + + std::array freqRepl = {0.2, 0.2, 0.2, 0.2}; + std::map sumOrigReplacedParticles = {{4122,0}}; + + // Hadrons to check + std::array checkPdgHadron{24124, 24126, 4125, 9422111, 5122}; + + // Correct decays + std::map>> checkHadronDecays{ + {24124, {{421, 2212}}}, + {24126, {{421, 2212}}}, + {4125, {{421, 2212}}}, + {9422111, {{413, 2212}}}, + {5122, {{421, 2212, -211}, {41221, -211}, {24124, -211}, {24126, -211}, {4125, -211}}} + }; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { std::cerr << "Cannot open ROOT file " << path << "\n"; return 1; } + + auto tree = (TTree*)file.Get("o2sim"); + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + o2::dataformats::MCEventHeader* eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int nEventsMB{}, nEventsInjOne{}, nEventsInjTwo{}; + int nSignals{}, nSignalGoodDecay{}; + auto nEvents = tree->GetEntries(); + + std::map signalHadronsPerType; + std::map signalGoodDecayPerType; + for (auto pdg : checkPdgHadron) { signalHadronsPerType[pdg] = 0; signalGoodDecayPerType[pdg] = 0; } + + for (int i=0; iGetEntry(i); + + int subGeneratorId{-1}; + if (eventHeader->hasInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID)) { + bool isValid=false; + subGeneratorId = eventHeader->getInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID, isValid); + if (subGeneratorId==0) nEventsMB++; + else if (subGeneratorId==checkPdgQuarkOne) nEventsInjOne++; + else if (subGeneratorId==checkPdgQuarkTwo) nEventsInjTwo++; + } + + for (auto& track : *tracks) { + int pdg = track.GetPdgCode(); + int absPdg = std::abs(pdg); + + if (std::find(checkPdgHadron.begin(), checkPdgHadron.end(), absPdg) != checkPdgHadron.end()) { + nSignals++; + signalHadronsPerType[absPdg]++; + + if (subGeneratorId==checkPdgQuarkOne) { + for (int iRepl=0; iRepl<3; ++iRepl) { + if (absPdg == pdgReplParticles[iRepl][0]) { pdgReplPartCounters[iRepl][0]++; sumOrigReplacedParticles[pdgReplParticles[iRepl][0]]++; } + else if (absPdg == pdgReplParticles[iRepl][1]) { pdgReplPartCounters[iRepl][1]++; sumOrigReplacedParticles[pdgReplParticles[iRepl][0]]++; } + } + } + + std::vector pdgsDecay; + std::vector pdgsDecayAnti; + if (track.getFirstDaughterTrackId()>=0) { + for (int j=track.getFirstDaughterTrackId(); j<=track.getLastDaughterTrackId(); ++j) { + int pdgDau = tracks->at(j).GetPdgCode(); + pdgsDecay.push_back(pdgDau); + pdgsDecayAnti.push_back((pdgDau==111||pdgDau==333)? pdgDau : -pdgDau); + } + } + + std::sort(pdgsDecay.begin(), pdgsDecay.end()); + std::sort(pdgsDecayAnti.begin(), pdgsDecayAnti.end()); + + bool matchedDecay = false; + for (auto& decay : checkHadronDecays[absPdg]) { + std::vector decayCopy = decay; + std::sort(decayCopy.begin(), decayCopy.end()); + if (pdgsDecay == decayCopy || pdgsDecayAnti == decayCopy) { + matchedDecay = true; + nSignalGoodDecay++; + signalGoodDecayPerType[absPdg]++; + break; + } + } + + // Print daughters + std::cout << "Particle " << absPdg << " daughters: "; + for (auto d : pdgsDecay) std::cout << d << " "; + if (matchedDecay) std::cout << "(matches expected decay)"; + else std::cout << "(does NOT match expected decay)"; + std::cout << "\n"; + } + } + } + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + std::cout << "# MB events: " << nEventsMB << "\n"; + std::cout << "# events injected with " << checkPdgQuarkOne << ": " << nEventsInjOne << "\n"; + std::cout << "# events injected with " << checkPdgQuarkTwo << ": " << nEventsInjTwo << "\n"; + std::cout << "# signal hadrons: " << nSignals << "\n"; + std::cout << "# signal hadrons decaying in correct channels: " << nSignalGoodDecay << "\n"; + + for (auto& [pdg, count] : signalHadronsPerType) { + int good = signalGoodDecayPerType[pdg]; + float frac = count>0 ? float(good)/count : 0.; + std::cout << "Particle " << pdg << ": " << count << " signals, " << good << " good decays, fraction: " << frac << "\n"; + } + + // Optional: sanity checks... + return 0; +} diff --git a/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_and_bbbar_gap5_Mode2.C b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_and_bbbar_gap5_Mode2.C new file mode 100644 index 000000000..3f348adb3 --- /dev/null +++ b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_and_bbbar_gap5_Mode2.C @@ -0,0 +1,191 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + + int checkPdgQuarkOne{4}; + int checkPdgQuarkTwo{5}; + float ratioTrigger = 1./5; // one event triggered out of 5 + + std::vector checkPdgHadron{411, 421, 431, 4122, 4132, 4232, 4332}; + std::map>> checkHadronDecays{ // sorted pdg of daughters + {421, { + {-321, 211}, // D0 -> K-, pi+ + {-321, 211, 111}, // D0 -> K-, pi+, pi0 + {213, -321}, // D0 -> rho(770)+, K- + {-313, 111}, // D0 -> Kbar^*(892)0, pi0 + {-323, 211}, // D0 -> K^*(892)-, pi+ + {-211, 211}, // D0 -> pi-, pi+ + {213, -211}, // D0 -> rho(770)+, pi- + {-211, 211, 111}, // D0 -> pi-, pi+, pi0 + {-321, 321}, // D0 -> K-, K+ + }}, + + {411, { + {-321, 211, 211}, // D+ -> K-, pi+, pi+ + {-10311, 211}, // D+ -> Kbar0^*(1430)0, pi+ + {-313, 211}, // D+ -> Kbar^*(892)0, pi+ + {-321, 211, 211, 111}, // D+ -> K-, pi+, pi+, pi0 + {333, 211}, // D+ -> phi(1020)0, pi+ + {-313, 321}, // D+ -> Kbar^*(892)0, K+ + {-10311, 321}, // D+ -> Kbar0^*(1430)0, K+ + {-321, 321, 211}, // D+ -> K-, K+, pi+ + {113, 211}, // D+ -> rho(770)0, pi+ + {225, 211}, // D+ -> f2(1270)0, pi+ + {-211, 211, 211}, // D+ -> pi-, pi+, pi+ + }}, + + {431, { + {333, 211}, // Ds+ -> phi(1020)0, pi+ + {-313, 321}, // Ds+ -> Kbar^*(892)0, K+ + {333, 213}, // Ds+ -> phi(1020)0, rho(770)+ + {113, 211}, // Ds+ -> rho(770)0, pi+ + {225, 211}, // Ds+ -> f2(1270)0, pi+ + {-211, 211, 211}, // Ds+ -> pi-, pi+, pi+ + {313, 211}, // Ds+ -> K^*(892)0, pi+ + {10221, 321}, // Ds+ -> f0(1370)0, K+ + {113, 321}, // Ds+ -> rho(770)0, K+ + {-211, 321, 211}, // Ds+ -> pi-, K+, pi+ + {221, 211}, // Ds+ -> eta, pi+ + }}, + + {4122, { + {2212, -321, 211}, // Lambdac+ -> p, K-, pi+ + {2212, -313}, // Lambdac+ -> p, Kbar^*(892)0 + {2224, -321}, // Lambdac+ -> Delta(1232)++, K- + {102134, 211}, // Lambdac+ -> 102134, pi+ + {2212, 311}, // Lambdac+ -> p, K0 + {2212, -321, 211, 111}, // Lambdac+ -> p, K-, pi+, pi0 + {2212, -211, 211}, // Lambdac+ -> p, pi-, pi+ + {2212, 333}, // Lambdac+ -> p, phi(1020)0 + }}, + + {4232, { + {2212, -321, 211}, // Xic+ -> p, K-, pi+ + {2212, -313}, // Xic+ -> p, Kbar^*(892)0 + {3312, 211, 211}, // Xic+ -> Xi-, pi+, pi+ + {2212, 333}, // Xic+ -> p, phi(1020)0 + {3222, -211, 211}, // Xic+ -> Sigma+, pi-, pi+ + {3324, 211}, // Xic+ -> Xi(1530)0, pi+ + }}, + + {4132, { + {3312, 211}, // Xic0 -> Xi-, pi+ + }}, + + {4332, { + {3334, 211}, // Omegac0 -> Omega-, pi+ + {3312, 211}, // Omegac0 -> Xi-, pi+ + }}, + }; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int nEventsMB{}, nEventsInjOne{}, nEventsInjTwo{}; + int nQuarksOne{}, nQuarksTwo{}, nSignals{}, nSignalGoodDecay{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + + // check subgenerator information + if (eventHeader->hasInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID)) { + bool isValid = false; + int subGeneratorId = eventHeader->getInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID, isValid); + if (subGeneratorId == 0) { + nEventsMB++; + } else if (subGeneratorId == checkPdgQuarkOne) { + nEventsInjOne++; + } else if (subGeneratorId == checkPdgQuarkTwo) { + nEventsInjTwo++; + } + } + + for (auto &track : *tracks) { + auto pdg = track.GetPdgCode(); + if (std::abs(pdg) == checkPdgQuarkOne) { + nQuarksOne++; + continue; + } + if (std::abs(pdg) == checkPdgQuarkTwo) { + nQuarksTwo++; + continue; + } + if (std::find(checkPdgHadron.begin(), checkPdgHadron.end(), std::abs(pdg)) != checkPdgHadron.end()) { // found signal + nSignals++; // count signal PDG + + std::vector pdgsDecay{}; + std::vector pdgsDecayAntiPart{}; + for (int j{track.getFirstDaughterTrackId()}; j <= track.getLastDaughterTrackId(); ++j) { + auto pdgDau = tracks->at(j).GetPdgCode(); + pdgsDecay.push_back(pdgDau); + if (pdgDau != 333 && pdgDau != 111 && pdgDau != 221 && pdgDau != 113 && pdgDau != 225) { // phi is antiparticle of itself + pdgsDecayAntiPart.push_back(-pdgDau); + } else { + pdgsDecayAntiPart.push_back(pdgDau); + } + } + + std::sort(pdgsDecay.begin(), pdgsDecay.end()); + std::sort(pdgsDecayAntiPart.begin(), pdgsDecayAntiPart.end()); + + for (auto &decay : checkHadronDecays[std::abs(pdg)]) { + std::sort(decay.begin(), decay.end()); + if (pdgsDecay == decay || pdgsDecayAntiPart == decay) { + nSignalGoodDecay++; + break; + } + } + } + } + } + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + std::cout << "# MB events: " << nEventsMB << "\n"; + std::cout << Form("# events injected with %d quark pair: ", checkPdgQuarkOne) << nEventsInjOne << "\n"; + std::cout << Form("# events injected with %d quark pair: ", checkPdgQuarkTwo) << nEventsInjTwo << "\n"; + std::cout << Form("# %d (anti)quarks: ", checkPdgQuarkOne) << nQuarksOne << "\n"; + std::cout << Form("# %d (anti)quarks: ", checkPdgQuarkTwo) << nQuarksTwo << "\n"; + std::cout <<"# signal hadrons: " << nSignals << "\n"; + std::cout <<"# signal hadrons decaying in the correct channel: " << nSignalGoodDecay << "\n"; + + if (nEventsMB < nEvents * (1 - ratioTrigger) * 0.95 || nEventsMB > nEvents * (1 - ratioTrigger) * 1.05) { // we put some tolerance since the number of generated events is small + std::cerr << "Number of generated MB events different than expected\n"; + return 1; + } + if (nEventsInjOne < nEvents * ratioTrigger * 0.5 * 0.95 || nEventsInjOne > nEvents * ratioTrigger * 0.5 * 1.05) { + std::cerr << "Number of generated events injected with " << checkPdgQuarkOne << " different than expected\n"; + return 1; + } + if (nEventsInjTwo < nEvents * ratioTrigger * 0.5 * 0.95 || nEventsInjTwo > nEvents * ratioTrigger * 0.5 * 1.05) { + std::cerr << "Number of generated events injected with " << checkPdgQuarkTwo << " different than expected\n"; + return 1; + } + + if (nQuarksOne < nEvents * ratioTrigger) { // we expect anyway more because the same quark is repeated several time, after each gluon radiation + std::cerr << "Number of generated (anti)quarks " << checkPdgQuarkOne << " lower than expected\n"; + return 1; + } + if (nQuarksTwo < nEvents * ratioTrigger) { // we expect anyway more because the same quark is repeated several time, after each gluon radiation + std::cerr << "Number of generated (anti)quarks " << checkPdgQuarkTwo << " lower than expected\n"; + return 1; + } + + float fracForcedDecays = nSignals ? float(nSignalGoodDecay) / nSignals : 0.0f; + float uncFracForcedDecays = nSignals ? std::sqrt(fracForcedDecays * (1 - fracForcedDecays) / nSignals) : 1.0f; + if (1 - fracForcedDecays > 0.15 + uncFracForcedDecays) { // we put some tolerance (e.g. due to oscillations which might change the final state) + std::cerr << "Fraction of signals decaying into the correct channel " << fracForcedDecays << " lower than expected\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_and_bbbar_gap5_Mode2_CharmBaryons.C b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_and_bbbar_gap5_Mode2_CharmBaryons.C new file mode 100644 index 000000000..951f38df2 --- /dev/null +++ b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_and_bbbar_gap5_Mode2_CharmBaryons.C @@ -0,0 +1,139 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + + int checkPdgQuarkOne{4}; + int checkPdgQuarkTwo{5}; + float ratioTrigger = 1./5; // one event triggered out of 5 + + std::vector checkPdgHadron{4132, 4232, 4332}; + std::map>> checkHadronDecays{ // sorted pdg of daughters + {4232, { + {-321, 211, 2212}, // Xic+ -> p, K-, pi+ + {-313, 2212}, // Xic+ -> p, Kbar^*(892)0 + {211, 211, 3312}, // Xic+ -> Xi-, pi+, pi+ + {333, 2212}, // Xic+ -> p, phi(1020)0 + {-211, 211, 3222}, // Xic+ -> Sigma+, pi-, pi+ + {211, 3324}, // Xic+ -> Xi(1530)0, pi+ + }}, + + {4132, { + {211, 3312}, // Xic0 -> Xi-, pi+ + }}, + + {4332, { + {211, 3334}, // Omegac0 -> Omega-, pi+ + {211, 3312}, // Omegac0 -> Xi-, pi+ + }}, + }; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int nEventsMB{}, nEventsInjOne{}, nEventsInjTwo{}; + int nQuarksOne{}, nQuarksTwo{}, nSignals{}, nSignalGoodDecay{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + + // check subgenerator information + if (eventHeader->hasInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID)) { + bool isValid = false; + int subGeneratorId = eventHeader->getInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID, isValid); + if (subGeneratorId == 0) { + nEventsMB++; + } else if (subGeneratorId == checkPdgQuarkOne) { + nEventsInjOne++; + } else if (subGeneratorId == checkPdgQuarkTwo) { + nEventsInjTwo++; + } + } + + for (auto &track : *tracks) { + auto pdg = track.GetPdgCode(); + if (std::abs(pdg) == checkPdgQuarkOne) { + nQuarksOne++; + continue; + } + if (std::abs(pdg) == checkPdgQuarkTwo) { + nQuarksTwo++; + continue; + } + if (std::find(checkPdgHadron.begin(), checkPdgHadron.end(), std::abs(pdg)) != checkPdgHadron.end()) { // found signal + nSignals++; // count signal PDG + + std::vector pdgsDecay{}; + std::vector pdgsDecayAntiPart{}; + for (int j{track.getFirstDaughterTrackId()}; j <= track.getLastDaughterTrackId(); ++j) { + auto pdgDau = tracks->at(j).GetPdgCode(); + pdgsDecay.push_back(pdgDau); + if (pdgDau != 333) { // phi is antiparticle of itself + pdgsDecayAntiPart.push_back(-pdgDau); + } else { + pdgsDecayAntiPart.push_back(pdgDau); + } + } + + std::sort(pdgsDecay.begin(), pdgsDecay.end()); + std::sort(pdgsDecayAntiPart.begin(), pdgsDecayAntiPart.end()); + + for (auto &decay : checkHadronDecays[std::abs(pdg)]) { + if (pdgsDecay == decay || pdgsDecayAntiPart == decay) { + nSignalGoodDecay++; + break; + } + } + } + } + } + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + std::cout << "# MB events: " << nEventsMB << "\n"; + std::cout << Form("# events injected with %d quark pair: ", checkPdgQuarkOne) << nEventsInjOne << "\n"; + std::cout << Form("# events injected with %d quark pair: ", checkPdgQuarkTwo) << nEventsInjTwo << "\n"; + std::cout << Form("# %d (anti)quarks: ", checkPdgQuarkOne) << nQuarksOne << "\n"; + std::cout << Form("# %d (anti)quarks: ", checkPdgQuarkTwo) << nQuarksTwo << "\n"; + std::cout <<"# signal hadrons: " << nSignals << "\n"; + std::cout <<"# signal hadrons decaying in the correct channel: " << nSignalGoodDecay << "\n"; + + if (nEventsMB < nEvents * (1 - ratioTrigger) * 0.95 || nEventsMB > nEvents * (1 - ratioTrigger) * 1.05) { // we put some tolerance since the number of generated events is small + std::cerr << "Number of generated MB events different than expected\n"; + return 1; + } + if (nEventsInjOne < nEvents * ratioTrigger * 0.5 * 0.95 || nEventsInjOne > nEvents * ratioTrigger * 0.5 * 1.05) { + std::cerr << "Number of generated events injected with " << checkPdgQuarkOne << " different than expected\n"; + return 1; + } + if (nEventsInjTwo < nEvents * ratioTrigger * 0.5 * 0.95 || nEventsInjTwo > nEvents * ratioTrigger * 0.5 * 1.05) { + std::cerr << "Number of generated events injected with " << checkPdgQuarkTwo << " different than expected\n"; + return 1; + } + + if (nQuarksOne < nEvents * ratioTrigger) { // we expect anyway more because the same quark is repeated several time, after each gluon radiation + std::cerr << "Number of generated (anti)quarks " << checkPdgQuarkOne << " lower than expected\n"; + return 1; + } + if (nQuarksTwo < nEvents * ratioTrigger) { // we expect anyway more because the same quark is repeated several time, after each gluon radiation + std::cerr << "Number of generated (anti)quarks " << checkPdgQuarkTwo << " lower than expected\n"; + return 1; + } + + float fracForcedDecays = nSignals ? float(nSignalGoodDecay) / nSignals : 0.0f; + float uncFracForcedDecays = nSignals ? std::sqrt(fracForcedDecays * (1 - fracForcedDecays) / nSignals) : 1.0f; + if (1 - fracForcedDecays > 0.15 + uncFracForcedDecays) { // we put some tolerance (e.g. due to oscillations which might change the final state) + std::cerr << "Fraction of signals decaying into the correct channel " << fracForcedDecays << " lower than expected\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_and_bbbar_gap5_Mode2_CharmBaryons_pp_ref.C b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_and_bbbar_gap5_Mode2_CharmBaryons_pp_ref.C new file mode 100644 index 000000000..ad9f15bfe --- /dev/null +++ b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_and_bbbar_gap5_Mode2_CharmBaryons_pp_ref.C @@ -0,0 +1,190 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + + int checkPdgQuarkOne{4}; + int checkPdgQuarkTwo{5}; + float ratioTrigger = 1./5; // one event triggered out of 5 + + std::vector checkPdgHadron{411, 421, 431, 4122, 4132, 4232, 4332}; + std::map>> checkHadronDecays{ // sorted pdg of daughters + {421, { + {-321, 211}, // D0 -> K-, pi+ + {-321, 211, 111}, // D0 -> K-, pi+, pi0 + {213, -321}, // D0 -> rho(770)+, K- + {-313, 111}, // D0 -> Kbar^*(892)0, pi0 + {-323, 211}, // D0 -> K^*(892)-, pi+ + {-211, 211}, // D0 -> pi-, pi+ + {213, -211}, // D0 -> rho(770)+, pi- + {-211, 211, 111}, // D0 -> pi-, pi+, pi0 + {-321, 321}, // D0 -> K-, K+ + }}, + + {411, { + {-321, 211, 211}, // D+ -> K-, pi+, pi+ + {-10311, 211}, // D+ -> Kbar0^*(1430)0, pi+ + {-313, 211}, // D+ -> Kbar^*(892)0, pi+ + {-321, 211, 211, 111}, // D+ -> K-, pi+, pi+, pi0 + {333, 211}, // D+ -> phi(1020)0, pi+ + {-313, 321}, // D+ -> Kbar^*(892)0, K+ + {-10311, 321}, // D+ -> Kbar0^*(1430)0, K+ + {-321, 321, 211}, // D+ -> K-, K+, pi+ + {113, 211}, // D+ -> rho(770)0, pi+ + {225, 211}, // D+ -> f2(1270)0, pi+ + {-211, 211, 211}, // D+ -> pi-, pi+, pi+ + }}, + + {431, { + {211, 333}, // Ds+ -> phi(1020)0, pi+ + {-313, 321}, // Ds+ -> Kbar^*(892)0, K+ + {333, 213}, // Ds+ -> phi(1020)0, rho(770)+ + {113, 211}, // Ds+ -> rho(770)0, pi+ + {225, 211}, // Ds+ -> f2(1270)0, pi+ + {-211, 211, 211}, // Ds+ -> pi-, pi+, pi+ + {313, 211}, // Ds+ -> K^*(892)0, pi+ + {10221, 321}, // Ds+ -> f0(1370)0, K+ + {113, 321}, // Ds+ -> rho(770)0, K+ + {-211, 321, 211}, // Ds+ -> pi-, K+, pi+ + {221, 211}, // Ds+ -> eta, pi+ + }}, + + {4122, { + {2212, -321, 211}, // Lambdac+ -> p, K-, pi+ + {2212, -313}, // Lambdac+ -> p, Kbar^*(892)0 + {2224, -321}, // Lambdac+ -> Delta(1232)++, K- + {102134, 211}, // Lambdac+ -> 102134, pi+ + {2212, 311}, // Lambdac+ -> p, K0 + {2212, -321, 211, 111}, // Lambdac+ -> p, K-, pi+, pi0 + {2212, -211, 211}, // Lambdac+ -> p, pi-, pi+ + {2212, 333}, // Lambdac+ -> p, phi(1020)0 + }}, + {4232, { + {2212, -321, 211}, // Xic+ -> p, K-, pi+ + {2212, -313}, // Xic+ -> p, Kbar^*(892)0 + {3312, 211, 211}, // Xic+ -> Xi-, pi+, pi+ + {2212, 333}, // Xic+ -> p, phi(1020)0 + {3222, -211, 211}, // Xic+ -> Sigma+, pi-, pi+ + {3324, 211}, // Xic+ -> Xi(1530)0, pi+ + }}, + + {4132, { + {3312, 211}, // Xic0 -> Xi-, pi+ + }}, + + {4332, { + {3334, 211}, // Omegac0 -> Omega-, pi+ + {3312, 211}, // Omegac0 -> Xi-, pi+ + }}, + }; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int nEventsMB{}, nEventsInjOne{}, nEventsInjTwo{}; + int nQuarksOne{}, nQuarksTwo{}, nSignals{}, nSignalGoodDecay{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + + // check subgenerator information + if (eventHeader->hasInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID)) { + bool isValid = false; + int subGeneratorId = eventHeader->getInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID, isValid); + if (subGeneratorId == 0) { + nEventsMB++; + } else if (subGeneratorId == checkPdgQuarkOne) { + nEventsInjOne++; + } else if (subGeneratorId == checkPdgQuarkTwo) { + nEventsInjTwo++; + } + } + + for (auto &track : *tracks) { + auto pdg = track.GetPdgCode(); + if (std::abs(pdg) == checkPdgQuarkOne) { + nQuarksOne++; + continue; + } + if (std::abs(pdg) == checkPdgQuarkTwo) { + nQuarksTwo++; + continue; + } + if (std::find(checkPdgHadron.begin(), checkPdgHadron.end(), std::abs(pdg)) != checkPdgHadron.end()) { // found signal + nSignals++; // count signal PDG + + std::vector pdgsDecay{}; + std::vector pdgsDecayAntiPart{}; + for (int j{track.getFirstDaughterTrackId()}; j <= track.getLastDaughterTrackId(); ++j) { + auto pdgDau = tracks->at(j).GetPdgCode(); + pdgsDecay.push_back(pdgDau); + if (pdgDau != 333 && pdgDau != 111 && pdgDau != 221 && pdgDau != 113 && pdgDau != 225) { // phi is antiparticle of itself + pdgsDecayAntiPart.push_back(-pdgDau); + } else { + pdgsDecayAntiPart.push_back(pdgDau); + } + } + + std::sort(pdgsDecay.begin(), pdgsDecay.end()); + std::sort(pdgsDecayAntiPart.begin(), pdgsDecayAntiPart.end()); + + for (auto &decay : checkHadronDecays[std::abs(pdg)]) { + std::sort(decay.begin(), decay.end()); + if (pdgsDecay == decay || pdgsDecayAntiPart == decay) { + nSignalGoodDecay++; + break; + } + } + } + } + } + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + std::cout << "# MB events: " << nEventsMB << "\n"; + std::cout << Form("# events injected with %d quark pair: ", checkPdgQuarkOne) << nEventsInjOne << "\n"; + std::cout << Form("# events injected with %d quark pair: ", checkPdgQuarkTwo) << nEventsInjTwo << "\n"; + std::cout << Form("# %d (anti)quarks: ", checkPdgQuarkOne) << nQuarksOne << "\n"; + std::cout << Form("# %d (anti)quarks: ", checkPdgQuarkTwo) << nQuarksTwo << "\n"; + std::cout <<"# signal hadrons: " << nSignals << "\n"; + std::cout <<"# signal hadrons decaying in the correct channel: " << nSignalGoodDecay << "\n"; + + if (nEventsMB < nEvents * (1 - ratioTrigger) * 0.95 || nEventsMB > nEvents * (1 - ratioTrigger) * 1.05) { // we put some tolerance since the number of generated events is small + std::cerr << "Number of generated MB events different than expected\n"; + return 1; + } + if (nEventsInjOne < nEvents * ratioTrigger * 0.5 * 0.95 || nEventsInjOne > nEvents * ratioTrigger * 0.5 * 1.05) { + std::cerr << "Number of generated events injected with " << checkPdgQuarkOne << " different than expected\n"; + return 1; + } + if (nEventsInjTwo < nEvents * ratioTrigger * 0.5 * 0.95 || nEventsInjTwo > nEvents * ratioTrigger * 0.5 * 1.05) { + std::cerr << "Number of generated events injected with " << checkPdgQuarkTwo << " different than expected\n"; + return 1; + } + + if (nQuarksOne < nEvents * ratioTrigger) { // we expect anyway more because the same quark is repeated several time, after each gluon radiation + std::cerr << "Number of generated (anti)quarks " << checkPdgQuarkOne << " lower than expected\n"; + return 1; + } + if (nQuarksTwo < nEvents * ratioTrigger) { // we expect anyway more because the same quark is repeated several time, after each gluon radiation + std::cerr << "Number of generated (anti)quarks " << checkPdgQuarkTwo << " lower than expected\n"; + return 1; + } + + float fracForcedDecays = nSignals ? float(nSignalGoodDecay) / nSignals : 0.0f; + float uncFracForcedDecays = nSignals ? std::sqrt(fracForcedDecays * (1 - fracForcedDecays) / nSignals) : 1.0f; + if (1 - fracForcedDecays > 0.15 + uncFracForcedDecays) { // we put some tolerance (e.g. due to oscillations which might change the final state) + std::cerr << "Fraction of signals decaying into the correct channel " << fracForcedDecays << " lower than expected\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_and_bbbar_gap5_Mode2_XiC.C b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_and_bbbar_gap5_Mode2_XiC.C new file mode 100644 index 000000000..2c56754d6 --- /dev/null +++ b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_and_bbbar_gap5_Mode2_XiC.C @@ -0,0 +1,123 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + + int checkPdgQuarkOne{4}; + int checkPdgQuarkTwo{5}; + float ratioTrigger = 1./5; // one event triggered out of 5 + + std::vector checkPdgHadron{4132}; + std::map>> checkHadronDecays{ // sorted pdg of daughters + {4132, {{211, 3312}}} // Xic0 + }; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int nEventsMB{}, nEventsInjOne{}, nEventsInjTwo{}; + int nQuarksOne{}, nQuarksTwo{}, nSignals{}, nSignalGoodDecay{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + + // check subgenerator information + if (eventHeader->hasInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID)) { + bool isValid = false; + int subGeneratorId = eventHeader->getInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID, isValid); + if (subGeneratorId == 0) { + nEventsMB++; + } else if (subGeneratorId == checkPdgQuarkOne) { + nEventsInjOne++; + } else if (subGeneratorId == checkPdgQuarkTwo) { + nEventsInjTwo++; + } + } + + for (auto &track : *tracks) { + auto pdg = track.GetPdgCode(); + if (std::abs(pdg) == checkPdgQuarkOne) { + nQuarksOne++; + continue; + } + if (std::abs(pdg) == checkPdgQuarkTwo) { + nQuarksTwo++; + continue; + } + if (std::find(checkPdgHadron.begin(), checkPdgHadron.end(), std::abs(pdg)) != checkPdgHadron.end()) { // found signal + nSignals++; // count signal PDG + + std::vector pdgsDecay{}; + std::vector pdgsDecayAntiPart{}; + for (int j{track.getFirstDaughterTrackId()}; j <= track.getLastDaughterTrackId(); ++j) { + auto pdgDau = tracks->at(j).GetPdgCode(); + pdgsDecay.push_back(pdgDau); + if (pdgDau != 333) { // phi is antiparticle of itself + pdgsDecayAntiPart.push_back(-pdgDau); + } else { + pdgsDecayAntiPart.push_back(pdgDau); + } + } + + std::sort(pdgsDecay.begin(), pdgsDecay.end()); + std::sort(pdgsDecayAntiPart.begin(), pdgsDecayAntiPart.end()); + + for (auto &decay : checkHadronDecays[std::abs(pdg)]) { + if (pdgsDecay == decay || pdgsDecayAntiPart == decay) { + nSignalGoodDecay++; + break; + } + } + } + } + } + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + std::cout << "# MB events: " << nEventsMB << "\n"; + std::cout << Form("# events injected with %d quark pair: ", checkPdgQuarkOne) << nEventsInjOne << "\n"; + std::cout << Form("# events injected with %d quark pair: ", checkPdgQuarkTwo) << nEventsInjTwo << "\n"; + std::cout << Form("# %d (anti)quarks: ", checkPdgQuarkOne) << nQuarksOne << "\n"; + std::cout << Form("# %d (anti)quarks: ", checkPdgQuarkTwo) << nQuarksTwo << "\n"; + std::cout <<"# signal hadrons: " << nSignals << "\n"; + std::cout <<"# signal hadrons decaying in the correct channel: " << nSignalGoodDecay << "\n"; + + if (nEventsMB < nEvents * (1 - ratioTrigger) * 0.95 || nEventsMB > nEvents * (1 - ratioTrigger) * 1.05) { // we put some tolerance since the number of generated events is small + std::cerr << "Number of generated MB events different than expected\n"; + return 1; + } + if (nEventsInjOne < nEvents * ratioTrigger * 0.5 * 0.95 || nEventsInjOne > nEvents * ratioTrigger * 0.5 * 1.05) { + std::cerr << "Number of generated events injected with " << checkPdgQuarkOne << " different than expected\n"; + return 1; + } + if (nEventsInjTwo < nEvents * ratioTrigger * 0.5 * 0.95 || nEventsInjTwo > nEvents * ratioTrigger * 0.5 * 1.05) { + std::cerr << "Number of generated events injected with " << checkPdgQuarkTwo << " different than expected\n"; + return 1; + } + + if (nQuarksOne < nEvents * ratioTrigger) { // we expect anyway more because the same quark is repeated several time, after each gluon radiation + std::cerr << "Number of generated (anti)quarks " << checkPdgQuarkOne << " lower than expected\n"; + return 1; + } + if (nQuarksTwo < nEvents * ratioTrigger) { // we expect anyway more because the same quark is repeated several time, after each gluon radiation + std::cerr << "Number of generated (anti)quarks " << checkPdgQuarkTwo << " lower than expected\n"; + return 1; + } + + float fracForcedDecays = nSignals ? float(nSignalGoodDecay) / nSignals : 0.0f; + float uncFracForcedDecays = nSignals ? std::sqrt(fracForcedDecays * (1 - fracForcedDecays) / nSignals) : 1.0f; + if (1 - fracForcedDecays > 0.15 + uncFracForcedDecays) { // we put some tolerance (e.g. due to oscillations which might change the final state) + std::cerr << "Fraction of signals decaying into the correct channel " << fracForcedDecays << " lower than expected\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_and_bbbar_gap5_Mode2_XiC_OmegaC.C b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_and_bbbar_gap5_Mode2_XiC_OmegaC.C new file mode 100644 index 000000000..ab13f5c41 --- /dev/null +++ b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_and_bbbar_gap5_Mode2_XiC_OmegaC.C @@ -0,0 +1,124 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + + int checkPdgQuarkOne{4}; + int checkPdgQuarkTwo{5}; + float ratioTrigger = 1./5; // one event triggered out of 5 + + std::vector checkPdgHadron{4132, 4332}; + std::map>> checkHadronDecays{ // sorted pdg of daughters + {4132, {{211, 3312}}}, // Xic0 + {4332, {{211, 3334}, {211, 3312}}} // Omegac0 + }; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int nEventsMB{}, nEventsInjOne{}, nEventsInjTwo{}; + int nQuarksOne{}, nQuarksTwo{}, nSignals{}, nSignalGoodDecay{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + + // check subgenerator information + if (eventHeader->hasInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID)) { + bool isValid = false; + int subGeneratorId = eventHeader->getInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID, isValid); + if (subGeneratorId == 0) { + nEventsMB++; + } else if (subGeneratorId == checkPdgQuarkOne) { + nEventsInjOne++; + } else if (subGeneratorId == checkPdgQuarkTwo) { + nEventsInjTwo++; + } + } + + for (auto &track : *tracks) { + auto pdg = track.GetPdgCode(); + if (std::abs(pdg) == checkPdgQuarkOne) { + nQuarksOne++; + continue; + } + if (std::abs(pdg) == checkPdgQuarkTwo) { + nQuarksTwo++; + continue; + } + if (std::find(checkPdgHadron.begin(), checkPdgHadron.end(), std::abs(pdg)) != checkPdgHadron.end()) { // found signal + nSignals++; // count signal PDG + + std::vector pdgsDecay{}; + std::vector pdgsDecayAntiPart{}; + for (int j{track.getFirstDaughterTrackId()}; j <= track.getLastDaughterTrackId(); ++j) { + auto pdgDau = tracks->at(j).GetPdgCode(); + pdgsDecay.push_back(pdgDau); + if (pdgDau != 333) { // phi is antiparticle of itself + pdgsDecayAntiPart.push_back(-pdgDau); + } else { + pdgsDecayAntiPart.push_back(pdgDau); + } + } + + std::sort(pdgsDecay.begin(), pdgsDecay.end()); + std::sort(pdgsDecayAntiPart.begin(), pdgsDecayAntiPart.end()); + + for (auto &decay : checkHadronDecays[std::abs(pdg)]) { + if (pdgsDecay == decay || pdgsDecayAntiPart == decay) { + nSignalGoodDecay++; + break; + } + } + } + } + } + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + std::cout << "# MB events: " << nEventsMB << "\n"; + std::cout << Form("# events injected with %d quark pair: ", checkPdgQuarkOne) << nEventsInjOne << "\n"; + std::cout << Form("# events injected with %d quark pair: ", checkPdgQuarkTwo) << nEventsInjTwo << "\n"; + std::cout << Form("# %d (anti)quarks: ", checkPdgQuarkOne) << nQuarksOne << "\n"; + std::cout << Form("# %d (anti)quarks: ", checkPdgQuarkTwo) << nQuarksTwo << "\n"; + std::cout <<"# signal hadrons: " << nSignals << "\n"; + std::cout <<"# signal hadrons decaying in the correct channel: " << nSignalGoodDecay << "\n"; + + if (nEventsMB < nEvents * (1 - ratioTrigger) * 0.95 || nEventsMB > nEvents * (1 - ratioTrigger) * 1.05) { // we put some tolerance since the number of generated events is small + std::cerr << "Number of generated MB events different than expected\n"; + return 1; + } + if (nEventsInjOne < nEvents * ratioTrigger * 0.5 * 0.95 || nEventsInjOne > nEvents * ratioTrigger * 0.5 * 1.05) { + std::cerr << "Number of generated events injected with " << checkPdgQuarkOne << " different than expected\n"; + return 1; + } + if (nEventsInjTwo < nEvents * ratioTrigger * 0.5 * 0.95 || nEventsInjTwo > nEvents * ratioTrigger * 0.5 * 1.05) { + std::cerr << "Number of generated events injected with " << checkPdgQuarkTwo << " different than expected\n"; + return 1; + } + + if (nQuarksOne < nEvents * ratioTrigger) { // we expect anyway more because the same quark is repeated several time, after each gluon radiation + std::cerr << "Number of generated (anti)quarks " << checkPdgQuarkOne << " lower than expected\n"; + return 1; + } + if (nQuarksTwo < nEvents * ratioTrigger) { // we expect anyway more because the same quark is repeated several time, after each gluon radiation + std::cerr << "Number of generated (anti)quarks " << checkPdgQuarkTwo << " lower than expected\n"; + return 1; + } + + float fracForcedDecays = nSignals ? float(nSignalGoodDecay) / nSignals : 0.0f; + float uncFracForcedDecays = nSignals ? std::sqrt(fracForcedDecays * (1 - fracForcedDecays) / nSignals) : 1.0f; + if (1 - fracForcedDecays > 0.15 + uncFracForcedDecays) { // we put some tolerance (e.g. due to oscillations which might change the final state) + std::cerr << "Fraction of signals decaying into the correct channel " << fracForcedDecays << " lower than expected\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_and_bbbar_gap5_Mode2_corrBkg.C b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_and_bbbar_gap5_Mode2_corrBkg.C new file mode 100644 index 000000000..0b40d30fd --- /dev/null +++ b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_and_bbbar_gap5_Mode2_corrBkg.C @@ -0,0 +1,181 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + + int checkPdgQuarkOne{4}; + int checkPdgQuarkTwo{5}; + float ratioTrigger = 1./5; // one event triggered out of 5 + + std::vector checkPdgHadron{411, 421, 431, 4122, 4132, 4232, 4332}; + std::map>> checkHadronDecays{ // sorted pdg of daughters + {411, { + {-321, 211, 211}, // K- π+ π+ (non-resonant) + {-321, 111, 211, 211}, // K- π+ π+ π0 (non-resonant) + {-313, 321}, // K*0(892) K+ + {-10311, 321}, // K*0(1430) K+ + {211, 333}, // φ π+ + {-321, 211, 321}, // K- K+ π+ (non-resonant) + {113, 211}, // ρ0 π+ + {211, 225}, // f2(1270) π+ + {-211, 211, 211} // π- π+ π+ (non-resonant) + }}, + {421, { + {-321, 211}, // K- π+ (non-resonant) + {-321, 111, 211}, // K- π+ π0 + {-321, 213}, // ρ+ K- + {-313, 111}, // antiK*0(892) π0 + {-323, 211}, // K*-(892) π+ + {-211, 211}, // π- π+ + {-211, 213}, // ρ+ π- + {-211, 111, 211}, // π- π+ π0 + {-321, 321} // K- K+ + }}, + {431, { + {211, 333}, // φ π+ + {-313, 321}, // antiK*(892) K+ + {213, 333}, // φ ρ + {113, 211}, // ρ π+ + {211, 225}, // f2(1270) π+ + {-211, 211, 211}, // π- π+ π+ (s-wave) + {211, 313}, // K*(892)0 π+ + {321, 10221}, // f0(1370) K+ + {113, 321}, // ρ0 K+ + {-211, 211, 321}, // π- K+ π+ (non-resonant) + {211, 221} // η π+ + }}, + {4122, { + {-321, 211, 2212}, // p K- π+ (non-resonant) + {-313, 2212}, // p K*0(892) + {-321, 2224}, // Δ++ K- + {211, 102134}, // Λ(1520) π+ + {-321, 111, 211, 2212}, // p K- π+ π0 + {-211, 211, 2212}, // p π- π+ + {333, 2212} // p φ + }}, + {4232, { + {-321, 211, 2212}, // Xic+ -> p, K-, pi+ + {-313, 2212}, // Xic+ -> p, Kbar^*(892)0 + {211, 211, 3312}, // Xic+ -> Xi-, pi+, pi+ + {333, 2212}, // Xic+ -> p, phi(1020)0 + {-211, 211, 3222}, // Xic+ -> Sigma+, pi-, pi+ + {211, 3324} // Xic+ -> Xi(1530)0, pi+ + }}, + {4132, { + {211, 3312}, // Xic0 -> Xi-, pi+ + }}, + {4332, { + {211, 3334}, // Omegac0 -> Omega-, pi+ + {211, 3312} // Omegac0 -> Xi-, pi+ + }} + }; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int nEventsMB{}, nEventsInjOne{}, nEventsInjTwo{}; + int nQuarksOne{}, nQuarksTwo{}, nSignals{}, nSignalGoodDecay{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + + // check subgenerator information + if (eventHeader->hasInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID)) { + bool isValid = false; + int subGeneratorId = eventHeader->getInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID, isValid); + if (subGeneratorId == 0) { + nEventsMB++; + } else if (subGeneratorId == checkPdgQuarkOne) { + nEventsInjOne++; + } else if (subGeneratorId == checkPdgQuarkTwo) { + nEventsInjTwo++; + } + } + + for (auto &track : *tracks) { + auto pdg = track.GetPdgCode(); + if (std::abs(pdg) == checkPdgQuarkOne) { + nQuarksOne++; + continue; + } + if (std::abs(pdg) == checkPdgQuarkTwo) { + nQuarksTwo++; + continue; + } + if (std::find(checkPdgHadron.begin(), checkPdgHadron.end(), std::abs(pdg)) != checkPdgHadron.end()) { // found signal + nSignals++; // count signal PDG + + std::vector pdgsDecay{}; + std::vector pdgsDecayAntiPart{}; + for (int j{track.getFirstDaughterTrackId()}; j <= track.getLastDaughterTrackId(); ++j) { + auto pdgDau = tracks->at(j).GetPdgCode(); + pdgsDecay.push_back(pdgDau); + if (pdgDau != 333 && pdgDau != 111 && pdgDau != 221 && pdgDau != 113 && pdgDau != 225) { // phi is antiparticle of itself + pdgsDecayAntiPart.push_back(-pdgDau); + } else { + pdgsDecayAntiPart.push_back(pdgDau); + } + } + + std::sort(pdgsDecay.begin(), pdgsDecay.end()); + std::sort(pdgsDecayAntiPart.begin(), pdgsDecayAntiPart.end()); + + for (auto &decay : checkHadronDecays[std::abs(pdg)]) { + if (pdgsDecay == decay || pdgsDecayAntiPart == decay) { + nSignalGoodDecay++; + break; + } + } + } + } + } + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + std::cout << "# MB events: " << nEventsMB << "\n"; + std::cout << Form("# events injected with %d quark pair: ", checkPdgQuarkOne) << nEventsInjOne << "\n"; + std::cout << Form("# events injected with %d quark pair: ", checkPdgQuarkTwo) << nEventsInjTwo << "\n"; + std::cout << Form("# %d (anti)quarks: ", checkPdgQuarkOne) << nQuarksOne << "\n"; + std::cout << Form("# %d (anti)quarks: ", checkPdgQuarkTwo) << nQuarksTwo << "\n"; + std::cout <<"# signal hadrons: " << nSignals << "\n"; + std::cout <<"# signal hadrons decaying in the correct channel: " << nSignalGoodDecay << "\n"; + + if (nEventsMB < nEvents * (1 - ratioTrigger) * 0.95 || nEventsMB > nEvents * (1 - ratioTrigger) * 1.05) { // we put some tolerance since the number of generated events is small + std::cerr << "Number of generated MB events different than expected\n"; + return 1; + } + if (nEventsInjOne < nEvents * ratioTrigger * 0.5 * 0.95 || nEventsInjOne > nEvents * ratioTrigger * 0.5 * 1.05) { + std::cerr << "Number of generated events injected with " << checkPdgQuarkOne << " different than expected\n"; + return 1; + } + if (nEventsInjTwo < nEvents * ratioTrigger * 0.5 * 0.95 || nEventsInjTwo > nEvents * ratioTrigger * 0.5 * 1.05) { + std::cerr << "Number of generated events injected with " << checkPdgQuarkTwo << " different than expected\n"; + return 1; + } + + if (nQuarksOne < nEvents * ratioTrigger) { // we expect anyway more because the same quark is repeated several time, after each gluon radiation + std::cerr << "Number of generated (anti)quarks " << checkPdgQuarkOne << " lower than expected\n"; + return 1; + } + if (nQuarksTwo < nEvents * ratioTrigger) { // we expect anyway more because the same quark is repeated several time, after each gluon radiation + std::cerr << "Number of generated (anti)quarks " << checkPdgQuarkTwo << " lower than expected\n"; + return 1; + } + + float fracForcedDecays = nSignals ? float(nSignalGoodDecay) / nSignals : 0.0f; + float uncFracForcedDecays = nSignals ? std::sqrt(fracForcedDecays * (1 - fracForcedDecays) / nSignals) : 1.0f; + if (1 - fracForcedDecays > 0.15 + uncFracForcedDecays) { // we put some tolerance (e.g. due to oscillations which might change the final state) + std::cerr << "Fraction of signals decaying into the correct channel " << fracForcedDecays << " lower than expected\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_and_bbbar_gap5_Mode2_corrBkgSigmaC.C b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_and_bbbar_gap5_Mode2_corrBkgSigmaC.C new file mode 100644 index 000000000..db96c1a06 --- /dev/null +++ b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_and_bbbar_gap5_Mode2_corrBkgSigmaC.C @@ -0,0 +1,230 @@ +#include "TFile.h" +#include "TTree.h" + +#include +#include + +int External() { + std::string path{"o2sim_Kine.root"}; + + int checkPdgQuarkOne{4}; + int checkPdgQuarkTwo{5}; + float ratioTrigger = 1./5.; // one event triggered out of 5 + std::array, 2> pdgReplParticles = {std::array{413, 14122}, std::array{413, 4124}}; + std::array, 2> pdgReplPartCounters = {std::array{0, 0}, std::array{0, 0}}; + std::array freqRepl = {0.35, 0.5}; + std::map sumOrigReplacedParticles = {{413, 0}}; + + std::array checkPdgHadron{413, 14122, 4124}; + std::map>> checkHadronDecays{ // sorted (!) pdg of daughters + //{14122, {{4222, -211}, {4112, 211}, {4122, 211, -211}}}, // Lc(2595)+ + //{4124, {{4222, -211}, {4112, 211}, {4122, 211, -211}}} // Lc(2625)+ + {14122, {{-211, 4222}, {211, 4112}, {-211, 211, 4122}}}, // Lc(2595)+ + {4124, {{-211, 4222}, {211, 4112}, {-211, 211, 4122}}}, // Lc(2625)+ + {413, {{22, 411}, {111, 411}, {211, 421}}} // D*+ as in PYTHIA + }; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int nEventsMB{}, nEventsInjOne{}, nEventsInjTwo{}; + int nSignals{}, nSignalGoodDecay{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + + std::cout << std::endl; + + tree->GetEntry(i); + + // check subgenerator information + int subGeneratorId{-1}; + if (eventHeader->hasInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID)) { + bool isValid = false; + subGeneratorId = eventHeader->getInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID, isValid); + if (subGeneratorId == 0) { + nEventsMB++; + } else if (subGeneratorId == checkPdgQuarkOne) { + nEventsInjOne++; + } else if (subGeneratorId == checkPdgQuarkTwo) { + nEventsInjTwo++; + } + } + + for (auto &track : *tracks) { + auto pdg = track.GetPdgCode(); + auto absPdg = std::abs(pdg); + if (std::find(checkPdgHadron.begin(), checkPdgHadron.end(), absPdg) != checkPdgHadron.end()) { // found signal + nSignals++; // count signal PDG + std::cout << std::endl << "==> signal " << absPdg << " found!" << std::endl; + + if (subGeneratorId == checkPdgQuarkOne) { // replacement only for prompt ---> BUT ALSO NON-PROMPT D* SEEM TO BE REPLACED + for (int iRepl{0}; iRepl<2; ++iRepl) { + if (absPdg == pdgReplParticles[iRepl][0]) { + pdgReplPartCounters[iRepl][0]++; + sumOrigReplacedParticles[pdgReplParticles[iRepl][0]]++; + } else if (absPdg == pdgReplParticles[iRepl][1]) { + pdgReplPartCounters[iRepl][1]++; + sumOrigReplacedParticles[pdgReplParticles[iRepl][0]]++; + } + } + } + else if (subGeneratorId == checkPdgQuarkTwo && (absPdg == pdgReplParticles[0][1] || absPdg == pdgReplParticles[1][1])) { + std::cout << " NB: we have a " << absPdg << " also in event with quark " << checkPdgQuarkTwo << ", i.e in an event with c-cbar pair present, tagged with a b-bbar (e.g. double-parton scattering)" << std::endl; + std::cout << " ### mother indices: "; + int idFirstMother = track.getMotherTrackId(); + int idSecondMother = track.getSecondMotherTrackId(); + std::vector motherIds = {}; + for(int i=idFirstMother; i<=idSecondMother; i++) { + std::cout << i << " "; + motherIds.push_back(i); + } + std::cout << std::endl; + + /// To establish if the partonic event is switched on or not, check if the motherIds are all -1 for the current hadron + /// This is ok for Lc excited states deriving from the replacement of a prompt D*+ + /// Instead, Lc excited states coming from Lb decays (i.e. not coming from a D*+ replacement))have at least one mother that has idx !=-1 (*) + bool partonicEventOn = false; + bool motherIdsAllMinus1 = true; + for(int idx : motherIds) { + if(idx != -1) { + /// one mother id different from + motherIdsAllMinus1 = false; + break; + } + } + //if(motherIds != std::vector{-1, -1}) { + if(!motherIdsAllMinus1) { + std::cout << "The " << absPdg << " particle has mothers. This should mean that it comes directly from parton hadronization, and that the partonic event was kept in the MC production " << std::endl; + partonicEventOn = true; + } + + std::cout << " ### mother PDG codes: "; + std::vector motherPdgCodes = {}; + bool updateCounters = true; + if(partonicEventOn) { + for(int i=idFirstMother; i<=idSecondMother; i++) { + motherPdgCodes.push_back(tracks->at(i).GetPdgCode()); + std::cout << motherPdgCodes.back() << " "; + } + + /// check that among the mothers there is a c/cbar quark + /// This means that the charm hadron comes from the c-quark hadronization, where the c/cbar quark + /// comes from a c-cbar pair present in the current event, tagged with a b-bbar (e.g. double-parton scattering) + /// we check also if it comes from a charmed diquark + if(std::find(motherPdgCodes.begin(), motherPdgCodes.end(), 4) == motherPdgCodes.end() && std::find(motherPdgCodes.begin(), motherPdgCodes.end(), -4) == motherPdgCodes.end() + && std::find(motherPdgCodes.begin(), motherPdgCodes.end(), 4101) == motherPdgCodes.end() && std::find(motherPdgCodes.begin(), motherPdgCodes.end(), -4101) == motherPdgCodes.end() + && std::find(motherPdgCodes.begin(), motherPdgCodes.end(), 4103) == motherPdgCodes.end() && std::find(motherPdgCodes.begin(), motherPdgCodes.end(), -4103) == motherPdgCodes.end() + && std::find(motherPdgCodes.begin(), motherPdgCodes.end(), 4201) == motherPdgCodes.end() && std::find(motherPdgCodes.begin(), motherPdgCodes.end(), -4201) == motherPdgCodes.end() + && std::find(motherPdgCodes.begin(), motherPdgCodes.end(), 4203) == motherPdgCodes.end() && std::find(motherPdgCodes.begin(), motherPdgCodes.end(), -4203) == motherPdgCodes.end() + && std::find(motherPdgCodes.begin(), motherPdgCodes.end(), 4301) == motherPdgCodes.end() && std::find(motherPdgCodes.begin(), motherPdgCodes.end(), -4301) == motherPdgCodes.end() + && std::find(motherPdgCodes.begin(), motherPdgCodes.end(), 4303) == motherPdgCodes.end() && std::find(motherPdgCodes.begin(), motherPdgCodes.end(), -4303) == motherPdgCodes.end() + && std::find(motherPdgCodes.begin(), motherPdgCodes.end(), 4403) == motherPdgCodes.end() && std::find(motherPdgCodes.begin(), motherPdgCodes.end(), -4403) == motherPdgCodes.end()){ + /// if we arrive here, it means that the hadron comes from the decay of a beauty hadron. + /// This can happen if and only if this is not a replaced one (i.e. native from Lambdab0 decay) + if (std::find(motherPdgCodes.begin(), motherPdgCodes.end(), 5122) == motherPdgCodes.end() && std::find(motherPdgCodes.begin(), motherPdgCodes.end(), -5122) == motherPdgCodes.end()) { + std::cerr << "The particle " << absPdg << " does not originate neither from a c/c-bar quark (replaced) nor from a Lambda_b0 decay. There is something wrong, aborting..." << std::endl; + return 1; + } + /// since this is a native Lc excited state from Lb0 decay, we must not update the replacement counters + updateCounters = false; + } + } + std::cout << std::endl; + + /// update the counters only if the Lc excited state comes from a replaced prompt D*+, i.e. not from a Lb0 decay + if (!updateCounters) continue; + + /// only if we arrive here it means that everything is ok, and we can safely update the counters for the final statistics + for (int iRepl{0}; iRepl<2; ++iRepl) { + if (absPdg == pdgReplParticles[iRepl][0]) { + pdgReplPartCounters[iRepl][0]++; + sumOrigReplacedParticles[pdgReplParticles[iRepl][0]]++; + } else if (absPdg == pdgReplParticles[iRepl][1]) { + pdgReplPartCounters[iRepl][1]++; + sumOrigReplacedParticles[pdgReplParticles[iRepl][0]]++; + } + } + } + + + std::vector pdgsDecay{}; + std::vector pdgsDecayAntiPart{}; + if (track.getFirstDaughterTrackId() >= 0 && track.getLastDaughterTrackId() >= 0) { + for (int j{track.getFirstDaughterTrackId()}; j <= track.getLastDaughterTrackId(); ++j) { + auto pdgDau = tracks->at(j).GetPdgCode(); + pdgsDecay.push_back(pdgDau); + std::cout << " -- daughter " << j << ": " << pdgDau << std::endl; + if (pdgDau != 333 && pdgDau != 111) { // phi and pi0 are antiparticles of themselves + pdgsDecayAntiPart.push_back(-pdgDau); + } else { + pdgsDecayAntiPart.push_back(pdgDau); + } + } + } + + std::sort(pdgsDecay.begin(), pdgsDecay.end()); + std::sort(pdgsDecayAntiPart.begin(), pdgsDecayAntiPart.end()); + + for (auto &decay : checkHadronDecays[std::abs(pdg)]) { + if (pdgsDecay == decay || pdgsDecayAntiPart == decay) { + nSignalGoodDecay++; + std::cout << " !!! GOOD DECAY FOUND !!!" << std::endl; + break; + } + } + } + } // end loop over tracks + } + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + std::cout << "# MB events: " << nEventsMB << "\n"; + std::cout << Form("# events injected with %d quark pair: ", checkPdgQuarkOne) << nEventsInjOne << "\n"; + std::cout << Form("# events injected with %d quark pair: ", checkPdgQuarkTwo) << nEventsInjTwo << "\n"; + std::cout <<"# signal hadrons: " << nSignals << "\n"; + std::cout <<"# signal hadrons decaying in the correct channel: " << nSignalGoodDecay << "\n"; + + if (nEventsMB < nEvents * (1 - ratioTrigger) * 0.90 || nEventsMB > nEvents * (1 - ratioTrigger) * 1.10) { // we put some tolerance since the number of generated events is small + std::cerr << "Number of generated MB events different than expected\n"; + return 1; + } + if (nEventsInjOne < nEvents * ratioTrigger * 0.5 * 0.90 || nEventsInjOne > nEvents * ratioTrigger * 0.5 * 1.10) { + std::cerr << "Number of generated events injected with " << checkPdgQuarkOne << " different than expected\n"; + return 1; + } + if (nEventsInjTwo < nEvents * ratioTrigger * 0.5 * 0.90 || nEventsInjTwo > nEvents * ratioTrigger * 0.5 * 1.10) { + std::cerr << "Number of generated events injected with " << checkPdgQuarkTwo << " different than expected\n"; + return 1; + } + + float fracForcedDecays = nSignals ? float(nSignalGoodDecay) / nSignals : 0.0f; + float uncFracForcedDecays = nSignals ? std::sqrt(fracForcedDecays * (1 - fracForcedDecays) / nSignals) : 1.0f; + if (1 - fracForcedDecays > 0.5 + uncFracForcedDecays) { // we put some tolerance (e.g. due to oscillations which might change the final state) + std::cerr << "Fraction of signals decaying into the correct channel " << fracForcedDecays << " lower than expected\n"; + return 1; + } + + // Check particle replacement ratios + for (int iRepl{0}; iRepl < 3; ++iRepl) { + float numPart = sumOrigReplacedParticles[pdgReplParticles[iRepl][0]]; + float fracMeas = numPart ? float(pdgReplPartCounters[iRepl][1]) / numPart : 0.0f; + float fracMeasUnc = (fracMeas && numPart != 1) ? std::sqrt(pdgReplPartCounters[iRepl][1]) / numPart : 1.0f; + + if (std::abs(fracMeas - freqRepl[iRepl]) > fracMeasUnc) { + std::cerr << "Fraction of replaced " << pdgReplParticles[iRepl][0] << " into " << pdgReplParticles[iRepl][1] << " is " << fracMeas <<" (expected "<< freqRepl[iRepl] << ")\n"; + return 1; + } + } + + return 0; +} diff --git a/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_and_bbbar_gap5_Mode2_pp_ref.C b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_and_bbbar_gap5_Mode2_pp_ref.C new file mode 100644 index 000000000..a6f9ed435 --- /dev/null +++ b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_and_bbbar_gap5_Mode2_pp_ref.C @@ -0,0 +1,191 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + + int checkPdgQuarkOne{4}; + int checkPdgQuarkTwo{5}; + float ratioTrigger = 1./5; // one event triggered out of 5 + + std::vector checkPdgHadron{411, 421, 431, 4122, 4132, 4232, 4332}; + std::map>> checkHadronDecays{ // sorted pdg of daughters + {421, { + {-321, 211}, // D0 -> K-, pi+ + {-321, 211, 111}, // D0 -> K-, pi+, pi0 + {213, -321}, // D0 -> rho(770)+, K- + {-313, 111}, // D0 -> Kbar^*(892)0, pi0 + {-323, 211}, // D0 -> K^*(892)-, pi+ + {-211, 211}, // D0 -> pi-, pi+ + {213, -211}, // D0 -> rho(770)+, pi- + {-211, 211, 111}, // D0 -> pi-, pi+, pi0 + {-321, 321}, // D0 -> K-, K+ + }}, + + {411, { + {-321, 211, 211}, // D+ -> K-, pi+, pi+ + {-10311, 211}, // D+ -> Kbar0^*(1430)0, pi+ + {-313, 211}, // D+ -> Kbar^*(892)0, pi+ + {-321, 211, 211, 111}, // D+ -> K-, pi+, pi+, pi0 + {333, 211}, // D+ -> phi(1020)0, pi+ + {-313, 321}, // D+ -> Kbar^*(892)0, K+ + {-10311, 321}, // D+ -> Kbar0^*(1430)0, K+ + {-321, 321, 211}, // D+ -> K-, K+, pi+ + {113, 211}, // D+ -> rho(770)0, pi+ + {225, 211}, // D+ -> f2(1270)0, pi+ + {-211, 211, 211}, // D+ -> pi-, pi+, pi+ + }}, + + {431, { + {211, 333}, // Ds+ -> phi(1020)0, pi+ + {-313, 321}, // Ds+ -> Kbar^*(892)0, K+ + {333, 213}, // Ds+ -> phi(1020)0, rho(770)+ + {113, 211}, // Ds+ -> rho(770)0, pi+ + {225, 211}, // Ds+ -> f2(1270)0, pi+ + {-211, 211, 211}, // Ds+ -> pi-, pi+, pi+ + {313, 211}, // Ds+ -> K^*(892)0, pi+ + {10221, 321}, // Ds+ -> f0(1370)0, K+ + {113, 321}, // Ds+ -> rho(770)0, K+ + {-211, 321, 211}, // Ds+ -> pi-, K+, pi+ + {221, 211}, // Ds+ -> eta, pi+ + }}, + + {4122, { + {2212, -321, 211}, // Lambdac+ -> p, K-, pi+ + {2212, -313}, // Lambdac+ -> p, Kbar^*(892)0 + {2224, -321}, // Lambdac+ -> Delta(1232)++, K- + {102134, 211}, // Lambdac+ -> 102134, pi+ + {2212, 311}, // Lambdac+ -> p, K0 + {2212, -321, 211, 111}, // Lambdac+ -> p, K-, pi+, pi0 + {2212, -211, 211}, // Lambdac+ -> p, pi-, pi+ + {2212, 333}, // Lambdac+ -> p, phi(1020)0 + }}, + + {4232, { + {2212, -321, 211}, // Xic+ -> p, K-, pi+ + {2212, -313}, // Xic+ -> p, Kbar^*(892)0 + {3312, 211, 211}, // Xic+ -> Xi-, pi+, pi+ + {2212, 333}, // Xic+ -> p, phi(1020)0 + {3222, -211, 211}, // Xic+ -> Sigma+, pi-, pi+ + {3324, 211}, // Xic+ -> Xi(1530)0, pi+ + }}, + + {4132, { + {3312, 211}, // Xic0 -> Xi-, pi+ + }}, + + {4332, { + {3334, 211}, // Omegac0 -> Omega-, pi+ + {3312, 211}, // Omegac0 -> Xi-, pi+ + }}, + }; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int nEventsMB{}, nEventsInjOne{}, nEventsInjTwo{}; + int nQuarksOne{}, nQuarksTwo{}, nSignals{}, nSignalGoodDecay{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + + // check subgenerator information + if (eventHeader->hasInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID)) { + bool isValid = false; + int subGeneratorId = eventHeader->getInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID, isValid); + if (subGeneratorId == 0) { + nEventsMB++; + } else if (subGeneratorId == checkPdgQuarkOne) { + nEventsInjOne++; + } else if (subGeneratorId == checkPdgQuarkTwo) { + nEventsInjTwo++; + } + } + + for (auto &track : *tracks) { + auto pdg = track.GetPdgCode(); + if (std::abs(pdg) == checkPdgQuarkOne) { + nQuarksOne++; + continue; + } + if (std::abs(pdg) == checkPdgQuarkTwo) { + nQuarksTwo++; + continue; + } + if (std::find(checkPdgHadron.begin(), checkPdgHadron.end(), std::abs(pdg)) != checkPdgHadron.end()) { // found signal + nSignals++; // count signal PDG + + std::vector pdgsDecay{}; + std::vector pdgsDecayAntiPart{}; + for (int j{track.getFirstDaughterTrackId()}; j <= track.getLastDaughterTrackId(); ++j) { + auto pdgDau = tracks->at(j).GetPdgCode(); + pdgsDecay.push_back(pdgDau); + if (pdgDau != 333 && pdgDau != 111 && pdgDau != 221 && pdgDau != 113 && pdgDau != 225) { // phi is antiparticle of itself + pdgsDecayAntiPart.push_back(-pdgDau); + } else { + pdgsDecayAntiPart.push_back(pdgDau); + } + } + + std::sort(pdgsDecay.begin(), pdgsDecay.end()); + std::sort(pdgsDecayAntiPart.begin(), pdgsDecayAntiPart.end()); + + for (auto &decay : checkHadronDecays[std::abs(pdg)]) { + std::sort(decay.begin(), decay.end()); + if (pdgsDecay == decay || pdgsDecayAntiPart == decay) { + nSignalGoodDecay++; + break; + } + } + } + } + } + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + std::cout << "# MB events: " << nEventsMB << "\n"; + std::cout << Form("# events injected with %d quark pair: ", checkPdgQuarkOne) << nEventsInjOne << "\n"; + std::cout << Form("# events injected with %d quark pair: ", checkPdgQuarkTwo) << nEventsInjTwo << "\n"; + std::cout << Form("# %d (anti)quarks: ", checkPdgQuarkOne) << nQuarksOne << "\n"; + std::cout << Form("# %d (anti)quarks: ", checkPdgQuarkTwo) << nQuarksTwo << "\n"; + std::cout <<"# signal hadrons: " << nSignals << "\n"; + std::cout <<"# signal hadrons decaying in the correct channel: " << nSignalGoodDecay << "\n"; + + if (nEventsMB < nEvents * (1 - ratioTrigger) * 0.95 || nEventsMB > nEvents * (1 - ratioTrigger) * 1.05) { // we put some tolerance since the number of generated events is small + std::cerr << "Number of generated MB events different than expected\n"; + return 1; + } + if (nEventsInjOne < nEvents * ratioTrigger * 0.5 * 0.95 || nEventsInjOne > nEvents * ratioTrigger * 0.5 * 1.05) { + std::cerr << "Number of generated events injected with " << checkPdgQuarkOne << " different than expected\n"; + return 1; + } + if (nEventsInjTwo < nEvents * ratioTrigger * 0.5 * 0.95 || nEventsInjTwo > nEvents * ratioTrigger * 0.5 * 1.05) { + std::cerr << "Number of generated events injected with " << checkPdgQuarkTwo << " different than expected\n"; + return 1; + } + + if (nQuarksOne < nEvents * ratioTrigger) { // we expect anyway more because the same quark is repeated several time, after each gluon radiation + std::cerr << "Number of generated (anti)quarks " << checkPdgQuarkOne << " lower than expected\n"; + return 1; + } + if (nQuarksTwo < nEvents * ratioTrigger) { // we expect anyway more because the same quark is repeated several time, after each gluon radiation + std::cerr << "Number of generated (anti)quarks " << checkPdgQuarkTwo << " lower than expected\n"; + return 1; + } + + float fracForcedDecays = nSignals ? float(nSignalGoodDecay) / nSignals : 0.0f; + float uncFracForcedDecays = nSignals ? std::sqrt(fracForcedDecays * (1 - fracForcedDecays) / nSignals) : 1.0f; + if (1 - fracForcedDecays > 0.15 + uncFracForcedDecays) { // we put some tolerance (e.g. due to oscillations which might change the final state) + std::cerr << "Fraction of signals decaying into the correct channel " << fracForcedDecays << " lower than expected\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_and_bbbar_gap8.C b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_and_bbbar_gap8.C new file mode 100644 index 000000000..b4a723630 --- /dev/null +++ b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_and_bbbar_gap8.C @@ -0,0 +1,129 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + + int checkPdgQuarkOne{4}; + int checkPdgQuarkTwo{5}; + float ratioTrigger = 1./8; // one event triggered out of 8 + + std::vector checkPdgHadron{411, 421, 431, 4122, 4132, 4232, 4332}; + std::map>> checkHadronDecays{ // sorted pdg of daughters + {411, {{-321, 211, 211}, {-313, 211}, {211, 311}, {211, 333}}}, // D+ + {421, {{-321, 211}, {-321, 111, 211}}}, // D0 + {431, {{211, 333}, {-313, 321}}}, // Ds+ + {4122, {{-313, 2212}, {-321, 2224}, {211, 102134}, {-321, 211, 2212}, {311, 2212}}}, // Lc+ + {4132, {{211, 3312}}}, // Xic0 + {4232, {{-313, 2212}, {-321, 3324}, {211, 211, 3312}, {-321, 211, 2212}}}, // Xic+ + {4332, {{211, 3334}}} // Omegac+ + }; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int nEventsMB{}, nEventsInjOne{}, nEventsInjTwo{}; + int nQuarksOne{}, nQuarksTwo{}, nSignals{}, nSignalGoodDecay{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + + // check subgenerator information + if (eventHeader->hasInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID)) { + bool isValid = false; + int subGeneratorId = eventHeader->getInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID, isValid); + if (subGeneratorId == 0) { + nEventsMB++; + } else if (subGeneratorId == checkPdgQuarkOne) { + nEventsInjOne++; + } else if (subGeneratorId == checkPdgQuarkTwo) { + nEventsInjTwo++; + } + } + + for (auto &track : *tracks) { + auto pdg = track.GetPdgCode(); + if (std::abs(pdg) == checkPdgQuarkOne) { + nQuarksOne++; + continue; + } + if (std::abs(pdg) == checkPdgQuarkTwo) { + nQuarksTwo++; + continue; + } + if (std::find(checkPdgHadron.begin(), checkPdgHadron.end(), std::abs(pdg)) != checkPdgHadron.end()) { // found signal + nSignals++; // count signal PDG + + std::vector pdgsDecay{}; + std::vector pdgsDecayAntiPart{}; + for (int j{track.getFirstDaughterTrackId()}; j <= track.getLastDaughterTrackId(); ++j) { + auto pdgDau = tracks->at(j).GetPdgCode(); + pdgsDecay.push_back(pdgDau); + if (pdgDau != 333) { // phi is antiparticle of itself + pdgsDecayAntiPart.push_back(-pdgDau); + } else { + pdgsDecayAntiPart.push_back(pdgDau); + } + } + + std::sort(pdgsDecay.begin(), pdgsDecay.end()); + std::sort(pdgsDecayAntiPart.begin(), pdgsDecayAntiPart.end()); + + for (auto &decay : checkHadronDecays[std::abs(pdg)]) { + if (pdgsDecay == decay || pdgsDecayAntiPart == decay) { + nSignalGoodDecay++; + break; + } + } + } + } + } + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + std::cout << "# MB events: " << nEventsMB << "\n"; + std::cout << Form("# events injected with %d quark pair: ", checkPdgQuarkOne) << nEventsInjOne << "\n"; + std::cout << Form("# events injected with %d quark pair: ", checkPdgQuarkTwo) << nEventsInjTwo << "\n"; + std::cout << Form("# %d (anti)quarks: ", checkPdgQuarkOne) << nQuarksOne << "\n"; + std::cout << Form("# %d (anti)quarks: ", checkPdgQuarkTwo) << nQuarksTwo << "\n"; + std::cout <<"# signal hadrons: " << nSignals << "\n"; + std::cout <<"# signal hadrons decaying in the correct channel: " << nSignalGoodDecay << "\n"; + + if (nEventsMB < nEvents * (1 - ratioTrigger) * 0.95 || nEventsMB > nEvents * (1 - ratioTrigger) * 1.05) { // we put some tolerance since the number of generated events is small + std::cerr << "Number of generated MB events different than expected\n"; + return 1; + } + if (nEventsInjOne < nEvents * ratioTrigger * 0.5 * 0.85 || nEventsInjOne > nEvents * ratioTrigger * 0.5 * 1.15) { + std::cerr << "Number of generated events injected with " << checkPdgQuarkOne << " different than expected\n"; + return 1; + } + if (nEventsInjTwo < nEvents * ratioTrigger * 0.5 * 0.85 || nEventsInjTwo > nEvents * ratioTrigger * 0.5 * 1.15) { + std::cerr << "Number of generated events injected with " << checkPdgQuarkTwo << " different than expected\n"; + return 1; + } + + if (nQuarksOne < nEvents * ratioTrigger) { // we expect anyway more because the same quark is repeated several time, after each gluon radiation + std::cerr << "Number of generated (anti)quarks " << checkPdgQuarkOne << " lower than expected\n"; + return 1; + } + if (nQuarksTwo < nEvents * ratioTrigger) { // we expect anyway more because the same quark is repeated several time, after each gluon radiation + std::cerr << "Number of generated (anti)quarks " << checkPdgQuarkTwo << " lower than expected\n"; + return 1; + } + + float fracForcedDecays = nSignals ? float(nSignalGoodDecay) / nSignals : 0.0f; + float uncFracForcedDecays = nSignals ? std::sqrt(fracForcedDecays * (1 - fracForcedDecays) / nSignals) : 1.0f; + if (1 - fracForcedDecays > 0.15 + uncFracForcedDecays) { // we put some tolerance (e.g. due to oscillations which might change the final state) + std::cerr << "Fraction of signals decaying into the correct channel " << fracForcedDecays << " lower than expected\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_and_bbbar_gap8_Mode2_XiC.C b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_and_bbbar_gap8_Mode2_XiC.C new file mode 100644 index 000000000..4e143c055 --- /dev/null +++ b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_and_bbbar_gap8_Mode2_XiC.C @@ -0,0 +1,123 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + + int checkPdgQuarkOne{4}; + int checkPdgQuarkTwo{5}; + float ratioTrigger = 1./8; // one event triggered out of 8 + + std::vector checkPdgHadron{4132}; + std::map>> checkHadronDecays{ // sorted pdg of daughters + {4132, {{211, 3312}}} // Xic0 + }; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int nEventsMB{}, nEventsInjOne{}, nEventsInjTwo{}; + int nQuarksOne{}, nQuarksTwo{}, nSignals{}, nSignalGoodDecay{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + + // check subgenerator information + if (eventHeader->hasInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID)) { + bool isValid = false; + int subGeneratorId = eventHeader->getInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID, isValid); + if (subGeneratorId == 0) { + nEventsMB++; + } else if (subGeneratorId == checkPdgQuarkOne) { + nEventsInjOne++; + } else if (subGeneratorId == checkPdgQuarkTwo) { + nEventsInjTwo++; + } + } + + for (auto &track : *tracks) { + auto pdg = track.GetPdgCode(); + if (std::abs(pdg) == checkPdgQuarkOne) { + nQuarksOne++; + continue; + } + if (std::abs(pdg) == checkPdgQuarkTwo) { + nQuarksTwo++; + continue; + } + if (std::find(checkPdgHadron.begin(), checkPdgHadron.end(), std::abs(pdg)) != checkPdgHadron.end()) { // found signal + nSignals++; // count signal PDG + + std::vector pdgsDecay{}; + std::vector pdgsDecayAntiPart{}; + for (int j{track.getFirstDaughterTrackId()}; j <= track.getLastDaughterTrackId(); ++j) { + auto pdgDau = tracks->at(j).GetPdgCode(); + pdgsDecay.push_back(pdgDau); + if (pdgDau != 333) { // phi is antiparticle of itself + pdgsDecayAntiPart.push_back(-pdgDau); + } else { + pdgsDecayAntiPart.push_back(pdgDau); + } + } + + std::sort(pdgsDecay.begin(), pdgsDecay.end()); + std::sort(pdgsDecayAntiPart.begin(), pdgsDecayAntiPart.end()); + + for (auto &decay : checkHadronDecays[std::abs(pdg)]) { + if (pdgsDecay == decay || pdgsDecayAntiPart == decay) { + nSignalGoodDecay++; + break; + } + } + } + } + } + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + std::cout << "# MB events: " << nEventsMB << "\n"; + std::cout << Form("# events injected with %d quark pair: ", checkPdgQuarkOne) << nEventsInjOne << "\n"; + std::cout << Form("# events injected with %d quark pair: ", checkPdgQuarkTwo) << nEventsInjTwo << "\n"; + std::cout << Form("# %d (anti)quarks: ", checkPdgQuarkOne) << nQuarksOne << "\n"; + std::cout << Form("# %d (anti)quarks: ", checkPdgQuarkTwo) << nQuarksTwo << "\n"; + std::cout <<"# signal hadrons: " << nSignals << "\n"; + std::cout <<"# signal hadrons decaying in the correct channel: " << nSignalGoodDecay << "\n"; + + if (nEventsMB < nEvents * (1 - ratioTrigger) * 0.95 || nEventsMB > nEvents * (1 - ratioTrigger) * 1.05) { // we put some tolerance since the number of generated events is small + std::cerr << "Number of generated MB events different than expected\n"; + return 1; + } + if (nEventsInjOne < nEvents * ratioTrigger * 0.5 * 0.85 || nEventsInjOne > nEvents * ratioTrigger * 0.5 * 1.15) { + std::cerr << "Number of generated events injected with " << checkPdgQuarkOne << " different than expected\n"; + return 1; + } + if (nEventsInjTwo < nEvents * ratioTrigger * 0.5 * 0.85 || nEventsInjTwo > nEvents * ratioTrigger * 0.5 * 1.15) { + std::cerr << "Number of generated events injected with " << checkPdgQuarkTwo << " different than expected\n"; + return 1; + } + + if (nQuarksOne < nEvents * ratioTrigger) { // we expect anyway more because the same quark is repeated several time, after each gluon radiation + std::cerr << "Number of generated (anti)quarks " << checkPdgQuarkOne << " lower than expected\n"; + return 1; + } + if (nQuarksTwo < nEvents * ratioTrigger) { // we expect anyway more because the same quark is repeated several time, after each gluon radiation + std::cerr << "Number of generated (anti)quarks " << checkPdgQuarkTwo << " lower than expected\n"; + return 1; + } + + float fracForcedDecays = nSignals ? float(nSignalGoodDecay) / nSignals : 0.0f; + float uncFracForcedDecays = nSignals ? std::sqrt(fracForcedDecays * (1 - fracForcedDecays) / nSignals) : 1.0f; + if (1 - fracForcedDecays > 0.15 + uncFracForcedDecays) { // we put some tolerance (e.g. due to oscillations which might change the final state) + std::cerr << "Fraction of signals decaying into the correct channel " << fracForcedDecays << " lower than expected\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_gap5.C b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_gap5.C new file mode 100644 index 000000000..38cc604cc --- /dev/null +++ b/MC/config/PWGHF/ini/tests/GeneratorHF_D2H_ccbar_gap5.C @@ -0,0 +1,112 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + + int checkPdgQuark{4}; + float ratioTrigger = 1./5; // one event triggered out of 5 + + std::vector checkPdgHadron{411, 421, 431, 4122, 4132, 4232, 4332}; + std::map>> checkHadronDecays{ // sorted pdg of daughters + {411, {{-321, 211, 211}, {-313, 211}, {211, 311}, {211, 333}}}, // D+ + {421, {{-321, 211}, {-321, 111, 211}}}, // D0 + {431, {{211, 333}, {-313, 321}}}, // Ds+ + {4122, {{-313, 2212}, {-321, 2224}, {211, 102134}, {-321, 211, 2212}, {311, 2212}}}, // Lc+ + {4132, {{211, 3312}}}, // Xic0 + {4232, {{-313, 2212}, {-321, 3324}, {211, 211, 3312}, {-321, 211, 2212}}}, // Xic+ + {4332, {{211, 3334}}} // Omegac+ + }; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int nEventsMB{}, nEventsInj{}; + int nQuarks{}, nSignals{}, nSignalGoodDecay{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + + // check subgenerator information + if (eventHeader->hasInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID)) { + bool isValid = false; + int subGeneratorId = eventHeader->getInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID, isValid); + if (subGeneratorId == 0) { + nEventsMB++; + } else if (subGeneratorId == checkPdgQuark) { + nEventsInj++; + } + } + + for (auto &track : *tracks) { + auto pdg = track.GetPdgCode(); + if (std::abs(pdg) == checkPdgQuark) { + nQuarks++; + continue; + } + if (std::find(checkPdgHadron.begin(), checkPdgHadron.end(), std::abs(pdg)) != checkPdgHadron.end()) { // found signal + nSignals++; // count signal PDG + + std::vector pdgsDecay{}; + std::vector pdgsDecayAntiPart{}; + for (int j{track.getFirstDaughterTrackId()}; j <= track.getLastDaughterTrackId(); ++j) { + auto pdgDau = tracks->at(j).GetPdgCode(); + pdgsDecay.push_back(pdgDau); + if (pdgDau != 333) { // phi is antiparticle of itself + pdgsDecayAntiPart.push_back(-pdgDau); + } else { + pdgsDecayAntiPart.push_back(pdgDau); + } + } + + std::sort(pdgsDecay.begin(), pdgsDecay.end()); + std::sort(pdgsDecayAntiPart.begin(), pdgsDecayAntiPart.end()); + + for (auto &decay : checkHadronDecays[std::abs(pdg)]) { + if (pdgsDecay == decay || pdgsDecayAntiPart == decay) { + nSignalGoodDecay++; + break; + } + } + } + } + } + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + std::cout << "# MB events: " << nEventsMB << "\n"; + std::cout << Form("# events injected with %d quark pair: ", checkPdgQuark) << nEventsInj << "\n"; + std::cout << Form("# %d (anti)quarks: ", checkPdgQuark) << nQuarks << "\n"; + std::cout <<"# signal hadrons: " << nSignals << "\n"; + std::cout <<"# signal hadrons decaying in the correct channel: " << nSignalGoodDecay << "\n"; + + if (nEventsMB < nEvents * (1 - ratioTrigger) * 0.95 || nEventsMB > nEvents * (1 - ratioTrigger) * 1.05) { // we put some tolerance since the number of generated events is small + std::cerr << "Number of generated MB events different than expected\n"; + return 1; + } + if (nEventsInj < nEvents * ratioTrigger * 0.95 || nEventsInj > nEvents * ratioTrigger * 1.05) { + std::cerr << "Number of generated events injected with " << checkPdgQuark << " different than expected\n"; + return 1; + } + + if (nQuarks < 2 * nEvents * ratioTrigger) { // we expect anyway more because the same quark is repeated several time, after each gluon radiation + std::cerr << "Number of generated (anti)quarks " << checkPdgQuark << " lower than expected\n"; + return 1; + } + + float fracForcedDecays = nSignals ? float(nSignalGoodDecay) / nSignals : 0.0f; + float uncFracForcedDecays = nSignals ? std::sqrt(fracForcedDecays * (1 - fracForcedDecays) / nSignals) : 1.0f; + if (1 - fracForcedDecays > 0.15 + uncFracForcedDecays) { // we put some tolerance (e.g. due to oscillations which might change the final state) + std::cerr << "Fraction of signals decaying into the correct channel " << fracForcedDecays << " lower than expected\n"; + return 1; + } + + return 0; +} diff --git a/MC/config/PWGHF/ini/tests/GeneratorHF_HFe_ccbar_and_bbar_gap5_Mode2.C b/MC/config/PWGHF/ini/tests/GeneratorHF_HFe_ccbar_and_bbar_gap5_Mode2.C new file mode 100644 index 000000000..fb9df800e --- /dev/null +++ b/MC/config/PWGHF/ini/tests/GeneratorHF_HFe_ccbar_and_bbar_gap5_Mode2.C @@ -0,0 +1,128 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + + int checkPdgDecayElectron = 11; + int checkPdgQuarkOne = 4; + int checkPdgQuarkTwo = 5; + float ratioTrigger = 1. / 5; // one event triggered out of 5 + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file" << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) { + std::cerr << "Cannot find tree o2sim in file" << path << "\n"; + return 1; + } + + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int nEventsMB{}, nEventsInjOne{}, nEventsInjTwo{}; + int nQuarksOne{}, nQuarksTwo{}; + int nElectrons{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + + // check subgenerator information + if (eventHeader->hasInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID)) { + bool isValid = false; + int subGeneratorId = eventHeader->getInfo( + o2::mcgenid::GeneratorProperty::SUBGENERATORID, isValid); + if (subGeneratorId == 0) { + nEventsMB++; + } else if (subGeneratorId == checkPdgQuarkOne) { + nEventsInjOne++; + } else if (subGeneratorId == checkPdgQuarkTwo) { + nEventsInjTwo++; + } + } // if event header + + int nelectronsev = 0; + + for (auto &track : *tracks) { + auto pdg = track.GetPdgCode(); + if (std::abs(pdg) == checkPdgQuarkOne) { + nQuarksOne++; + continue; + } + if (std::abs(pdg) == checkPdgQuarkTwo) { + nQuarksTwo++; + continue; + } + + auto y = track.GetRapidity(); + if (std::abs(pdg) == checkPdgDecayElectron) { + int igmother = track.getMotherTrackId(); + auto gmTrack = (*tracks)[igmother]; + int gmpdg = gmTrack.GetPdgCode(); + if (int(std::abs(gmpdg) / 100.) == 4 || + int(std::abs(gmpdg) / 1000.) == 4 || + int(std::abs(gmpdg) / 100.) == 5 || + int(std::abs(gmpdg) / 1000.) == 5) { + nElectrons++; + nelectronsev++; + } // gmpdg + } // pdgdecay + } // loop track + // std::cout << "#electrons per event: " << nelectronsev << "\n"; + } + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + std::cout << "# MB events: " << nEventsMB << "\n"; + std::cout << Form("# events injected with %d quark pair: ", checkPdgQuarkOne) + << nEventsInjOne << "\n"; + std::cout << Form("# events injected with %d quark pair: ", checkPdgQuarkTwo) + << nEventsInjTwo << "\n"; + std::cout << Form("# %d (anti)quarks: ", checkPdgQuarkOne) << nQuarksOne + << "\n"; + std::cout << Form("# %d (anti)quarks: ", checkPdgQuarkTwo) << nQuarksTwo + << "\n"; + + if (nEventsMB < nEvents * (1 - ratioTrigger) * 0.95 || + nEventsMB > nEvents * (1 - ratioTrigger) * + 1.05) { // we put some tolerance since the number of + // generated events is small + std::cerr << "Number of generated MB events different than expected\n"; + return 1; + } + if (nEventsInjOne < nEvents * ratioTrigger * 0.5 * 0.95 || + nEventsInjOne > nEvents * ratioTrigger * 0.5 * 1.05) { + std::cerr << "Number of generated events injected with " << checkPdgQuarkOne + << " different than expected\n"; + return 1; + } + if (nEventsInjTwo < nEvents * ratioTrigger * 0.5 * 0.95 || + nEventsInjTwo > nEvents * ratioTrigger * 0.5 * 1.05) { + std::cerr << "Number of generated events injected with " << checkPdgQuarkTwo + << " different than expected\n"; + return 1; + } + if (nQuarksOne < + nEvents * + ratioTrigger) { // we expect anyway more because the same quark is + // repeated several time, after each gluon radiation + std::cerr << "Number of generated (anti)quarks " << checkPdgQuarkOne + << " lower than expected\n"; + return 1; + } + if (nQuarksTwo < + nEvents * + ratioTrigger) { // we expect anyway more because the same quark is + // repeated several time, after each gluon radiation + std::cerr << "Number of generated (anti)quarks " << checkPdgQuarkTwo + << " lower than expected\n"; + return 1; + } + std::cout << "#electrons: " << nElectrons << "\n"; + + return 0; +} // external diff --git a/MC/config/PWGHF/ini/tests/GeneratorHF_HFe_ccbar_and_bbar_gap5_Mode2_pp_ref.C b/MC/config/PWGHF/ini/tests/GeneratorHF_HFe_ccbar_and_bbar_gap5_Mode2_pp_ref.C new file mode 100644 index 000000000..fb9df800e --- /dev/null +++ b/MC/config/PWGHF/ini/tests/GeneratorHF_HFe_ccbar_and_bbar_gap5_Mode2_pp_ref.C @@ -0,0 +1,128 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + + int checkPdgDecayElectron = 11; + int checkPdgQuarkOne = 4; + int checkPdgQuarkTwo = 5; + float ratioTrigger = 1. / 5; // one event triggered out of 5 + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file" << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) { + std::cerr << "Cannot find tree o2sim in file" << path << "\n"; + return 1; + } + + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int nEventsMB{}, nEventsInjOne{}, nEventsInjTwo{}; + int nQuarksOne{}, nQuarksTwo{}; + int nElectrons{}; + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + + // check subgenerator information + if (eventHeader->hasInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID)) { + bool isValid = false; + int subGeneratorId = eventHeader->getInfo( + o2::mcgenid::GeneratorProperty::SUBGENERATORID, isValid); + if (subGeneratorId == 0) { + nEventsMB++; + } else if (subGeneratorId == checkPdgQuarkOne) { + nEventsInjOne++; + } else if (subGeneratorId == checkPdgQuarkTwo) { + nEventsInjTwo++; + } + } // if event header + + int nelectronsev = 0; + + for (auto &track : *tracks) { + auto pdg = track.GetPdgCode(); + if (std::abs(pdg) == checkPdgQuarkOne) { + nQuarksOne++; + continue; + } + if (std::abs(pdg) == checkPdgQuarkTwo) { + nQuarksTwo++; + continue; + } + + auto y = track.GetRapidity(); + if (std::abs(pdg) == checkPdgDecayElectron) { + int igmother = track.getMotherTrackId(); + auto gmTrack = (*tracks)[igmother]; + int gmpdg = gmTrack.GetPdgCode(); + if (int(std::abs(gmpdg) / 100.) == 4 || + int(std::abs(gmpdg) / 1000.) == 4 || + int(std::abs(gmpdg) / 100.) == 5 || + int(std::abs(gmpdg) / 1000.) == 5) { + nElectrons++; + nelectronsev++; + } // gmpdg + } // pdgdecay + } // loop track + // std::cout << "#electrons per event: " << nelectronsev << "\n"; + } + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + std::cout << "# MB events: " << nEventsMB << "\n"; + std::cout << Form("# events injected with %d quark pair: ", checkPdgQuarkOne) + << nEventsInjOne << "\n"; + std::cout << Form("# events injected with %d quark pair: ", checkPdgQuarkTwo) + << nEventsInjTwo << "\n"; + std::cout << Form("# %d (anti)quarks: ", checkPdgQuarkOne) << nQuarksOne + << "\n"; + std::cout << Form("# %d (anti)quarks: ", checkPdgQuarkTwo) << nQuarksTwo + << "\n"; + + if (nEventsMB < nEvents * (1 - ratioTrigger) * 0.95 || + nEventsMB > nEvents * (1 - ratioTrigger) * + 1.05) { // we put some tolerance since the number of + // generated events is small + std::cerr << "Number of generated MB events different than expected\n"; + return 1; + } + if (nEventsInjOne < nEvents * ratioTrigger * 0.5 * 0.95 || + nEventsInjOne > nEvents * ratioTrigger * 0.5 * 1.05) { + std::cerr << "Number of generated events injected with " << checkPdgQuarkOne + << " different than expected\n"; + return 1; + } + if (nEventsInjTwo < nEvents * ratioTrigger * 0.5 * 0.95 || + nEventsInjTwo > nEvents * ratioTrigger * 0.5 * 1.05) { + std::cerr << "Number of generated events injected with " << checkPdgQuarkTwo + << " different than expected\n"; + return 1; + } + if (nQuarksOne < + nEvents * + ratioTrigger) { // we expect anyway more because the same quark is + // repeated several time, after each gluon radiation + std::cerr << "Number of generated (anti)quarks " << checkPdgQuarkOne + << " lower than expected\n"; + return 1; + } + if (nQuarksTwo < + nEvents * + ratioTrigger) { // we expect anyway more because the same quark is + // repeated several time, after each gluon radiation + std::cerr << "Number of generated (anti)quarks " << checkPdgQuarkTwo + << " lower than expected\n"; + return 1; + } + std::cout << "#electrons: " << nElectrons << "\n"; + + return 0; +} // external diff --git a/MC/config/PWGHF/ini/tests/GeneratorHF_Non_Hfe.C b/MC/config/PWGHF/ini/tests/GeneratorHF_Non_Hfe.C new file mode 100644 index 000000000..fb2c280d9 --- /dev/null +++ b/MC/config/PWGHF/ini/tests/GeneratorHF_Non_Hfe.C @@ -0,0 +1,61 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + + const int pdgPi0 = 111; + const int pdgEta = 221; + const double yMin = -1.5; + const double yMax = 1.5; + const int minNb = 1; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree*)file.Get("o2sim"); + if (!tree) { + std::cerr << "Cannot find tree o2sim\n"; + return 1; + } + + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int nEvents = tree->GetEntries(); + int nAccepted = 0; + int totalPi0 = 0, totalEta = 0; + + for (int i = 0; i < nEvents; ++i) { + tree->GetEntry(i); + + int count = 0; + for (auto& track : *tracks) { + int pdg = std::abs(track.GetPdgCode()); + double y = track.GetRapidity(); + + if ((pdg == pdgPi0 || pdg == pdgEta) && y >= yMin && y <= yMax) { + count++; + if (pdg == pdgPi0) totalPi0++; + if (pdg == pdgEta) totalEta++; + } + } + + if (count < minNb) { + std::cerr << " Trigger violation in event " << i + << " (found " << count << " π0/η in rapidity window)\n"; + return 1; + } + + nAccepted++; + } + + std::cout << "--------------------------------------\n"; + std::cout << "Trigger test: π0/η within rapidity window\n"; + std::cout << "Events tested: " << nEvents << "\n"; + std::cout << "Events accepted: " << nAccepted << "\n"; + std::cout << "# π0: " << totalPi0 << ", # η: " << totalEta << "\n"; + std::cout << "Trigger test PASSED\n"; + + return 0; +} diff --git a/MC/config/PWGHF/ini/tests/GeneratorHF_bbbar_fwd_gap4_natural_inel_Mode2.C b/MC/config/PWGHF/ini/tests/GeneratorHF_bbbar_fwd_gap4_natural_inel_Mode2.C new file mode 100644 index 000000000..34072820e --- /dev/null +++ b/MC/config/PWGHF/ini/tests/GeneratorHF_bbbar_fwd_gap4_natural_inel_Mode2.C @@ -0,0 +1,103 @@ +int External() { + + int checkPdgDecayMuon = 13; + int checkPdgQuark = 5; + + float ratioTrigger = 1. / 4; // one event triggered out of 4 + + std::string path{"o2sim_Kine.root"}; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file" << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) { + std::cerr << "Cannot find tree o2sim in file" << path << "\n"; + return 1; + } + + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int nEventsMB{}; + int nEventsInj{}; + int nQuarks{}; + int nMuons{}; + + int nMuonsInAcceptance{}; + + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + // check subgenerator information + if (eventHeader->hasInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID)) { + bool isValid = false; + int subGeneratorId = eventHeader->getInfo( + o2::mcgenid::GeneratorProperty::SUBGENERATORID, isValid); + if (subGeneratorId == 0) { + nEventsMB++; + } else if (subGeneratorId == checkPdgQuark) { + nEventsInj++; + } + } // if event header + + int nmuonsev = 0; + int nmuonsevinacc = 0; + + for (auto &track : *tracks) { + auto pdg = track.GetPdgCode(); + if (std::abs(pdg) == checkPdgQuark) { + nQuarks++; + continue; + } // pdgquark + auto y = track.GetRapidity(); + if (std::abs(pdg) == checkPdgDecayMuon) { + int igmother = track.getMotherTrackId(); + auto gmTrack = (*tracks)[igmother]; + int gmpdg = gmTrack.GetPdgCode(); + if (int(std::abs(gmpdg) / 100.) == 5 || + int(std::abs(gmpdg) / 1000.) == 5) { + nMuons++; + nmuonsev++; + if (-4.3 < y && y < -2.2) { + nMuonsInAcceptance++; + nmuonsevinacc++; + } + } // gmpdg + + } // pdgdecay + + } // loop track + // std::cout << "#muons per event: " << nmuonsev << "\n"; + // std::cout << "#muons in acceptance per event: " << nmuonsev << "\n"; + } // events + + std::cout << "#events: " << nEvents << "\n"; + std::cout << "# MB events: " << nEventsMB << "\n"; + std::cout << Form("# events injected with %d quark pair: ", checkPdgQuark) + << nEventsInj << "\n"; + if (nEventsMB < nEvents * (1 - ratioTrigger) * 0.95 || + nEventsMB > nEvents * (1 - ratioTrigger) * + 1.05) { // we put some tolerance since the number of + // generated events is small + std::cerr << "Number of generated MB events different than expected\n"; + return 1; + } + if (nEventsInj < nEvents * ratioTrigger * 0.95 || + nEventsInj > nEvents * ratioTrigger * 1.05) { + std::cerr << "Number of generated events injected with " << checkPdgQuark + << " different than expected\n"; + return 1; + } + std::cout << "#muons: " << nMuons << "\n"; + std::cout << "#muons in acceptance: " << nMuonsInAcceptance << "\n"; + + return 0; +} // external diff --git a/MC/config/PWGHF/ini/tests/GeneratorHF_bbbar_fwd_gap4_natural_inel_Mode2_muTrig.C b/MC/config/PWGHF/ini/tests/GeneratorHF_bbbar_fwd_gap4_natural_inel_Mode2_muTrig.C new file mode 100644 index 000000000..34072820e --- /dev/null +++ b/MC/config/PWGHF/ini/tests/GeneratorHF_bbbar_fwd_gap4_natural_inel_Mode2_muTrig.C @@ -0,0 +1,103 @@ +int External() { + + int checkPdgDecayMuon = 13; + int checkPdgQuark = 5; + + float ratioTrigger = 1. / 4; // one event triggered out of 4 + + std::string path{"o2sim_Kine.root"}; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file" << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) { + std::cerr << "Cannot find tree o2sim in file" << path << "\n"; + return 1; + } + + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int nEventsMB{}; + int nEventsInj{}; + int nQuarks{}; + int nMuons{}; + + int nMuonsInAcceptance{}; + + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + // check subgenerator information + if (eventHeader->hasInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID)) { + bool isValid = false; + int subGeneratorId = eventHeader->getInfo( + o2::mcgenid::GeneratorProperty::SUBGENERATORID, isValid); + if (subGeneratorId == 0) { + nEventsMB++; + } else if (subGeneratorId == checkPdgQuark) { + nEventsInj++; + } + } // if event header + + int nmuonsev = 0; + int nmuonsevinacc = 0; + + for (auto &track : *tracks) { + auto pdg = track.GetPdgCode(); + if (std::abs(pdg) == checkPdgQuark) { + nQuarks++; + continue; + } // pdgquark + auto y = track.GetRapidity(); + if (std::abs(pdg) == checkPdgDecayMuon) { + int igmother = track.getMotherTrackId(); + auto gmTrack = (*tracks)[igmother]; + int gmpdg = gmTrack.GetPdgCode(); + if (int(std::abs(gmpdg) / 100.) == 5 || + int(std::abs(gmpdg) / 1000.) == 5) { + nMuons++; + nmuonsev++; + if (-4.3 < y && y < -2.2) { + nMuonsInAcceptance++; + nmuonsevinacc++; + } + } // gmpdg + + } // pdgdecay + + } // loop track + // std::cout << "#muons per event: " << nmuonsev << "\n"; + // std::cout << "#muons in acceptance per event: " << nmuonsev << "\n"; + } // events + + std::cout << "#events: " << nEvents << "\n"; + std::cout << "# MB events: " << nEventsMB << "\n"; + std::cout << Form("# events injected with %d quark pair: ", checkPdgQuark) + << nEventsInj << "\n"; + if (nEventsMB < nEvents * (1 - ratioTrigger) * 0.95 || + nEventsMB > nEvents * (1 - ratioTrigger) * + 1.05) { // we put some tolerance since the number of + // generated events is small + std::cerr << "Number of generated MB events different than expected\n"; + return 1; + } + if (nEventsInj < nEvents * ratioTrigger * 0.95 || + nEventsInj > nEvents * ratioTrigger * 1.05) { + std::cerr << "Number of generated events injected with " << checkPdgQuark + << " different than expected\n"; + return 1; + } + std::cout << "#muons: " << nMuons << "\n"; + std::cout << "#muons in acceptance: " << nMuonsInAcceptance << "\n"; + + return 0; +} // external diff --git a/MC/config/PWGHF/ini/tests/GeneratorHF_mu_bbbar_gap3_Mode2_accSmall.C b/MC/config/PWGHF/ini/tests/GeneratorHF_mu_bbbar_gap3_Mode2_accSmall.C new file mode 100644 index 000000000..b915d734e --- /dev/null +++ b/MC/config/PWGHF/ini/tests/GeneratorHF_mu_bbbar_gap3_Mode2_accSmall.C @@ -0,0 +1,103 @@ +int External() { + + int checkPdgDecayMuon = 13; + int checkPdgQuark = 5; + + float ratioTrigger = 1./3; // one event triggered out of 5 + + std::string path{"o2sim_Kine.root"}; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file" << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) { + std::cerr << "Cannot find tree o2sim in file" << path << "\n"; + return 1; + } + + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int nEventsMB{}; + int nEventsInj{}; + int nQuarks{}; + int nMuons{}; + + int nMuonsInAcceptance{}; + + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + // check subgenerator information + if (eventHeader->hasInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID)) { + bool isValid = false; + int subGeneratorId = eventHeader->getInfo( + o2::mcgenid::GeneratorProperty::SUBGENERATORID, isValid); + if (subGeneratorId == 0) { + nEventsMB++; + } else if (subGeneratorId == checkPdgQuark) { + nEventsInj++; + } + } // if event header + + int nmuonsev = 0; + int nmuonsevinacc = 0; + + for (auto &track : *tracks) { + auto pdg = track.GetPdgCode(); + if (std::abs(pdg) == checkPdgQuark) { + nQuarks++; + continue; + } // pdgquark + auto y = track.GetRapidity(); + if (std::abs(pdg) == checkPdgDecayMuon) { + int igmother = track.getMotherTrackId(); + auto gmTrack = (*tracks)[igmother]; + int gmpdg = gmTrack.GetPdgCode(); + if (int(std::abs(gmpdg) / 100.) == 5 || + int(std::abs(gmpdg) / 1000.) == 5) { + nMuons++; + nmuonsev++; + if (-4.3 < y && y < -2.2) { + nMuonsInAcceptance++; + nmuonsevinacc++; + } + } // gmpdg + + } // pdgdecay + + } // loop track + // std::cout << "#muons per event: " << nmuonsev << "\n"; + // std::cout << "#muons in acceptance per event: " << nmuonsev << "\n"; + } // events + + std::cout << "#events: " << nEvents << "\n"; + std::cout << "# MB events: " << nEventsMB << "\n"; + std::cout << Form("# events injected with %d quark pair: ", checkPdgQuark) + << nEventsInj << "\n"; + if (nEventsMB < nEvents * (1 - ratioTrigger) * 0.95 || + nEventsMB > nEvents * (1 - ratioTrigger) * + 1.05) { // we put some tolerance since the number of + // generated events is small + std::cerr << "Number of generated MB events different than expected\n"; + return 1; + } + if (nEventsInj < nEvents * ratioTrigger * 0.95 || + nEventsInj > nEvents * ratioTrigger * 1.05) { + std::cerr << "Number of generated events injected with " << checkPdgQuark + << " different than expected\n"; + return 1; + } + std::cout << "#muons: " << nMuons << "\n"; + std::cout << "#muons in acceptance: " << nMuonsInAcceptance << "\n"; + + return 0; +} // external diff --git a/MC/config/PWGHF/ini/tests/GeneratorHF_mu_bbbar_gap5_Mode2_accInt_muTrig.C b/MC/config/PWGHF/ini/tests/GeneratorHF_mu_bbbar_gap5_Mode2_accInt_muTrig.C new file mode 100644 index 000000000..bc26e5e7e --- /dev/null +++ b/MC/config/PWGHF/ini/tests/GeneratorHF_mu_bbbar_gap5_Mode2_accInt_muTrig.C @@ -0,0 +1,103 @@ +int External() { + + int checkPdgDecayMuon = 13; + int checkPdgQuark = 5; + + float ratioTrigger = 1. / 5; // one event triggered out of 5 + + std::string path{"o2sim_Kine.root"}; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file" << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) { + std::cerr << "Cannot find tree o2sim in file" << path << "\n"; + return 1; + } + + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int nEventsMB{}; + int nEventsInj{}; + int nQuarks{}; + int nMuons{}; + + int nMuonsInAcceptance{}; + + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + // check subgenerator information + if (eventHeader->hasInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID)) { + bool isValid = false; + int subGeneratorId = eventHeader->getInfo( + o2::mcgenid::GeneratorProperty::SUBGENERATORID, isValid); + if (subGeneratorId == 0) { + nEventsMB++; + } else if (subGeneratorId == checkPdgQuark) { + nEventsInj++; + } + } // if event header + + int nmuonsev = 0; + int nmuonsevinacc = 0; + + for (auto &track : *tracks) { + auto pdg = track.GetPdgCode(); + if (std::abs(pdg) == checkPdgQuark) { + nQuarks++; + continue; + } // pdgquark + auto y = track.GetRapidity(); + if (std::abs(pdg) == checkPdgDecayMuon) { + int igmother = track.getMotherTrackId(); + auto gmTrack = (*tracks)[igmother]; + int gmpdg = gmTrack.GetPdgCode(); + if (int(std::abs(gmpdg) / 100.) == 5 || + int(std::abs(gmpdg) / 1000.) == 5) { + nMuons++; + nmuonsev++; + if (-4.3 < y && y < -2.2) { + nMuonsInAcceptance++; + nmuonsevinacc++; + } + } // gmpdg + + } // pdgdecay + + } // loop track + // std::cout << "#muons per event: " << nmuonsev << "\n"; + // std::cout << "#muons in acceptance per event: " << nmuonsev << "\n"; + } // events + + std::cout << "#events: " << nEvents << "\n"; + std::cout << "# MB events: " << nEventsMB << "\n"; + std::cout << Form("# events injected with %d quark pair: ", checkPdgQuark) + << nEventsInj << "\n"; + if (nEventsMB < nEvents * (1 - ratioTrigger) * 0.95 || + nEventsMB > nEvents * (1 - ratioTrigger) * + 1.05) { // we put some tolerance since the number of + // generated events is small + std::cerr << "Number of generated MB events different than expected\n"; + return 1; + } + if (nEventsInj < nEvents * ratioTrigger * 0.95 || + nEventsInj > nEvents * ratioTrigger * 1.05) { + std::cerr << "Number of generated events injected with " << checkPdgQuark + << " different than expected\n"; + return 1; + } + std::cout << "#muons: " << nMuons << "\n"; + std::cout << "#muons in acceptance: " << nMuonsInAcceptance << "\n"; + + return 0; +} // external diff --git a/MC/config/PWGHF/ini/tests/GeneratorHF_mu_bbbar_gap5_Mode2_accLarge.C b/MC/config/PWGHF/ini/tests/GeneratorHF_mu_bbbar_gap5_Mode2_accLarge.C new file mode 100644 index 000000000..bc26e5e7e --- /dev/null +++ b/MC/config/PWGHF/ini/tests/GeneratorHF_mu_bbbar_gap5_Mode2_accLarge.C @@ -0,0 +1,103 @@ +int External() { + + int checkPdgDecayMuon = 13; + int checkPdgQuark = 5; + + float ratioTrigger = 1. / 5; // one event triggered out of 5 + + std::string path{"o2sim_Kine.root"}; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file" << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) { + std::cerr << "Cannot find tree o2sim in file" << path << "\n"; + return 1; + } + + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int nEventsMB{}; + int nEventsInj{}; + int nQuarks{}; + int nMuons{}; + + int nMuonsInAcceptance{}; + + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + // check subgenerator information + if (eventHeader->hasInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID)) { + bool isValid = false; + int subGeneratorId = eventHeader->getInfo( + o2::mcgenid::GeneratorProperty::SUBGENERATORID, isValid); + if (subGeneratorId == 0) { + nEventsMB++; + } else if (subGeneratorId == checkPdgQuark) { + nEventsInj++; + } + } // if event header + + int nmuonsev = 0; + int nmuonsevinacc = 0; + + for (auto &track : *tracks) { + auto pdg = track.GetPdgCode(); + if (std::abs(pdg) == checkPdgQuark) { + nQuarks++; + continue; + } // pdgquark + auto y = track.GetRapidity(); + if (std::abs(pdg) == checkPdgDecayMuon) { + int igmother = track.getMotherTrackId(); + auto gmTrack = (*tracks)[igmother]; + int gmpdg = gmTrack.GetPdgCode(); + if (int(std::abs(gmpdg) / 100.) == 5 || + int(std::abs(gmpdg) / 1000.) == 5) { + nMuons++; + nmuonsev++; + if (-4.3 < y && y < -2.2) { + nMuonsInAcceptance++; + nmuonsevinacc++; + } + } // gmpdg + + } // pdgdecay + + } // loop track + // std::cout << "#muons per event: " << nmuonsev << "\n"; + // std::cout << "#muons in acceptance per event: " << nmuonsev << "\n"; + } // events + + std::cout << "#events: " << nEvents << "\n"; + std::cout << "# MB events: " << nEventsMB << "\n"; + std::cout << Form("# events injected with %d quark pair: ", checkPdgQuark) + << nEventsInj << "\n"; + if (nEventsMB < nEvents * (1 - ratioTrigger) * 0.95 || + nEventsMB > nEvents * (1 - ratioTrigger) * + 1.05) { // we put some tolerance since the number of + // generated events is small + std::cerr << "Number of generated MB events different than expected\n"; + return 1; + } + if (nEventsInj < nEvents * ratioTrigger * 0.95 || + nEventsInj > nEvents * ratioTrigger * 1.05) { + std::cerr << "Number of generated events injected with " << checkPdgQuark + << " different than expected\n"; + return 1; + } + std::cout << "#muons: " << nMuons << "\n"; + std::cout << "#muons in acceptance: " << nMuonsInAcceptance << "\n"; + + return 0; +} // external diff --git a/MC/config/PWGHF/ini/tests/GeneratorHF_mu_bbbar_gap5_Mode2_accLarge_muTrig.C b/MC/config/PWGHF/ini/tests/GeneratorHF_mu_bbbar_gap5_Mode2_accLarge_muTrig.C new file mode 100644 index 000000000..bc26e5e7e --- /dev/null +++ b/MC/config/PWGHF/ini/tests/GeneratorHF_mu_bbbar_gap5_Mode2_accLarge_muTrig.C @@ -0,0 +1,103 @@ +int External() { + + int checkPdgDecayMuon = 13; + int checkPdgQuark = 5; + + float ratioTrigger = 1. / 5; // one event triggered out of 5 + + std::string path{"o2sim_Kine.root"}; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file" << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) { + std::cerr << "Cannot find tree o2sim in file" << path << "\n"; + return 1; + } + + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int nEventsMB{}; + int nEventsInj{}; + int nQuarks{}; + int nMuons{}; + + int nMuonsInAcceptance{}; + + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + // check subgenerator information + if (eventHeader->hasInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID)) { + bool isValid = false; + int subGeneratorId = eventHeader->getInfo( + o2::mcgenid::GeneratorProperty::SUBGENERATORID, isValid); + if (subGeneratorId == 0) { + nEventsMB++; + } else if (subGeneratorId == checkPdgQuark) { + nEventsInj++; + } + } // if event header + + int nmuonsev = 0; + int nmuonsevinacc = 0; + + for (auto &track : *tracks) { + auto pdg = track.GetPdgCode(); + if (std::abs(pdg) == checkPdgQuark) { + nQuarks++; + continue; + } // pdgquark + auto y = track.GetRapidity(); + if (std::abs(pdg) == checkPdgDecayMuon) { + int igmother = track.getMotherTrackId(); + auto gmTrack = (*tracks)[igmother]; + int gmpdg = gmTrack.GetPdgCode(); + if (int(std::abs(gmpdg) / 100.) == 5 || + int(std::abs(gmpdg) / 1000.) == 5) { + nMuons++; + nmuonsev++; + if (-4.3 < y && y < -2.2) { + nMuonsInAcceptance++; + nmuonsevinacc++; + } + } // gmpdg + + } // pdgdecay + + } // loop track + // std::cout << "#muons per event: " << nmuonsev << "\n"; + // std::cout << "#muons in acceptance per event: " << nmuonsev << "\n"; + } // events + + std::cout << "#events: " << nEvents << "\n"; + std::cout << "# MB events: " << nEventsMB << "\n"; + std::cout << Form("# events injected with %d quark pair: ", checkPdgQuark) + << nEventsInj << "\n"; + if (nEventsMB < nEvents * (1 - ratioTrigger) * 0.95 || + nEventsMB > nEvents * (1 - ratioTrigger) * + 1.05) { // we put some tolerance since the number of + // generated events is small + std::cerr << "Number of generated MB events different than expected\n"; + return 1; + } + if (nEventsInj < nEvents * ratioTrigger * 0.95 || + nEventsInj > nEvents * ratioTrigger * 1.05) { + std::cerr << "Number of generated events injected with " << checkPdgQuark + << " different than expected\n"; + return 1; + } + std::cout << "#muons: " << nMuons << "\n"; + std::cout << "#muons in acceptance: " << nMuonsInAcceptance << "\n"; + + return 0; +} // external diff --git a/MC/config/PWGHF/ini/tests/GeneratorHF_mu_bbbar_gap5_Mode2_accSmall.C b/MC/config/PWGHF/ini/tests/GeneratorHF_mu_bbbar_gap5_Mode2_accSmall.C new file mode 100644 index 000000000..bc26e5e7e --- /dev/null +++ b/MC/config/PWGHF/ini/tests/GeneratorHF_mu_bbbar_gap5_Mode2_accSmall.C @@ -0,0 +1,103 @@ +int External() { + + int checkPdgDecayMuon = 13; + int checkPdgQuark = 5; + + float ratioTrigger = 1. / 5; // one event triggered out of 5 + + std::string path{"o2sim_Kine.root"}; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file" << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) { + std::cerr << "Cannot find tree o2sim in file" << path << "\n"; + return 1; + } + + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int nEventsMB{}; + int nEventsInj{}; + int nQuarks{}; + int nMuons{}; + + int nMuonsInAcceptance{}; + + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + // check subgenerator information + if (eventHeader->hasInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID)) { + bool isValid = false; + int subGeneratorId = eventHeader->getInfo( + o2::mcgenid::GeneratorProperty::SUBGENERATORID, isValid); + if (subGeneratorId == 0) { + nEventsMB++; + } else if (subGeneratorId == checkPdgQuark) { + nEventsInj++; + } + } // if event header + + int nmuonsev = 0; + int nmuonsevinacc = 0; + + for (auto &track : *tracks) { + auto pdg = track.GetPdgCode(); + if (std::abs(pdg) == checkPdgQuark) { + nQuarks++; + continue; + } // pdgquark + auto y = track.GetRapidity(); + if (std::abs(pdg) == checkPdgDecayMuon) { + int igmother = track.getMotherTrackId(); + auto gmTrack = (*tracks)[igmother]; + int gmpdg = gmTrack.GetPdgCode(); + if (int(std::abs(gmpdg) / 100.) == 5 || + int(std::abs(gmpdg) / 1000.) == 5) { + nMuons++; + nmuonsev++; + if (-4.3 < y && y < -2.2) { + nMuonsInAcceptance++; + nmuonsevinacc++; + } + } // gmpdg + + } // pdgdecay + + } // loop track + // std::cout << "#muons per event: " << nmuonsev << "\n"; + // std::cout << "#muons in acceptance per event: " << nmuonsev << "\n"; + } // events + + std::cout << "#events: " << nEvents << "\n"; + std::cout << "# MB events: " << nEventsMB << "\n"; + std::cout << Form("# events injected with %d quark pair: ", checkPdgQuark) + << nEventsInj << "\n"; + if (nEventsMB < nEvents * (1 - ratioTrigger) * 0.95 || + nEventsMB > nEvents * (1 - ratioTrigger) * + 1.05) { // we put some tolerance since the number of + // generated events is small + std::cerr << "Number of generated MB events different than expected\n"; + return 1; + } + if (nEventsInj < nEvents * ratioTrigger * 0.95 || + nEventsInj > nEvents * ratioTrigger * 1.05) { + std::cerr << "Number of generated events injected with " << checkPdgQuark + << " different than expected\n"; + return 1; + } + std::cout << "#muons: " << nMuons << "\n"; + std::cout << "#muons in acceptance: " << nMuonsInAcceptance << "\n"; + + return 0; +} // external diff --git a/MC/config/PWGHF/ini/tests/GeneratorHF_mu_ccbar_bbbar_charmdecays_gap3_Mode2_accSmall.C b/MC/config/PWGHF/ini/tests/GeneratorHF_mu_ccbar_bbbar_charmdecays_gap3_Mode2_accSmall.C new file mode 100644 index 000000000..d308ba734 --- /dev/null +++ b/MC/config/PWGHF/ini/tests/GeneratorHF_mu_ccbar_bbbar_charmdecays_gap3_Mode2_accSmall.C @@ -0,0 +1,114 @@ +int External() { + + int checkPdgDecayMuon = 13; + int checkPdgQuarkOne{4}; + int checkPdgQuarkTwo{5}; + float ratioQuarkOneTwo{2.}; + + float ratioTrigger = 1./3; // one event triggered out of 5 + + std::string path{"o2sim_Kine.root"}; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file" << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) { + std::cerr << "Cannot find tree o2sim in file" << path << "\n"; + return 1; + } + + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int nEventsMB{0}; + int nEventsInjOne{0}; + int nEventsInjTwo{0}; + int nQuarks{0}; + int nMuons{0}; + + int nMuonsInAcceptance{}; + + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + // check subgenerator information + if (eventHeader->hasInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID)) { + bool isValid = false; + int subGeneratorId = eventHeader->getInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID, isValid); + if (subGeneratorId == 0) { + nEventsMB++; + } else if (subGeneratorId == checkPdgQuarkOne) { + nEventsInjOne++; + } else if (subGeneratorId == checkPdgQuarkTwo) { + nEventsInjTwo++; + } + } + + int nmuonsev = 0; + int nmuonsevinacc = 0; + + for (auto &track : *tracks) { + auto pdg = track.GetPdgCode(); + if (std::abs(pdg) == checkPdgQuarkOne || std::abs(pdg) == checkPdgQuarkTwo) { + nQuarks++; + continue; + } // pdgquark + auto y = track.GetRapidity(); + if (std::abs(pdg) == checkPdgDecayMuon) { + int igmother = track.getMotherTrackId(); + auto gmTrack = (*tracks)[igmother]; + int gmpdg = gmTrack.GetPdgCode(); + if (int(std::abs(gmpdg) / 100.) == 4 || + int(std::abs(gmpdg) / 1000.) == 4) { + nMuons++; + nmuonsev++; + if (-4.3 < y && y < -2.2) { + nMuonsInAcceptance++; + nmuonsevinacc++; + } + } // gmpdg + + } // pdgdecay + + } // loop track + // std::cout << "#muons per event: " << nmuonsev << "\n"; + // std::cout << "#muons in acceptance per event: " << nmuonsev << "\n"; + } // events + + std::cout << "#events: " << nEvents << "\n"; + std::cout << "# MB events: " << nEventsMB << "\n"; + std::cout << Form("# events injected with %d quark pair: ", checkPdgQuarkOne) + << nEventsInjOne << "\n"; + std::cout << Form("# events injected with %d quark pair: ", checkPdgQuarkTwo) + << nEventsInjTwo << "\n"; + if (nEventsMB < nEvents * (1 - ratioTrigger) * 0.95 || + nEventsMB > nEvents * (1 - ratioTrigger) * + 1.05) { // we put some tolerance since the number of + // generated events is small + std::cerr << "Number of generated MB events different than expected\n"; + return 1; + } + if ((nEventsInjOne + nEventsInjTwo) < nEvents * ratioTrigger * 0.95 || + (nEventsInjOne + nEventsInjTwo) > nEvents * ratioTrigger * 1.05) { + std::cerr << "Number of generated events injected with " << checkPdgQuarkOne << " and " << checkPdgQuarkTwo + << " different than expected\n"; + return 1; + } + if (std::abs(float(nEventsInjOne) / nEventsInjTwo - ratioQuarkOneTwo) / ratioQuarkOneTwo > 0.1) { + std::cerr << "Ratio between quark " << checkPdgQuarkOne << " and quark " << checkPdgQuarkTwo + << " different than expected\n"; + return 1; + } + std::cout << "#muons: " << nMuons << "\n"; + std::cout << "#muons in acceptance: " << nMuonsInAcceptance << "\n"; + + return 0; +} // external diff --git a/MC/config/PWGHF/ini/tests/GeneratorHF_mu_ccbar_gap3_Mode2_accSmall.C b/MC/config/PWGHF/ini/tests/GeneratorHF_mu_ccbar_gap3_Mode2_accSmall.C new file mode 100644 index 000000000..262973fc2 --- /dev/null +++ b/MC/config/PWGHF/ini/tests/GeneratorHF_mu_ccbar_gap3_Mode2_accSmall.C @@ -0,0 +1,103 @@ +int External() { + + int checkPdgDecayMuon = 13; + int checkPdgQuark = 4; + + float ratioTrigger = 1./3; // one event triggered out of 5 + + std::string path{"o2sim_Kine.root"}; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file" << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) { + std::cerr << "Cannot find tree o2sim in file" << path << "\n"; + return 1; + } + + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int nEventsMB{}; + int nEventsInj{}; + int nQuarks{}; + int nMuons{}; + + int nMuonsInAcceptance{}; + + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + // check subgenerator information + if (eventHeader->hasInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID)) { + bool isValid = false; + int subGeneratorId = eventHeader->getInfo( + o2::mcgenid::GeneratorProperty::SUBGENERATORID, isValid); + if (subGeneratorId == 0) { + nEventsMB++; + } else if (subGeneratorId == checkPdgQuark) { + nEventsInj++; + } + } // if event header + + int nmuonsev = 0; + int nmuonsevinacc = 0; + + for (auto &track : *tracks) { + auto pdg = track.GetPdgCode(); + if (std::abs(pdg) == checkPdgQuark) { + nQuarks++; + continue; + } // pdgquark + auto y = track.GetRapidity(); + if (std::abs(pdg) == checkPdgDecayMuon) { + int igmother = track.getMotherTrackId(); + auto gmTrack = (*tracks)[igmother]; + int gmpdg = gmTrack.GetPdgCode(); + if (int(std::abs(gmpdg) / 100.) == 4 || + int(std::abs(gmpdg) / 1000.) == 4) { + nMuons++; + nmuonsev++; + if (-4.3 < y && y < -2.2) { + nMuonsInAcceptance++; + nmuonsevinacc++; + } + } // gmpdg + + } // pdgdecay + + } // loop track + // std::cout << "#muons per event: " << nmuonsev << "\n"; + // std::cout << "#muons in acceptance per event: " << nmuonsev << "\n"; + } // events + + std::cout << "#events: " << nEvents << "\n"; + std::cout << "# MB events: " << nEventsMB << "\n"; + std::cout << Form("# events injected with %d quark pair: ", checkPdgQuark) + << nEventsInj << "\n"; + if (nEventsMB < nEvents * (1 - ratioTrigger) * 0.95 || + nEventsMB > nEvents * (1 - ratioTrigger) * + 1.05) { // we put some tolerance since the number of + // generated events is small + std::cerr << "Number of generated MB events different than expected\n"; + return 1; + } + if (nEventsInj < nEvents * ratioTrigger * 0.95 || + nEventsInj > nEvents * ratioTrigger * 1.05) { + std::cerr << "Number of generated events injected with " << checkPdgQuark + << " different than expected\n"; + return 1; + } + std::cout << "#muons: " << nMuons << "\n"; + std::cout << "#muons in acceptance: " << nMuonsInAcceptance << "\n"; + + return 0; +} // external diff --git a/MC/config/PWGHF/ini/tests/GeneratorHF_mu_ccbar_gap5_Mode2_accInt_muTrig.C b/MC/config/PWGHF/ini/tests/GeneratorHF_mu_ccbar_gap5_Mode2_accInt_muTrig.C new file mode 100644 index 000000000..1cf192133 --- /dev/null +++ b/MC/config/PWGHF/ini/tests/GeneratorHF_mu_ccbar_gap5_Mode2_accInt_muTrig.C @@ -0,0 +1,103 @@ +int External() { + + int checkPdgDecayMuon = 13; + int checkPdgQuark = 4; + + float ratioTrigger = 1. / 5; // one event triggered out of 5 + + std::string path{"o2sim_Kine.root"}; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file" << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) { + std::cerr << "Cannot find tree o2sim in file" << path << "\n"; + return 1; + } + + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int nEventsMB{}; + int nEventsInj{}; + int nQuarks{}; + int nMuons{}; + + int nMuonsInAcceptance{}; + + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + // check subgenerator information + if (eventHeader->hasInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID)) { + bool isValid = false; + int subGeneratorId = eventHeader->getInfo( + o2::mcgenid::GeneratorProperty::SUBGENERATORID, isValid); + if (subGeneratorId == 0) { + nEventsMB++; + } else if (subGeneratorId == checkPdgQuark) { + nEventsInj++; + } + } // if event header + + int nmuonsev = 0; + int nmuonsevinacc = 0; + + for (auto &track : *tracks) { + auto pdg = track.GetPdgCode(); + if (std::abs(pdg) == checkPdgQuark) { + nQuarks++; + continue; + } // pdgquark + auto y = track.GetRapidity(); + if (std::abs(pdg) == checkPdgDecayMuon) { + int igmother = track.getMotherTrackId(); + auto gmTrack = (*tracks)[igmother]; + int gmpdg = gmTrack.GetPdgCode(); + if (int(std::abs(gmpdg) / 100.) == 4 || + int(std::abs(gmpdg) / 1000.) == 4) { + nMuons++; + nmuonsev++; + if (-4.3 < y && y < -2.2) { + nMuonsInAcceptance++; + nmuonsevinacc++; + } + } // gmpdg + + } // pdgdecay + + } // loop track + // std::cout << "#muons per event: " << nmuonsev << "\n"; + // std::cout << "#muons in acceptance per event: " << nmuonsev << "\n"; + } // events + + std::cout << "#events: " << nEvents << "\n"; + std::cout << "# MB events: " << nEventsMB << "\n"; + std::cout << Form("# events injected with %d quark pair: ", checkPdgQuark) + << nEventsInj << "\n"; + if (nEventsMB < nEvents * (1 - ratioTrigger) * 0.95 || + nEventsMB > nEvents * (1 - ratioTrigger) * + 1.05) { // we put some tolerance since the number of + // generated events is small + std::cerr << "Number of generated MB events different than expected\n"; + return 1; + } + if (nEventsInj < nEvents * ratioTrigger * 0.95 || + nEventsInj > nEvents * ratioTrigger * 1.05) { + std::cerr << "Number of generated events injected with " << checkPdgQuark + << " different than expected\n"; + return 1; + } + std::cout << "#muons: " << nMuons << "\n"; + std::cout << "#muons in acceptance: " << nMuonsInAcceptance << "\n"; + + return 0; +} // external diff --git a/MC/config/PWGHF/ini/tests/GeneratorHF_mu_ccbar_gap5_Mode2_accLarge.C b/MC/config/PWGHF/ini/tests/GeneratorHF_mu_ccbar_gap5_Mode2_accLarge.C new file mode 100644 index 000000000..1cf192133 --- /dev/null +++ b/MC/config/PWGHF/ini/tests/GeneratorHF_mu_ccbar_gap5_Mode2_accLarge.C @@ -0,0 +1,103 @@ +int External() { + + int checkPdgDecayMuon = 13; + int checkPdgQuark = 4; + + float ratioTrigger = 1. / 5; // one event triggered out of 5 + + std::string path{"o2sim_Kine.root"}; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file" << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) { + std::cerr << "Cannot find tree o2sim in file" << path << "\n"; + return 1; + } + + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int nEventsMB{}; + int nEventsInj{}; + int nQuarks{}; + int nMuons{}; + + int nMuonsInAcceptance{}; + + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + // check subgenerator information + if (eventHeader->hasInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID)) { + bool isValid = false; + int subGeneratorId = eventHeader->getInfo( + o2::mcgenid::GeneratorProperty::SUBGENERATORID, isValid); + if (subGeneratorId == 0) { + nEventsMB++; + } else if (subGeneratorId == checkPdgQuark) { + nEventsInj++; + } + } // if event header + + int nmuonsev = 0; + int nmuonsevinacc = 0; + + for (auto &track : *tracks) { + auto pdg = track.GetPdgCode(); + if (std::abs(pdg) == checkPdgQuark) { + nQuarks++; + continue; + } // pdgquark + auto y = track.GetRapidity(); + if (std::abs(pdg) == checkPdgDecayMuon) { + int igmother = track.getMotherTrackId(); + auto gmTrack = (*tracks)[igmother]; + int gmpdg = gmTrack.GetPdgCode(); + if (int(std::abs(gmpdg) / 100.) == 4 || + int(std::abs(gmpdg) / 1000.) == 4) { + nMuons++; + nmuonsev++; + if (-4.3 < y && y < -2.2) { + nMuonsInAcceptance++; + nmuonsevinacc++; + } + } // gmpdg + + } // pdgdecay + + } // loop track + // std::cout << "#muons per event: " << nmuonsev << "\n"; + // std::cout << "#muons in acceptance per event: " << nmuonsev << "\n"; + } // events + + std::cout << "#events: " << nEvents << "\n"; + std::cout << "# MB events: " << nEventsMB << "\n"; + std::cout << Form("# events injected with %d quark pair: ", checkPdgQuark) + << nEventsInj << "\n"; + if (nEventsMB < nEvents * (1 - ratioTrigger) * 0.95 || + nEventsMB > nEvents * (1 - ratioTrigger) * + 1.05) { // we put some tolerance since the number of + // generated events is small + std::cerr << "Number of generated MB events different than expected\n"; + return 1; + } + if (nEventsInj < nEvents * ratioTrigger * 0.95 || + nEventsInj > nEvents * ratioTrigger * 1.05) { + std::cerr << "Number of generated events injected with " << checkPdgQuark + << " different than expected\n"; + return 1; + } + std::cout << "#muons: " << nMuons << "\n"; + std::cout << "#muons in acceptance: " << nMuonsInAcceptance << "\n"; + + return 0; +} // external diff --git a/MC/config/PWGHF/ini/tests/GeneratorHF_mu_ccbar_gap5_Mode2_accLarge_muTrig.C b/MC/config/PWGHF/ini/tests/GeneratorHF_mu_ccbar_gap5_Mode2_accLarge_muTrig.C new file mode 100644 index 000000000..d53003560 --- /dev/null +++ b/MC/config/PWGHF/ini/tests/GeneratorHF_mu_ccbar_gap5_Mode2_accLarge_muTrig.C @@ -0,0 +1,103 @@ +int External() { + + int checkPdgDecayMuon = 13; + int checkPdgQuark = 4; + + float ratioTrigger = 1. / 5; // one event triggered out of 5 + + std::string path{"o2sim_Kine.root"}; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file" << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) { + std::cerr << "Cannot find tree o2sim in file" << path << "\n"; + return 1; + } + + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int nEventsMB{}; + int nEventsInj{}; + int nQuarks{}; + int nMuons{}; + + int nMuonsInAcceptance{}; + + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + // check subgenerator information + if (eventHeader->hasInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID)) { + bool isValid = false; + int subGeneratorId = eventHeader->getInfo( + o2::mcgenid::GeneratorProperty::SUBGENERATORID, isValid); + if (subGeneratorId == 0) { + nEventsMB++; + } else if (subGeneratorId == checkPdgQuark) { + nEventsInj++; + } + } // if event header + + int nmuonsev = 0; + int nmuonsevinacc = 0; + + for (auto &track : *tracks) { + auto pdg = track.GetPdgCode(); + if (std::abs(pdg) == checkPdgQuark) { + nQuarks++; + continue; + } // pdgquark + auto y = track.GetRapidity(); + if (std::abs(pdg) == checkPdgDecayMuon) { + int igmother = track.getMotherTrackId(); + auto gmTrack = (*tracks)[igmother]; + int gmpdg = gmTrack.GetPdgCode(); + if (int(std::abs(gmpdg) / 100.) == 4|| + int(std::abs(gmpdg) / 1000.) == 4) { + nMuons++; + nmuonsev++; + if (-4.3 < y && y < -2.2) { + nMuonsInAcceptance++; + nmuonsevinacc++; + } + } // gmpdg + + } // pdgdecay + + } // loop track + // std::cout << "#muons per event: " << nmuonsev << "\n"; + // std::cout << "#muons in acceptance per event: " << nmuonsev << "\n"; + } // events + + std::cout << "#events: " << nEvents << "\n"; + std::cout << "# MB events: " << nEventsMB << "\n"; + std::cout << Form("# events injected with %d quark pair: ", checkPdgQuark) + << nEventsInj << "\n"; + if (nEventsMB < nEvents * (1 - ratioTrigger) * 0.95 || + nEventsMB > nEvents * (1 - ratioTrigger) * + 1.05) { // we put some tolerance since the number of + // generated events is small + std::cerr << "Number of generated MB events different than expected\n"; + return 1; + } + if (nEventsInj < nEvents * ratioTrigger * 0.95 || + nEventsInj > nEvents * ratioTrigger * 1.05) { + std::cerr << "Number of generated events injected with " << checkPdgQuark + << " different than expected\n"; + return 1; + } + std::cout << "#muons: " << nMuons << "\n"; + std::cout << "#muons in acceptance: " << nMuonsInAcceptance << "\n"; + + return 0; +} // external diff --git a/MC/config/PWGHF/ini/tests/GeneratorHF_mu_ccbar_gap5_Mode2_accSmall.C b/MC/config/PWGHF/ini/tests/GeneratorHF_mu_ccbar_gap5_Mode2_accSmall.C new file mode 100644 index 000000000..1cf192133 --- /dev/null +++ b/MC/config/PWGHF/ini/tests/GeneratorHF_mu_ccbar_gap5_Mode2_accSmall.C @@ -0,0 +1,103 @@ +int External() { + + int checkPdgDecayMuon = 13; + int checkPdgQuark = 4; + + float ratioTrigger = 1. / 5; // one event triggered out of 5 + + std::string path{"o2sim_Kine.root"}; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file" << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) { + std::cerr << "Cannot find tree o2sim in file" << path << "\n"; + return 1; + } + + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + o2::dataformats::MCEventHeader *eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + int nEventsMB{}; + int nEventsInj{}; + int nQuarks{}; + int nMuons{}; + + int nMuonsInAcceptance{}; + + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + // check subgenerator information + if (eventHeader->hasInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID)) { + bool isValid = false; + int subGeneratorId = eventHeader->getInfo( + o2::mcgenid::GeneratorProperty::SUBGENERATORID, isValid); + if (subGeneratorId == 0) { + nEventsMB++; + } else if (subGeneratorId == checkPdgQuark) { + nEventsInj++; + } + } // if event header + + int nmuonsev = 0; + int nmuonsevinacc = 0; + + for (auto &track : *tracks) { + auto pdg = track.GetPdgCode(); + if (std::abs(pdg) == checkPdgQuark) { + nQuarks++; + continue; + } // pdgquark + auto y = track.GetRapidity(); + if (std::abs(pdg) == checkPdgDecayMuon) { + int igmother = track.getMotherTrackId(); + auto gmTrack = (*tracks)[igmother]; + int gmpdg = gmTrack.GetPdgCode(); + if (int(std::abs(gmpdg) / 100.) == 4 || + int(std::abs(gmpdg) / 1000.) == 4) { + nMuons++; + nmuonsev++; + if (-4.3 < y && y < -2.2) { + nMuonsInAcceptance++; + nmuonsevinacc++; + } + } // gmpdg + + } // pdgdecay + + } // loop track + // std::cout << "#muons per event: " << nmuonsev << "\n"; + // std::cout << "#muons in acceptance per event: " << nmuonsev << "\n"; + } // events + + std::cout << "#events: " << nEvents << "\n"; + std::cout << "# MB events: " << nEventsMB << "\n"; + std::cout << Form("# events injected with %d quark pair: ", checkPdgQuark) + << nEventsInj << "\n"; + if (nEventsMB < nEvents * (1 - ratioTrigger) * 0.95 || + nEventsMB > nEvents * (1 - ratioTrigger) * + 1.05) { // we put some tolerance since the number of + // generated events is small + std::cerr << "Number of generated MB events different than expected\n"; + return 1; + } + if (nEventsInj < nEvents * ratioTrigger * 0.95 || + nEventsInj > nEvents * ratioTrigger * 1.05) { + std::cerr << "Number of generated events injected with " << checkPdgQuark + << " different than expected\n"; + return 1; + } + std::cout << "#muons: " << nMuons << "\n"; + std::cout << "#muons in acceptance: " << nMuonsInAcceptance << "\n"; + + return 0; +} // external diff --git a/MC/config/PWGHF/ini/trigger_hf.ini b/MC/config/PWGHF/ini/trigger_hf.ini deleted file mode 100644 index 91a51907b..000000000 --- a/MC/config/PWGHF/ini/trigger_hf.ini +++ /dev/null @@ -1,13 +0,0 @@ -[GeneratorPythia8] -config=pythia8.cfg - -[TriggerExternal] -fileName=${O2DPG_ROOT}/MC/config/PWGHF/trigger/trigger_ccbar.C -funcName=trigger_ccbar(-1.5,1.5) - -[DecayerPythia8] -config[0] = ${O2DPG_ROOT}/MC/config/common/pythia8/decayer/base.cfg -config[1] = ${O2DPG_ROOT}/MC/config/PWGHF/pythia8/decayer/force_hadronic_D.cfg -config[2] = ${O2DPG_ROOT}/MC/config/PWGHF/pythia8/decayer/force_hadronic_D_use4bodies.cfg - -#/alice/O2DPG/MC/config/PWGHF/external/trigger diff --git a/MC/config/PWGHF/pythia8/decayer/EVTGEN_DECAY_B2JPSI.DEC b/MC/config/PWGHF/pythia8/decayer/EVTGEN_DECAY_B2JPSI.DEC new file mode 100644 index 000000000..3ced468f5 --- /dev/null +++ b/MC/config/PWGHF/pythia8/decayer/EVTGEN_DECAY_B2JPSI.DEC @@ -0,0 +1,4554 @@ + +######################################################################## +# Copyright 1998-2020 CERN for the benefit of the EvtGen authors # +# # +# This file is part of EvtGen. # +# # +# EvtGen is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# EvtGen is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with EvtGen. If not, see . # +######################################################################## + +# Updated to PDG 2010 by Tomas Pilar - T.Pilar@warwick.ac.uk +# Updated Mixing Parameters Mark Whitehead May 2009 +# +Define qoverp_incohMix_B_s0 1.0 +Define dm_incohMix_B_s0 17.8e12 +Define qoverp_incohMix_B0 1.0 +Define dm_incohMix_B0 0.507e12 +# Old definition of dm still in some decay models +Define dm 0.507e12 +#Define dgamma 0 +#Define qoverp 1 +#Define phaseqoverp 0 +#Define values for B0s mixing +#Define dms 20.e12 +# DeltaGammas corresponds to DG/G = 10% +#Define dgammas 6.852e10 +# Activate incoherent Mixing +### TODO: find a way to give mixing parameters through decay file to EvtGen +#####yesIncoherentB0Mixing dm dgamma +#####yesIncoherentBsMixing dms dgammas +# define the values of the CKM angles (alpha=70, beta=40) +Define alpha 1.365 +Define beta 0.39 +Define gamma 1.387 +Define twoBetaPlusGamma 2.167 +Define betaPlusHalfGamma 1.0835 +Define minusGamma -1.387 +Define minusTwoBeta -0.78 + +# New definitions for psiKstar modes (Lange, July 26, 2000) +Define PKHplus 0.159 +Define PKHzero 0.775 +Define PKHminus 0.612 +Define PKphHplus 1.563 +Define PKphHzero 0.0 +Define PKphHminus 2.712 + +Define Aplus 0.490 +Define Azero 1.10 +Define Aminus 0.4 +# +Define phAplus 2.5 +Define phAzero 0.0 +Define phAminus -0.17 + +# +# These particle aliases are used in for CP violating decays +# in which the decay distributions depends on how the K* decayed. +# +# +Alias K*L K*0 +Alias K*S K*0 +Alias K*BL anti-K*0 +Alias K*BS anti-K*0 +Alias K*0T K*0 +Alias anti-K*0T anti-K*0 +Alias K*BR anti-K*0 +Alias K*0R K*0 +Alias anti-K_0*0N anti-K_0*0 +Alias K_0*0N K_0*0 +# +ChargeConj K*L K*BL +ChargeConj K*S K*BS +ChargeConj K*0T anti-K*0T +ChargeConj K_0*0N anti-K_0*0N +ChargeConj K*0R K*BR +#JetSet parameter modifications +#(Very important that there are no blank spaces in the parameter string!) +#Turn of B0-B0B mixing in JetSet: +#JetSetPar MSTJ(26)=0 +# control of L=1 mesons (in order: J1S0 J0S1 J1S1 J2S1) - commented out by NB/WP +#JetSetPar PARJ(14)=0.05 +#JetSetPar PARJ(15)=0.05 +#JetSetPar PARJ(16)=0.05 +#JetSetPar PARJ(17)=0.05 +#cut-off parameter used to stop fragmentation process (should not be changed) +#####JetSetPar PARJ(33)=0.3 SET NOW IN GAUSS + +# PR LHCb 10/01/2006 +# Turn on PHOTOS for all decays +yesPhotos +# +# +#Decay vpho +#1.000 PYCONT; +#Enddecay +# +# use new VSS_BMIX mixing decay model (DK,28-Oct-1999) +Decay Upsilon(4S) +0.515122645 B+ B- VSS; #[Reconstructed PDG2011] +0.483122645 B0 anti-B0 VSS_BMIX dm; #[Reconstructed PDG2011] +0.000015583 e+ e- PHOTOS VLL; #[Reconstructed PDG2011] +0.000015766 mu+ mu- PHOTOS VLL; #[Reconstructed PDG2011] +0.000015766 tau+ tau- PHOTOS VLL; #[Reconstructed PDG2011] +0.000084099 Upsilon(2S) pi+ pi- VVPIPI; #[Reconstructed PDG2011] +0.000044342 Upsilon(2S) pi0 pi0 VVPIPI; #[Reconstructed PDG2011] +0.000080123 Upsilon pi+ pi- VVPIPI; #[Reconstructed PDG2011] +0.000044342 Upsilon pi0 pi0 VVPIPI; #[Reconstructed PDG2011] +0.000194392 Upsilon eta PARTWAVE 0.0 0.0 1.0 0.0 0.0 0.0; #[Reconstructed PDG2011] +# BF ~ (2J+1)E^3_gamma; see PRL 94, 032001 +# V-> gamma S Partial wave (L,S)=(0,0) +0.000092625 gamma chi_b0(3P) HELAMP 1. 0. 1. 0.; #[Reconstructed PDG2011] +# V-> gamma V Partial wave (L,S)=(0,1) +0.000138938 gamma chi_b1(3P) HELAMP 1. 0. 1. 0. -1. 0. -1. 0.; #[Reconstructed PDG2011] +# V-> gamma T Partial wave (L,S)=(0,1) +0.000129084 gamma chi_b2(3P) HELAMP 2.4494897 0. 1.7320508 0. 1. 0. 1. 0. 1.7320508 0. 2.4494897 0.; #[Reconstructed PDG2011] +# V-> gamma S Partial wave (L,S)=(0,0) +0.000002956 gamma chi_b0(2P) HELAMP 1. 0. 1. 0.; #[Reconstructed PDG2011] +# V-> gamma V Partial wave (L,S)=(0,1) +0.000007883 gamma chi_b1(2P) HELAMP 1. 0. 1. 0. -1. 0. -1. 0.; #[Reconstructed PDG2011] +# V-> gamma T Partial wave (L,S)=(0,1) +0.000011825 gamma chi_b2(2P) HELAMP 2.4494897 0. 1.7320508 0. 1. 0. 1. 0. 1.7320508 0. 2.4494897 0.; #[Reconstructed PDG2011] +0.000837571 g g g PYTHIA 92; +0.000039415 gamma g g PYTHIA 92; +Enddecay +# +# +# +Decay anti-B0 +# Charmonium states - updated from Lange's recommendations (august 23,2000) +# Based on new BABAR results I'm making the following changes (Lange, March 13, 2001 +# J/psi K0 was 10, now 9 x 10^-4 +# J/psi pi0 was 2.5, now 2.0 x 10^-4 +# J/psi Kstar was 15, now 13 +# Fix chic1 KS CP eigenstate +# adding J/psi rho and omega - for lack of better thing will use the Kstar helicity amplitudes. +# Psi2sKs 30 ->31 +# chic1 Kstar: 12 ->6 +0.000891000 J/psi anti-K0 SVS; #[New mode added] #[Reconstructed PDG2011] +# +# B -> J/psi anti-K*0 BR increased by a factor of 10 +0.01330000 J/psi anti-K*0 SVV_HELAMP PKHminus PKphHminus PKHzero PKphHzero PKHplus PKphHplus; #[Reconstructed PDG2011] +0.000027000 J/psi rho0 SVV_HELAMP PKHminus PKphHminus PKHzero PKphHzero PKHplus PKphHplus; #[Reconstructed PDG2011] +0.000030 J/psi omega SVV_HELAMP PKHminus PKphHminus PKHzero PKphHzero PKHplus PKphHplus; +0.000000000 J/psi K- pi+ PHSP; #[Reconstructed PDG2011] +0.00035 J/psi K- pi+ pi0 PHSP; +0.001300000 J/psi anti-K_10 SVV_HELAMP 0.5 0.0 1.0 0.0 0.5 0.0; #[Reconstructed PDG2011] +0.0001 J/psi anti-K'_10 SVV_HELAMP 0.5 0.0 1.0 0.0 0.5 0.0; +0.0005 J/psi anti-K_2*0 PHSP; +# +# below multiplied by chi_c or psi(2S) BR to J/psi +0.00036963 psi(2S) anti-K*0 SVV_HELAMP PKHminus PKphHminus PKHzero PKphHzero PKHplus PKphHplus; #[Reconstructed PDG2011] +0.00024238 psi(2S) K- pi+ PHSP; +0.00006059 psi(2S) K- pi+ pi0 PHSP; +0.00024238 psi(2S) anti-K_10 PHSP; +# +0.00007637 chi_c1 anti-K*0 SVV_HELAMP PKHminus PKphHminus PKHzero PKphHzero PKHplus PKphHplus; #[Reconstructed PDG2011] +0.00013760 chi_c1 K- pi+ PHSP; +0.00006880 chi_c1 K- pi+ pi0 PHSP; +# +0.00000585 chi_c2 anti-K*0 PHSP; +0.000039 chi_c2 K- pi+ PHSP; +0.0000195 chi_c2 K- pi+ pi0 PHSP; +# +Enddecay +# +Decay B0 +# B -> cc= s +# B -> J/psi K*0 BR increased by a factor of 10 +0.01330000 J/psi K*0 SVV_HELAMP PKHplus PKphHplus PKHzero PKphHzero PKHminus PKphHminus; #[Reconstructed PDG2011] +0.000027000 J/psi rho0 SVV_HELAMP PKHplus PKphHplus PKHzero PKphHzero PKHminus PKphHminus; #[Reconstructed PDG2011] +0.00003 J/psi omega SVV_HELAMP PKHplus PKphHplus PKHzero PKphHzero PKHminus PKphHminus; +0.000000000 J/psi K+ pi- PHSP; #[Reconstructed PDG2011] +#rl0.00035 J/psi K+ pi- pi0 PHSP; +0.001300000 J/psi K_10 SVV_HELAMP 0.5 0.0 1.0 0.0 0.5 0.0; #[Reconstructed PDG2011] +0.0001 J/psi K'_10 SVV_HELAMP 0.5 0.0 1.0 0.0 0.5 0.0; +0.0005 J/psi K_2*0 PHSP; +# +# below multiplied by chi_c or psi(2S) BR to J/psi +0.00036963 psi(2S) K*0 SVV_HELAMP PKHplus PKphHplus PKHzero PKphHzero PKHminus PKphHminus; #[Reconstructed PDG2011] +0.00024238 psi(2S) K+ pi- PHSP; +0.00006059 psi(2S) K+ pi- pi0 PHSP; +0.00024238 psi(2S) K_10 PHSP; +# +0.00007637 chi_c1 K*0 SVV_HELAMP PKHplus PKphHplus PKHzero PKphHzero PKHminus PKphHminus; #[Reconstructed PDG2011] +0.00013760 chi_c1 K+ pi- PHSP; +0.00006880 chi_c1 K+ pi- pi0 PHSP; +# +0.00000585 chi_c2 K*0 PHSP; +0.000039 chi_c2 K+ pi- PHSP; +0.0000195 chi_c2 K+ pi- pi0 PHSP; +# +Enddecay +# +# +Decay B- +# B -> cc= s sum = 1.92% +# B -> J/psi K- increased by a factor of 10 +0.01014000 J/psi K- SVS; #[Reconstructed PDG2011] +0.001430000 J/psi K*- SVV_HELAMP PKHminus PKphHminus PKHzero PKphHzero PKHplus PKphHplus; #[Reconstructed PDG2011] +0.000049000 J/psi pi- SVS; #[Reconstructed PDG2011] +0.000050000 J/psi rho- SVV_HELAMP PKHminus PKphHminus PKHzero PKphHzero PKHplus PKphHplus; #[Reconstructed PDG2011] +0.0002 J/psi anti-K0 pi- PHSP; +0.0001 J/psi K- pi0 PHSP; +#rl0.0007 J/psi K- pi+ pi- PHSP; +#rl0.00035 J/psi K- pi0 pi0 PHSP; +#rl0.00035 J/psi anti-K0 pi- pi0 PHSP; +0.0001 J/psi K'_1- SVV_HELAMP 0.5 0.0 1.0 0.0 0.5 0.0; +0.0005 J/psi K_2*- PHSP; +0.001800000 J/psi K_1- SVV_HELAMP 0.5 0.0 1.0 0.0 0.5 0.0; #[Reconstructed PDG2011] +0.000052000 J/psi phi K- PHSP; #[Reconstructed PDG2011] +# +# below multiplied by chi_c or psi(2S) BR to J/psi +0.000391411 psi(2S) K- SVS; #[Reconstructed PDG2011] +0.000375658 psi(2S) K*- SVV_HELAMP PKHminus PKphHminus PKHzero PKphHzero PKHplus PKphHplus; #[Reconstructed PDG2011] +0.000121180 psi(2S) K- pi0 PHSP; +0.001151210 psi(2S) K- pi+ pi- PHSP; #[Reconstructed PDG2011] +0.000060590 psi(2S) K- pi0 pi0 PHSP; +0.000242360 psi(2S) K_1- PHSP; +# +0.00015824 chi_c1 K- SVS; #[Reconstructed PDG2011] +0.0001032 chi_c1 K*- SVV_HELAMP PKHminus PKphHminus PKHzero PKphHzero PKHplus PKphHplus; #[Reconstructed PDG2011] +0.0000688 chi_c1 K- pi0 PHSP; +0.0001376 chi_c1 K- pi+ pi- PHSP; +0.0000688 chi_c1 K- pi0 pi0 PHSP; +# +0.0000039 chi_c2 K- STS; +0.0000039 chi_c2 K*- PHSP; +0.0000195 chi_c2 K- pi0 PHSP; +0.000039 chi_c2 K- pi+ pi- PHSP; +0.0000195 chi_c2 K- pi0 pi0 PHSP; +Enddecay + +# +Decay B+ +# B -> cc= s sum = 1.92% +# +# B -> J/psi K+ BR increased by a factor of 10 +0.01014000 J/psi K+ SVS; #[Reconstructed PDG2011] +0.001430000 J/psi K*+ SVV_HELAMP PKHplus PKphHplus PKHzero PKphHzero PKHminus PKphHminus; #[Reconstructed PDG2011] +0.000049000 J/psi pi+ SVS; #[Reconstructed PDG2011] +0.000050000 J/psi rho+ SVV_HELAMP PKHplus PKphHplus PKHzero PKphHzero PKHminus PKphHminus; #[Reconstructed PDG2011] +0.0001 J/psi K+ pi0 PHSP; +#rl0.0007 J/psi K+ pi- pi+ PHSP; +#rl0.00035 J/psi K+ pi0 pi0 PHSP; +0.0001 J/psi K'_1+ SVV_HELAMP 0.5 0.0 1.0 0.0 0.5 0.0; +0.0005 J/psi K_2*+ PHSP; +0.001800000 J/psi K_1+ SVV_HELAMP 0.5 0.0 1.0 0.0 0.5 0.0; #[Reconstructed PDG2011] +0.000052000 J/psi phi K+ PHSP; #[Reconstructed PDG2011] +# +# below multiplied by chi_c or psi(2S) BR to J/psi +0.000391411 psi(2S) K+ SVS; #[Reconstructed PDG2011] +0.000375658 psi(2S) K*+ SVV_HELAMP PKHplus PKphHplus PKHzero PKphHzero PKHminus PKphHminus; #[Reconstructed PDG2011] +0.000121180 psi(2S) K+ pi0 PHSP; +0.001151210 psi(2S) K+ pi- pi+ PHSP; #[Reconstructed PDG2011] +0.000060590 psi(2S) K+ pi0 pi0 PHSP; +0.000242360 psi(2S) K_1+ PHSP; +# +0.00015824 chi_c1 K+ SVS; #[Reconstructed PDG2011] +0.0001032 chi_c1 K*+ SVV_HELAMP PKHplus PKphHplus PKHzero PKphHzero PKHminus PKphHminus; #[Reconstructed PDG2011] +0.0000688 chi_c1 K+ pi0 PHSP; +0.0001376 chi_c1 K+ pi- pi+ PHSP; +0.0000688 chi_c1 K+ pi0 pi0 PHSP; +# +0.0000039 chi_c2 K+ STS; +0.0000039 chi_c2 K*+ PHSP; +0.0000195 chi_c2 K+ pi0 PHSP; +0.000039 chi_c2 K+ pi- pi+ PHSP; +0.0000195 chi_c2 K+ pi0 pi0 PHSP; +Enddecay + +#----------------------------------------------------------------- +# B references: +# +# [B1]: http://www.slac.stanford.edu/BFROOT/www/Physics/Tools/generators/dec-update/2002/Breco-recommendations.html +# +#----------------------------------------------------------------- + + +# +# +# B* mesons +# +Decay B*+ +1.0000 B+ gamma VSP_PWAVE; +Enddecay +Decay B*- +1.0000 B- gamma VSP_PWAVE; +Enddecay +Decay B*0 +1.0000 B0 gamma VSP_PWAVE; +Enddecay +Decay anti-B*0 +1.0000 anti-B0 gamma VSP_PWAVE; +Enddecay +Decay B_s*0 +1.0000 B_s0 gamma VSP_PWAVE; +Enddecay +Decay anti-B_s*0 +1.0000 anti-B_s0 gamma VSP_PWAVE; +Enddecay +# +# B_s decays. +# +# +# To count up the BR for this decay: +# awk ' /^0./{ sum = sum + $1 ;print sum } ' junk.dec +# +# anti-B_s decays. +# ------------------------------------------------------- +# whb&fkw 3/28/01 Taken from fkw's QQ tables. +# most dubious part are the B to baryons. +# fkw 4/28/00 made the Bs parallel to the Bd as best as I could +# ------------------------------------------------------- +# +# Lange - Nov14 - NOT adjusted for D_s->phipi change +# +Decay anti-B_s0 +# +# fkw 4/28/00 Strategy for charmonium modes: +# Take Bd BR's, replace spectator, +# assume etaprime = 2/3 ss +# and eta = 1/3 ss +# +# Note: Just for kicks I gave the c\bar c decays a small piece that is +# self tagging. See if you can find it. This is already in +# the B0 decays in cleo's version of decay.dec . +# +# B --> (c c=) (s s=) +# 2.65% +# should be: psi = 0.80% CLNS 94/1315 but isn't quite right. +# B_s0 -> J/psi phi increased by a factor fo 10 and includes phi->KK +0.00064 J/psi eta' SVS; +0.00032 J/psi eta SVS; +0.006305 J/psi phi SVV_HELAMP 1.0 0.0 1.0 0.0 1.0 0.0; #[Reconstructed PDG2011] +0.00070 J/psi K- K+ PHSP; +0.00070 J/psi anti-K0 K+ pi- PHSP; +0.00070 J/psi K- K+ pi0 PHSP; +0.000035 J/psi anti-K*0 PHSP; +0.000107 J/psi K*0 anti-K*0 PHSP; +# LHCb PR 04/02/04 Add (cc) phi n pi(+/0) +0.00039 J/psi phi pi+ pi- PHSP; +0.00039 J/psi phi pi0 pi0 PHSP; +# LHCb PR add (cc) phi eta(') + npi see CDF QQ +0.0002 J/psi eta pi+ pi- PHSP; +0.0004 J/psi eta' pi+ pi- PHSP; +0.0002 J/psi pi+ pi- PHSP; +# psi' = 0.34% CLNS 94/1315 +# +0.000281767 psi(2S) eta' SVS; +0.000142398 psi(2S) eta SVS; +0.000201697 psi(2S) phi SVV_HELAMP 1.0 0.0 1.0 0.0 1.0 0.0; #[Reconstructed PDG2011] +0.000181785 psi(2S) K- K+ PHSP; +0.000181785 psi(2S) anti-K0 K+ pi- PHSP; +0.000181785 psi(2S) K- K+ pi0 PHSP; +0.000206023 psi(2S) phi pi+ pi- PHSP; +0.000206023 psi(2S) phi pi0 pi0 PHSP; +0.000121190 psi(2S) eta pi+ pi- PHSP; +0.000242381 psi(2S) eta' pi+ pi- PHSP; +0.000121190 psi(2S) pi+ pi- PHSP; +# +# chi_c1 = 0.37% CLNS 94/1315 +0.0002408 chi_c1 eta' SVS; +0.0001032 chi_c1 eta SVS; +0.0004816 chi_c1 phi SVV_HELAMP 1.0 0.0 1.0 0.0 1.0 0.0; +0.00008944 chi_c1 K- K+ PHSP; +0.00008944 chi_c1 anti-K0 K+ pi- PHSP; +0.00008944 chi_c1 K- K+ pi0 PHSP; +0.0001376 chi_c1 phi pi+ pi- PHSP; +0.0000344 chi_c1 eta pi+ pi- PHSP; +0.0000688 chi_c1 eta' pi+ pi- PHSP; +# +# +# chic2 = 0.25% CLNS 94/1315 +0.00009067 chi_c2 eta' STS; +0.00004583 chi_c2 eta STS; +#0.000195 chi_c2 phi STV; whb: model doesn't exist! +0.0000312 chi_c2 K- K+ PHSP; +0.0000312 chi_c2 anti-K0 K+ pi- PHSP; +0.0000312 chi_c2 K- K+ pi0 PHSP; +# +Enddecay +# +# B_s decays. +# ------------------------------------------------------- +# whb&fkw 3/28/01 Taken from fkw's QQ tables. +# most dubious part are the B to baryons. +# fkw 4/28/00 made the Bs parallel to the Bd as best as I could +# ------------------------------------------------------- +Decay B_s0 +# fkw 4/28/00 Strategy for charmonium modes: +# Take Bd BR's, replace spectator, +# assume etaprime = 2/3 ss +# and eta = 1/3 ss +# +# Note: Just for kicks I gave the c\bar c decays a small piece that is +# self tagging. See if you can find it. This is already in +# the B0 decays in cleo's version of decay.dec . +# +# B --> (c c=) (s s=) +# 2.65% +# should be: psi = 0.80% CLNS 94/1315 but isn't quite right. +# B_s0 -> J/psi phi increased by a factor of 10 and including phi->KK +0.00064 J/psi eta' SVS; +0.00032 J/psi eta SVS; +0.006305 J/psi phi SVV_HELAMP 1.0 0.0 1.0 0.0 1.0 0.0; #[Reconstructed PDG2011] +0.00070 J/psi K- K+ PHSP; +0.00070 J/psi K0 K- pi+ PHSP; +0.00070 J/psi K- K+ pi0 PHSP; +0.000035 J/psi K*0 PHSP; +0.000107 J/psi K*0 anti-K*0 PHSP; +# LHCb PR 04/02/04 Add (cc) phi n pi(+/0) +0.00039 J/psi phi pi+ pi- PHSP; +0.00039 J/psi phi pi0 pi0 PHSP; +# LHCb PR Add (cc) phi eta(') + npi like in CDF QQ +0.0002 J/psi eta pi+ pi- PHSP; +0.0002 J/psi eta pi0 pi0 PHSP; +0.0004 J/psi eta' pi+ pi- PHSP; +0.0004 J/psi eta' pi0 pi0 PHSP; +0.0002 J/psi pi+ pi- PHSP; +# psi' = 0.34% CLNS 94/1315 +# below multiplied by chi_c or psi(2S) BR to J/psi and phi BR to KK +0.000281767 psi(2S) eta' SVS; +0.000142398 psi(2S) eta SVS; +0.000201697 psi(2S) phi SVV_HELAMP 1.0 0.0 1.0 0.0 1.0 0.0; #[Reconstructed PDG2011] +0.000181785 psi(2S) K- K+ PHSP; +0.000181785 psi(2S) K0 K- pi+ PHSP; +0.000181785 psi(2S) K- K+ pi0 PHSP; +0.000206023 psi(2S) phi pi+ pi- PHSP; +0.000206023 psi(2S) phi pi0 pi0 PHSP; +0.000121190 psi(2S) eta pi+ pi- PHSP; +0.000242381 psi(2S) eta' pi+ pi- PHSP; +0.000121190 psi(2S) pi+ pi- PHSP; +# +# chic1 = 0.37% CLNS 94/1315 +0.0002408 chi_c1 eta' SVS; +0.0001032 chi_c1 eta SVS; +0.0004816 chi_c1 phi SVV_HELAMP 1.0 0.0 1.0 0.0 1.0 0.0; +0.00008944 chi_c1 K- K+ PHSP; +0.00008944 chi_c1 anti-K0 K+ pi- PHSP; +0.00008944 chi_c1 K- K+ pi0 PHSP; +0.0001376 chi_c1 phi pi+ pi- PHSP; +0.0000344 chi_c1 eta pi+ pi- PHSP; +0.0000688 chi_c1 eta' pi+ pi- PHSP; +# +# chic2 = 0.25% CLNS 94/1315 +0.00009067 chi_c2 eta' STS; +0.00004583 chi_c2 eta STS; +#0.000195 chi_c2 phi STV; whb: doesn't exist +0.0000312 chi_c2 K- K+ PHSP; +0.0000312 chi_c2 K0 K- pi+ PHSP; +0.0000312 chi_c2 K- K+ pi0 PHSP; +# +Enddecay + +# whb: QQ B_c included- Near end of File +#Decay B_c- +#CHANNEL 0 0.0700 TAU- NUTB +#CHANNEL 1 0.1100 NUEB E- *CC* +#CHANNEL 1 0.1100 NUMB MU- *CC* +#CHANNEL 1 0.0300 NUTB TAU- *CC* +#CHANNEL 0 0.3400 *DU* *CC* +#CHANNEL 0 0.0700 *SC* *CC* +#CHANNEL 0 0.2700 *SC* +#0.0700 tau- anti-nu_tau PHSP; +#0.1100 anti-nu_e e- *CC* Simpleleptonic; +#0.1100 anti-nu_mu mu- *CC* Simple leptonic; +#0.0300 anti-nu_tau tau- *CC* Simple leptonic; +#0.3400 *DU* *CC* PHSP; +#0.0700 *SC* *CC* PHSP; +#0.2700 *SC* PHSP; +#Enddecay +# +#Decay B_c+ +#CHANNEL 0 0.0700 TAU+ NUT +#CHANNEL 1 0.1100 NUE E+ *CC* +#CHANNEL 1 0.1100 NUM MU+ *CC* +#CHANNEL 1 0.0300 NUT TAU+ *CC* +#CHANNEL 0 0.3400 *UD* *CC* +#CHANNEL 0 0.0700 *CS* *CC* +#CHANNEL 0 0.2700 *CS* +#0.0700 tau+ nu_tau PHSP; +#0.1100 nu_e E+ *CC* Simple leptonic; +#0.1100 nu_mu mu+ *CC* Simple leptonic; +#0.0300 nu_tau tau+ *CC* Simple leptonic; +#0.3400 *UD* *CC* PHSP; +#0.0700 *CS* *CC* PHSP; +#0.2700 *CS* PHSP; +#Enddecay + +# +# Updated to PDG 2008 +# +Decay tau- +0.154002925 e- anti-nu_e nu_tau PHOTOS TAULNUNU; #[Reconstructed PDG2011] +0.170000000 mu- anti-nu_mu nu_tau PHOTOS TAULNUNU; #[Reconstructed PDG2011] +0.109100000 pi- nu_tau TAUSCALARNU; #[Reconstructed PDG2011] +0.006960000 K- nu_tau TAUSCALARNU; #[Reconstructed PDG2011] +0.255100000 pi- pi0 nu_tau TAUHADNU -0.108 0.775 0.149 1.364 0.400; #[Reconstructed PDG2011] +0.001124109 nu_tau gamma pi- pi0 PYTHIA 21; +0.079301562 pi0 pi0 pi- nu_tau TAUHADNU -0.108 0.775 0.149 1.364 0.400 1.23 0.4; #[Reconstructed PDG2011] +0.000385656 nu_tau K- pi0 pi0 PYTHIA 21; +0.008508640 nu_tau pi- pi0 pi0 pi0 PYTHIA 21; +0.000864699 nu_tau pi- pi0 pi0 pi0 pi0 PHSP; #[Reconstructed PDG2011] +#0.2515 rho- nu_tau TAUVECTORNU; +#0.1790 a_1- nu_tau TAUVECTORNU; +# Modes with K0s +# +0.000319939 nu_tau pi- anti-K0 PYTHIA 21; +0.001590000 nu_tau K- K0 PYTHIA 21; +0.001700000 nu_tau K0 pi- anti-K0 PYTHIA 21; +0.001590000 nu_tau K- pi0 K0 PYTHIA 21; +0.004000000 nu_tau pi- anti-K0 pi0 PYTHIA 21; +# 3 Charged Particles +# +0.093200000 pi- pi- pi+ nu_tau TAUHADNU -0.108 0.775 0.149 1.364 0.400 1.23 0.4; #[Reconstructed PDG2011] +0.046100000 nu_tau pi- pi+ pi- pi0 PYTHIA 21; +0.000501526 nu_tau pi- pi- pi+ pi0 pi0 PHSP; #[Reconstructed PDG2011] +0.000155646 nu_tau pi- pi- pi+ pi0 pi0 pi0 PHSP; #[Reconstructed PDG2011] +0.003420000 nu_tau K- pi+ pi- PYTHIA 21; +0.001360000 nu_tau K- pi+ pi- pi0 PYTHIA 21; +0.001400000 nu_tau K- pi- K+ PYTHIA 21; +0.000015800 nu_tau K- K+ K- PYTHIA 21; +# 5 Charged Particles +# +0.000700406 nu_tau pi- pi- pi- pi+ pi+ PHSP; #[Reconstructed PDG2011] +0.000129705 nu_tau pi- pi- pi- pi+ pi+ pi0 PHSP; #[Reconstructed PDG2011] +# Misc other modes +# +0.012000000 K*- nu_tau TAUVECTORNU; #[Reconstructed PDG2011] +0.001390000 nu_tau eta pi- pi0 PYTHIA 21; +0.003199387 nu_tau pi- omega pi0 PYTHIA 21; +0.000410000 nu_tau K- omega PYTHIA 21; +0.002200000 anti-K*0 pi- nu_tau PHSP; #[Reconstructed PDG2011] +0.002100000 K*0 K- nu_tau PHSP; #[Reconstructed PDG2011] +0.000034000 phi pi- nu_tau PHSP; #[Reconstructed PDG2011] +0.000037000 phi K- nu_tau PHSP; #[Reconstructed PDG2011] +0.003600000 mu- anti-nu_mu nu_tau gamma PHSP; #[New mode added] #[Reconstructed PDG2011] +0.017500000 e- anti-nu_e nu_tau gamma PHSP; #[New mode added] #[Reconstructed PDG2011] +0.004290000 K- pi0 nu_tau PHSP; #[New mode added] #[Reconstructed PDG2011] +0.002200000 anti-K0 rho- nu_tau PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000260000 pi- anti-K0 pi0 pi0 nu_tau PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000310000 pi- K0 anti-K0 pi0 nu_tau PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000061000 K- K+ pi- pi0 nu_tau PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000028000 e- e- e+ anti-nu_e nu_tau PHSP; #[New mode added] #[Reconstructed PDG2011] +0.004700000 K_1- nu_tau PHSP; #[New mode added] #[Reconstructed PDG2011] +0.001700000 K'_1- nu_tau PHSP; #[New mode added] #[Reconstructed PDG2011] +0.001500000 K'*- nu_tau PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000150000 eta pi- pi0 pi0 nu_tau PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000161000 eta K- nu_tau PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000138000 eta K*- nu_tau PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000048000 eta K- pi0 nu_tau PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000093000 eta anti-K0 pi- nu_tau PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000360000 f_1 pi- nu_tau PHSP; #[New mode added] #[Reconstructed PDG2011] +Enddecay +# +CDecay tau+ +# +# Vector Mesons Updated PDG 2008 +# +Decay D*+ +0.6770 D0 pi+ VSS; +0.3070 D+ pi0 VSS; +0.0160 D+ gamma VSP_PWAVE; +Enddecay +Decay D*- +0.6770 anti-D0 pi- VSS; +0.3070 D- pi0 VSS; +0.0160 D- gamma VSP_PWAVE; +Enddecay +Decay D*0 +0.619000000 D0 pi0 VSS; #[Reconstructed PDG2011] +0.381000000 D0 gamma VSP_PWAVE; #[Reconstructed PDG2011] +Enddecay +Decay anti-D*0 +0.6190 anti-D0 pi0 VSS; +0.3810 anti-D0 gamma VSP_PWAVE; +Enddecay +# +Decay D_s*+ +0.942000000 D_s+ gamma VSP_PWAVE; #[Reconstructed PDG2011] +0.058000000 D_s+ pi0 VSS; #[Reconstructed PDG2011] +Enddecay +Decay D_s*- +0.942000000 D_s- gamma VSP_PWAVE; #[Reconstructed PDG2011] +0.058000000 D_s- pi0 VSS; #[Reconstructed PDG2011] +Enddecay +# +# +# D+ Meson +# +Decay D+ +0.055200000 anti-K*0 e+ nu_e PHOTOS ISGW2; # PDG2014 +0.088300000 anti-K0 e+ nu_e PHOTOS ISGW2; # PDG2014 +0.002773020 anti-K_10 e+ nu_e PHOTOS ISGW2; # Keep same as in 2010 version +0.002927076 anti-K_2*0 e+ nu_e PHOTOS ISGW2; # Keep same as in 2010 version +0.004050000 pi0 e+ nu_e PHOTOS ISGW2; # PDG2014 +0.001140000 eta e+ nu_e PHOTOS ISGW2; # PDG2014 +0.000220000 eta' e+ nu_e PHOTOS ISGW2; # PDG2014 +0.002180000 rho0 e+ nu_e PHOTOS ISGW2; # PDG2014 +0.001820000 omega e+ nu_e PHOTOS ISGW2; # PDG2014 +0.003321725 K- pi+ e+ nu_e PHOTOS PHSP; # PDG2014 (subtracted K*)+tiny bit to have sum=1 +0.001200122 anti-K0 pi0 e+ nu_e PHOTOS PHSP; # Keep same as in 2010 version+tiny bit to have sum=1 +# +0.052800000 anti-K*0 mu+ nu_mu PHOTOS ISGW2; # PDG2014 +0.092000000 anti-K0 mu+ nu_mu PHOTOS ISGW2; # PDG2014 +0.002773020 anti-K_10 mu+ nu_mu PHOTOS ISGW2; # Keep same as in 2010 version +0.002927076 anti-K_2*0 mu+ nu_mu PHOTOS ISGW2; # Keep same as in 2010 version +0.004050000 pi0 mu+ nu_mu PHOTOS ISGW2; # Same as electron +0.001140000 eta mu+ nu_mu PHOTOS ISGW2; # Same as electron +0.002200000 eta' mu+ nu_mu PHOTOS ISGW2; # Same as electron +0.002400000 rho0 mu+ nu_mu PHOTOS ISGW2; # PDG2014 +0.001820000 omega mu+ nu_mu PHOTOS ISGW2; # Same as electron +0.002921725 K- pi+ mu+ nu_mu PHOTOS PHSP; # PDG2014 (subtracted K*)+tiny bit to have sum=1 +0.001200122 anti-K0 pi0 mu+ nu_mu PHOTOS PHSP; # Keep same as in 2010 version+tiny bit to have sum=1 +# +# +0.000382000 mu+ nu_mu PHOTOS SLN; #[Reconstructed PDG2011] +0.000770283 tau+ nu_tau SLN; #[Reconstructed PDG2011] +# +0.014900000 K_S0 pi+ PHSP; #[Reconstructed PDG2011] +0.014600000 K_L0 pi+ PHSP; #[Reconstructed PDG2011] +0.000000000 anti-K0 pi+ PHSP; #[Reconstructed PDG2011] +# +0.025950843 a_1+ K_S0 SVS; #[Reconstructed PDG2011] +0.025950843 a_1+ K_L0 SVS; #[Reconstructed PDG2011] +0.000000000 a_1+ anti-K0 SVS; #[Reconstructed PDG2011] +# +0.027090862 anti-K'_10 pi+ SVS; #[Reconstructed PDG2011] +#0.0115 anti-K_0*0N pi+ PHSP; +0.013387523 anti-K*0 rho+ SVV_HELAMP 1.0 0.0 1.0 0.0 1.0 0.0; #[Reconstructed PDG2011] +# +# the Dalitz mode below includes K*bar(892)0 pi+, +# K*bar(1430)0 pi+, and K*bar(1680)0 pi+ resonances. +0.094000000 K- pi+ pi+ D_DALITZ; #[Reconstructed PDG2011] +# the Dalitz mode below includes K0bar rho+, +# and K*bar(892)0 pi+ resonances +0.069900000 K_S0 pi+ pi0 D_DALITZ; # PDG2014 +0.069900000 K_L0 pi+ pi0 D_DALITZ; # Assume to be same as K_S0 pi+ pi0 +0.000000000 anti-K0 pi+ pi0 D_DALITZ; #[Reconstructed PDG2011] +#0.0100 anti-K0 eta pi+ PHSP; +# +0.001247859 K_S0 rho0 pi+ PHSP; #[Reconstructed PDG2011] +0.001247859 K_L0 rho0 pi+ PHSP; #[Reconstructed PDG2011] +0.000000000 anti-K0 rho0 pi+ PHSP; #[Reconstructed PDG2011] +# +0.003851416 anti-K0 omega pi+ PHSP; #[Reconstructed PDG2011] +0.007032686 K- rho+ pi+ PHSP; #[Reconstructed PDG2011] +0.009274210 K*- pi+ pi+ PHSP; #[Reconstructed PDG2011] +0.047187552 anti-K*0 pi0 pi+ PHSP; #[Reconstructed PDG2011] +#0.0100 anti-K*0 eta pi+ PHSP; +0.001101505 anti-K*0 rho0 pi+ PHSP; #[Reconstructed PDG2011] +0.003851416 anti-K*0 omega pi+ PHSP; #[Reconstructed PDG2011] +#0.0100 K*- rho+ pi+ PHSP; +# +0.008473116 K- pi+ pi+ pi0 PHSP; #[Reconstructed PDG2011] +# +#0.002472609 K_S0 pi+ pi+ pi- PHSP; #[Reconstructed PDG2011] +#0.002472609 K_L0 pi+ pi+ pi- PHSP; #[Reconstructed PDG2011] +0.031200000 K_S0 pi+ pi+ pi- PHSP; # PDG2014 (why it was much smaller?) +0.031200000 K_L0 pi+ pi+ pi- PHSP; # PDG2014 +0.000000000 anti-K0 pi+ pi+ pi- PHSP; #[Reconstructed PDG2011] +# +#0.0188 anti-K0 pi+ pi0 pi0 PHSP; +0.005700000 K- pi+ pi+ pi+ pi- PHSP; #[Reconstructed PDG2011] +0.003851416 K- pi+ pi+ pi0 pi0 PHSP; #[Reconstructed PDG2011] +0.006701464 anti-K0 pi+ pi+ pi- pi0 PHSP; #[Reconstructed PDG2011] +0.002695991 anti-K0 pi+ pi0 pi0 pi0 PHSP; #[Reconstructed PDG2011] +# +0.004600000 K_S0 K_S0 K+ PHSP; #[Reconstructed PDG2011] +0.003111944 K_L0 K_L0 K+ PHSP; #[Reconstructed PDG2011] +0.000000000 anti-K0 anti-K0 K+ PHSP; #[Reconstructed PDG2011] +# +0.004660214 phi pi+ SVS; #[Reconstructed PDG2011] +0.023000000 phi pi+ pi0 PHSP; #[Reconstructed PDG2011] +# +0.002860000 K_S0 K+ PHSP; #[Reconstructed PDG2011] +0.002195307 K_L0 K+ PHSP; #[Reconstructed PDG2011] +0.000000000 anti-K0 K+ PHSP; #[Reconstructed PDG2011] +# +0.002179902 anti-K*0 K+ SVS; #[Reconstructed PDG2011] +# +0.016000000 K*+ K_S0 SVS; #[Reconstructed PDG2011] +0.011145999 K*+ K_L0 SVS; #[Reconstructed PDG2011] +0.000000000 K*+ anti-K0 SVS; #[Reconstructed PDG2011] +# +#0.0180 anti-K*0 K*+ SVV_HELAMP 1.0 0.0 1.0 0.0 1.0 0.0; +0.002826940 K+ K- pi+ PHSP; #[Reconstructed PDG2011] +0.000770283 K+ anti-K0 pi0 PHSP; #[Reconstructed PDG2011] +0.000000000 anti-K0 K0 pi+ PHSP; #[Reconstructed PDG2011] +0.000770283 K*+ K- pi+ PHSP; #[Reconstructed PDG2011] +0.000770283 K+ K*- pi+ PHSP; #[Reconstructed PDG2011] +0.000770283 K*+ anti-K0 pi0 PHSP; #[Reconstructed PDG2011] +0.000770283 K+ anti-K*0 pi0 PHSP; #[Reconstructed PDG2011] +0.000770283 anti-K*0 K0 pi+ PHSP; #[Reconstructed PDG2011] +0.000770283 anti-K0 K*0 pi+ PHSP; #[Reconstructed PDG2011] +# +0.001260000 pi0 pi+ PHSP; #[Reconstructed PDG2011] +0.000830000 rho0 pi+ SVS; #[Reconstructed PDG2011] +0.002440000 pi+ pi+ pi- PHSP; #[Reconstructed PDG2011] +0.004700000 pi+ pi0 pi0 PHSP; #[Reconstructed PDG2011] +0.011600000 pi+ pi+ pi- pi0 PHSP; #[Reconstructed PDG2011] +0.003851416 pi+ pi0 pi0 pi0 PHSP; #[Reconstructed PDG2011] +0.003430000 eta pi+ PHSP; #[Reconstructed PDG2011] +0.004400000 eta' pi+ PHSP; #[Reconstructed PDG2011] +0.001380000 eta pi+ pi0 PHSP; #[Reconstructed PDG2011] +0.002310850 eta pi+ pi+ pi- PHSP; #[Reconstructed PDG2011] +0.001540566 eta pi+ pi0 pi0 PHSP; #[Reconstructed PDG2011] +#lange jul22,2002 +0.001660000 pi+ pi+ pi+ pi- pi- PHSP; #[Reconstructed PDG2011] +# +# March 2009 New Modes +0.009300000 anti-K*0 a_1+ PHSP; #[Reconstructed PDG2011] +0.010545178 K+ K- pi+ pi0 PHSP; #[Reconstructed PDG2011] +# +0.001740000 K+ K_S0 pi+ pi- PHSP; #[Reconstructed PDG2011] +0.001270967 K+ K_L0 pi+ pi- PHSP; #[Reconstructed PDG2011] +0.000000000 K+ anti-K0 pi+ pi- PHSP; #[Reconstructed PDG2011] +# +0.002380000 K_S0 K- pi+ pi+ PHSP; #[Reconstructed PDG2011] +0.001756246 K_L0 K- pi+ pi+ PHSP; #[Reconstructed PDG2011] +0.000000000 K0 K- pi+ pi+ PHSP; #[Reconstructed PDG2011] +# +0.000230000 K+ K- pi+ pi+ pi- PHSP; #[Reconstructed PDG2011] +# +0.000240000 K+ K- K_S0 pi+ PHSP; #[Reconstructed PDG2011] +0.000161759 K+ K- K_L0 pi+ PHSP; #[Reconstructed PDG2011] +0.000000000 K+ K- anti-K0 pi+ PHSP; #[Reconstructed PDG2011] +# Doubly Cabibbo suppressed modes +0.000237000 K+ pi0 PHSP; #[Reconstructed PDG2011] +0.000332000 K+ pi+ pi- PHSP; #[Reconstructed PDG2011] +0.000089000 K+ K+ K- PHSP; #[Reconstructed PDG2011] +0.001720000 K- rho0 pi+ pi+ PHSP; #[New mode added] #[Reconstructed PDG2011] +0.001600000 eta' pi+ pi0 PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000210000 K+ rho0 PHSP; #[New mode added] #[Reconstructed PDG2011] +Enddecay +# +# +Decay D- +0.055200000 K*0 e- anti-nu_e PHOTOS ISGW2; # PDG2014 +0.088300000 K0 e- anti-nu_e PHOTOS ISGW2; # PDG2014 +0.002773020 K_10 e- anti-nu_e PHOTOS ISGW2; # Keep same as in 2010 version +0.002927076 K_2*0 e- anti-nu_e PHOTOS ISGW2; # Keep same as in 2010 version +0.004050000 pi0 e- anti-nu_e PHOTOS ISGW2; # PDG2014 +0.001140000 eta e- anti-nu_e PHOTOS ISGW2; # PDG2014 +0.000220000 eta' e- anti-nu_e PHOTOS ISGW2; # PDG2014 +0.002180000 rho0 e- anti-nu_e PHOTOS ISGW2; # PDG2014 +0.001820000 omega e- anti-nu_e PHOTOS ISGW2; # PDG2014 +0.003321725 K+ pi- e- anti-nu_e PHOTOS PHSP; # PDG2014 (subtracted K*)+tiny bit to have sum=1 +0.001200122 K0 pi0 e- anti-nu_e PHOTOS PHSP; # Keep same as in 2010 version+tiny bit to have sum=1 +# +0.052800000 K*0 mu- anti-nu_mu PHOTOS ISGW2; #[Reconstructed PDG2011] +0.092000000 K0 mu- anti-nu_mu PHOTOS ISGW2; #[Reconstructed PDG2011] +0.002773020 K_10 mu- anti-nu_mu PHOTOS ISGW2; #[Reconstructed PDG2011] +0.002927076 K_2*0 mu- anti-nu_mu PHOTOS ISGW2; #[Reconstructed PDG2011] +0.004050000 pi0 mu- anti-nu_mu PHOTOS ISGW2; #[Reconstructed PDG2011] +0.001140000 eta mu- anti-nu_mu PHOTOS ISGW2; #[Reconstructed PDG2011] +0.002200000 eta' mu- anti-nu_mu PHOTOS ISGW2; #[Reconstructed PDG2011] +0.002400000 rho0 mu- anti-nu_mu PHOTOS ISGW2; #[Reconstructed PDG2011] +0.001820000 omega mu- anti-nu_mu PHOTOS ISGW2; #[Reconstructed PDG2011] +0.002921725 K+ pi- mu- anti-nu_mu PHOTOS PHSP; #[Reconstructed PDG2011] +0.001200122 K0 pi0 mu- anti-nu_mu PHOTOS PHSP; #[Reconstructed PDG2011] +# +0.000382000 mu- anti-nu_mu PHOTOS SLN; #[Reconstructed PDG2011] +0.000770283 tau- anti-nu_tau SLN; #[Reconstructed PDG2011] +# +0.014900000 K_S0 pi- PHSP; #[Reconstructed PDG2011] +0.014600000 K_L0 pi- PHSP; #[Reconstructed PDG2011] +0.000000000 K0 pi- PHSP; #[Reconstructed PDG2011] +# +0.025950843 a_1- K_S0 SVS; #[Reconstructed PDG2011] +0.025950843 a_1- K_L0 SVS; #[Reconstructed PDG2011] +0.000000000 a_1- K0 SVS; #[Reconstructed PDG2011] +# +0.027090862 K'_10 pi- SVS; #[Reconstructed PDG2011] +#0.0115 K_0*0N pi- PHSP; +0.013387523 K*0 rho- SVV_HELAMP 1.0 0.0 1.0 0.0 1.0 0.0; #[Reconstructed PDG2011] +# +# the Dalitz mode below includes K*(892)0 pi-, K*(1430)0 pi-, and K*(1680)0 pi- resonances. +0.094000000 K+ pi- pi- D_DALITZ; #[Reconstructed PDG2011] +# the Dalitz mode below includes K0 rho-, and K*(892)0 pi- resonances. +0.069900000 K_S0 pi- pi0 D_DALITZ; # PDG2014 +0.069900000 K_L0 pi- pi0 D_DALITZ; # Assume to be same as K_S0 pi- pi0 +0.000000000 K0 pi- pi0 D_DALITZ; #[Reconstructed PDG2011] +# +#0.0100 K0 eta pi- PHSP; +0.001247859 K_S0 rho0 pi- PHSP; #[Reconstructed PDG2011] +0.001247859 K_L0 rho0 pi- PHSP; #[Reconstructed PDG2011] +0.000000000 K0 rho0 pi- PHSP; #[Reconstructed PDG2011] +# +0.003851416 K0 omega pi- PHSP; #[Reconstructed PDG2011] +0.007032686 K+ rho- pi- PHSP; #[Reconstructed PDG2011] +0.009274210 K*+ pi- pi- PHSP; #[Reconstructed PDG2011] +0.047187552 K*0 pi0 pi- PHSP; #[Reconstructed PDG2011] +#0.0100 K*0 eta pi- PHSP; +0.001101505 K*0 rho0 pi- PHSP; #[Reconstructed PDG2011] +0.003851416 K*0 omega pi- PHSP; #[Reconstructed PDG2011] +#0.0100 K*+ rho- pi- PHSP; +# +0.008473116 K+ pi- pi- pi0 PHSP; #[Reconstructed PDG2011] +# +0.031200000 K_S0 pi- pi- pi+ PHSP; # PDG2014 (why it was much smaller?) +0.031200000 K_L0 pi- pi- pi+ PHSP; # PDG2014 +0.000000000 K0 pi- pi- pi+ PHSP; #[Reconstructed PDG2011] +# +#0.0188 K0 pi- pi0 pi0 PHSP; +0.005700000 K+ pi- pi- pi- pi+ PHSP; #[Reconstructed PDG2011] +0.003851416 K+ pi- pi- pi0 pi0 PHSP; #[Reconstructed PDG2011] +0.006701464 K0 pi- pi- pi+ pi0 PHSP; #[Reconstructed PDG2011] +0.002695991 K0 pi- pi0 pi0 pi0 PHSP; #[Reconstructed PDG2011] +# +0.004600000 K_S0 K_S0 K- PHSP; #[Reconstructed PDG2011] +0.003111944 K_L0 K_L0 K- PHSP; #[Reconstructed PDG2011] +0.000000000 K0 K0 K- PHSP; #[Reconstructed PDG2011] +# +0.004660214 phi pi- SVS; #[Reconstructed PDG2011] +0.023000000 phi pi- pi0 PHSP; #[Reconstructed PDG2011] +# +0.002860000 K_S0 K- PHSP; #[Reconstructed PDG2011] +0.002195307 K_L0 K- PHSP; #[Reconstructed PDG2011] +0.000000000 K0 K- PHSP; #[Reconstructed PDG2011] +# +0.002179902 K*0 K- SVS; #[Reconstructed PDG2011] +# +0.016000000 K*- K_S0 SVS; #[Reconstructed PDG2011] +0.011145999 K*- K_L0 SVS; #[Reconstructed PDG2011] +0.000000000 K*- K0 SVS; #[Reconstructed PDG2011] +# +#0.0180 K*0 K*- SVV_HELAMP 1.0 0.0 1.0 0.0 1.0 0.0; +0.002826940 K- K+ pi- PHSP; #[Reconstructed PDG2011] +0.000770283 K- K0 pi0 PHSP; #[Reconstructed PDG2011] +0.000000000 anti-K0 K0 pi- PHSP; #[Reconstructed PDG2011] +0.000770283 K*- K+ pi- PHSP; #[Reconstructed PDG2011] +0.000770283 K- K*+ pi- PHSP; #[Reconstructed PDG2011] +0.000770283 K*- K0 pi0 PHSP; #[Reconstructed PDG2011] +0.000770283 K- K*0 pi0 PHSP; #[Reconstructed PDG2011] +0.000770283 K*0 anti-K0 pi- PHSP; #[Reconstructed PDG2011] +0.000770283 K0 anti-K*0 pi- PHSP; #[Reconstructed PDG2011] +# +0.001260000 pi0 pi- PHSP; #[Reconstructed PDG2011] +0.000830000 rho0 pi- SVS; #[Reconstructed PDG2011] +0.002440000 pi- pi+ pi- PHSP; #[Reconstructed PDG2011] +0.004700000 pi- pi0 pi0 PHSP; #[Reconstructed PDG2011] +0.011600000 pi- pi+ pi- pi0 PHSP; #[Reconstructed PDG2011] +0.003851416 pi- pi0 pi0 pi0 PHSP; #[Reconstructed PDG2011] +0.003430000 eta pi- PHSP; #[Reconstructed PDG2011] +0.004400000 eta' pi- PHSP; #[Reconstructed PDG2011] +0.001380000 eta pi- pi0 PHSP; #[Reconstructed PDG2011] +0.002310850 eta pi- pi+ pi- PHSP; #[Reconstructed PDG2011] +0.001540566 eta pi- pi0 pi0 PHSP; #[Reconstructed PDG2011] +#lange jul22,2002 +0.001660000 pi+ pi- pi+ pi- pi- PHSP; #[Reconstructed PDG2011] +# +# March 2009 New Modes +0.009300000 K*0 a_1- PHSP; #[Reconstructed PDG2011] +0.010545178 K+ K- pi- pi0 PHSP; #[Reconstructed PDG2011] +# +0.001740000 K- K_S0 pi+ pi- PHSP; #[Reconstructed PDG2011] +0.001270967 K- K_L0 pi+ pi- PHSP; #[Reconstructed PDG2011] +0.000000000 K- K0 pi+ pi- PHSP; #[Reconstructed PDG2011] +# +0.002380000 K_S0 K+ pi- pi- PHSP; #[Reconstructed PDG2011] +0.001756246 K_L0 K+ pi- pi- PHSP; #[Reconstructed PDG2011] +0.000000000 anti-K0 K+ pi- pi- PHSP; #[Reconstructed PDG2011] +# +0.000230000 K+ K- pi+ pi- pi- PHSP; #[Reconstructed PDG2011] +# +0.000240000 K+ K- K_S0 pi- PHSP; #[Reconstructed PDG2011] +0.000161759 K+ K- K_L0 pi- PHSP; #[Reconstructed PDG2011] +0.000000000 K+ K- K0 pi- PHSP; #[Reconstructed PDG2011] +# Doubly Cabibbo suppressed modes +0.000237000 K- pi0 PHSP; #[Reconstructed PDG2011] +0.000332000 K- pi+ pi- PHSP; #[Reconstructed PDG2011] +0.000089000 K- K+ K- PHSP; #[Reconstructed PDG2011] +0.001720000 K+ rho0 pi- pi- PHSP; #[New mode added] #[Reconstructed PDG2011] +0.001600000 eta' pi- pi0 PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000210000 K- rho0 PHSP; #[New mode added] #[Reconstructed PDG2011] +Enddecay +# +Decay K*BR +1.0000 anti-K0 pi0 VSS; +Enddecay +# +# +Decay D0 +# updated according to suggestions by P. Roudeau, +# using PDG2004 measurements and imposing the equality +# of sl partial widths for D+ and D0. +# Include additional decay anti-K0 pi- e+ nu_e , K- pi0 e+ nu_e. +# +0.021600000 K*- e+ nu_e PHOTOS ISGW2; # PDG2014 +0.035500000 K- e+ nu_e PHOTOS ISGW2; # PDG2014 +0.000760000 K_1- e+ nu_e PHOTOS ISGW2; # PDG2014 +0.001100000 K_2*- e+ nu_e PHOTOS ISGW2; # Small decrease as this is unmeasured +0.002890000 pi- e+ nu_e PHOTOS ISGW2; # PDG2014 +0.001770000 rho- e+ nu_e PHOTOS ISGW2; # PDG2014 +0.001080000 anti-K0 pi- e+ nu_e PHOTOS PHSP; # PDG2014 subtracting K* (decreased due large uncertaity ) +0.000400000 K- pi0 e+ nu_e PHOTOS PHSP; # PDG2014 subtracting K* (decreased due large uncertaity ) +# +0.021000000 K*- mu+ nu_mu PHOTOS ISGW2; # 1.1 * PDG2014 +0.034700000 K- mu+ nu_mu PHOTOS ISGW2; # 1.05 * PDG2014 +0.000076000 K_1- mu+ nu_mu PHOTOS ISGW2; # PDG2014 for electron +0.001100000 K_2*- mu+ nu_mu PHOTOS ISGW2; # copy from electron +0.002370000 pi- mu+ nu_mu PHOTOS ISGW2; # PDG2014 +0.002015940 rho- mu+ nu_mu PHOTOS ISGW2; # PDG2014 for electron +0.001080000 anti-K0 pi- mu+ nu_mu PHOTOS PHSP; # copy electron +0.000400000 K- pi0 mu+ nu_mu PHOTOS PHSP; # copy electron +# +0.038900000 K- pi+ PHSP; #[Reconstructed PDG2011] +# +0.012200000 K_S0 pi0 PHSP; #[Reconstructed PDG2011] +0.010000000 K_L0 pi0 PHSP; #[Reconstructed PDG2011] +0.000000000 anti-K0 pi0 PHSP; #[Reconstructed PDG2011] +# +0.004290000 K_S0 eta PHSP; #[Reconstructed PDG2011] +0.003802795 K_L0 eta PHSP; #[Reconstructed PDG2011] +0.000000000 anti-K0 eta PHSP; #[Reconstructed PDG2011] +# +0.009300000 K_S0 eta' PHSP; #[Reconstructed PDG2011] +0.008980094 K_L0 eta' PHSP; #[Reconstructed PDG2011] +0.000000000 anti-K0 eta' PHSP; #[Reconstructed PDG2011] +# +0.011100000 omega K_S0 SVS; #[Reconstructed PDG2011] +0.010904400 omega K_L0 SVS; #[Reconstructed PDG2011] +0.000000000 omega anti-K0 SVS; #[Reconstructed PDG2011] +# +0.001603588 anti-K*0 eta SVS; #[Reconstructed PDG2011] +0.000916336 anti-K*0 eta' SVS; #[Reconstructed PDG2011] +0.078000000 a_1+ K- SVS; #[Reconstructed PDG2011] +0.067625607 K*- rho+ SVV_HELAMP 1.0 0.0 1.0 0.0 1.0 0.0; #[Reconstructed PDG2011] +0.015800000 anti-K*0 rho0 SVV_HELAMP 1.0 0.0 1.0 0.0 1.0 0.0; #[Reconstructed PDG2011] +0.011000000 anti-K*0 omega SVV_HELAMP 1.0 0.0 1.0 0.0 1.0 0.0; #[Reconstructed PDG2011] +# the Dalitz mode below includes K*bar(892)0 pi0, +# K*(892)- pi+, and K- rho(770)+ resonances +0.139000000 K- pi+ pi0 D_DALITZ; #[Reconstructed PDG2011] +0.006634274 K*BR pi0 SVS; #[Reconstructed PDG2011] +0.016000000 K_1- pi+ SVS; #[Reconstructed PDG2011] +0.006505987 anti-K_10 pi0 SVS; #[Reconstructed PDG2011] +# +# the Dalitz mode below includes K*(892)- pi+ and Kbar0 rho(770)0 resonances +# LHCb PR 09 Apr 2004 split into KS/KL +0.029400000 K_S0 pi+ pi- D_DALITZ; #[Reconstructed PDG2011] +0.027856619 K_L0 pi+ pi- D_DALITZ; #[Reconstructed PDG2011] +0.000000000 anti-K0 pi+ pi- D_DALITZ; #[Reconstructed PDG2011] +# +0.008300000 K_S0 pi0 pi0 PHSP; #[Reconstructed PDG2011] +0.004425904 K_L0 pi0 pi0 PHSP; #[Reconstructed PDG2011] +0.000000000 anti-K0 pi0 pi0 PHSP; #[Reconstructed PDG2011] +# +0.024000000 anti-K*0 pi+ pi- PHSP; #[Reconstructed PDG2011] +0.010629499 anti-K*0 pi0 pi0 PHSP; #[Reconstructed PDG2011] +0.009163361 K*- pi+ pi0 PHSP; #[Reconstructed PDG2011] +0.006231086 K- rho+ pi0 PHSP; #[Reconstructed PDG2011] +0.005305586 K- pi+ rho0 PHSP; #[Reconstructed PDG2011] +0.019000000 K- pi+ omega PHSP; #[Reconstructed PDG2011] +0.009163361 K- pi+ eta PHSP; #[Reconstructed PDG2011] +0.007500000 K- pi+ eta' PHSP; #[Reconstructed PDG2011] +0.013300000 K- pi+ pi+ pi- PHSP; #[Reconstructed PDG2011] +# +0.054000000 K_S0 pi+ pi- pi0 PHSP; #[Reconstructed PDG2011] +0.010079698 K_L0 pi+ pi- pi0 PHSP; #[Reconstructed PDG2011] +0.000000000 anti-K0 pi+ pi- pi0 PHSP; #[Reconstructed PDG2011] +# +# K- pi+ pi0 pi0 is (15 +/- 5)% in the PDG, but we decrease it to +# have everything add to 1 and get enough neutral kaons: +#0.02575 K- pi+ pi0 pi0 PHSP; +# +#0.0143 anti-K0 pi0 pi0 pi0 PHSP; +0.040316317 anti-K0 pi0 pi0 pi0 PHSP; # Add in to fill sum to 1. [MK June 2015] +0.000000000 K- pi+ pi+ pi- pi0 PHSP; #[Reconstructed PDG2011] +0.003482077 K- pi+ pi0 pi0 pi0 PHSP; #[Reconstructed PDG2011] +# +0.002800000 K_S0 pi+ pi- pi+ pi- PHSP; #[Reconstructed PDG2011] +0.002684865 K_L0 pi+ pi- pi+ pi- PHSP; #[Reconstructed PDG2011] +0.000000000 anti-K0 pi+ pi- pi+ pi- PHSP; #[Reconstructed PDG2011] +# +#0.0638 anti-K0 pi+ pi- pi0 pi0 PHSP; +#0.0192 anti-K0 pi+ pi- pi0 pi0 pi0 PHSP; +# +0.002034266 phi K_S0 SVS; #[Reconstructed PDG2011] +0.002034266 phi K_L0 SVS; #[Reconstructed PDG2011] +0.000000000 phi anti-K0 SVS; #[Reconstructed PDG2011] +# +0.004650000 K_S0 K+ K- PHSP; #[Reconstructed PDG2011] +0.002785662 K_L0 K+ K- PHSP; #[Reconstructed PDG2011] +0.000000000 anti-K0 K+ K- PHSP; #[Reconstructed PDG2011] +# +0.000950000 K_S0 K_S0 K_S0 PHSP; #[Reconstructed PDG2011] +0.003940000 K+ K- PHSP; #[Reconstructed PDG2011] +0.000190000 K_S0 K_S0 PHSP; #[Reconstructed PDG2011] +0.000366534 K_L0 K_L0 PHSP; #[Reconstructed PDG2011] +0.000366534 K*0 anti-K0 SVS; #[Reconstructed PDG2011] +# +0.000091634 anti-K*0 K_S0 SVS; #[Reconstructed PDG2011] +0.000091634 anti-K*0 K_L0 SVS; #[Reconstructed PDG2011] +0.000000000 anti-K*0 K0 SVS; #[Reconstructed PDG2011] +# +0.000485658 K*- K+ SVS; #[Reconstructed PDG2011] +0.001365341 K*+ K- SVS; #[Reconstructed PDG2011] +0.001282871 anti-K*0 K*0 SVV_HELAMP 1.0 0.0 1.0 0.0 1.0 0.0; #[Reconstructed PDG2011] +0.000714742 phi pi0 SVS; #[Reconstructed PDG2011] +0.001007970 phi pi+ pi- PHSP; #[Reconstructed PDG2011] +0.002430000 K+ K- pi+ pi- PHSP; #[Reconstructed PDG2011] +0.002749008 K+ K- pi0 pi0 PHSP; #[Reconstructed PDG2011] +# +0.001280000 K_S0 K_S0 pi+ pi- PHSP; #[Reconstructed PDG2011] +0.001255381 K_L0 K_L0 pi+ pi- PHSP; #[Reconstructed PDG2011] +0.000000000 anti-K0 K0 pi+ pi- PHSP; #[Reconstructed PDG2011] +# +0.001374504 anti-K0 K0 pi0 pi0 PHSP; #[Reconstructed PDG2011] +# +0.001397000 pi+ pi- PHSP; #[Reconstructed PDG2011] +0.000800000 pi0 pi0 PHSP; #[Reconstructed PDG2011] +0.000640000 eta pi0 PHSP; #[Reconstructed PDG2011] +0.000810000 eta' pi0 PHSP; #[Reconstructed PDG2011] +0.001670000 eta eta PHSP; #[Reconstructed PDG2011] +0.009800000 rho+ pi- SVS; #[Reconstructed PDG2011] +0.004970000 rho- pi+ SVS; #[Reconstructed PDG2011] +0.003730000 rho0 pi0 SVS; #[Reconstructed PDG2011] +0.001209564 pi+ pi- pi0 PHSP; #[Reconstructed PDG2011] +0.000091634 pi0 pi0 pi0 PHSP; #[Reconstructed PDG2011] +0.005620000 pi+ pi+ pi- pi- PHSP; #[Reconstructed PDG2011] +0.009360000 pi+ pi- pi0 pi0 PHSP; #[Reconstructed PDG2011] +0.001510000 pi+ pi- pi+ pi- pi0 PHSP; #[Reconstructed PDG2011] +0.005498017 pi+ pi- pi0 pi0 pi0 PHSP; #[Reconstructed PDG2011] +0.000420000 pi+ pi- pi+ pi- pi+ pi- PHSP; #[Reconstructed PDG2011] +# +# Doubly Cabibbo suppressed decays: +0.000137450 pi- K+ PHSP; #[Reconstructed PDG2011] +0.000128287 pi- K*+ PHSP; #[Reconstructed PDG2011] +0.000274901 pi- K+ pi0 PHSP; #[Reconstructed PDG2011] +0.000247411 K+ pi- pi+ pi- PHSP; #[Reconstructed PDG2011] +# PR LHCb - 19 Apr. 2004 Add D0 -> mu+ mu- +0.000000000 mu+ mu- PHSP; #[Reconstructed PDG2011] +# +# March 2009 New Modes +0.000140000 phi eta PHSP; #[Reconstructed PDG2011] +0.019000000 anti-K*0 pi+ pi- pi0 PHSP; #[Reconstructed PDG2011] +0.000220000 K- pi+ pi- pi+ pi- pi+ PHSP; #[Reconstructed PDG2011] +0.000641435 K+ K- pi0 PHSP; #[Reconstructed PDG2011] +0.003100000 K+ K- pi+ pi- pi0 PHSP; #[Reconstructed PDG2011] +0.000221000 K+ K- K- pi+ PHSP; #[Reconstructed PDG2011] +# +0.005600000 K_S0 eta pi0 PHSP; #[Reconstructed PDG2011] +0.003665345 K_L0 eta pi0 PHSP; #[Reconstructed PDG2011] +0.000000000 anti-K0 eta pi0 PHSP; #[Reconstructed PDG2011] +# +0.002600000 K_S0 K+ pi- PHSP; #[Reconstructed PDG2011] +0.002382474 K_L0 K+ pi- PHSP; #[Reconstructed PDG2011] +0.000000000 anti-K0 K+ pi- PHSP; #[Reconstructed PDG2011] +# +0.003500000 K_S0 K- pi+ PHSP; #[Reconstructed PDG2011] +0.003344627 K_L0 K- pi+ PHSP; #[Reconstructed PDG2011] +0.000000000 K0 K- pi+ PHSP; #[Reconstructed PDG2011] +# +0.000310000 K_S0 K_S0 K+ pi- PHSP; #[Reconstructed PDG2011] +0.000293228 K_L0 K_L0 K+ pi- PHSP; #[Reconstructed PDG2011] +0.000000000 anti-K0 K0 K+ pi- PHSP; #[Reconstructed PDG2011] +# +0.000310000 K_S0 K_S0 K- pi+ PHSP; #[Reconstructed PDG2011] +0.000293228 K_L0 K_L0 K- pi+ PHSP; #[Reconstructed PDG2011] +0.000000000 anti-K0 K0 K- pi+ PHSP; #[Reconstructed PDG2011] +0.000000000 K- pi+ pi- e+ nu_e PHSP; #[New mode added] #[Reconstructed PDG2011] +0.001820000 rho0 rho0 PHSP; #[New mode added] #[Reconstructed PDG2011] +0.001090000 eta pi+ pi- PHSP; #[New mode added] #[Reconstructed PDG2011] +0.001600000 omega pi+ pi- PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000450000 eta' pi+ pi- PHSP; #[New mode added] #[Reconstructed PDG2011] +0.001260000 eta eta' PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000027000 phi gamma PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000328000 anti-K*0 gamma PHSP; #[New mode added] #[Reconstructed PDG2011] +Enddecay +# +# +Decay K*0R +1.0000 K0 pi0 VSS; +Enddecay +# +# +Decay anti-D0 +0.021600000 K*+ e- anti-nu_e PHOTOS ISGW2; # PDG2014 +0.035500000 K+ e- anti-nu_e PHOTOS ISGW2; # PDG2014 +0.000760000 K_1+ e- anti-nu_e PHOTOS ISGW2; # PDG2014 +0.001100000 K_2*+ e- anti-nu_e PHOTOS ISGW2; # Small decrease as this is unmeasured +0.002890000 pi+ e- anti-nu_e PHOTOS ISGW2; # PDG2014 +0.001770000 rho+ e- anti-nu_e PHOTOS ISGW2; # PDG2014 +0.001080000 K0 pi+ e- anti-nu_e PHOTOS PHSP; # PDG2014 subtracting K* (decreased due large uncertaity ) +0.000400000 K+ pi0 e- anti-nu_e PHOTOS PHSP; # PDG2014 subtracting K* (decreased due large uncertaity ) +# +0.021000000 K*+ mu- anti-nu_mu PHOTOS ISGW2; # 1.1 * PDG2014 +0.034700000 K+ mu- anti-nu_mu PHOTOS ISGW2; # 1.05 * PDG2014 +0.000076000 K_1+ mu- anti-nu_mu PHOTOS ISGW2; # PDG2014 for electron +0.001100000 K_2*+ mu- anti-nu_mu PHOTOS ISGW2; # copy from electron +0.002370000 pi+ mu- anti-nu_mu PHOTOS ISGW2; # PDG2014 +0.002015940 rho+ mu- anti-nu_mu PHOTOS ISGW2; # PDG2014 for electron +0.001080000 K0 pi+ mu- anti-nu_mu PHOTOS PHSP; # copy electron +0.000400000 K+ pi0 mu- anti-nu_mu PHOTOS PHSP; # copy electron +# +0.038900000 K+ pi- PHSP; #[Reconstructed PDG2011] +# +0.012200000 K_S0 pi0 PHSP; #[Reconstructed PDG2011] +0.010000000 K_L0 pi0 PHSP; #[Reconstructed PDG2011] +0.000000000 K0 pi0 PHSP; #[Reconstructed PDG2011] +# +0.004290000 K_S0 eta PHSP; #[Reconstructed PDG2011] +0.003818748 K_L0 eta PHSP; #[Reconstructed PDG2011] +0.000000000 K0 eta PHSP; #[Reconstructed PDG2011] +# +0.009300000 K_S0 eta' PHSP; #[Reconstructed PDG2011] +0.009017767 K_L0 eta' PHSP; #[Reconstructed PDG2011] +0.000000000 K0 eta' PHSP; #[Reconstructed PDG2011] +# +0.011100000 omega K_S0 SVS; #[Reconstructed PDG2011] +0.010950146 omega K_L0 SVS; #[Reconstructed PDG2011] +0.000000000 omega K0 SVS; #[Reconstructed PDG2011] +# +0.001610316 K*0 eta SVS; #[Reconstructed PDG2011] +0.000920180 K*0 eta' SVS; #[Reconstructed PDG2011] +0.078000000 a_1- K+ SVS; #[Reconstructed PDG2011] +0.067909308 K*+ rho- SVV_HELAMP 1.0 0.0 1.0 0.0 1.0 0.0; #[Reconstructed PDG2011] +0.015800000 K*0 rho0 SVV_HELAMP 1.0 0.0 1.0 0.0 1.0 0.0; #[Reconstructed PDG2011] +0.011000000 K*0 omega SVV_HELAMP 1.0 0.0 1.0 0.0 1.0 0.0; #[Reconstructed PDG2011] +# the Dalitz mode below includes K*(892)0 pi0, K*(892)+ pi-, and K+ rho(770)- resonances +0.139000000 K+ pi- pi0 D_DALITZ; #[Reconstructed PDG2011] +0.006662106 K*0R pi0 SVS; #[Reconstructed PDG2011] +0.016000000 K_1+ pi- SVS; #[Reconstructed PDG2011] +0.006533280 K_10 pi0 SVS; #[Reconstructed PDG2011] +# the Dalitz mode below includes K*(892)+ pi- and K0 rho(770)0 resonances +# LHCb PR 09 Apr 2004, split into KS/KL +0.000000000 K0 pi+ pi- D_DALITZ; #[Reconstructed PDG2011] +0.029400000 K_S0 pi+ pi- D_DALITZ; #[Reconstructed PDG2011] +0.027973482 K_L0 pi+ pi- D_DALITZ; #[Reconstructed PDG2011] +# +0.008300000 K_S0 pi0 pi0 PHSP; #[Reconstructed PDG2011] +0.004444471 K_L0 pi0 pi0 PHSP; #[Reconstructed PDG2011] +0.000000000 K0 pi0 pi0 PHSP; #[Reconstructed PDG2011] +# +0.024000000 K*0 pi+ pi- PHSP; #[Reconstructed PDG2011] +0.010674092 K*0 pi0 pi0 PHSP; #[Reconstructed PDG2011] +0.009201803 K*+ pi- pi0 PHSP; #[Reconstructed PDG2011] +0.006257226 K+ rho- pi0 PHSP; #[Reconstructed PDG2011] +0.005327844 K+ pi- rho0 PHSP; #[Reconstructed PDG2011] +0.019000000 K+ pi- omega PHSP; #[Reconstructed PDG2011] +0.009201803 K+ pi- eta PHSP; #[Reconstructed PDG2011] +0.007500000 K+ pi- eta' PHSP; #[Reconstructed PDG2011] +0.013300000 K+ pi- pi+ pi- PHSP; #[Reconstructed PDG2011] +# +0.054000000 K_S0 pi+ pi- pi0 PHSP; #[Reconstructed PDG2011] +0.010121984 K_L0 pi+ pi- pi0 PHSP; #[Reconstructed PDG2011] +0.000000000 K0 pi+ pi- pi0 PHSP; #[Reconstructed PDG2011] +# +# K+ pi- pi0 pi0 is (15 +/- 5)% in the PDG, but we decrease it to +# have everything add to 1 and get enough neutral kaons: +#0.02575 K+ pi- pi0 pi0 PHSP; +# +#0.0143 K0 pi0 pi0 pi0 PHSP; +0.040346268 K0 pi0 pi0 pi0 PHSP; +0.000000000 K+ pi- pi+ pi- pi0 PHSP; #[Reconstructed PDG2011] +0.003496685 K+ pi- pi0 pi0 pi0 PHSP; #[Reconstructed PDG2011] +# +0.002800000 K_S0 pi+ pi- pi+ pi- PHSP; #[Reconstructed PDG2011] +0.002696128 K_L0 pi+ pi- pi+ pi- PHSP; #[Reconstructed PDG2011] +0.000000000 K0 pi+ pi- pi+ pi- PHSP; #[Reconstructed PDG2011] +# +#0.0638 K0 pi- pi+ pi0 pi0 PHSP; +#0.0192 K0 pi- pi+ pi0 pi0 pi0 PHSP; +# +0.002042800 phi K_S0 SVS; #[Reconstructed PDG2011] +0.002042800 phi K_L0 SVS; #[Reconstructed PDG2011] +0.000000000 phi K0 SVS; #[Reconstructed PDG2011] +# +0.004650000 K_S0 K+ K- PHSP; #[Reconstructed PDG2011] +0.002797348 K_L0 K+ K- PHSP; #[Reconstructed PDG2011] +0.000000000 K0 K+ K- PHSP; #[Reconstructed PDG2011] +# +0.000950000 K_S0 K_S0 K_S0 PHSP; #[Reconstructed PDG2011] +0.003940000 K+ K- PHSP; #[Reconstructed PDG2011] +0.000190000 K_S0 K_S0 PHSP; #[Reconstructed PDG2011] +0.000368072 K_L0 K_L0 PHSP; #[Reconstructed PDG2011] +0.000368072 anti-K*0 K0 SVS; #[Reconstructed PDG2011] +# +0.000092018 K*0 K_S0 SVS; #[Reconstructed PDG2011] +0.000092018 K*0 K_L0 SVS; #[Reconstructed PDG2011] +0.000000000 K*0 anti-K0 SVS; #[Reconstructed PDG2011] +# +0.000487696 K*+ K- SVS; #[Reconstructed PDG2011] +0.001371069 K*- K+ SVS; #[Reconstructed PDG2011] +0.001288252 K*0 anti-K*0 SVV_HELAMP 1.0 0.0 1.0 0.0 1.0 0.0; #[Reconstructed PDG2011] +0.000717741 phi pi0 SVS; #[Reconstructed PDG2011] +0.001012198 phi pi+ pi- PHSP; #[Reconstructed PDG2011] +0.002430000 K+ K- pi+ pi- PHSP; #[Reconstructed PDG2011] +0.002760541 K+ K- pi0 pi0 PHSP; #[Reconstructed PDG2011] +# +0.001280000 K_S0 K_S0 pi+ pi- PHSP; #[Reconstructed PDG2011] +0.001260647 K_L0 K_L0 pi+ pi- PHSP; #[Reconstructed PDG2011] +0.000000000 anti-K0 K0 pi+ pi- PHSP; #[Reconstructed PDG2011] +# +0.001380270 anti-K0 K0 pi0 pi0 PHSP; #[Reconstructed PDG2011] +# +0.001397000 pi+ pi- PHSP; #[Reconstructed PDG2011] +0.000800000 pi0 pi0 PHSP; #[Reconstructed PDG2011] +0.000640000 eta pi0 PHSP; #[Reconstructed PDG2011] +0.000810000 eta' pi0 PHSP; #[Reconstructed PDG2011] +0.001670000 eta eta PHSP; #[Reconstructed PDG2011] +0.009128189 rho+ pi- SVS; #[Reconstructed PDG2011] +0.004637709 rho- pi+ SVS; #[Reconstructed PDG2011] +0.003730000 rho0 pi0 SVS; #[Reconstructed PDG2011] +0.001214638 pi+ pi- pi0 PHSP; #[Reconstructed PDG2011] +0.000092018 pi0 pi0 pi0 PHSP; #[Reconstructed PDG2011] +0.005620000 pi+ pi+ pi- pi- PHSP; #[Reconstructed PDG2011] +0.009360000 pi+ pi- pi0 pi0 PHSP; #[Reconstructed PDG2011] +0.001510000 pi+ pi- pi+ pi- pi0 PHSP; #[Reconstructed PDG2011] +0.005521082 pi+ pi- pi0 pi0 pi0 PHSP; #[Reconstructed PDG2011] +0.000420000 pi+ pi- pi+ pi- pi+ pi- PHSP; #[Reconstructed PDG2011] +# +# Doubly Cabibbo suppressed decays: +0.000138027 pi+ K- PHSP; #[Reconstructed PDG2011] +0.000128825 pi+ K*- PHSP; #[Reconstructed PDG2011] +0.000276054 pi+ K- pi0 PHSP; #[Reconstructed PDG2011] +0.000248449 K- pi+ pi+ pi- PHSP; #[Reconstructed PDG2011] +# PR LHCb - 19 Apr. 2004 Add anti-D0 -> mu+ mu- +0.000000000 mu- mu+ PHSP; #[Reconstructed PDG2011] +# +# March 2009 New Modes +0.000140000 phi eta PHSP; #[Reconstructed PDG2011] +0.019000000 K*0 pi+ pi- pi0 PHSP; #[Reconstructed PDG2011] +0.000220000 K+ pi- pi- pi+ pi- pi+ PHSP; #[Reconstructed PDG2011] +0.000644126 K+ K- pi0 PHSP; #[Reconstructed PDG2011] +0.003100000 K+ K- pi+ pi- pi0 PHSP; #[Reconstructed PDG2011] +0.000221000 K+ K+ K- pi- PHSP; #[Reconstructed PDG2011] +# +0.005600000 K_S0 eta pi0 PHSP; #[Reconstructed PDG2011] +0.003680721 K_L0 eta pi0 PHSP; #[Reconstructed PDG2011] +0.000000000 K0 eta pi0 PHSP; #[Reconstructed PDG2011] +# +0.002600000 K_S0 K- pi+ PHSP; #[Reconstructed PDG2011] +0.002392469 K_L0 K- pi+ PHSP; #[Reconstructed PDG2011] +0.000000000 K0 K- pi+ PHSP; #[Reconstructed PDG2011] +# +0.003500000 K_S0 K+ pi- PHSP; #[Reconstructed PDG2011] +0.003358658 K_L0 K+ pi- PHSP; #[Reconstructed PDG2011] +0.000000000 anti-K0 K+ pi- PHSP; #[Reconstructed PDG2011] +# +0.000310000 K_S0 K_S0 K+ pi- PHSP; #[Reconstructed PDG2011] +0.000294458 K_L0 K_L0 K+ pi- PHSP; #[Reconstructed PDG2011] +0.000000000 anti-K0 K0 K+ pi- PHSP; #[Reconstructed PDG2011] +# +0.000310000 K_S0 K_S0 K- pi+ PHSP; #[Reconstructed PDG2011] +0.000294458 K_L0 K_L0 K- pi+ PHSP; #[Reconstructed PDG2011] +0.000000000 anti-K0 K0 K- pi+ PHSP; #[Reconstructed PDG2011] +0.000000000 K+ pi- pi+ e- anti-nu_e PHSP; #[New mode added] #[Reconstructed PDG2011] +0.001820000 rho0 rho0 PHSP; #[New mode added] #[Reconstructed PDG2011] +0.001090000 eta pi- pi+ PHSP; #[New mode added] #[Reconstructed PDG2011] +0.001600000 omega pi- pi+ PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000450000 eta' pi- pi+ PHSP; #[New mode added] #[Reconstructed PDG2011] +0.001260000 eta eta' PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000027000 phi gamma PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000328000 K*0 gamma PHSP; #[New mode added] #[Reconstructed PDG2011] +Enddecay +# +# Updated to PDG 2008 +Decay D_s+ +0.024900000 phi e+ nu_e PHOTOS ISGW2; #[Reconstructed PDG2011] +0.026700000 eta e+ nu_e PHOTOS ISGW2; #[Reconstructed PDG2011] +0.009900000 eta' e+ nu_e PHOTOS ISGW2; #[Reconstructed PDG2011] +0.002058115 anti-K0 e+ nu_e PHOTOS ISGW2; #[Reconstructed PDG2011] +0.000762265 anti-K*0 e+ nu_e PHOTOS ISGW2; #[Reconstructed PDG2011] +0.018309605 phi mu+ nu_mu PHOTOS ISGW2; #[Reconstructed PDG2011] +0.022845082 eta mu+ nu_mu PHOTOS ISGW2; #[Reconstructed PDG2011] +0.008186726 eta' mu+ nu_mu PHOTOS ISGW2; #[Reconstructed PDG2011] +0.002058115 anti-K0 mu+ nu_mu PHOTOS ISGW2; #[Reconstructed PDG2011] +0.000762265 anti-K*0 mu+ nu_mu PHOTOS ISGW2; #[Reconstructed PDG2011] +0.005800000 mu+ nu_mu PHOTOS SLN; #[Reconstructed PDG2011] +0.031100000 tau+ nu_tau SLN; #[Reconstructed PDG2011] +### Lange Nov14, 2004 - average cleo + babar (prelim) using stat error only.. +0.045000000 phi pi+ SVS; #[Reconstructed PDG2011] +0.015600000 eta pi+ PHSP; #[Reconstructed PDG2011] +0.038000000 eta' pi+ PHSP; #[Reconstructed PDG2011] +0.002300000 omega pi+ SVS; #[Reconstructed PDG2011] +0.000304906 rho+ pi0 SVS; #[Reconstructed PDG2011] +0.000076226 pi+ pi0 PHSP; #[Reconstructed PDG2011] +#? +0.000196200 rho0 pi+ SVS; # PDG 2014 +0.007852048 f_0 pi+ PHSP; # PDG 2014 +0.001753646 f_2 pi+ PHSP; # PDG 2014 +0.004754077 f'_0 pi+ PHSP; # PDG 2014 +0.005768112 f_0(1500) pi+ PHSP; # PDG 2014 +#0.000735750 rho(2S)0 pi+ PHSP; # PDG 2014 +0.000000000 pi+ pi- pi+ PHSP; # PDG 2014 (filled by exclusives) +0.084000000 phi rho+ SVV_HELAMP 1.0 0.0 1.0 0.0 1.0 0.0; #[Reconstructed PDG2011] +0.089000000 rho+ eta SVS; #[Reconstructed PDG2011] +0.124213286 rho+ eta' SVS; # Decrease compared to PDG 2014 to preserve unitarity after +# # fixing Ds --> 3pi, we also have too much of inclusive eta', +# #so pick this decay +0.006500000 pi+ pi0 pi0 PHSP; #[Reconstructed PDG2011] +0.007622650 phi pi+ pi0 PHSP; #[Reconstructed PDG2011] +0.011433975 eta pi+ pi0 PHSP; #[Reconstructed PDG2011] +0.011433975 eta' pi+ pi0 PHSP; #[Reconstructed PDG2011] +0.012100000 phi pi+ pi- pi+ PHSP; #[Reconstructed PDG2011] +0.003811325 phi pi+ pi0 pi0 PHSP; #[Reconstructed PDG2011] +0.003811325 eta pi+ pi- pi+ PHSP; #[Reconstructed PDG2011] +0.003811325 eta pi+ pi0 pi0 PHSP; #[Reconstructed PDG2011] +# +0.014900000 K_S0 K+ PHSP; #[Reconstructed PDG2011] +0.011472088 K_L0 K+ PHSP; #[Reconstructed PDG2011] +0.000000000 anti-K0 K+ PHSP; #[Reconstructed PDG2011] +# +0.030490600 anti-K*0 K+ SVS; #[Reconstructed PDG2011] +0.054000000 K*+ anti-K0 SVS; #[Reconstructed PDG2011] +0.072000000 anti-K*0 K*+ SVV_HELAMP 1.0 0.0 1.0 0.0 1.0 0.0; #[Reconstructed PDG2011] +0.002286795 anti-K0 K+ pi0 PHSP; #[Reconstructed PDG2011] +0.000914718 anti-K*0 K+ pi0 PHSP; #[Reconstructed PDG2011] +0.000914718 K*+ anti-K0 pi0 PHSP; #[Reconstructed PDG2011] +0.003049060 anti-K*0 K*+ pi0 PHSP; #[Reconstructed PDG2011] +0.003811325 K+ K- pi+ PHSP; #[Reconstructed PDG2011] +# +0.009600000 K_S0 K+ pi+ pi- PHSP; #[Reconstructed PDG2011] +0.007470197 K_L0 K+ pi+ pi- PHSP; #[Reconstructed PDG2011] +0.000000000 anti-K0 K+ pi+ pi- PHSP; #[Reconstructed PDG2011] +# +0.000762265 anti-K0 K+ pi0 pi0 PHSP; #[Reconstructed PDG2011] +0.000000000 K+ K- pi+ pi- pi+ PHSP; #[Reconstructed PDG2011] +0.000152453 phi K+ SVS; #[Reconstructed PDG2011] +0.001390000 eta K+ PHSP; #[Reconstructed PDG2011] +0.001600000 eta' K+ PHSP; #[Reconstructed PDG2011] +0.000152453 eta K+ pi0 PHSP; #[Reconstructed PDG2011] +0.000152453 eta K+ pi+ pi- PHSP; #[Reconstructed PDG2011] +0.000152453 eta' K+ pi0 PHSP; #[Reconstructed PDG2011] +0.000152453 eta' K+ pi+ pi- PHSP; #[Reconstructed PDG2011] +0.000490000 K+ K- K+ PHSP; #[Reconstructed PDG2011] +# +0.001200000 K_S0 pi+ PHSP; #[Reconstructed PDG2011] +0.000968077 K_L0 pi+ PHSP; #[Reconstructed PDG2011] +0.000000000 K0 pi+ PHSP; #[Reconstructed PDG2011] +# +0.001143397 rho+ K0 SVS; #[Reconstructed PDG2011] +0.002700000 rho0 K+ SVS; #[Reconstructed PDG2011] +0.010000000 K0 pi+ pi0 PHSP; #[Reconstructed PDG2011] +0.001905662 a_1+ K0 SVS; #[Reconstructed PDG2011] +0.006021893 K*0 pi+ SVS; #[Reconstructed PDG2011] +0.003811325 K*0 rho+ SVV_HELAMP 1.0 0.0 1.0 0.0 1.0 0.0; #[Reconstructed PDG2011] +0.003811325 K*0 pi+ pi0 PHSP; #[Reconstructed PDG2011] +# +# March 2009 New Modes +0.000820000 K+ pi0 PHSP; #[Reconstructed PDG2011] +0.004200000 K+ pi+ pi- PHSP; #[Reconstructed PDG2011] +0.008000000 pi+ pi+ pi+ pi- pi- PHSP; #[Reconstructed PDG2011] +0.049000000 pi+ pi+ pi+ pi- pi- pi0 PHSP; #[Reconstructed PDG2011] +0.001300000 p+ anti-n0 PHSP; #[Reconstructed PDG2011] +# +0.000840000 K_S0 K_S0 pi+ pi+ pi- PHSP; #[Reconstructed PDG2011] +0.000686038 K_L0 K_L0 pi+ pi+ pi- PHSP; #[Reconstructed PDG2011] +0.000000000 anti-K0 K0 pi+ pi+ pi- PHSP; #[Reconstructed PDG2011] +# +0.002900000 K_S0 pi+ pi+ pi- PHSP; #[Reconstructed PDG2011] +0.002424003 K_L0 pi+ pi+ pi- PHSP; #[Reconstructed PDG2011] +0.000000000 anti-K0 pi+ pi+ pi- PHSP; #[Reconstructed PDG2011] +# Doubly Cabibbo suppressed +0.000129000 K+ K+ pi- PHSP; #[Reconstructed PDG2011] +0.003700000 K0 e+ nu_e PHSP; #[New mode added] #[Reconstructed PDG2011] +0.001800000 K*0 e+ nu_e PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000000000 K+ K- pi+ pi0 PHSP; #[New mode added] #[Reconstructed PDG2011] +0.028000000 omega pi+ pi0 PHSP; #[New mode added] #[Reconstructed PDG2011] +0.016000000 omega pi+ pi+ pi- PHSP; #[New mode added] #[Reconstructed PDG2011] +Enddecay +# +# Updated to PDG 2008 +Decay D_s- +0.024900000 phi e- anti-nu_e PHOTOS ISGW2; #[Reconstructed PDG2011] +0.026700000 eta e- anti-nu_e PHOTOS ISGW2; #[Reconstructed PDG2011] +0.009900000 eta' e- anti-nu_e PHOTOS ISGW2; #[Reconstructed PDG2011] +0.002058115 K0 e- anti-nu_e PHOTOS ISGW2; #[Reconstructed PDG2011] +0.000762265 K*0 e- anti-nu_e PHOTOS ISGW2; #[Reconstructed PDG2011] +0.018309605 phi mu- anti-nu_mu PHOTOS ISGW2; #[Reconstructed PDG2011] +0.022845082 eta mu- anti-nu_mu PHOTOS ISGW2; #[Reconstructed PDG2011] +0.008186726 eta' mu- anti-nu_mu PHOTOS ISGW2; #[Reconstructed PDG2011] +0.002058115 K0 mu- anti-nu_mu PHOTOS ISGW2; #[Reconstructed PDG2011] +0.000762265 K*0 mu- anti-nu_mu PHOTOS ISGW2; #[Reconstructed PDG2011] +0.005800000 mu- anti-nu_mu PHOTOS SLN; #[Reconstructed PDG2011] +0.031100000 tau- anti-nu_tau SLN; #[Reconstructed PDG2011] +0.045000000 phi pi- SVS; #[Reconstructed PDG2011] +0.015600000 eta pi- PHSP; #[Reconstructed PDG2011] +0.038000000 eta' pi- PHSP; #[Reconstructed PDG2011] +0.002300000 omega pi- SVS; #[Reconstructed PDG2011] +0.000304906 rho- pi0 SVS; #[Reconstructed PDG2011] +0.000076226 pi- pi0 PHSP; #[Reconstructed PDG2011] +0.000196200 rho0 pi- SVS; # PDG 2014 +0.007852048 f_0 pi- PHSP; # PDG 2014 +0.001753646 f_2 pi- PHSP; # PDG 2014 +0.004754077 f'_0 pi- PHSP; # PDG 2014 +0.005768112 f_0(1500) pi- PHSP; # PDG 2014 +#0.000735750 rho(2S)0 pi- PHSP; # PDG 2014 +0.000000000 pi- pi+ pi- PHSP; # PDG 2014 (filled by exclusives) +0.084000000 phi rho- SVV_HELAMP 1.0 0.0 1.0 0.0 1.0 0.0; #[Reconstructed PDG2011] +0.089000000 rho- eta SVS; #[Reconstructed PDG2011] +0.124213286 rho- eta' SVS; # Decrease compared to PDG 2014 to preserve unitarity after +0.006500000 pi- pi0 pi0 PHSP; #[Reconstructed PDG2011] +0.007622650 phi pi- pi0 PHSP; #[Reconstructed PDG2011] +0.011433975 eta pi- pi0 PHSP; #[Reconstructed PDG2011] +0.011433975 eta' pi- pi0 PHSP; #[Reconstructed PDG2011] +0.012100000 phi pi- pi- pi+ PHSP; #[Reconstructed PDG2011] +0.003811325 phi pi- pi0 pi0 PHSP; #[Reconstructed PDG2011] +0.003811325 eta pi- pi- pi+ PHSP; #[Reconstructed PDG2011] +0.003811325 eta pi- pi0 pi0 PHSP; #[Reconstructed PDG2011] +# +0.014900000 K_S0 K- PHSP; #[Reconstructed PDG2011] +0.011472088 K_L0 K- PHSP; #[Reconstructed PDG2011] +0.000000000 K0 K- PHSP; #[Reconstructed PDG2011] +# +0.030490600 K*0 K- SVS; #[Reconstructed PDG2011] +0.054000000 K*- K0 SVS; #[Reconstructed PDG2011] +0.072000000 K*0 K*- SVV_HELAMP 1.0 0.0 1.0 0.0 1.0 0.0; #[Reconstructed PDG2011] +0.002286795 K0 K- pi0 PHSP; #[Reconstructed PDG2011] +0.000914718 K*0 K- pi0 PHSP; #[Reconstructed PDG2011] +0.000914718 K*- K0 pi0 PHSP; #[Reconstructed PDG2011] +0.003049060 K*0 K*- pi0 PHSP; #[Reconstructed PDG2011] +0.003811325 K+ K- pi- PHSP; #[Reconstructed PDG2011] +# +0.009600000 K_S0 K- pi+ pi- PHSP; #[Reconstructed PDG2011] +0.007470197 K_L0 K- pi+ pi- PHSP; #[Reconstructed PDG2011] +0.000000000 K0 K- pi+ pi- PHSP; #[Reconstructed PDG2011] +# +0.000762265 K0 K- pi0 pi0 PHSP; #[Reconstructed PDG2011] +0.000000000 K- K+ pi- pi+ pi- PHSP; #[Reconstructed PDG2011] +0.000152453 phi K- SVS; #[Reconstructed PDG2011] +0.001390000 eta K- PHSP; #[Reconstructed PDG2011] +0.001600000 eta' K- PHSP; #[Reconstructed PDG2011] +0.000152453 eta K- pi0 PHSP; #[Reconstructed PDG2011] +0.000152453 eta K- pi+ pi- PHSP; #[Reconstructed PDG2011] +0.000152453 eta' K- pi0 PHSP; #[Reconstructed PDG2011] +0.000152453 eta' K- pi+ pi- PHSP; #[Reconstructed PDG2011] +0.000490000 K- K- K+ PHSP; #[Reconstructed PDG2011] +# +0.001200000 K_S0 pi- PHSP; #[Reconstructed PDG2011] +0.000968077 K_L0 pi- PHSP; #[Reconstructed PDG2011] +0.000000000 anti-K0 pi- PHSP; #[Reconstructed PDG2011] +# +0.001143397 rho- anti-K0 SVS; #[Reconstructed PDG2011] +0.002700000 rho0 K- SVS; #[Reconstructed PDG2011] +0.010000000 anti-K0 pi- pi0 PHSP; #[Reconstructed PDG2011] +0.001905662 a_1- anti-K0 SVS; #[Reconstructed PDG2011] +0.006021893 anti-K*0 pi- SVS; #[Reconstructed PDG2011] +0.003811325 anti-K*0 rho- SVV_HELAMP 1.0 0.0 1.0 0.0 1.0 0.0; #[Reconstructed PDG2011] +0.003811325 anti-K*0 pi- pi0 PHSP; #[Reconstructed PDG2011] +# +# March 2009 New Modes +0.000820000 K- pi0 PHSP; #[Reconstructed PDG2011] +0.004200000 K- pi+ pi- PHSP; #[Reconstructed PDG2011] +0.008000000 pi+ pi+ pi- pi- pi- PHSP; #[Reconstructed PDG2011] +0.049000000 pi+ pi+ pi- pi- pi- pi0 PHSP; #[Reconstructed PDG2011] +0.001300000 anti-p- n0 PHSP; #[Reconstructed PDG2011] +# +0.000840000 K_S0 K_S0 pi+ pi- pi- PHSP; #[Reconstructed PDG2011] +0.000686038 K_L0 K_L0 pi+ pi- pi- PHSP; #[Reconstructed PDG2011] +0.000000000 anti-K0 K0 pi+ pi- pi- PHSP; #[Reconstructed PDG2011] +# +0.002900000 K_S0 pi+ pi- pi- PHSP; #[Reconstructed PDG2011] +0.002424003 K_L0 pi+ pi- pi- PHSP; #[Reconstructed PDG2011] +0.000000000 K0 pi+ pi- pi- PHSP; #[Reconstructed PDG2011] +# Doubly Cabibbo suppressed +0.000129000 K- K- pi+ PHSP; #[Reconstructed PDG2011] +0.003700000 anti-K0 e- anti-nu_e PHSP; #[New mode added] #[Reconstructed PDG2011] +0.001800000 anti-K*0 e- anti-nu_e PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000000000 K- K+ pi- pi0 PHSP; #[New mode added] #[Reconstructed PDG2011] +0.028000000 omega pi- pi0 PHSP; #[New mode added] #[Reconstructed PDG2011] +0.016000000 omega pi- pi- pi+ PHSP; #[New mode added] #[Reconstructed PDG2011] +Enddecay +# +# D** +# +Decay D_0*+ +0.3333 D+ pi0 PHSP; +0.6667 D0 pi+ PHSP; +Enddecay +Decay D_0*- +0.3333 D- pi0 PHSP; +0.6667 anti-D0 pi- PHSP; +Enddecay +Decay D_0*0 +0.3333 D0 pi0 PHSP; +0.6667 D+ pi- PHSP; +Enddecay +Decay anti-D_0*0 +0.3333 anti-D0 pi0 PHSP; +0.6667 D- pi+ PHSP; +Enddecay +# + +SetLineshapePW D_1+ D*+ pi0 2 +SetLineshapePW D_1+ D*0 pi+ 2 +SetLineshapePW D_1- D*- pi0 2 +SetLineshapePW D_1- anti-D*0 pi- 2 +SetLineshapePW D_10 D*0 pi0 2 +SetLineshapePW D_10 D*+ pi- 2 +SetLineshapePW anti-D_10 anti-D*0 pi0 2 +SetLineshapePW anti-D_10 D*- pi+ 2 + +Decay D_1+ +0.3333 D*+ pi0 VVS_PWAVE 0.0 0.0 0.0 0.0 1.0 0.0; +0.6667 D*0 pi+ VVS_PWAVE 0.0 0.0 0.0 0.0 1.0 0.0; +Enddecay +Decay D_1- +0.3333 D*- pi0 VVS_PWAVE 0.0 0.0 0.0 0.0 1.0 0.0; +0.6667 anti-D*0 pi- VVS_PWAVE 0.0 0.0 0.0 0.0 1.0 0.0; +Enddecay +Decay D_10 +0.3333 D*0 pi0 VVS_PWAVE 0.0 0.0 0.0 0.0 1.0 0.0; +0.6667 D*+ pi- VVS_PWAVE 0.0 0.0 0.0 0.0 1.0 0.0; +Enddecay +Decay anti-D_10 +0.3333 anti-D*0 pi0 VVS_PWAVE 0.0 0.0 0.0 0.0 1.0 0.0; +0.6667 D*- pi+ VVS_PWAVE 0.0 0.0 0.0 0.0 1.0 0.0; +Enddecay +# +Decay D'_1+ +0.3333 D*+ pi0 VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.6667 D*0 pi+ VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +Enddecay +Decay D'_1- +0.3333 D*- pi0 VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.6667 anti-D*0 pi- VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +Enddecay +Decay D'_10 +0.6667 D*+ pi- VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.3333 D*0 pi0 VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +Enddecay +Decay anti-D'_10 +0.3333 anti-D*0 pi0 VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.6667 D*- pi+ VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +Enddecay +# + +SetLineshapePW D_2*+ D*+ pi0 2 +SetLineshapePW D_2*+ D*0 pi+ 2 +SetLineshapePW D_2*- D*- pi0 2 +SetLineshapePW D_2*- anti-D*0 pi- 2 +SetLineshapePW D_2*0 D*0 pi0 2 +SetLineshapePW D_2*0 D*+ pi- 2 +SetLineshapePW anti-D_2*0 anti-D*0 pi0 2 +SetLineshapePW anti-D_2*0 D*- pi+ 2 + +Decay D_2*+ +0.1030 D*+ pi0 TVS_PWAVE 0.0 0.0 1.0 0.0 0.0 0.0; +0.2090 D*0 pi+ TVS_PWAVE 0.0 0.0 1.0 0.0 0.0 0.0; +0.2290 D+ pi0 TSS; +0.4590 D0 pi+ TSS; +Enddecay +Decay D_2*- +0.1030 D*- pi0 TVS_PWAVE 0.0 0.0 1.0 0.0 0.0 0.0; +0.2090 anti-D*0 pi- TVS_PWAVE 0.0 0.0 1.0 0.0 0.0 0.0; +0.2290 D- pi0 TSS; +0.4590 anti-D0 pi- TSS; +Enddecay +Decay D_2*0 +0.2090 D*+ pi- TVS_PWAVE 0.0 0.0 1.0 0.0 0.0 0.0; +0.1030 D*0 pi0 TVS_PWAVE 0.0 0.0 1.0 0.0 0.0 0.0; +0.2290 D0 pi0 TSS; +0.4590 D+ pi- TSS; +Enddecay +Decay anti-D_2*0 +0.1030 anti-D*0 pi0 TVS_PWAVE 0.0 0.0 1.0 0.0 0.0 0.0; +0.2090 D*- pi+ TVS_PWAVE 0.0 0.0 1.0 0.0 0.0 0.0; +0.2290 anti-D0 pi0 TSS; +0.4590 D- pi+ TSS; +Enddecay +# +# D_s ** +# +Decay D_s0*+ +# 0.5000 D+ K0 PHSP; +# 0.5000 D0 K+ PHSP; +1.000 D_s+ pi0 PHSP; +Enddecay +Decay D_s0*- +# 0.5000 D- anti-K0 PHSP; +# 0.5000 anti-D0 K- PHSP; +1.000 D_s- pi0 PHSP; +Enddecay +Decay D'_s1+ +0.5000 D*+ K0 VVS_PWAVE 0.0 0.0 0.0 0.0 1.0 0.0; +0.5000 D*0 K+ VVS_PWAVE 0.0 0.0 0.0 0.0 1.0 0.0; +0.0000 gamma D_s*+ PHSP; +0.0000 gamma D_s+ PHSP; +Enddecay +Decay D'_s1- +0.5000 D*- anti-K0 VVS_PWAVE 0.0 0.0 0.0 0.0 1.0 0.0; +0.5000 anti-D*0 K- VVS_PWAVE 0.0 0.0 0.0 0.0 1.0 0.0; +0.0000 gamma D_s*- PHSP; +0.0000 gamma D_s- PHSP; +Enddecay +Decay D_s1+ +0.80000 D_s*+ pi0 PARTWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.20000 D_s+ gamma VSP_PWAVE; +Enddecay +Decay D_s1- +0.80000 D_s*- pi0 PARTWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.20000 D_s- gamma VSP_PWAVE; +Enddecay +Decay D_s2*+ +0.0500 D*+ K0 TVS_PWAVE 0.0 0.0 1.0 0.0 0.0 0.0; +0.0500 D*0 K+ TVS_PWAVE 0.0 0.0 1.0 0.0 0.0 0.0; +0.4300 D+ K0 TSS; +0.4700 D0 K+ TSS; +Enddecay +Decay D_s2*- +0.0500 D*- anti-K0 TVS_PWAVE 0.0 0.0 1.0 0.0 0.0 0.0; +0.0500 anti-D*0 K- TVS_PWAVE 0.0 0.0 1.0 0.0 0.0 0.0; +0.4300 D- anti-K0 TSS; +0.4700 anti-D0 K- TSS; +Enddecay +# +# +# Charm Mesons: Radially Excited States +# +Decay D(2S)0 +0.6667 D*+ pi- SVS; +0.3333 D*0 pi0 SVS; +Enddecay +Decay anti-D(2S)0 +0.3333 anti-D*0 pi0 SVS; +0.6667 D*- pi+ SVS; +Enddecay +Decay D(2S)+ +0.3333 D*+ pi0 SVS; +0.6667 D*0 pi+ SVS; +Enddecay +Decay D(2S)- +0.3333 D*- pi0 SVS; +0.6667 anti-D*0 pi- SVS; +Enddecay +Decay D*(2S)0 +0.3333 D*+ pi- VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.1667 D*0 pi0 VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.1667 D0 pi0 VSS; +0.3333 D+ pi- VSS; +Enddecay +Decay anti-D*(2S)0 +0.1667 anti-D*0 pi0 VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.3333 D*- pi+ VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.1667 anti-D0 pi0 VSS; +0.3333 D- pi+ VSS; +Enddecay +Decay D*(2S)+ +0.1667 D*+ pi0 VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.3333 D*0 pi+ VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.1667 D+ pi0 VSS; +0.3333 D0 pi+ VSS; +Enddecay +Decay D*(2S)- +0.1667 D*- pi0 VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.3333 anti-D*0 pi- VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.1667 D- pi0 VSS; +0.3333 anti-D0 pi- VSS; +Enddecay +# +# +# Strange Mesons updated to PDG 2008 +# +# +Decay K_S0 +0.691086452 pi+ pi- PHSP; #[Reconstructed PDG2011] +0.305986452 pi0 pi0 PHSP; #[Reconstructed PDG2011] +0.000000201 pi+ pi- pi0 PHSP; #[New mode added] #[Reconstructed PDG2011] +0.001722185 pi+ pi- gamma PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000042831 pi+ pi- e+ e- PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000000025 pi0 gamma gamma PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000002399 gamma gamma PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000344328 pi+ e- anti-nu_e PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000344328 pi- e+ nu_e PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000235400 pi+ mu- anti-nu_mu PHSP; #[New mode added] #TP manually added 9/1/2011 according to pdg +0.000235400 pi- mu+ nu_mu PHSP; #[New mode added] #TP manually added 9/1/2011 according to pdg +Enddecay +# +# K_L0 is decayed in GEANT +# +Decay K0 +0.500 K_L0 PHSP; +0.500 K_S0 PHSP; +Enddecay +Decay anti-K0 +0.500 K_L0 PHSP; +0.500 K_S0 PHSP; +Enddecay +# +Decay K*0 +0.6657 K+ pi- VSS; +0.3323 K0 pi0 VSS; +0.0020 K0 gamma VSP_PWAVE; +Enddecay +# +Decay anti-K*0 +0.6657 K- pi+ VSS; +0.3323 anti-K0 pi0 VSS; +0.0020 anti-K0 gamma VSP_PWAVE; +Enddecay +# +Decay K*+ +0.6657 K0 pi+ VSS; +0.3323 K+ pi0 VSS; +0.0020 K+ gamma VSP_PWAVE; +Enddecay +# +Decay K*- +0.6657 anti-K0 pi- VSS; +0.3323 K- pi0 VSS; +0.0020 K- gamma VSP_PWAVE; +Enddecay +# +# The decays below are for particle aliases and used when +# generating CP violating decays that depends on the decay +# of the neutral K* +# +Decay K*L +1.0000 pi0 K_L0 VSS; +Enddecay +Decay K*S +1.0000 pi0 K_S0 VSS; +Enddecay +Decay K*BL +1.0000 pi0 K_L0 VSS; +Enddecay +Decay K*BS +1.0000 pi0 K_S0 VSS; +Enddecay +Decay K*0T +1.0000 pi- K+ VSS; +Enddecay +Decay anti-K*0T +1.0000 pi+ K- VSS; +Enddecay +# +# +Decay K_0*+ +0.6667 K0 pi+ PHSP; +0.3333 K+ pi0 PHSP; +Enddecay +# +Decay K_0*- +0.6667 anti-K0 pi- PHSP; +0.3333 K- pi0 PHSP; +Enddecay +# +Decay K_0*0 +0.6667 K+ pi- PHSP; +0.3333 K0 pi0 PHSP; +Enddecay +# +Decay K_0*0N +1.0000 K0 pi0 PHSP; +Enddecay +# +Decay anti-K_0*0 +0.6667 K- pi+ PHSP; +0.3333 anti-K0 pi0 PHSP; +Enddecay +# +Decay anti-K_0*0N +1.0000 anti-K0 pi0 PHSP; +Enddecay +# +Decay K_10 +0.2800 rho- K+ VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.1400 rho0 K0 VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.1067 K*+ pi- VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.0533 K*0 pi0 VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +#To large masses can cause infinit loops +0.1100 omega K0 VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.1444 K0 pi+ pi- PHSP; +0.1244 K+ pi- pi0 PHSP; +0.0412 K0 pi0 pi0 PHSP; +Enddecay +# +Decay anti-K_10 +0.2800 rho+ K- VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.1400 rho0 anti-K0 VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.1067 K*- pi+ VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.0533 anti-K*0 pi0 VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +#To large masses can cause infinit loops +0.1100 omega anti-K0 VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.1444 anti-K0 pi+ pi- PHSP; +0.1244 K- pi+ pi0 PHSP; +0.0412 anti-K0 pi0 pi0 PHSP; +Enddecay +# +Decay K_1+ +0.2800 rho+ K0 VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.1400 rho0 K+ VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.1067 K*0 pi+ VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.0533 K*+ pi0 VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +#To large masses can cause infinit loops +0.1100 omega K+ VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.1444 K+ pi+ pi- PHSP; +0.1244 K0 pi+ pi0 PHSP; +0.0412 K+ pi0 pi0 PHSP; +Enddecay +# +Decay K_1- +0.2800 rho- anti-K0 VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.1400 rho0 K- VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.1067 anti-K*0 pi- VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.0533 K*- pi0 VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +#To large masses can cause infinit loops +0.1100 omega K- VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.1444 K- pi+ pi- PHSP; +0.1244 anti-K0 pi- pi0 PHSP; +0.0412 K- pi0 pi0 PHSP; +Enddecay +# +Decay K'_1+ +0.6300 K*0 pi+ VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.3100 K*+ pi0 VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.0200 rho+ K0 VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.0100 rho0 K+ VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.0100 omega K+ VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.0133 K+ pi+ pi- PHSP; +0.0067 K+ pi0 pi0 PHSP; +Enddecay +# +Decay K'_1- +0.6300 anti-K*0 pi- VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.3100 K*- pi0 VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.0200 rho- anti-K0 VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.0100 rho0 K- VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.0100 omega K- VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.0133 K- pi+ pi- PHSP; +0.0067 K- pi0 pi0 PHSP; +Enddecay +# +Decay K'_10 +0.6300 K*+ pi- VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.3100 K*0 pi0 VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.0200 rho- K+ VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.0100 rho0 K0 VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.0100 omega K0 VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.0133 K0 pi+ pi- PHSP; +0.0067 K0 pi0 pi0 PHSP; +Enddecay +# +Decay anti-K'_10 +0.6300 K*- pi+ VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.3100 anti-K*0 pi0 VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.0200 rho+ K- VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.0100 rho0 anti-K0 VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.0100 omega anti-K0 VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.0133 anti-K0 pi+ pi- PHSP; +0.0067 anti-K0 pi0 pi0 PHSP; +Enddecay +# +Decay K_2*+ +0.3340 K0 pi+ TSS; +0.1670 K+ pi0 TSS; +0.1645 K*0 pi+ TVS_PWAVE 0.0 0.0 1.0 0.0 0.0 0.0; +0.0835 K*+ pi0 TVS_PWAVE 0.0 0.0 1.0 0.0 0.0 0.0; +0.0450 K*0 pi+ pi0 PHSP; +0.0450 K*+ pi+ pi- PHSP; +0.0450 K*+ pi0 pi0 PHSP; +0.0580 rho+ K0 TVS_PWAVE 0.0 0.0 1.0 0.0 0.0 0.0; +0.0290 rho0 K+ TVS_PWAVE 0.0 0.0 1.0 0.0 0.0 0.0; +0.0290 omega K+ TVS_PWAVE 0.0 0.0 1.0 0.0 0.0 0.0; +Enddecay +# +Decay K_2*- +0.3340 anti-K0 pi- TSS; +0.1670 K- pi0 TSS; +0.1645 anti-K*0 pi- TVS_PWAVE 0.0 0.0 1.0 0.0 0.0 0.0; +0.0835 K*- pi0 TVS_PWAVE 0.0 0.0 1.0 0.0 0.0 0.0; +0.0450 anti-K*0 pi- pi0 PHSP; +0.0450 K*- pi+ pi- PHSP; +0.0450 K*- pi0 pi0 PHSP; +0.0580 rho- K0 TVS_PWAVE 0.0 0.0 1.0 0.0 0.0 0.0; +0.0290 rho0 K- TVS_PWAVE 0.0 0.0 1.0 0.0 0.0 0.0; +0.0290 omega K- TVS_PWAVE 0.0 0.0 1.0 0.0 0.0 0.0; +Enddecay +# +Decay K_2*0 +0.3340 K+ pi- TSS; +0.1670 K0 pi0 TSS; +0.1645 K*+ pi- TVS_PWAVE 0.0 0.0 1.0 0.0 0.0 0.0; +0.0835 K*0 pi0 TVS_PWAVE 0.0 0.0 1.0 0.0 0.0 0.0; +0.0450 K*0 pi+ pi- PHSP; +0.0450 K*0 pi0 pi0 PHSP; +0.0450 K*+ pi- pi0 PHSP; +0.0580 rho- K+ TVS_PWAVE 0.0 0.0 1.0 0.0 0.0 0.0; +0.0290 rho0 K0 TVS_PWAVE 0.0 0.0 1.0 0.0 0.0 0.0; +0.0290 omega K0 TVS_PWAVE 0.0 0.0 1.0 0.0 0.0 0.0; +Enddecay +# +Decay anti-K_2*0 +0.3340 K- pi+ TSS; +0.1670 anti-K0 pi0 TSS; +0.1645 K*- pi+ TVS_PWAVE 0.0 0.0 1.0 0.0 0.0 0.0; +0.0835 anti-K*0 pi0 TVS_PWAVE 0.0 0.0 1.0 0.0 0.0 0.0; +0.0450 anti-K*0 pi+ pi- PHSP; +0.0450 anti-K*0 pi0 pi0 PHSP; +0.0450 K*- pi+ pi0 PHSP; +0.0580 rho+ K- TVS_PWAVE 0.0 0.0 1.0 0.0 0.0 0.0; +0.0290 rho0 anti-K0 TVS_PWAVE 0.0 0.0 1.0 0.0 0.0 0.0; +0.0290 omega anti-K0 TVS_PWAVE 0.0 0.0 1.0 0.0 0.0 0.0; +Enddecay + + + + +# Decay K*(1410) +Decay K'*0 +0.0467 K+ pi- VSS; +0.0233 K0 pi0 VSS; +0.5760 K*+ pi- VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.2880 K*0 pi0 VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.0440 rho- K+ VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.0220 rho0 K0 VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +Enddecay +# +Decay anti-K'*0 +0.0467 K- pi+ VSS; +0.0233 anti-K0 pi0 VSS; +0.5760 K*- pi+ VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.2880 anti-K*0 pi0 VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.0440 rho+ K- VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.0220 rho0 anti-K0 VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +Enddecay +# +Decay K'*+ +0.0467 K0 pi+ VSS; +0.0233 K+ pi0 VSS; +0.5760 K*0 pi+ VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.2880 K*+ pi0 VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.0440 rho+ K0 VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.0220 rho0 K+ VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +Enddecay +# +Decay K'*- +0.0467 anti-K0 pi- VSS; +0.0233 K- pi0 VSS; +0.5760 anti-K*0 pi- VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.2880 K*- pi0 VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.0440 rho- K0 VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.0220 rho0 K- VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +Enddecay +# + + +# Decay K*(1680) +Decay K''*0 +0.2580 K+ pi- VSS; +0.1290 K0 pi0 VSS; +0.2093 K*+ pi- VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.1047 K*0 pi0 VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.1993 rho- K+ VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.0997 rho0 K0 VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +Enddecay +# +Decay anti-K''*0 +0.2580 K- pi+ VSS; +0.1290 anti-K0 pi0 VSS; +0.2093 K*- pi+ VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.1047 anti-K*0 pi0 VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.1993 rho+ K- VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.0997 rho0 anti-K0 VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +Enddecay +# +Decay K''*+ +0.2580 K0 pi+ VSS; +0.1290 K+ pi0 VSS; +0.2093 K*0 pi+ VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.1047 K*+ pi0 VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.1993 rho+ K0 VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.0997 rho0 K+ VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +Enddecay +# +Decay K''*- +0.2580 anti-K0 pi- VSS; +0.1290 K- pi0 VSS; +0.2093 anti-K*0 pi- VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.1047 K*- pi0 VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.1993 rho- K0 VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.0997 rho0 K- VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +Enddecay + +Decay Xsd +1.00000 d anti-s PYTHIA 42; +Enddecay + +Decay anti-Xsd +1.00000 anti-d s PYTHIA 42; +Enddecay + +Decay Xsu +1.00000 u anti-s PYTHIA 42; +Enddecay + +Decay anti-Xsu +1.00000 anti-u s PYTHIA 42; +Enddecay + +# Xss decays can be uncommented when Jst74/lucomp.F recognizes Xss +Decay Xss +1.00000 s anti-s PYTHIA 42; +Enddecay + +Decay anti-Xss +1.00000 anti-s s PYTHIA 42; +Enddecay + +# +# Light Mesons Updated to PDG 2008 +# +Decay pi0 +0.988228297 gamma gamma PHSP; #[Reconstructed PDG2011] +0.011738247 e+ e- gamma PI0_DALITZ; #[Reconstructed PDG2011] +0.000033392 e+ e+ e- e- PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000000065 e+ e- PHSP; #[New mode added] #[Reconstructed PDG2011] +Enddecay +# +Decay eta +0.393100000 gamma gamma PHSP; #[Reconstructed PDG2011] +0.325700000 pi0 pi0 pi0 PHSP; #[Reconstructed PDG2011] +0.227400000 pi- pi+ pi0 ETA_DALITZ; #[Reconstructed PDG2011] +0.046000000 gamma pi- pi+ PHSP; #[Reconstructed PDG2011] +0.007000000 gamma e+ e- PHSP; #[Reconstructed PDG2011] +0.000310000 gamma mu+ mu- PHSP; #[Reconstructed PDG2011] +0.000270000 gamma gamma pi0 PHSP; #[Reconstructed PDG2011] +0.000214200 pi+ pi- e+ e- PHSP; #[Reconstructed PDG2011] +0.000005800 mu+ mu- PHSP; #[New mode added] #[Reconstructed PDG2011] +Enddecay +# +Decay rho0 +1.000 pi+ pi- VSS; +Enddecay +Decay rho+ +1.000 pi+ pi0 VSS; +Enddecay +Decay rho- +1.000 pi- pi0 VSS; +Enddecay +# +Decay omega +0.892000000 pi- pi+ pi0 OMEGA_DALITZ; #[Reconstructed PDG2011] +0.082800000 pi0 gamma VSP_PWAVE; #[Reconstructed PDG2011] +0.015300000 pi- pi+ VSS; #[Reconstructed PDG2011] +0.000460000 eta gamma VSP_PWAVE; #[Reconstructed PDG2011] +0.000770000 pi0 e+ e- PHOTOS PHSP; #[Reconstructed PDG2011] +0.000130000 pi0 mu+ mu- PHOTOS PHSP; #[Reconstructed PDG2011] +0.00150 pi+ pi- gamma PHSP; +0.000066000 pi0 pi0 gamma PHSP; #[Reconstructed PDG2011] +0.00050 pi+ pi- pi+ pi- PHSP; +0.000072800 e+ e- PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000090000 mu+ mu- PHSP; #[New mode added] #[Reconstructed PDG2011] +Enddecay +# +Decay eta' +0.432000000 pi+ pi- eta PHSP; #[Reconstructed PDG2011] +0.217000000 pi0 pi0 eta PHSP; #[Reconstructed PDG2011] +0.293511000 rho0 gamma SVP_HELAMP 1.0 0.0 1.0 0.0; #[Reconstructed PDG2011] +0.027500000 omega gamma SVP_HELAMP 1.0 0.0 1.0 0.0; #[Reconstructed PDG2011] +0.022200000 gamma gamma PHSP; #[Reconstructed PDG2011] +0.001680000 pi0 pi0 pi0 PHSP; #[Reconstructed PDG2011] +0.000109000 gamma mu- mu+ PHOTOS PHSP; #[Reconstructed PDG2011] +0.003600000 pi+ pi- pi0 PHSP; #[New mode added] #[Reconstructed PDG2011] +0.002400000 pi+ pi- e+ e- PHSP; #[New mode added] #[Reconstructed PDG2011] +Enddecay +# +Decay phi +0.489000000 K+ K- VSS; #[Reconstructed PDG2011] +Enddecay +# +Decay a_1+ +0.4920 rho0 pi+ VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.5080 rho+ pi0 VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +Enddecay +Decay a_10 +0.5000 rho- pi+ VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.5000 rho+ pi- VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +Enddecay +Decay a_1- +0.4920 rho0 pi- VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.5080 rho- pi0 VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +Enddecay +# +Decay a_2+ +0.3500 rho0 pi+ TVS_PWAVE 0.0 0.0 1.0 0.0 0.0 0.0; +0.3500 rho+ pi0 TVS_PWAVE 0.0 0.0 1.0 0.0 0.0 0.0; +0.1430 eta pi+ TSS; +0.1000 omega pi+ pi0 PHSP; +0.0490 anti-K0 K+ TSS; +0.0027 pi+ gamma PHSP; +0.0053 eta' pi+ TSS; +Enddecay +# +Decay a_2- +0.3500 rho0 pi- TVS_PWAVE 0.0 0.0 1.0 0.0 0.0 0.0; +0.3500 rho- pi0 TVS_PWAVE 0.0 0.0 1.0 0.0 0.0 0.0; +0.1430 eta pi- TSS; +0.1000 omega pi- pi0 PHSP; +0.0490 anti-K0 K- TSS; +0.0027 pi- gamma PHSP; +0.0053 eta' pi- TSS; +Enddecay +# +Decay a_20 +0.3500 rho+ pi- TVS_PWAVE 0.0 0.0 1.0 0.0 0.0 0.0; +0.3500 rho- pi+ TVS_PWAVE 0.0 0.0 1.0 0.0 0.0 0.0; +0.1430 eta pi0 TSS; +0.1000 omega pi+ pi- PHSP; +0.0245 K+ K- TSS; +0.01225 K_L0 K_L0 TSS; +0.01225 K_S0 K_S0 TSS; +0.0027 pi0 gamma PHSP; +0.0053 eta' pi0 TSS; +Enddecay +# +Decay b_1+ +0.9984 omega pi+ VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.0016 pi+ gamma VSP_PWAVE; +Enddecay +Decay b_1- +0.9984 omega pi- VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.0016 pi- gamma VSP_PWAVE; +Enddecay +Decay b_10 +0.9984 omega pi0 VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.0016 pi0 gamma VSP_PWAVE; +Enddecay +# +Decay a_00 +0.9000 eta pi0 PHSP; +0.0500 K+ K- PHSP; +0.0250 K_S0 K_S0 PHSP; +0.0250 K_L0 K_L0 PHSP; +Enddecay +Decay a_0+ +0.9000 eta pi+ PHSP; +0.1000 anti-K0 K+ PHSP; +Enddecay +Decay a_0- +0.9000 eta pi- PHSP; +0.1000 K0 K- PHSP; +Enddecay +# +Decay f_0 +0.6667 pi+ pi- PHSP; +0.3333 pi0 pi0 PHSP; +#0.1100 K+ K- PHSP; +#0.0550 K_S0 K_S0 PHSP; +#0.0550 K_L0 K_L0 PHSP; +Enddecay +# +# +# MK: Based on PDG 2014 without any attempt to really include everything. One +# thing, which is missing is KK mode, but data are scattered quite lot and +# I would also need to find place where to make space for it. For needs I +# have at this moment, this should be sufficient +#Decay f_0(1370) +#0.17333 pi+ pi- PHSP; +#0.08667 pi0 pi0 PHSP; +#0.26000 pi+ pi+ pi- pi- PHSP; +#0.32000 pi+ pi- pi0 pi0 PHSP; +#0.10667 rho+ rho- PHSP; +#0.05333 rho0 rho0 PHSP; +#Enddecay +Decay f'_0 +0.5200 pi+ pi- PHSP; +0.2600 pi0 pi0 PHSP; +0.0750 pi+ pi+ pi- pi- PHSP; +0.0750 pi+ pi- pi0 pi0 PHSP; +0.0350 K+ K- PHSP; +0.0175 K_S0 K_S0 PHSP; +0.0175 K_L0 K_L0 PHSP; +Enddecay +# +Decay f_1 +0.110000000 rho0 pi+ pi- PHSP; #[Reconstructed PDG2011] +0.043364159 rho0 pi0 pi0 PHSP; #[Reconstructed PDG2011] +0.043442860 rho+ pi- pi0 PHSP; #[Reconstructed PDG2011] +0.043442860 rho- pi+ pi0 PHSP; #[Reconstructed PDG2011] +0.094440999 a_00 pi0 VSS; #[Reconstructed PDG2011] +0.094440999 a_0+ pi- VSS; #[Reconstructed PDG2011] +0.094440999 a_0- pi+ VSS; #[Reconstructed PDG2011] +0.086570916 eta pi+ pi- PHSP; #[Reconstructed PDG2011] +0.043285458 eta pi0 pi0 PHSP; #[Reconstructed PDG2011] +0.020226114 K+ K- pi0 PHSP; #[Reconstructed PDG2011] +0.010152407 K0 anti-K0 pi0 PHSP; #[Reconstructed PDG2011] +0.020226114 anti-K0 K+ pi- PHSP; #[Reconstructed PDG2011] +0.020226114 K0 K- pi+ PHSP; #[Reconstructed PDG2011] +0.055000000 gamma rho0 PHSP; #[Reconstructed PDG2011] +0.220000000 pi0 pi0 pi+ pi- PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000000000 pi+ pi+ pi- pi- PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000740000 phi gamma PHSP; #[New mode added] #[Reconstructed PDG2011] +Enddecay +# +Decay f'_1 +0.2500 K+ K- pi0 PHSP; +0.2500 K0 anti-K0 pi0 PHSP; +0.2500 anti-K0 K+ pi- PHSP; +0.2500 K0 K- pi+ PHSP; +Enddecay +# +Decay f_2 +0.5650 pi+ pi- TSS; +0.2820 pi0 pi0 TSS; +0.028000000 pi+ pi- pi+ pi- PHSP; #[Reconstructed PDG2011] +0.071000000 pi+ pi- pi0 pi0 PHSP; #[Reconstructed PDG2011] +0.003000000 pi0 pi0 pi0 pi0 PHSP; #[Reconstructed PDG2011] +0.0230 K+ K- TSS; +0.0115 K_S0 K_S0 TSS; +0.0115 K_L0 K_L0 TSS; +0.004000000 eta eta TSS; #[Reconstructed PDG2011] +0.000016400 gamma gamma PHSP; #[New mode added] #[Reconstructed PDG2011] +Enddecay +# +Decay f_0(1500) +0.019000000 eta eta' PHSP; #[Reconstructed PDG2011] +0.051000000 eta eta PHSP; #[Reconstructed PDG2011] +0.1410 pi0 pi0 pi0 pi0 PHSP; +0.3540 pi+ pi- pi+ pi- PHSP; +0.2330 pi+ pi- PHSP; +0.1160 pi0 pi0 PHSP; +0.0430 K+ K- PHSP; +0.0215 K_S0 K_S0 PHSP; +0.0215 K_L0 K_L0 PHSP; +Enddecay +# +Decay f'_2 +0.443904021 K+ K- TSS; #[Reconstructed PDG2011] +0.221952010 K_S0 K_S0 TSS; #[Reconstructed PDG2011] +0.221952010 K_L0 K_L0 TSS; #[Reconstructed PDG2011] +0.104000000 eta eta TSS; #[Reconstructed PDG2011] +0.005493862 pi+ pi- TSS; #[Reconstructed PDG2011] +0.002696987 pi0 pi0 TSS; #[Reconstructed PDG2011] +0.000001110 gamma gamma PHSP; #[New mode added] #[Reconstructed PDG2011] +Enddecay +# +Decay h_1 +0.3333 rho+ pi- VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.3333 rho- pi+ VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.3334 rho0 pi0 VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +Enddecay +# +Decay h'_1 +0.2500 K*+ K- VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.2500 K*- K+ VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.2500 anti-K*0 K0 VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +0.2500 K*0 anti-K0 VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +Enddecay +# +Decay rho(2S)+ +0.4000 pi+ pi0 PHSP; +0.4000 pi+ pi+ pi- pi0 PHSP; +0.2000 pi+ pi0 pi0 pi0 PHSP; +Enddecay +Decay rho(2S)- +0.4000 pi- pi0 PHSP; +0.4000 pi- pi+ pi- pi0 PHSP; +0.2000 pi- pi0 pi0 pi0 PHSP; +Enddecay +Decay rho(2S)0 +0.4000 pi+ pi- PHSP; +0.3500 pi+ pi- pi+ pi- PHSP; +0.1500 pi+ pi- pi0 pi0 PHSP; +0.1000 pi0 pi0 pi0 pi0 PHSP; +Enddecay +Decay phi(1680) +0.08 pi+ pi- PHSP; +0.05 pi+ pi+ pi- pi- PHSP; +0.10 pi0 pi+ pi- PHSP; +0.10 pi0 pi+ pi+ pi- pi- PHSP; +0.12 pi0 pi0 pi+ pi- PHSP; +0.04 pi0 pi+ pi+ pi+ pi- pi- pi- PHSP; +0.14 pi0 pi0 pi+ pi+ pi- pi- PHSP; +0.05 pi0 pi0 pi0 pi+ pi- PHSP; +0.06 pi0 pi0 pi0 pi+ pi+ pi- pi- PHSP; +0.03 pi0 pi0 pi0 pi+ pi+ pi+ pi- pi- pi- PHSP; +0.03 pi0 pi0 pi0 pi0 pi+ pi+ pi- pi- PHSP; +0.04 K+ K- pi+ pi- PHSP; +0.02 K0 K- pi+ PHSP; +0.02 anti-K0 K+ pi- PHSP; +0.02 K0 K- pi+ pi+ pi- PHSP; +0.02 anti-K0 K+ pi- pi- pi+ PHSP; +0.02 K0 K- pi0 pi+ PHSP; +0.02 anti-K0 K+ pi0 pi- PHSP; +0.02 eta pi0 pi+ pi- PHSP; +0.01 eta rho- pi0 pi+ PHSP; +0.01 eta rho+ pi0 pi- PHSP; +Enddecay +Decay rho(3S)0 +0.08 pi+ pi- PHSP; +0.05 pi+ pi+ pi- pi- PHSP; +0.10 pi0 pi+ pi- PHSP; +0.10 pi0 pi+ pi+ pi- pi- PHSP; +0.12 pi0 pi0 pi+ pi- PHSP; +0.04 pi0 pi+ pi+ pi+ pi- pi- pi- PHSP; +0.14 pi0 pi0 pi+ pi+ pi- pi- PHSP; +0.05 pi0 pi0 pi0 pi+ pi- PHSP; +0.06 pi0 pi0 pi0 pi+ pi+ pi- pi- PHSP; +0.03 pi0 pi0 pi0 pi+ pi+ pi+ pi- pi- pi- PHSP; +0.03 pi0 pi0 pi0 pi0 pi+ pi+ pi- pi- PHSP; +0.04 K+ K- pi+ pi- PHSP; +0.02 K0 K- pi+ PHSP; +0.02 anti-K0 K+ pi- PHSP; +0.02 K0 K- pi+ pi+ pi- PHSP; +0.02 anti-K0 K+ pi- pi- pi+ PHSP; +0.02 K0 K- pi0 pi+ PHSP; +0.02 anti-K0 K+ pi0 pi- PHSP; +0.02 eta pi0 pi+ pi- PHSP; +0.01 eta rho- pi0 pi+ PHSP; +0.01 eta rho+ pi0 pi- PHSP; +Enddecay +Decay omega(1650) +0.08 pi+ pi- PHSP; +0.05 pi+ pi+ pi- pi- PHSP; +0.10 pi0 pi+ pi- PHSP; +0.10 pi0 pi+ pi+ pi- pi- PHSP; +0.12 pi0 pi0 pi+ pi- PHSP; +0.04 pi0 pi+ pi+ pi+ pi- pi- pi- PHSP; +0.14 pi0 pi0 pi+ pi+ pi- pi- PHSP; +0.05 pi0 pi0 pi0 pi+ pi- PHSP; +0.06 pi0 pi0 pi0 pi+ pi+ pi- pi- PHSP; +0.03 pi0 pi0 pi0 pi+ pi+ pi+ pi- pi- pi- PHSP; +0.03 pi0 pi0 pi0 pi0 pi+ pi+ pi- pi- PHSP; +0.04 K+ K- pi+ pi- PHSP; +0.02 K0 K- pi+ PHSP; +0.02 anti-K0 K+ pi- PHSP; +0.02 K0 K- pi+ pi+ pi- PHSP; +0.02 anti-K0 K+ pi- pi- pi+ PHSP; +0.02 K0 K- pi0 pi+ PHSP; +0.02 anti-K0 K+ pi0 pi- PHSP; +0.02 eta pi0 pi+ pi- PHSP; +0.01 eta rho- pi0 pi+ PHSP; +0.01 eta rho+ pi0 pi- PHSP; +Enddecay +#Decay rho(1900) +#0.08 pi+ pi- PHSP; +#0.05 pi+ pi+ pi- pi- PHSP; +#0.10 pi0 pi+ pi- PHSP; +#0.10 pi0 pi+ pi+ pi- pi- PHSP; +#0.12 pi0 pi0 pi+ pi- PHSP; +#0.04 pi0 pi+ pi+ pi+ pi- pi- pi- PHSP; +#0.14 pi0 pi0 pi+ pi+ pi- pi- PHSP; +#0.05 pi0 pi0 pi0 pi+ pi- PHSP; +#0.06 pi0 pi0 pi0 pi+ pi+ pi- pi- PHSP; +#0.03 pi0 pi0 pi0 pi+ pi+ pi+ pi- pi- pi- PHSP; +#0.03 pi0 pi0 pi0 pi0 pi+ pi+ pi- pi- PHSP; +#0.04 K+ K- pi+ pi- PHSP; +#0.02 K0 K- pi+ PHSP; +#0.02 anti-K0 K+ pi- PHSP; +#0.02 K0 K- pi+ pi+ pi- PHSP; +#0.02 anti-K0 K+ pi- pi- pi+ PHSP; +#0.02 K0 K- pi0 pi+ PHSP; +#0.02 anti-K0 K+ pi0 pi- PHSP; +#0.02 eta pi0 pi+ pi- PHSP; +#0.01 eta rho- pi0 pi+ PHSP; +#0.01 eta rho+ pi0 pi- PHSP; +#Enddecay +Decay omega(2S) +0.08 pi+ pi- PHSP; +0.05 pi+ pi+ pi- pi- PHSP; +0.10 pi0 pi+ pi- PHSP; +0.10 pi0 pi+ pi+ pi- pi- PHSP; +0.12 pi0 pi0 pi+ pi- PHSP; +0.04 pi0 pi+ pi+ pi+ pi- pi- pi- PHSP; +0.14 pi0 pi0 pi+ pi+ pi- pi- PHSP; +0.05 pi0 pi0 pi0 pi+ pi- PHSP; +0.06 pi0 pi0 pi0 pi+ pi+ pi- pi- PHSP; +0.03 pi0 pi0 pi0 pi+ pi+ pi+ pi- pi- pi- PHSP; +0.03 pi0 pi0 pi0 pi0 pi+ pi+ pi- pi- PHSP; +0.04 K+ K- pi+ pi- PHSP; +0.02 K0 K- pi+ PHSP; +0.02 anti-K0 K+ pi- PHSP; +0.02 K0 K- pi+ pi+ pi- PHSP; +0.02 anti-K0 K+ pi- pi- pi+ PHSP; +0.02 K0 K- pi0 pi+ PHSP; +0.02 anti-K0 K+ pi0 pi- PHSP; +0.02 eta pi0 pi+ pi- PHSP; +0.01 eta rho- pi0 pi+ PHSP; +0.01 eta rho+ pi0 pi- PHSP; +Enddecay +#Decay rho(2150) +#0.08 pi+ pi- PHSP; +#0.05 pi+ pi+ pi- pi- PHSP; +#0.10 pi0 pi+ pi- PHSP; +#0.10 pi0 pi+ pi+ pi- pi- PHSP; +#0.12 pi0 pi0 pi+ pi- PHSP; +#0.04 pi0 pi+ pi+ pi+ pi- pi- pi- PHSP; +#0.14 pi0 pi0 pi+ pi+ pi- pi- PHSP; +#0.05 pi0 pi0 pi0 pi+ pi- PHSP; +#0.06 pi0 pi0 pi0 pi+ pi+ pi- pi- PHSP; +#0.03 pi0 pi0 pi0 pi+ pi+ pi+ pi- pi- pi- PHSP; +#0.03 pi0 pi0 pi0 pi0 pi+ pi+ pi- pi- PHSP; +#0.04 K+ K- pi+ pi- PHSP; +#0.02 K0 K- pi+ PHSP; +#0.02 anti-K0 K+ pi- PHSP; +#0.02 K0 K- pi+ pi+ pi- PHSP; +#0.02 anti-K0 K+ pi- pi- pi+ PHSP; +#0.02 K0 K- pi0 pi+ PHSP; +#0.02 anti-K0 K+ pi0 pi- PHSP; +#0.02 eta pi0 pi+ pi- PHSP; +#0.01 eta rho- pi0 pi+ PHSP; +#0.01 eta rho+ pi0 pi- PHSP; +#Enddecay +#Decay omega(1960) +#0.08 pi+ pi- PHSP; +#0.05 pi+ pi+ pi- pi- PHSP; +#0.10 pi0 pi+ pi- PHSP; +#0.10 pi0 pi+ pi+ pi- pi- PHSP; +#0.12 pi0 pi0 pi+ pi- PHSP; +#0.04 pi0 pi+ pi+ pi+ pi- pi- pi- PHSP; +#0.14 pi0 pi0 pi+ pi+ pi- pi- PHSP; +#0.05 pi0 pi0 pi0 pi+ pi- PHSP; +#0.06 pi0 pi0 pi0 pi+ pi+ pi- pi- PHSP; +#0.03 pi0 pi0 pi0 pi+ pi+ pi+ pi- pi- pi- PHSP; +#0.03 pi0 pi0 pi0 pi0 pi+ pi+ pi- pi- PHSP; +#0.04 K+ K- pi+ pi- PHSP; +#0.02 K0 K- pi+ PHSP; +#0.02 anti-K0 K+ pi- PHSP; +#0.02 K0 K- pi+ pi+ pi- PHSP; +#0.02 anti-K0 K+ pi- pi- pi+ PHSP; +#0.02 K0 K- pi0 pi+ PHSP; +#0.02 anti-K0 K+ pi0 pi- PHSP; +#0.02 eta pi0 pi+ pi- PHSP; +#0.01 eta rho- pi0 pi+ PHSP; +#0.01 eta rho+ pi0 pi- PHSP; +#Enddecay +#Decay rho(1965) +#0.08 pi+ pi- PHSP; +#0.05 pi+ pi+ pi- pi- PHSP; +#0.10 pi0 pi+ pi- PHSP; +#0.10 pi0 pi+ pi+ pi- pi- PHSP; +#0.12 pi0 pi0 pi+ pi- PHSP; +#0.04 pi0 pi+ pi+ pi+ pi- pi- pi- PHSP; +#0.14 pi0 pi0 pi+ pi+ pi- pi- PHSP; +#0.05 pi0 pi0 pi0 pi+ pi- PHSP; +#0.06 pi0 pi0 pi0 pi+ pi+ pi- pi- PHSP; +#0.03 pi0 pi0 pi0 pi+ pi+ pi+ pi- pi- pi- PHSP; +#0.03 pi0 pi0 pi0 pi0 pi+ pi+ pi- pi- PHSP; +#0.04 K+ K- pi+ pi- PHSP; +#0.02 K0 K- pi+ PHSP; +#0.02 anti-K0 K+ pi- PHSP; +#0.02 K0 K- pi+ pi+ pi- PHSP; +#0.02 anti-K0 K+ pi- pi- pi+ PHSP; +#0.02 K0 K- pi0 pi+ PHSP; +#0.02 anti-K0 K+ pi0 pi- PHSP; +#0.02 eta pi0 pi+ pi- PHSP; +#0.01 eta rho- pi0 pi+ PHSP; +#0.01 eta rho+ pi0 pi- PHSP; +#Enddecay +# +# +# cc= mesons Updated to PDG 2008 +# +Decay eta_c +0.001300000 p+ anti-p- PHSP; #[Reconstructed PDG2011] +0.002700000 phi phi SVV_HELAMP 1.0 0.0 0.0 0.0 -1.0 0.0; #[Reconstructed PDG2011] +0.002900000 phi K+ K- PHSP; #[Reconstructed PDG2011] +0.0067 rho0 rho0 SVV_HELAMP 1.0 0.0 0.0 0.0 -1.0 0.0; +0.0133 rho+ rho- SVV_HELAMP 1.0 0.0 0.0 0.0 -1.0 0.0; +0.012000000 pi+ pi- pi+ pi- PHSP; #[Reconstructed PDG2011] +0.001600000 K+ K- K+ K- PHSP; #[Reconstructed PDG2011] +0.015000000 K+ K- pi+ pi- PHSP; #[Reconstructed PDG2011] +0.0327 eta pi+ pi- PHSP; +0.0163 eta pi0 pi0 PHSP; +0.0273 eta' pi+ pi- PHSP; +0.0137 eta' pi0 pi0 PHSP; +0.0117 pi0 K+ K- PHSP; +0.0233 K+ anti-K0 pi- PHSP; +0.0233 K- K0 pi+ PHSP; +0.0117 K0 anti-K0 pi0 PHSP; +0.0046 K*+ K*- SVV_HELAMP 1.0 0.0 0.0 0.0 -1.0 0.0; +0.0046 K*0 anti-K*0 SVV_HELAMP 1.0 0.0 0.0 0.0 -1.0 0.0 ; +0.01 K*0 K- pi+ PHSP; +0.01 anti-K*0 K+ pi- PHSP; +# +#March 2009 New Modes +0.01500 K*0 anti-K*0 pi+ pi- PHSP; +0.007100000 K+ K- pi+ pi- pi+ pi- PHSP; #[Reconstructed PDG2011] +0.015000000 pi+ pi- pi+ pi- pi+ pi- PHSP; #[Reconstructed PDG2011] +0.001040000 Lambda0 anti-Lambda0 PHSP; #[Reconstructed PDG2011] +0.007600000 f_2 f_2 PHSP; #[Reconstructed PDG2011] +0.027000000 f_2 f'_2 PHSP; #[Reconstructed PDG2011] +# +0.682497000 rndmflav anti-rndmflav PYTHIA 42; +0.000063000 gamma gamma PHSP; #[New mode added] #[Reconstructed PDG2011] +Enddecay +# +Decay eta_c(2S) + 0.019 K+ anti-K0 pi- PHSP; + 0.019 K- K0 pi+ PHSP; + 0.0095 pi0 K+ K- PHSP; + 0.0095 K0 anti-K0 pi0 PHSP; +0.943 rndmflav anti-rndmflav PYTHIA 42; +Enddecay +# +Decay J/psi +0.3 e+ e- PHOTOS VLL; #[Reconstructed PDG2011] +0.7 mu+ mu- PHOTOS VLL; #[Reconstructed PDG2011] +Enddecay +# +Decay psi(2S) +0.336000000 J/psi pi+ pi- VVPIPI; #[Reconstructed PDG2011] +0.177300000 J/psi pi0 pi0 VVPIPI; #[Reconstructed PDG2011] +0.032800000 J/psi eta PARTWAVE 0.0 0.0 1.0 0.0 0.0 0.0; #[Reconstructed PDG2011] +0.001300000 J/psi pi0 PARTWAVE 0.0 0.0 1.0 0.0 0.0 0.0; #[Reconstructed PDG2011] +# below we also account for chi_c -> JPsi BR +0.00111592 gamma chi_c0 PHSP; #[Reconstructed PDG2011] +0.03164800 gamma chi_c1 PHSP; #[Reconstructed PDG2011] +0.01704300 gamma chi_c2 PHSP; #[Reconstructed PDG2011] +Enddecay + +Decay psi(4040) +0.000010700 e+ e- PHOTOS VLL; #[Reconstructed PDG2011] +0.000014000 mu+ mu- PHOTOS VLL; #[Reconstructed PDG2011] +0.000005000 tau+ tau- VLL; #[Reconstructed PDG2011] +0.002099938 D0 anti-D0 VSS; #[Reconstructed PDG2011] +0.000899973 D+ D- VSS; #[Reconstructed PDG2011] +0.167895013 D*0 anti-D0 VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; #[Reconstructed PDG2011] +0.167895013 anti-D*0 D0 VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; #[Reconstructed PDG2011] +0.181494610 D*+ D- VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; #[Reconstructed PDG2011] +0.181494610 D*- D+ VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; #[Reconstructed PDG2011] +0.159695257 D*0 anti-D*0 PHSP; #[Reconstructed PDG2011] +0.098197084 D*+ D*- PHSP; #[Reconstructed PDG2011] +0.000000000 D0 anti-D0 pi0 PHSP; #[Reconstructed PDG2011] +0.000000000 D+ D- pi0 PHSP; #[Reconstructed PDG2011] +0.000000000 D0 D- pi+ PHSP; #[Reconstructed PDG2011] +0.000000000 anti-D0 D+ pi- PHSP; #[Reconstructed PDG2011] +0.000000000 D*+ D- pi0 PHSP; #[Reconstructed PDG2011] +0.000000000 D*- D+ pi0 PHSP; #[Reconstructed PDG2011] +0.000000000 D*+ anti-D0 pi- PHSP; #[Reconstructed PDG2011] +0.000000000 D*- D0 pi+ PHSP; #[Reconstructed PDG2011] +0.000000000 D+ anti-D*0 pi- PHSP; #[Reconstructed PDG2011] +0.000000000 D- D*0 pi+ PHSP; #[Reconstructed PDG2011] +0.040298803 D_s+ D_s- VSS; #[Reconstructed PDG2011] +Enddecay +# +Decay psi(4160) +0.000008100 e+ e- PHOTOS VLL; #[Reconstructed PDG2011] +0.000009999 mu+ mu- PHOTOS VLL; #[Reconstructed PDG2011] +0.000006000 tau+ tau- VLL; #[Reconstructed PDG2011] +0.039697852 D0 anti-D0 VSS; #[Reconstructed PDG2011] +0.038697906 D+ D- VSS; #[Reconstructed PDG2011] +0.051997187 D*0 anti-D0 VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; #[Reconstructed PDG2011] +0.051997187 anti-D*0 D0 VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; #[Reconstructed PDG2011] +0.051497214 D*+ D- VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; #[Reconstructed PDG2011] +0.051497214 D*- D+ VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; #[Reconstructed PDG2011] +0.298613845 D*0 anti-D*0 PHSP; #[Reconstructed PDG2011] +0.308183327 D*+ D*- PHSP; #[Reconstructed PDG2011] +0.000000000 D0 anti-D0 pi0 PHSP; #[Reconstructed PDG2011] +0.000000000 D+ D- pi0 PHSP; #[Reconstructed PDG2011] +0.000000000 D0 D- pi+ PHSP; #[Reconstructed PDG2011] +0.000000000 anti-D0 D+ pi- PHSP; #[Reconstructed PDG2011] +0.000000000 D*+ D- pi0 PHSP; #[Reconstructed PDG2011] +0.000000000 D*- D+ pi0 PHSP; #[Reconstructed PDG2011] +0.000000000 D*+ anti-D0 pi- PHSP; #[Reconstructed PDG2011] +0.000000000 D*- D0 pi+ PHSP; #[Reconstructed PDG2011] +0.000000000 D+ anti-D*0 pi- PHSP; #[Reconstructed PDG2011] +0.000000000 D- D*0 pi+ PHSP; #[Reconstructed PDG2011] +0.009999459 D_s+ D_s- VSS; #[Reconstructed PDG2011] +0.048897355 D_s*+ D_s- VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; #[Reconstructed PDG2011] +0.048897355 D_s*- D_s+ VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; #[Reconstructed PDG2011] +0.000000000 D_s+ D_s- pi0 PHSP; #[Reconstructed PDG2011] +Enddecay +# +Decay psi(4415) +0.000009400 e+ e- PHOTOS VLL; #[Reconstructed PDG2011] +0.000011000 mu+ mu- PHOTOS VLL; #[Reconstructed PDG2011] +0.000007000 tau+ tau- VLL; #[Reconstructed PDG2011] +0.024399331 D0 anti-D0 VSS; #[Reconstructed PDG2011] +0.021899400 D+ D- VSS; #[Reconstructed PDG2011] +0.031999123 D*0 anti-D0 VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; #[Reconstructed PDG2011] +0.031999123 anti-D*0 D0 VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; #[Reconstructed PDG2011] +0.034299060 D*+ D- VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; #[Reconstructed PDG2011] +0.034299060 D*- D+ VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; #[Reconstructed PDG2011] +0.345090544 D*0 anti-D*0 PHSP; #[Reconstructed PDG2011] +0.353890303 D*+ D*- PHSP; #[Reconstructed PDG2011] +0.000000000 D0 anti-D0 pi0 PHSP; #[Reconstructed PDG2011] +0.000000000 D+ D- pi0 PHSP; #[Reconstructed PDG2011] +0.000000000 D0 D- pi+ PHSP; #[Reconstructed PDG2011] +0.000000000 anti-D0 D+ pi- PHSP; #[Reconstructed PDG2011] +0.000000000 D*+ D- pi0 PHSP; #[Reconstructed PDG2011] +0.000000000 D*- D+ pi0 PHSP; #[Reconstructed PDG2011] +0.000000000 D*+ anti-D0 pi- PHSP; #[Reconstructed PDG2011] +0.000000000 D*- D0 pi+ PHSP; #[Reconstructed PDG2011] +0.000000000 D+ anti-D*0 pi- PHSP; #[Reconstructed PDG2011] +0.000000000 D- D*0 pi+ PHSP; #[Reconstructed PDG2011] +0.011799677 D_s+ D_s- VSS; #[Reconstructed PDG2011] +0.041998849 D_s*+ D_s- VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; #[Reconstructed PDG2011] +0.041998849 D_s*- D_s+ VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; #[Reconstructed PDG2011] +0.026299279 D_s*+ D_s*- PHSP; #[Reconstructed PDG2011] +0.000000000 D_s+ D_s- pi0 PHSP; #[Reconstructed PDG2011] +0.000000000 D_s*+ D_s- pi0 PHSP; #[Reconstructed PDG2011] +0.000000000 D_s+ D_s*- pi0 PHSP; #[Reconstructed PDG2011] +0.000000000 D_s+ D- anti-K0 PHSP; #[Reconstructed PDG2011] +0.000000000 D_s- D+ anti-K0 PHSP; #[Reconstructed PDG2011] +0.000000000 D0 D_s- K+ PHSP; #[Reconstructed PDG2011] +0.000000000 anti-D0 D_s+ K- PHSP; #[Reconstructed PDG2011] +Enddecay +# +Decay chi_c0 +0.011600000 gamma J/psi PHSP; #[Reconstructed PDG2011] +Enddecay +# +Decay chi_c1 +0.344000000 J/psi gamma VVP 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; #[Reconstructed PDG2011] +Enddecay +# +Decay chi_c2 +0.195000000 gamma J/psi PHSP; #[Reconstructed PDG2011] +Enddecay +# +Decay psi(3770) +0.410000000 D+ D- VSS; #[Reconstructed PDG2011] +0.520000000 D0 anti-D0 VSS; #[Reconstructed PDG2011] +0.001930000 J/psi pi+ pi- PHSP; #[Reconstructed PDG2011] +0.000800000 J/psi pi0 pi0 PHSP; #[Reconstructed PDG2011] +0.0073 gamma chi_c0 PHSP; +0.0029 gamma chi_c1 PHSP; +0.055850300 rndmflav anti-rndmflav PYTHIA 42; +0.000900000 J/psi eta PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000009700 e+ e- PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000310000 phi eta PHSP; #[New mode added] #[Reconstructed PDG2011] +Enddecay +# +Decay X_1(3872) +1.000 rndmflav anti-rndmflav PYTHIA 42; +Enddecay +# +# +# bb= Mesons Updated to PDG 2008 +# +Decay eta_b +1.000 rndmflav anti-rndmflav PYTHIA 42; +Enddecay +# +Decay Upsilon +0.024800000 e+ e- PHOTOS VLL; #[Reconstructed PDG2011] +0.024800000 mu+ mu- PHOTOS VLL; #[Reconstructed PDG2011] +0.026000000 tau+ tau- VLL; #[Reconstructed PDG2011] +0.014959973 d anti-d PYTHIA 91; +0.044879919 u anti-u PYTHIA 91; +0.014959973 s anti-s PYTHIA 91; +0.044879919 c anti-c PYTHIA 91; +0.774328202 g g g PYTHIA 92; +0.028922614 gamma g g PYTHIA 92; +0.000063000 gamma pi+ pi- PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000017000 gamma pi0 pi0 PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000011400 gamma K+ K- PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000290000 gamma pi+ pi- K+ K- PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000250000 gamma pi+ pi+ pi- pi- PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000250000 gamma pi+ pi+ pi+ pi- pi- pi- PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000240000 gamma pi+ pi+ pi- pi- K+ K- PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000150000 gamma pi+ pi- p+ anti-p- PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000040000 gamma pi+ pi+ pi- pi- p+ anti-p- PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000020000 gamma K+ K+ K- K- PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000037000 gamma f'_2 PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000101000 gamma f_2 PHSP; #[New mode added] #[Reconstructed PDG2011] +Enddecay +# +Decay Upsilon(2S) +0.019100000 e+ e- PHOTOS VLL; #[Reconstructed PDG2011] +0.019300000 mu+ mu- PHOTOS VLL; #[Reconstructed PDG2011] +0.020000000 tau+ tau- VLL; #[Reconstructed PDG2011] +0.181000000 Upsilon pi+ pi- PHSP; #[Reconstructed PDG2011] +0.086000000 Upsilon pi0 pi0 PHSP; #[Reconstructed PDG2011] +# V-> gamma S Partial wave (L,S)=(0,1) +0.038000000 gamma chi_b0 HELAMP 1. 0. +1. 0.; #[Reconstructed PDG2011] +# V-> gamma V Partial wave (L,S)=(0,1) +0.069000000 gamma chi_b1 HELAMP 1. 0. 1. 0. -1. 0. -1. 0.; #[Reconstructed PDG2011] +# V-> gamma T Partial wave (L,S)=(0,1) +0.071500000 gamma chi_b2 HELAMP 2.4494897 0. 1.7320508 0. 1. 0. 1. 0. 1.7320508 0. 2.4494897 0.; #[Reconstructed PDG2011] +0.00500 d anti-d PYTHIA 91; +0.02000 u anti-u PYTHIA 91; +0.00500 s anti-s PYTHIA 91; +0.02000 c anti-c PYTHIA 91; +0.42160 g g g PYTHIA 92; +0.01600 gamma g g PYTHIA 92; +0.000210000 Upsilon eta PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000390000 gamma eta_b PHSP; #[New mode added] #[Reconstructed PDG2011] +Enddecay +# +Decay Upsilon(3S) +0.0181 e+ e- PHOTOS VLL; +0.021800000 mu+ mu- PHOTOS VLL; #[Reconstructed PDG2011] +0.022900000 tau+ tau- VLL; #[Reconstructed PDG2011] +0.044000000 Upsilon pi+ pi- PHSP; #[Reconstructed PDG2011] +0.022000000 Upsilon pi0 pi0 PHSP; #[Reconstructed PDG2011] +0.024500000 Upsilon(2S) pi+ pi- PHSP; #[Reconstructed PDG2011] +0.018500000 Upsilon(2S) pi0 pi0 PHSP; #[Reconstructed PDG2011] +0.050000000 Upsilon(2S) gamma gamma PHSP; #[Reconstructed PDG2011] +# V-> gamma S Partial wave (L,S)=(0,1) +0.059000000 gamma chi_b0(2P) HELAMP 1. 0. 1. 0.; #[Reconstructed PDG2011] +# V-> gamma V Partial wave (L,S)=(0,1) +0.126000000 gamma chi_b1(2P) HELAMP 1. 0. 1. 0. -1. 0. -1. 0.; #[Reconstructed PDG2011] +# V-> gamma T Partial wave (L,S)=(0,1) +0.131000000 gamma chi_b2(2P) HELAMP 2.4494897 0. 1.7320508 0. 1. 0. 1. 0. 1.7320508 0. 2.4494897 0.; #[Reconstructed PDG2011] +0.00700 d anti-d PYTHIA 91; +0.02800 u anti-u PYTHIA 91; +0.00700 s anti-s PYTHIA 91; +0.02800 c anti-c PYTHIA 91; +0.37780 g g g PYTHIA 92; +0.01000 gamma g g PYTHIA 92; +0.003000000 gamma chi_b0 PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000510000 gamma eta_b PHSP; #[New mode added] #[Reconstructed PDG2011] +Enddecay +# + +Decay Upsilon(5S) +0.5 B+ B- PHSP; +0.481487200 g g g PYTHIA 92; +0.000002800 e+ e- PHSP; #[New mode added] #[Reconstructed PDG2011] +0.005300000 Upsilon pi+ pi- PHSP; #[New mode added] #[Reconstructed PDG2011] +0.007800000 Upsilon(2S) pi+ pi- PHSP; #[New mode added] #[Reconstructed PDG2011] +0.004800000 Upsilon(3S) pi+ pi- PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000610000 Upsilon K+ K- PHSP; #[New mode added] #[Reconstructed PDG2011] +Enddecay + +Decay chi_b0 +# S-> gamma V Partial wave (L,S)=(0,0) +0.0500 gamma Upsilon HELAMP 1. 0. 1. 0.; +0.949650000 rndmflav anti-rndmflav PYTHIA 42; +0.000110000 pi+ pi+ pi- pi- K+ K- PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000240000 pi+ pi+ pi+ pi- pi- pi- K+ K- PHSP; #[New mode added] #[Reconstructed PDG2011] +Enddecay +# +Decay chi_b1 +# V-> gamma V Partial wave (L,S)=(0,1) +0.350000000 gamma Upsilon HELAMP 1. 0. 1. 0. -1. 0. -1. 0.; #[Reconstructed PDG2011] +0.643080000 g g PYTHIA 91; +0.000200000 pi+ pi- K+ K- pi0 PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000800000 pi+ pi+ pi- pi- pi0 pi0 PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000150000 pi+ pi+ pi- pi- K+ K- PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000350000 pi+ pi+ pi- pi- K+ K- pi0 PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000860000 pi+ pi+ pi- pi- K+ K- pi0 pi0 PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000190000 pi+ pi+ pi+ pi- pi- pi- PHSP; #[New mode added] #[Reconstructed PDG2011] +0.001700000 pi+ pi+ pi+ pi- pi- pi- pi0 pi0 PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000260000 pi+ pi+ pi+ pi- pi- pi- K+ K- PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000750000 pi+ pi+ pi+ pi- pi- pi- K+ K- pi0 PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000260000 pi+ pi+ pi+ pi+ pi- pi- pi- pi- PHSP; #[New mode added] #[Reconstructed PDG2011] +0.001400000 pi+ pi+ pi+ pi+ pi- pi- pi- pi- pi0 pi0 PHSP; #[New mode added] #[Reconstructed PDG2011] +Enddecay +# +Decay chi_b2 +# T-> gamma V Partial wave (L,S)=(0,2) Use PHSP. +0.220000000 gamma Upsilon PHSP; #[Reconstructed PDG2011] +#0.2200 gamma Upsilon HELAMP 1. 0. 1.7320508 0. 2.4494897 0. +# 2.4494897 0. 1.7320508 0. 1. 0.; +0.775550000 g g PYTHIA 91; +0.000080000 pi+ pi- K+ K- pi0 PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000350000 pi+ pi+ pi- pi- pi0 pi0 PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000110000 pi+ pi+ pi- pi- K+ K- PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000210000 pi+ pi+ pi- pi- K+ K- pi0 PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000390000 pi+ pi+ pi- pi- K+ K- pi0 pi0 PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000070000 pi+ pi+ pi+ pi- pi- pi- PHSP; #[New mode added] #[Reconstructed PDG2011] +0.001000000 pi+ pi+ pi+ pi- pi- pi- pi0 pi0 PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000360000 pi+ pi+ pi+ pi- pi- pi- K+ K- pi0 PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000080000 pi+ pi+ pi+ pi+ pi- pi- pi- pi- PHSP; #[New mode added] #[Reconstructed PDG2011] +0.001800000 pi+ pi+ pi+ pi+ pi- pi- pi- pi- pi0 pi0 PHSP; #[New mode added] #[Reconstructed PDG2011] +Enddecay +# +Decay chi_b0(2P) +# S-> gamma V Partial wave (L,S)=(0,0) +0.009000000 gamma Upsilon HELAMP 1. 0. 1. 0.; #[Reconstructed PDG2011] +0.046000000 gamma Upsilon(2S) HELAMP 1. 0. 1. 0.; #[Reconstructed PDG2011] +# S-> gamma V Partial wave (L,S)=(0,0) +0.00150 gamma Upsilon_1(1D) HELAMP 1. 0. 1. 0.; +0.94350 g g PYTHIA 91; +Enddecay +# +Decay chi_b1(2P) +# V-> gamma V Partial wave (L,S)=(0,1) +0.085000000 gamma Upsilon HELAMP 1. 0. 1. 0. -1. 0. -1. 0.; #[Reconstructed PDG2011] +0.210000000 gamma Upsilon(2S) HELAMP 1. 0. 1. 0. -1. 0. -1. 0.; #[Reconstructed PDG2011] +# V-> gamma V Partial wave (L,S)=(0,1) +0.0097 gamma Upsilon_1(1D) HELAMP 1. 0. 1. 0. -1. 0. -1. 0.; +# V-> gamma T Partial wave (L,S)=(0,1) +0.0236 gamma Upsilon_2(1D) HELAMP 2.4494897 0. 1.7320508 0. 1. 0. 1. 0. 1.7320508 0. 2.4494897 0.; +0.648650000 g g PYTHIA 91; +0.016300000 omega Upsilon PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000310000 pi+ pi- K+ K- pi0 PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000590000 pi+ pi+ pi- pi- pi0 pi0 PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000100000 pi+ pi+ pi- pi- K+ K- PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000550000 pi+ pi+ pi- pi- K+ K- pi0 PHSP; #[New mode added] #[Reconstructed PDG2011] +0.001000000 pi+ pi+ pi- pi- K+ K- pi0 pi0 PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000120000 pi+ pi+ pi+ pi- pi- pi- PHSP; #[New mode added] #[Reconstructed PDG2011] +0.001200000 pi+ pi+ pi+ pi- pi- pi- pi0 pi0 PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000200000 pi+ pi+ pi+ pi- pi- pi- K+ K- PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000610000 pi+ pi+ pi+ pi- pi- pi- K+ K- pi0 PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000170000 pi+ pi+ pi+ pi+ pi- pi- pi- pi- PHSP; #[New mode added] #[Reconstructed PDG2011] +0.001900000 pi+ pi+ pi+ pi+ pi- pi- pi- pi- pi0 pi0 PHSP; #[New mode added] #[Reconstructed PDG2011] +Enddecay +# +Decay chi_b2(2P) +# T-> gamma V Partial wave (L,S)=(0,2) Use PHSP. +0.071000000 gamma Upsilon PHSP; #[Reconstructed PDG2011] +#0.0710 gamma Upsilon HELAMP 1. 0. 1.7320508 0. 2.4494897 0. +# 2.4494897 0. 1.7320508 0. 1. 0.; +0.162000000 gamma Upsilon(2S) PHSP; #[Reconstructed PDG2011] +#0.1620 gamma Upsilon(2S) HELAMP 1. 0. 1.7320508 0. 2.4494897 0. +# 2.4494897 0. 1.7320508 0. 1. 0.; +# T-> gamma V Partial wave (L,S)=(0,2) Use PHSP. +0.00023 gamma Upsilon_1(1D) PHSP; +#0.00023 gamma Upsilon_1(1D) HELAMP 1. 0. 1.7320508 0. 2.4494897 0. +# 2.4494897 0. 1.7320508 0. 1. 0.; +# T -> gamma T Partial wave (L,S)=(0,2) +0.00290 gamma Upsilon_2(1D) PHSP; +#0.00290 gamma Upsilon_2(1D) HELAMP -1. 0. -1.2247449 0. +# -1.2247449 0. -1. 0. +# 1. 0. 1.2247449 0. +# 1.2247449 0. 1. 0.; +# spin 3 not yet in HELAMP. +0.01420 gamma Upsilon_3(1D) PYTHIA 0; +# T-> gamma 3 Partial wave (L,S)=(0,2) +#0.01420 gamma Upsilon_3(1D) HELAMP 3.8729833 0. 3.1622777 0. +# 2.4494897 0. 1.7320508. 0. 1. 0. +# 1. 0. 1.7320508 0. 2.4494897 0. +# 3.1622777 0. 3.8729833 0.; +0.734240000 g g PYTHIA 91; +0.011000000 omega Upsilon PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000390000 pi+ pi+ pi- pi- pi0 pi0 PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000090000 pi+ pi+ pi- pi- K+ K- PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000240000 pi+ pi+ pi- pi- K+ K- pi0 PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000470000 pi+ pi+ pi- pi- K+ K- pi0 pi0 PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000090000 pi+ pi+ pi+ pi- pi- pi- PHSP; #[New mode added] #[Reconstructed PDG2011] +0.001200000 pi+ pi+ pi+ pi- pi- pi- pi0 pi0 PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000140000 pi+ pi+ pi+ pi- pi- pi- K+ K- PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000420000 pi+ pi+ pi+ pi- pi- pi- K+ K- pi0 PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000090000 pi+ pi+ pi+ pi+ pi- pi- pi- pi- PHSP; #[New mode added] #[Reconstructed PDG2011] +0.001300000 pi+ pi+ pi+ pi+ pi- pi- pi- pi- pi0 pi0 PHSP; #[New mode added] #[Reconstructed PDG2011] +Enddecay +# +Decay h_b +1.00000 g g PYTHIA 91; +Enddecay + +Decay chi_b0(3P) +#see Kwong and Rosner, PRD 38,279 (1988) +# S-> gamma V Partial wave (L,S)=(0,0) +0.01700 gamma Upsilon(3S) HELAMP 1. 0. 1. 0.; +# S-> gamma V Partial wave (L,S)=(0,0) +0.00400 gamma Upsilon_1(2D) HELAMP 1. 0. 1. 0.; +0.97900 g g PYTHIA 91; +Enddecay + +Decay chi_b1(3P) +#see Kwong and Rosner, PRD 38,279 (1988) +# V-> gamma V Partial wave (L,S)=(0,1) +0.15000 gamma Upsilon(3S) HELAMP 1. 0. 1. 0. -1. 0. -1. 0.; +# V-> gamma T Partial wave (L,S)=(0,1) +0.03100 gamma Upsilon_2(2D) HELAMP 2.4494897 0. 1.7320508 0. 1. 0. 1. 0. 1.7320508 0. 2.4494897 0.; +# V-> gamma V Partial wave (L,S)=(0,1) +0.01300 gamma Upsilon_1(2D) HELAMP 1. 0. 1. 0. -1. 0. -1. 0.; +0.80600 g g PYTHIA 91; +Enddecay + +Decay chi_b2(3P) +#see Kwong and Rosner, PRD 38,279 (1988) +# T-> gamma V Partial wave (L,S)=(0,2) Use PHSP. +0.08700 gamma Upsilon(3S) PHSP; +0.00000 gamma Upsilon(2S) PHSP; +0.00000 gamma Upsilon PHSP; +#0.08700 gamma Upsilon(3S) HELAMP 1. 0. 1.7320508 0. 2.4494897 0. +# 2.4494897 0. 1.7320508 0. 1. 0.; +#0.00000 gamma Upsilon(2S) HELAMP 1. 0. 1.7320508 0. 2.4494897 0. +# 2.4494897 0. 1.7320508 0. 1. 0.; +#0.00000 gamma Upsilon HELAMP 1. 0. 1.7320508 0. 2.4494897 0. +# 2.4494897 0. 1.7320508 0. 1. 0.; +# spin 3 not yet in HELAMP +0.02200 Upsilon_3(2D) gamma PHSP; +# T -> gamma 3 Partial wave (L,S)=(0,2) +#0.02200 gamma Upsilon_3(2D) HELAMP 3.8729833 0. 3.1622777 0. +# 2.4494897 0. 1.7320508. 0. 1. 0. +# 1. 0. 1.7320508 0. 2.4494897 0. +# 3.1622777 0. 3.8729833 0.; +# T -> gamma T Partial wave (L,S)=(0,2) +0.00400 gamma Upsilon_2(2D) PHSP; +#0.00400 gamma Upsilon_2(2D) HELAMP -1. 0. -1.2247449 0. +# -1.2247449 0. -1. 0. +# 1. 0. 1.2247449 0. +# 1.2247449 0. 1. 0.; +# T-> gamma V Partial wave (L,S)=(0,2) +0.00040 gamma Upsilon_1(2D) PHSP; +#0.00040 gamma Upsilon_1(2D) HELAMP 1. 0. 1.7320508 0. 2.4494897 0. +# 2.4494897 0. 1.7320508 0. 1. 0.; +0.88660 g g PYTHIA 91; +Enddecay + +Decay Upsilon_1(1D) +#see Kwong and Rosner, PRD 38,279 (1988) +0.00140 Upsilon pi+ pi- PHSP; +0.00070 Upsilon pi0 pi0 PHSP; +# V-> gamma S Partial wave (L,S)=(0,1) +0.60200 gamma chi_b0 HELAMP 1. 0. +1. 0.; +# V-> gamma V Partial wave (L,S)=(0,1) +0.31800 gamma chi_b1 HELAMP 1. 0. 1. 0. -1. 0. -1. 0.; +# V-> gamma T Partial wave (L,S)=(0,1) +0.02600 gamma chi_b2 HELAMP 2.4494897 0. 1.7320508 0. 1. 0. 1. 0. 1.7320508 0. 2.4494897 0.; +0.05190 g g g PYTHIA 92; +Enddecay + +Decay Upsilon_2(1D) +#see Kwong and Rosner, PRD 38,279 (1988) +0.00140 Upsilon pi+ pi- PHSP; +0.00070 Upsilon pi0 pi0 PHSP; +# T-> gamma T Partial wave (L,S)=(0,2) +0.20300 gamma chi_b2 PHSP; +#0.20300 gamma chi_b2 HELAMP -1. 0. -1.2247449 0. +# -1.2247449 0. -1. 0. +# 1. 0. 1.2247449 0. +# 1.2247449 0. 1. 0.; +# T-> gamma V Partial wave (L,S)=(0,2) Use PHSP. +0.78500 gamma chi_b1 PHSP; +#0.78500 gamma chi_b1 HELAMP 1. 0. 1.7320508 0. 2.4494897 0. +# 2.4494897 0. 1.7320508 0. 1. 0.; +0.00990 g g g PYTHIA 92; +Enddecay + +Decay Upsilon_3(1D) +#see Kwong and Rosner, PRD 38,279 (1988) +0.00200 Upsilon pi+ pi- PHSP; +0.00100 Upsilon pi0 pi0 PHSP; +0.95400 chi_b2 gamma PHSP; +0.04300 g g g PYTHIA 92; +Enddecay + +Decay Upsilon_1(2D) +#see Kwong and Rosner, PRD 38,279 (1988) +0.00000 Upsilon pi+ pi- PHSP; +0.00000 Upsilon pi0 pi0 PHSP; +0.00000 Upsilon(2S) pi+ pi- PHSP; +0.00000 Upsilon(2S) pi0 pi0 PHSP; +# V-> gamma S Partial wave (L,S)=(0,1) +0.51000 gamma chi_b0(2P) HELAMP 1. 0. +1. 0.; +# V-> gamma V Partial wave (L,S)=(0,1) +0.26000 gamma chi_b1(2P) HELAMP 1. 0. 1. 0. -1. 0. -1. 0.; +# V-> gamma T Partial wave (L,S)=(0,1) +0.01400 gamma chi_b2(2P) HELAMP 2.4494897 0. 1.7320508 0. 1. 0. 1. 0. 1.7320508 0. 2.4494897 0.; +0.21600 g g g PYTHIA 92; +Enddecay + +Decay Upsilon_2(2D) +#see Kwong and Rosner, PRD 38,279 (1988) +0.00000 Upsilon pi+ pi- PHSP; +0.00000 Upsilon pi0 pi0 PHSP; +0.00000 Upsilon(2S) pi+ pi- PHSP; +0.00000 Upsilon(2S) pi0 pi0 PHSP; +# T-> gamma T Partial wave (L,S)=(0,2) +0.04000 gamma chi_b2(2P) PHSP; +#0.04000 gamma chi_b2(2P) HELAMP -1. 0. -1.2247449 0. +# -1.2247449 0. -1. 0. +# 1. 0. 1.2247449 0. +# 1.2247449 0. 1. 0.; +# T-> gamma V Partial wave (L,S)=(0,2) Use PHSP. +0.13000 gamma chi_b1(2P) PHSP; +#0.13000 gamma chi_b1(2P) HELAMP 1. 0. 1.7320508 0. 2.4494897 0. +# 2.4494897 0. 1.7320508 0. 1. 0.; +0.83000 g g g PYTHIA 92; +Enddecay + +Decay Upsilon_3(2D) +#see Kwong and Rosner, PRD 38,279 (1988) +0.00000 Upsilon pi+ pi- PHSP; +0.00000 Upsilon pi0 pi0 PHSP; +0.00000 Upsilon(2S) pi+ pi- PHSP; +0.00000 Upsilon(2S) pi0 pi0 PHSP; +0.72000 chi_b2(2P) gamma PHSP; +0.28000 g g g PYTHIA 92; +Enddecay + +Decay h_b(2P) +1.00000 g g PYTHIA 91; +Enddecay +Decay h_b(3P) +1.00000 g g PYTHIA 91; +Enddecay +Decay eta_b2(1D) +1.00000 g g PYTHIA 91; +Enddecay +Decay eta_b2(2D) +1.00000 g g PYTHIA 91; +Enddecay +# +# Charm Baryons +# +Decay Lambda_c+ +0.021000000 e+ nu_e Lambda0 PYTHIA 22; +0.00500 e+ nu_e Sigma0 PYTHIA 22; +0.00500 e+ nu_e Sigma*0 PYTHIA 22; +0.00300 e+ nu_e n0 PYTHIA 22; +0.00200 e+ nu_e Delta0 PYTHIA 22; +0.00600 e+ nu_e p+ pi- PYTHIA 22; +0.00600 e+ nu_e n0 pi0 PYTHIA 22; +0.020000000 mu+ nu_mu Lambda0 PYTHIA 22; +0.00500 mu+ nu_mu Sigma0 PYTHIA 22; +0.00500 mu+ nu_mu Sigma*0 PYTHIA 22; +0.00300 mu+ nu_mu n0 PYTHIA 22; +0.00200 mu+ nu_mu Delta0 PYTHIA 22; +0.00600 mu+ nu_mu p+ pi- PYTHIA 22; +0.00600 mu+ nu_mu n0 pi0 PYTHIA 22; +0.008600000 Delta++ K- PYTHIA 0; +0.02500 Delta++ K*- PYTHIA 0; +0.023000000 p+ anti-K0 PYTHIA 0; +0.016000000 p+ anti-K*0 PYTHIA 0; +0.00500 Delta+ anti-K0 PYTHIA 0; +0.00500 Delta+ anti-K*0 PYTHIA 0; +0.010700000 Lambda0 pi+ PYTHIA 0; +0.00500 Lambda0 rho+ PYTHIA 0; +0.010500000 Sigma0 pi+ PYTHIA 0; +0.00400 Sigma0 rho+ PYTHIA 0; +0.00400 Sigma*0 pi+ PYTHIA 0; +0.00400 Sigma*0 rho+ PYTHIA 0; +0.010000000 Sigma+ pi0 PYTHIA 0; +0.005500000 Sigma+ eta PYTHIA 0; +0.00200 Sigma+ eta' PYTHIA 0; +0.00400 Sigma+ rho0 PYTHIA 0; +0.027000000 Sigma+ omega PYTHIA 0; +0.00300 Sigma*+ pi0 PYTHIA 0; +0.008500000 Sigma*+ eta PYTHIA 0; +0.00300 Sigma*+ rho0 PYTHIA 0; +0.00300 Sigma*+ omega PYTHIA 0; +0.003900000 Xi0 K+ PYTHIA 0; +0.00200 Xi0 K*+ PYTHIA 0; +0.002600000 Xi*0 K+ PYTHIA 0; +0.00100 Delta++ pi- PYTHIA 0; +0.00100 Delta++ rho- PYTHIA 0; +0.00200 p+ pi0 PYTHIA 0; +0.00100 p+ eta PYTHIA 0; +0.00100 p+ eta' PYTHIA 0; +0.00200 p+ rho0 PYTHIA 0; +0.00200 p+ omega PYTHIA 0; +0.000820000 p+ phi PYTHIA 0; +0.002800000 p+ f_0 PYTHIA 0; +0.00100 Delta+ pi0 PYTHIA 0; +0.00100 Delta+ eta PYTHIA 0; +0.00100 Delta+ eta' PYTHIA 0; +0.00100 Delta+ rho0 PYTHIA 0; +0.00100 Delta+ omega PYTHIA 0; +0.00300 n0 pi+ PYTHIA 0; +0.00300 n0 rho+ PYTHIA 0; +0.00300 Delta0 pi+ PYTHIA 0; +0.00300 Delta0 rho+ PYTHIA 0; +0.00500 Lambda0 K+ PYTHIA 0; +0.00500 Lambda0 K*+ PYTHIA 0; +0.00200 Sigma0 K+ PYTHIA 0; +0.00200 Sigma0 K*+ PYTHIA 0; +0.00100 Sigma*0 K+ PYTHIA 0; +0.00100 Sigma*0 K*+ PYTHIA 0; +0.00200 Sigma+ K0 PYTHIA 0; +0.002800000 Sigma+ K*0 PYTHIA 0; +0.00100 Sigma*+ K0 PYTHIA 0; +0.00100 Sigma*+ K*0 PYTHIA 0; +0.064094509 u anti-d d su_0 PYTHIA 43; +0.028102977 u anti-d d su_1 PYTHIA 43; +0.017256214 u anti-s d su_0 PYTHIA 43; +0.017256214 u anti-d d ud_0 PYTHIA 43; +0.046838295 s uu_1 PYTHIA 43; +0.069024855 u su_0 PYTHIA 43; +0.069024855 u su_1 PYTHIA 43; +0.014791040 d uu_1 PYTHIA 43; +0.007395520 u ud_0 PYTHIA 43; +0.007395520 u ud_1 PYTHIA 43; +0.025400000 p+ K- pi+ PHSP; #[New mode added] #[Reconstructed PDG2011] +0.033000000 p+ anti-K0 pi0 PHSP; #[New mode added] #[Reconstructed PDG2011] +0.012000000 p+ anti-K0 eta PHSP; #[New mode added] #[Reconstructed PDG2011] +0.026000000 p+ anti-K0 pi+ pi- PHSP; #[New mode added] #[Reconstructed PDG2011] +0.023000000 p+ K- pi+ pi0 PHSP; #[New mode added] #[Reconstructed PDG2011] +0.011000000 p+ K*- pi+ PHSP; #[New mode added] #[Reconstructed PDG2011] +0.001100000 p+ K- pi+ pi+ pi- PHSP; #[New mode added] #[Reconstructed PDG2011] +0.008000000 p+ K- pi+ pi0 pi0 PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000700000 p+ pi+ pi- PHSP; #[New mode added] #[Reconstructed PDG2011] +0.001800000 p+ pi+ pi+ pi- pi- PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000000000 p+ K+ K- PHSP; #[New mode added] #[Reconstructed PDG2011] +0.036000000 Lambda0 pi+ pi0 PHSP; #[New mode added] #[Reconstructed PDG2011] +0.015000000 Lambda0 pi+ pi+ pi- PHSP; #[New mode added] #[Reconstructed PDG2011] +0.011000000 Lambda0 pi+ rho0 PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000000000 Lambda0 pi+ pi+ pi- pi0 PHSP; #[New mode added] #[Reconstructed PDG2011] +0.009500000 Lambda0 pi+ eta PHSP; #[New mode added] #[Reconstructed PDG2011] +0.012000000 Lambda0 pi+ omega PHSP; #[New mode added] #[Reconstructed PDG2011] +0.004700000 Lambda0 K+ anti-K0 PHSP; #[New mode added] #[Reconstructed PDG2011] +0.036000000 Sigma+ pi+ pi- PHSP; #[New mode added] #[Reconstructed PDG2011] +0.017000000 Sigma- pi+ pi+ PHSP; #[New mode added] #[Reconstructed PDG2011] +0.018000000 Sigma0 pi+ pi0 PHSP; #[New mode added] #[Reconstructed PDG2011] +0.008300000 Sigma0 pi+ pi+ pi- PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000000000 Sigma+ K+ K- PHSP; #[New mode added] #[Reconstructed PDG2011] +0.003100000 Sigma+ phi PHSP; #[New mode added] #[Reconstructed PDG2011] +0.002500000 Xi- K+ pi+ PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000000000 Sigma+ K+ pi- PHSP; #[New mode added] #[Reconstructed PDG2011] +Enddecay +CDecay anti-Lambda_c- +# +Decay Xi_c0 +1.000 PYTHIA 42; +Enddecay +Decay anti-Xi_c0 +1.000 PYTHIA 42; +Enddecay +# +Decay c-hadron +0.08000 e+ nu_e s specflav PYTHIA 22; +0.08000 mu+ nu_mu s specflav PYTHIA 22; +0.76000 u anti-d s specflav PYTHIA 42; +0.08000 u anti-s s specflav PYTHIA 42; +Enddecay +CDecay anti-c-hadron +# +Decay Sigma_c0 +1.0000 Lambda_c+ pi- PHSP; +Enddecay +Decay anti-Sigma_c0 +1.0000 anti-Lambda_c- pi+ PHSP; +Enddecay +# +Decay Sigma_c+ +1.0000 Lambda_c+ pi0 PHSP; +Enddecay +Decay anti-Sigma_c- +1.0000 anti-Lambda_c- pi0 PHSP; +Enddecay +# +Decay Xi_c+ +0.079535513 PYTHIA 42; +0.079535513 Sigma*+ anti-K0 PHSP; #[New mode added] #[Reconstructed PDG2011] +0.025689971 Lambda0 K- pi+ pi+ PHSP; #[New mode added] #[Reconstructed PDG2011] +0.010339617 Sigma+ K- pi+ PHSP; #[New mode added] #[Reconstructed PDG2011] +0.064423765 Sigma+ anti-K*0 PHSP; #[New mode added] #[Reconstructed PDG2011] +0.023065299 Sigma0 K- pi+ pi+ PHSP; #[New mode added] #[Reconstructed PDG2011] +0.043744532 Xi0 pi+ PHSP; #[New mode added] #[Reconstructed PDG2011] +0.079535513 Xi- pi+ pi+ PHSP; #[New mode added] #[Reconstructed PDG2011] +0.186113099 Xi0 pi+ pi0 PHSP; #[New mode added] #[Reconstructed PDG2011] +0.138391792 Xi0 pi- pi+ pi+ PHSP; #[New mode added] #[Reconstructed PDG2011] +0.182931679 Xi0 e+ nu_e PHSP; #[New mode added] #[Reconstructed PDG2011] +0.005567486 Omega- K+ pi+ PHSP; #[New mode added] #[Reconstructed PDG2011] +0.007158196 p+ K- pi+ PHSP; #[New mode added] #[Reconstructed PDG2011] +0.009544262 p+ anti-K*0 PHSP; #[New mode added] #[Reconstructed PDG2011] +0.038177046 Sigma+ pi+ pi- PHSP; #[New mode added] #[Reconstructed PDG2011] +0.014316392 Sigma- pi+ pi+ PHSP; #[New mode added] #[Reconstructed PDG2011] +0.011930327 Sigma+ K+ K- PHSP; #[New mode added] #[Reconstructed PDG2011] +Enddecay +Decay anti-Xi_c- +0.079535513 PYTHIA 42; +0.079535513 anti-Sigma*- K0 PHSP; #[New mode added] #[Reconstructed PDG2011] +0.025689971 anti-Lambda0 K+ pi- pi- PHSP; #[New mode added] #[Reconstructed PDG2011] +0.010339617 anti-Sigma- K+ pi- PHSP; #[New mode added] #[Reconstructed PDG2011] +0.064423765 anti-Sigma- K*0 PHSP; #[New mode added] #[Reconstructed PDG2011] +0.023065299 anti-Sigma0 K+ pi- pi- PHSP; #[New mode added] #[Reconstructed PDG2011] +0.043744532 anti-Xi0 pi- PHSP; #[New mode added] #[Reconstructed PDG2011] +0.079535513 anti-Xi+ pi- pi- PHSP; #[New mode added] #[Reconstructed PDG2011] +0.186113099 anti-Xi0 pi- pi0 PHSP; #[New mode added] #[Reconstructed PDG2011] +0.138391792 anti-Xi0 pi+ pi- pi- PHSP; #[New mode added] #[Reconstructed PDG2011] +0.182931679 anti-Xi0 e- anti-nu_e PHSP; #[New mode added] #[Reconstructed PDG2011] +0.005567486 anti-Omega+ K- pi- PHSP; #[New mode added] #[Reconstructed PDG2011] +0.007158196 anti-p- K+ pi- PHSP; #[New mode added] #[Reconstructed PDG2011] +0.009544262 anti-p- K*0 PHSP; #[New mode added] #[Reconstructed PDG2011] +0.038177046 anti-Sigma- pi- pi+ PHSP; #[New mode added] #[Reconstructed PDG2011] +0.014316392 anti-Sigma+ pi- pi- PHSP; #[New mode added] #[Reconstructed PDG2011] +0.011930327 anti-Sigma- K- K+ PHSP; #[New mode added] #[Reconstructed PDG2011] +Enddecay +# +Decay Sigma_c++ +1.0000 Lambda_c+ pi+ PHSP; +Enddecay +Decay anti-Sigma_c-- +1.0000 anti-Lambda_c- pi- PHSP; +Enddecay +# +Decay Xi'_c+ +0.1000 Lambda0 K- pi+ pi+ PHSP; +0.0800 Sigma0 K- pi+ pi+ PHSP; +0.8200 gamma Xi_c+ PHSP; +Enddecay +Decay anti-Xi'_c- +0.1000 anti-Lambda0 K+ pi- pi- PHSP; +0.0800 anti-Sigma0 K+ pi- pi- PHSP; +0.8200 gamma anti-Xi_c- PHSP; +Enddecay +# +Decay Xi'_c0 +1.0000 gamma Xi_c0 PHSP; +Enddecay +Decay anti-Xi'_c0 +1.0000 gamma anti-Xi_c0 PHSP; +Enddecay +# +# Light Baryons +# Lambda0 updated Feb 2009 +Decay Lambda0 +0.638719992 p+ pi- PHSP; #[Reconstructed PDG2011] +0.357719992 n0 pi0 PHSP; #[Reconstructed PDG2011] +0.001741600 n0 gamma PHSP; #[Reconstructed PDG2011] +0.000832160 p+ pi- gamma PHSP; #[Reconstructed PDG2011] +0.000831216 p+ e- anti-nu_e PHOTOS PHSP; #[Reconstructed PDG2011] +0.000155040 p+ mu- anti-nu_mu PHOTOS PHSP; #[Reconstructed PDG2011] +Enddecay +# +Decay anti-Lambda0 +0.638719992 anti-p- pi+ PHSP; #[Reconstructed PDG2011] +0.357719992 anti-n0 pi0 PHSP; #[Reconstructed PDG2011] +0.001741600 anti-n0 gamma PHSP; #[Reconstructed PDG2011] +0.000832160 anti-p- pi+ gamma PHSP; #[Reconstructed PDG2011] +0.000831216 anti-p- e+ nu_e PHOTOS PHSP; #[Reconstructed PDG2011] +0.000155040 anti-p- mu+ nu_mu PHOTOS PHSP; #[Reconstructed PDG2011] +Enddecay +# +Decay Lambda(1405)0 +0.3333 Sigma+ pi- PHSP; +0.3333 Sigma- pi+ PHSP; +0.3333 Sigma0 pi0 PHSP; +Enddecay +# +Decay anti-Lambda(1405)0 +0.3333 anti-Sigma+ pi- PHSP; +0.3333 anti-Sigma- pi+ PHSP; +0.3333 anti-Sigma0 pi0 PHSP; +Enddecay +# +Decay Lambda(1520)0 +0.23 p+ K- PHSP; +0.23 n0 anti-K0 PHSP; +0.14 Sigma+ pi- PHSP; +0.14 Sigma- pi+ PHSP; +0.14 Sigma0 pi0 PHSP; +0.0333 Lambda0 pi0 pi0 PHSP; +0.0667 Lambda0 pi+ pi- PHSP; +0.003 Sigma+ pi- pi0 PHSP; +0.003 Sigma- pi+ pi0 PHSP; +0.001 Sigma0 pi0 pi0 PHSP; +0.002 Sigma0 pi+ pi- PHSP; +0.011 Lambda0 gamma PHSP; +Enddecay +# +Decay anti-Lambda(1520)0 +0.23 anti-p- K+ PHSP; +0.23 anti-n0 K0 PHSP; +0.14 anti-Sigma- pi+ PHSP; +0.14 anti-Sigma+ pi- PHSP; +0.14 anti-Sigma0 pi0 PHSP; +0.0333 anti-Lambda0 pi0 pi0 PHSP; +0.0667 anti-Lambda0 pi+ pi- PHSP; +0.003 anti-Sigma+ pi- pi0 PHSP; +0.003 anti-Sigma- pi+ pi0 PHSP; +0.001 anti-Sigma0 pi0 pi0 PHSP; +0.002 anti-Sigma0 pi+ pi- PHSP; +0.011 anti-Lambda0 gamma PHSP; +Enddecay +# +Decay Lambda(1600)0 +0.176 p+ K- PHSP; +0.176 n0 anti-K0 PHSP; +0.216 Sigma+ pi- PHSP; +0.216 Sigma- pi+ PHSP; +0.216 Sigma0 pi0 PHSP; +Enddecay +# +Decay anti-Lambda(1600)0 +0.176 anti-p- K+ PHSP; +0.176 anti-n0 K0 PHSP; +0.216 anti-Sigma+ pi- PHSP; +0.216 anti-Sigma- pi+ PHSP; +0.216 anti-Sigma0 pi0 PHSP; +Enddecay +# +Decay Lambda(1670)0 +0.10 p+ K- PHSP; +0.10 n0 anti-K0 PHSP; +0.16 Sigma+ pi- PHSP; +0.16 Sigma- pi+ PHSP; +0.16 Sigma0 pi0 PHSP; +0.32 Lambda0 eta PHSP; +Enddecay +# +Decay anti-Lambda(1670)0 +0.10 anti-p- K+ PHSP; +0.10 anti-n0 K0 PHSP; +0.16 anti-Sigma+ pi- PHSP; +0.16 anti-Sigma- pi+ PHSP; +0.16 anti-Sigma0 pi0 PHSP; +0.32 anti-Lambda0 eta PHSP; +Enddecay +# +Decay Lambda(1690)0 +0.125 p+ K- PHSP; +0.125 n0 anti-K0 PHSP; +0.10 Sigma+ pi- PHSP; +0.10 Sigma- pi+ PHSP; +0.10 Sigma0 pi0 PHSP; +0.0833 Lambda0 pi0 pi0 PHSP; +0.1667 Lambda0 pi+ pi- PHSP; +0.067 Sigma+ pi- pi0 PHSP; +0.067 Sigma- pi+ pi0 PHSP; +0.022 Sigma0 pi0 pi0 PHSP; +0.044 Sigma0 pi+ pi- PHSP; +Enddecay +# +Decay anti-Lambda(1690)0 +0.125 anti-p- K+ PHSP; +0.125 anti-n0 K0 PHSP; +0.10 anti-Sigma+ pi- PHSP; +0.10 anti-Sigma- pi+ PHSP; +0.10 anti-Sigma0 pi0 PHSP; +0.0833 anti-Lambda0 pi0 pi0 PHSP; +0.1667 anti-Lambda0 pi+ pi- PHSP; +0.067 anti-Sigma+ pi- pi0 PHSP; +0.067 anti-Sigma- pi+ pi0 PHSP; +0.022 anti-Sigma0 pi0 pi0 PHSP; +0.044 anti-Sigma0 pi+ pi- PHSP; +Enddecay +# +Decay Lambda(1800)0 +0.165 p+ K- PHSP; +0.165 n0 anti-K0 PHSP; +0.08 Sigma+ pi- PHSP; +0.08 Sigma- pi+ PHSP; +0.08 Sigma0 pi0 PHSP; +0.003 Sigma*+ pi- PHSP; +0.003 Sigma*- pi+ PHSP; +0.004 Sigma*0 pi0 PHSP; +0.21 p+ K*- PHSP; +0.21 n0 anti-K*0 PHSP; +Enddecay +# +Decay anti-Lambda(1800)0 +0.165 anti-p- K+ PHSP; +0.165 anti-n0 K0 PHSP; +0.08 anti-Sigma+ pi- PHSP; +0.08 anti-Sigma- pi+ PHSP; +0.08 anti-Sigma0 pi0 PHSP; +0.003 anti-Sigma*+ pi- PHSP; +0.003 anti-Sigma*- pi+ PHSP; +0.004 anti-Sigma*0 pi0 PHSP; +0.21 anti-p- K*+ PHSP; +0.21 anti-n0 K*0 PHSP; +Enddecay +# +Decay Lambda(1810)0 +0.165 p+ K- PHSP; +0.165 n0 anti-K0 PHSP; +0.08 Sigma+ pi- PHSP; +0.08 Sigma- pi+ PHSP; +0.08 Sigma0 pi0 PHSP; +0.003 Sigma*+ pi- PHSP; +0.003 Sigma*- pi+ PHSP; +0.004 Sigma*0 pi0 PHSP; +0.21 p+ K*- PHSP; +0.21 n0 anti-K*0 PHSP; +Enddecay +# +Decay anti-Lambda(1810)0 +0.165 anti-p- K+ PHSP; +0.165 anti-n0 K0 PHSP; +0.08 anti-Sigma+ pi- PHSP; +0.08 anti-Sigma- pi+ PHSP; +0.08 anti-Sigma0 pi0 PHSP; +0.003 anti-Sigma*+ pi- PHSP; +0.003 anti-Sigma*- pi+ PHSP; +0.004 anti-Sigma*0 pi0 PHSP; +0.21 anti-p- K*+ PHSP; +0.21 anti-n0 K*0 PHSP; +Enddecay +# +Decay Lambda(1820)0 +0.34 p+ K- PHSP; +0.34 n0 anti-K0 PHSP; +0.06 Sigma+ pi- PHSP; +0.06 Sigma- pi+ PHSP; +0.06 Sigma0 pi0 PHSP; +0.04 Sigma*+ pi- PHSP; +0.04 Sigma*- pi+ PHSP; +0.04 Sigma*0 pi0 PHSP; +0.01 Lambda0 eta PHSP; +0.003 Sigma+ pi- pi0 PHSP; +0.004 Sigma- pi+ pi0 PHSP; +0.001 Sigma0 pi0 pi0 PHSP; +0.002 Sigma0 pi+ pi- PHSP; +Enddecay +# +Decay anti-Lambda(1820)0 +0.34 anti-p- K+ PHSP; +0.34 anti-n0 K0 PHSP; +0.06 anti-Sigma+ pi- PHSP; +0.06 anti-Sigma- pi+ PHSP; +0.06 anti-Sigma0 pi0 PHSP; +0.04 anti-Sigma*+ pi- PHSP; +0.04 anti-Sigma*- pi+ PHSP; +0.04 anti-Sigma*0 pi0 PHSP; +0.01 anti-Lambda0 eta PHSP; +0.003 anti-Sigma+ pi- pi0 PHSP; +0.004 anti-Sigma- pi+ pi0 PHSP; +0.001 anti-Sigma0 pi0 pi0 PHSP; +0.002 anti-Sigma0 pi+ pi- PHSP; +Enddecay +# +Decay Lambda(1830)0 +0.05 p+ K- PHSP; +0.05 n0 anti-K0 PHSP; +0.24 Sigma+ pi- PHSP; +0.24 Sigma- pi+ PHSP; +0.24 Sigma0 pi0 PHSP; +0.06 Sigma*+ pi- PHSP; +0.06 Sigma*- pi+ PHSP; +0.06 Sigma*0 pi0 PHSP; +Enddecay +# +Decay anti-Lambda(1830)0 +0.05 anti-p- K+ PHSP; +0.05 anti-n0 K0 PHSP; +0.24 anti-Sigma+ pi- PHSP; +0.24 anti-Sigma- pi+ PHSP; +0.24 anti-Sigma0 pi0 PHSP; +0.06 anti-Sigma*+ pi- PHSP; +0.06 anti-Sigma*- pi+ PHSP; +0.06 anti-Sigma*0 pi0 PHSP; +Enddecay +# +# +Decay Sigma(1660)0 +0.07 p+ K- PHSP; +0.07 n0 anti-K0 PHSP; +0.16 Lambda0 pi0 PHSP; +0.35 Sigma+ pi- PHSP; +0.35 Sigma- pi+ PHSP; +Enddecay +# +Decay anti-Sigma(1660)0 +0.07 anti-p- K+ PHSP; +0.07 anti-n0 K0 PHSP; +0.16 anti-Lambda0 pi0 PHSP; +0.35 anti-Sigma+ pi- PHSP; +0.35 anti-Sigma- pi+ PHSP; +Enddecay +# +Decay Sigma(1670)0 +0.07 p+ K- PHSP; +0.07 n0 anti-K0 PHSP; +0.16 Lambda0 pi0 PHSP; +0.35 Sigma+ pi- PHSP; +0.35 Sigma- pi+ PHSP; +Enddecay +# +Decay anti-Sigma(1670)0 +0.07 anti-p- K+ PHSP; +0.07 anti-n0 K0 PHSP; +0.16 anti-Lambda0 pi0 PHSP; +0.35 anti-Sigma+ pi- PHSP; +0.35 anti-Sigma- pi+ PHSP; +Enddecay +# +Decay Sigma(1775)0 +0.215 p+ K- PHSP; +0.215 n0 anti-K0 PHSP; +0.20 Lambda0 pi0 PHSP; +0.02 Sigma+ pi- PHSP; +0.02 Sigma- pi+ PHSP; +0.055 Sigma*+ pi- PHSP; +0.055 Sigma*- pi+ PHSP; +0.22 Lambda(1520)0 pi0 PHSP; +Enddecay +# +Decay anti-Sigma(1775)0 +0.215 anti-p- K+ PHSP; +0.215 anti-n0 K0 PHSP; +0.20 anti-Lambda0 pi0 PHSP; +0.02 anti-Sigma+ pi- PHSP; +0.02 anti-Sigma- pi+ PHSP; +0.055 anti-Sigma*+ pi- PHSP; +0.055 anti-Sigma*- pi+ PHSP; +0.22 anti-Lambda(1520)0 pi0 PHSP; +Enddecay +Decay Sigma+ +0.515454300 p+ pi0 PHSP; #[Reconstructed PDG2011] +0.482854300 n0 pi+ PHSP; #[Reconstructed PDG2011] +0.001225905 p+ gamma PHSP; #[Reconstructed PDG2011] +0.000445905 n0 pi+ gamma PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000019590 Lambda0 e+ nu_e PHSP; #[New mode added] #[Reconstructed PDG2011] +Enddecay +Decay anti-Sigma- +0.515454300 anti-p- pi0 PHSP; #[Reconstructed PDG2011] +0.482854300 anti-n0 pi- PHSP; #[Reconstructed PDG2011] +0.001225905 anti-p- gamma PHSP; #[Reconstructed PDG2011] +0.000445905 anti-n0 pi- gamma PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000019590 anti-Lambda0 e- anti-nu_e PHSP; #[New mode added] #[Reconstructed PDG2011] +Enddecay +# +Decay Delta+ +0.6630 p+ pi0 PHSP; +0.3310 n0 pi+ PHSP; +0.0060 p+ gamma PHSP; +Enddecay +Decay anti-Delta- +0.6630 anti-p- pi0 PHSP; +0.3310 anti-n0 pi- PHSP; +0.0060 anti-p- gamma PHSP; +Enddecay +# +Decay Delta++ +1.0000 p+ pi+ PHSP; +Enddecay +Decay anti-Delta-- +1.0000 anti-p- pi- PHSP; +Enddecay +# +Decay Xi*0 +0.3330 Xi0 pi0 PHSP; +0.6670 Xi- pi+ PHSP; +Enddecay +Decay anti-Xi*0 +0.3330 anti-Xi0 pi0 PHSP; +0.6670 anti-Xi+ pi- PHSP; +Enddecay +# +Decay Delta0 +0.3310 p+ pi- PHSP; +0.6630 n0 pi0 PHSP; +0.0060 n0 gamma PHSP; +Enddecay +Decay anti-Delta0 +0.3310 anti-p- pi+ PHSP; +0.6630 anti-n0 pi0 PHSP; +0.0060 anti-n0 gamma PHSP; +Enddecay +# +Decay Delta- +1.0000 n0 pi- PHSP; +Enddecay +Decay anti-Delta+ +1.0000 anti-n0 pi+ PHSP; +Enddecay +# +Decay Sigma- +0.998015700 n0 pi- PHSP; #[Reconstructed PDG2011] +0.000460000 n0 pi- gamma PHSP; #[New mode added] #[Reconstructed PDG2011] +0.001017000 n0 e- anti-nu_e PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000450000 n0 mu- anti-nu_mu PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000057300 Lambda0 e- anti-nu_e PHSP; #[New mode added] #[Reconstructed PDG2011] +Enddecay +Decay anti-Sigma+ +0.998015700 anti-n0 pi+ PHSP; #[Reconstructed PDG2011] +0.000460000 anti-n0 pi+ gamma PHSP; #[New mode added] #[Reconstructed PDG2011] +0.001017000 anti-n0 e+ nu_e PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000450000 anti-n0 mu+ nu_mu PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000057300 anti-Lambda0 e+ nu_e PHSP; #[New mode added] #[Reconstructed PDG2011] +Enddecay +# +Decay Sigma0 +0.995024876 gamma Lambda0 PHSP; #[Reconstructed PDG2011] +0.004975124 Lambda0 e+ e- PHSP; #[New mode added] #[Reconstructed PDG2011] +Enddecay +Decay anti-Sigma0 +0.995024876 gamma anti-Lambda0 PHSP; #[Reconstructed PDG2011] +0.004975124 anti-Lambda0 e- e+ PHSP; #[New mode added] #[Reconstructed PDG2011] +Enddecay +# +Decay Sigma*- +0.8800 Lambda0 pi- PHSP; +0.0600 Sigma0 pi- PHSP; +0.0600 Sigma- pi0 PHSP; +Enddecay +Decay anti-Sigma*+ +0.8800 anti-Lambda0 pi+ PHSP; +0.0600 anti-Sigma0 pi+ PHSP; +0.0600 anti-Sigma+ pi0 PHSP; +Enddecay +# +Decay Sigma*+ +0.8800 Lambda0 pi+ PHSP; +0.0600 Sigma+ pi0 PHSP; +0.0600 Sigma0 pi+ PHSP; +Enddecay +Decay anti-Sigma*- +0.8800 anti-Lambda0 pi- PHSP; +0.0600 anti-Sigma- pi0 PHSP; +0.0600 anti-Sigma0 pi- PHSP; +Enddecay +# +Decay Sigma*0 +0.8800 Lambda0 pi0 PHSP; +0.0600 Sigma+ pi- PHSP; +0.0600 Sigma- pi+ PHSP; +Enddecay +Decay anti-Sigma*0 +0.8800 anti-Lambda0 pi0 PHSP; +0.0600 anti-Sigma- pi+ PHSP; +0.0600 anti-Sigma+ pi- PHSP; +Enddecay +# +Decay Xi0 +0.995242400 Lambda0 pi0 PHSP; #[Reconstructed PDG2011] +0.001162400 Lambda0 gamma PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000007600 Lambda0 e+ e- PHSP; #[New mode added] #[Reconstructed PDG2011] +0.003330000 Sigma0 gamma PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000253000 Sigma+ e- anti-nu_e PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000004600 Sigma+ mu- anti-nu_mu PHSP; #[New mode added] #[Reconstructed PDG2011] +Enddecay +Decay anti-Xi0 +0.995242400 anti-Lambda0 pi0 PHSP; #[Reconstructed PDG2011] +0.001162400 anti-Lambda0 gamma PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000007600 anti-Lambda0 e- e+ PHSP; #[New mode added] #[Reconstructed PDG2011] +0.003330000 anti-Sigma0 gamma PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000253000 anti-Sigma- e+ nu_e PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000004600 anti-Sigma- mu+ nu_mu PHSP; #[New mode added] #[Reconstructed PDG2011] +Enddecay +# +Decay Xi- +0.998870000 Lambda0 pi- PHSP; #[Reconstructed PDG2011] +0.000127000 Sigma- gamma PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000563000 Lambda0 e- anti-nu_e PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000350000 Lambda0 mu- anti-nu_mu PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000087000 Sigma0 e- anti-nu_e PHSP; #[New mode added] #[Reconstructed PDG2011] +Enddecay +Decay anti-Xi+ +0.998870000 anti-Lambda0 pi+ PHSP; #[Reconstructed PDG2011] +0.000127000 anti-Sigma+ gamma PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000563000 anti-Lambda0 e+ nu_e PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000350000 anti-Lambda0 mu+ nu_mu PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000087000 anti-Sigma0 e+ nu_e PHSP; #[New mode added] #[Reconstructed PDG2011] +Enddecay +# +Decay Xi*- +0.6670 Xi0 pi- PHSP; +0.3330 Xi- pi0 PHSP; +Enddecay +Decay anti-Xi*+ +0.6670 anti-Xi0 pi+ PHSP; +0.3330 anti-Xi+ pi0 PHSP; +Enddecay +# +Decay Omega- +0.675949296 Lambda0 K- PHSP; #[Reconstructed PDG2011] +0.233949296 Xi0 pi- PHSP; #[Reconstructed PDG2011] +0.084828169 Xi- pi0 PHSP; #[Reconstructed PDG2011] +0.000000000 Xi- pi+ pi- PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000493521 Xi*0 pi- PHSP; #[New mode added] #[Reconstructed PDG2011] +0.004779718 Xi0 e- anti-nu_e PHSP; #[New mode added] #[Reconstructed PDG2011] +Enddecay +Decay anti-Omega+ +0.675949296 anti-Lambda0 K+ PHSP; #[Reconstructed PDG2011] +0.233949296 anti-Xi0 pi+ PHSP; #[Reconstructed PDG2011] +0.084828169 anti-Xi+ pi0 PHSP; #[Reconstructed PDG2011] +0.000000000 anti-Xi+ pi- pi+ PHSP; #[New mode added] #[Reconstructed PDG2011] +0.000493521 anti-Xi*0 pi+ PHSP; #[New mode added] #[Reconstructed PDG2011] +0.004779718 anti-Xi0 e+ nu_e PHSP; #[New mode added] #[Reconstructed PDG2011] +Enddecay +# +# +Decay Sigma_c*0 +1.0000 Lambda_c+ pi- PHSP; +Enddecay +CDecay anti-Sigma_c*0 +# +Decay Sigma_c*+ +1.0000 Lambda_c+ pi0 PHSP; +Enddecay +CDecay anti-Sigma_c*- +# +Decay Sigma_c*++ +1.0000 Lambda_c+ pi+ PHSP; +Enddecay +CDecay anti-Sigma_c*-- +# +Decay Xi_c*0 +0.5000 Xi_c0 pi0 PHSP; +0.5000 Xi_c0 gamma PHSP; +Enddecay +CDecay anti-Xi_c*0 +# +Decay Xi_c*+ +0.5000 Xi_c+ pi0 PHSP; +0.5000 Xi_c+ gamma PHSP; +Enddecay +CDecay anti-Xi_c*- +# +Decay Omega_c0 +1.00000 PYTHIA 42; +Enddecay +# +CDecay anti-Omega_c0 +# +Decay Omega_c*0 +1.0000 Omega_c0 gamma PHSP; +Enddecay +# +CDecay anti-Omega_c*0 +# +Decay Xu0 +# X_u^0 -> u anti-u +# +1.0 u anti-u PYTHIA 42; +Enddecay +Decay Xu+ +# X_u^+ -> u anti-d +# +1.0 u anti-d PYTHIA 42; +Enddecay +CDecay Xu- + +#lange - PYTHIA is making h_c mesons - better add a decay +# channel for them. +Decay h_c +0.01 J/psi pi0 PHSP; +0.5 eta_c gamma PHSP; +0.49 rndmflav anti-rndmflav PYTHIA 42; +Enddecay + +# 12/08/03 RJT Update b-baryon decays... +# chi_c or psi(2S) BR to J/psi are included below + +Decay Lambda_b0 +# add from PDG 2026 + 0.00032 J/psi p+ K- PHSP; + 0.000026 J/psi p+ pi- PHSP; + 0.000000454 psi(2S) p+ pi- PHSP; + 0.00000454 psi(2S) p+ K- PHSP; + 0.00000258 chi_c1 p+ K- PHSP; + 0.000000175 chi_c1 p+ pi- PHSP; + 0.00000156 chi_c2 p+ K- PHSP; + 0.0000000937 chi_c2 p+ pi- PHSP; +Enddecay + +Decay anti-Lambda_b0 +# add from PDG 2026; + 0.00032 J/psi anti-p- K+ PHSP; + 0.000026 J/psi anti-p- pi+ PHSP; + 0.000000454 psi(2S) anti-p- pi+ PHSP; + 0.00000454 psi(2S) anti-p- K+ PHSP; + 0.00000258 chi_c1 anti-p- K+ PHSP; + 0.000000175 chi_c1 anti-p- pi+ PHSP; + 0.00000156 chi_c2 anti-p- K+ PHSP; + 0.0000000937 chi_c2 anti-p- pi+ PHSP; +Enddecay + +Decay Xi_b- +# SemiLeptonic Decays + 0.05460 Xi_c0 e- anti-nu_e PHSP; + 0.05460 Xi_c0 mu- anti-nu_mu PHSP; + 0.02000 Xi_c0 tau- anti-nu_tau PHSP; +# Hadronic Decays with Xi_c0 + 0.00600 Xi_c0 pi- PHSP; + 0.02200 Xi_c0 pi- pi+ pi- PHSP; + 0.00055 Xi_c0 K- PHSP; + 0.02200 Xi_c0 D_s- PHSP; + 0.00047 Xi- J/psi PHSP; +# added by D.Litvintsev 12/8/03: + 0.00038 Xi- psi(2S) PHSP; + 0.00018 D0 Lambda0 pi- PHSP; + 0.00020 Sigma_c0 K- PHSP; + 0.00020 Omega_c0 K- PHSP; +# +# aaa 10/19/05 Do not if this is correct but filling out with inclusives +0.38757 d anti-u d cs_0 PYTHIA 43; +0.16995 d anti-u d cs_1 PYTHIA 43; +0.06335 s anti-u d cs_0 PYTHIA 43; +0.00568 d anti-u s cs_1 PYTHIA 43; +0.00797 d anti-u s cs_0 PYTHIA 43; +0.06148 s anti-c d cs_0 PYTHIA 43; +0.06918 d anti-u d cd_0 PYTHIA 43; +0.03999 s anti-c d cd_0 PYTHIA 43; +0.00865 d anti-u d su_0 PYTHIA 43; +0.00500 s anti-c d su_0 PYTHIA 43; +Enddecay + +Decay anti-Xi_b+ +# SemiLeptonic Decays + 0.05460 anti-Xi_c0 e+ nu_e PHSP; + 0.05460 anti-Xi_c0 mu+ nu_mu PHSP; + 0.02000 anti-Xi_c0 tau+ nu_tau PHSP; +# Hadronic Decays with anti-Xi_c0 + 0.00600 anti-Xi_c0 pi+ PHSP; + 0.02200 anti-Xi_c0 pi+ pi+ pi- PHSP; + 0.00055 anti-Xi_c0 K+ PHSP; + 0.02200 anti-Xi_c0 D_s+ PHSP; + 0.00047 anti-Xi+ J/psi PHSP; +# added by D.Litvintsev 12/8/03: + 0.00038 anti-Xi+ psi(2S) PHSP; + 0.00018 anti-D0 anti-Lambda0 pi+ PHSP; + 0.00020 anti-Sigma_c0 K+ PHSP; + 0.00020 anti-Omega_c0 K+ PHSP; +# aaa 10/19/05 Do not if this is correct but filling out with inclusives +0.38757 u anti-d anti-d anti-cs_0 PYTHIA 43; +0.16995 u anti-d anti-d anti-cs_1 PYTHIA 43; +0.06335 u anti-s anti-d anti-cs_0 PYTHIA 43; +0.00568 u anti-d anti-s anti-cs_1 PYTHIA 43; +0.00797 u anti-d anti-s anti-cs_0 PYTHIA 43; +0.06148 c anti-s anti-d anti-cs_0 PYTHIA 43; +0.06918 u anti-d anti-d anti-cd_0 PYTHIA 43; +0.03999 c anti-s anti-d anti-cd_0 PYTHIA 43; +0.00865 u anti-d anti-d anti-su_0 PYTHIA 43; +0.00500 c anti-s anti-d anti-su_0 PYTHIA 43; +Enddecay + +Decay Xi_b0 +# SemiLeptonic Decays + 0.05460 Xi_c+ e- anti-nu_e PHSP; + 0.05460 Xi_c+ mu- anti-nu_mu PHSP; + 0.02000 Xi_c+ tau- anti-nu_tau PHSP; +# Hadronic Decays with Xi_c+ + 0.00600 Xi_c+ pi- PHSP; + 0.02200 Xi_c+ pi- pi+ pi- PHSP; + 0.00055 Xi_c+ K- PHSP; + 0.02200 Xi_c+ D_s- PHSP; + 0.00047 Xi0 J/psi PHSP; +# added by D.Litvintsev 12/8/03: + 0.00020 D0 Lambda0 PHSP; + 0.00010 Lambda_c+ K- PHSP; + 0.00020 Sigma_c0 anti-K0 PHSP; + 0.00020 Omega_c0 K0 PHSP; +# +# Additional decays from hep-ph/9709372 scaled to the Lambda_c0 pi rate + 0.00020 Xi'_c+ pi- PHSP; + 0.00003 Xi_c0 pi0 PHSP; + 0.00015 Xi_c0 eta PHSP; + 0.00004 Xi_c0 eta' PHSP; + 0.00020 Xi'_c0 pi0 PHSP; + 0.00020 Xi'_c0 eta PHSP; + 0.00030 Xi'_c0 eta' PHSP; + 0.00040 Sigma_c+ K- PHSP; +# aaa 10/19/05 Do not if this is correct but filling out with inclusives +0.58591 d anti-u s cu_0 PYTHIA 43; +0.01363 s anti-u s cu_1 PYTHIA 43; +0.09539 s anti-u s cc_1 PYTHIA 43; +0.10900 s anti-u d cu_1 PYTHIA 43; +0.01363 d anti-u d uu_1 PYTHIA 43; +Enddecay + +Decay anti-Xi_b0 + 0.05460 anti-Xi_c- e+ nu_e PHSP; + 0.05460 anti-Xi_c- mu+ nu_mu PHSP; + 0.02000 anti-Xi_c- tau+ nu_tau PHSP; +# Hadronic Decays with anti-Xi_c- + 0.00600 anti-Xi_c- pi+ PHSP; + 0.02200 anti-Xi_c- pi+ pi+ pi- PHSP; + 0.00055 anti-Xi_c- K+ PHSP; + 0.02200 anti-Xi_c- D_s+ PHSP; + 0.00047 anti-Xi0 J/psi PHSP; +# added by D.Litvintsev 12/8/03: + 0.00020 anti-D0 anti-Lambda0 PHSP; + 0.00010 anti-Lambda_c- K+ PHSP; + 0.00020 anti-Sigma_c0 K0 PHSP; + 0.00020 anti-Omega_c0 anti-K0 PHSP; +# +# Additional decays from hep-ph/9709372 scaled to the Lambda_c0 pi rate + 0.00020 anti-Xi'_c- pi+ PHSP; + 0.00003 anti-Xi_c0 pi0 PHSP; + 0.00015 anti-Xi_c0 eta PHSP; + 0.00004 anti-Xi_c0 eta' PHSP; + 0.00020 anti-Xi'_c0 pi0 PHSP; + 0.00020 anti-Xi'_c0 eta PHSP; + 0.00030 anti-Xi'_c0 eta' PHSP; + 0.00040 anti-Sigma_c- K+ PHSP; +# aaa 10/19/05 Do not if this is correct but filling out with inclusives +0.58591 u anti-d anti-s anti-cu_0 PYTHIA 43; +0.01363 u anti-s anti-s anti-cu_1 PYTHIA 43; +0.09539 u anti-s anti-s anti-cc_1 PYTHIA 43; +0.10900 u anti-s anti-d anti-cu_1 PYTHIA 43; +0.01363 u anti-d anti-d anti-uu_1 PYTHIA 43; +Enddecay + +# added by D.Litvintsev 12/8/03: +Decay Omega_b- +# SemiLeptonic Decays + 0.05460 Omega_c0 e- anti-nu_e PHSP; + 0.05460 Omega_c0 mu- anti-nu_mu PHSP; + 0.02000 Omega_c0 tau- anti-nu_tau PHSP; +# Hadronic Decays with Xi_c+ + 0.00600 Omega_c0 pi- PHSP; + 0.02200 Omega_c0 pi- pi+ pi- PHSP; + 0.00055 Omega_c0 K- PHSP; + 0.02200 Omega_c0 D_s- PHSP; + 0.0011 D0 Xi- PHSP; +# + 0.00047 Omega- J/psi PHSP; + 0.00038 Omega- psi(2S) PHSP; +# aaa 10/19/05 Do not if this is correct but filling out with inclusives +0.68192 d anti-u s cs_0 PYTHIA 43; +0.10910 d anti-u d cs_0 PYTHIA 43; +0.02728 d anti-u s su_0 PYTHIA 43; +Enddecay + +Decay anti-Omega_b+ +# SemiLeptonic Decays + 0.05460 anti-Omega_c0 e+ nu_e PHSP; + 0.05460 anti-Omega_c0 mu+ nu_mu PHSP; + 0.02000 anti-Omega_c0 tau+ nu_tau PHSP; +# Hadronic Decays with Xi_c+ + 0.00600 anti-Omega_c0 pi+ PHSP; + 0.02200 anti-Omega_c0 pi+ pi+ pi- PHSP; + 0.00055 anti-Omega_c0 K+ PHSP; + 0.02200 anti-Omega_c0 D_s+ PHSP; + 0.0011 anti-D0 anti-Xi+ PHSP; +# + 0.00047 anti-Omega+ J/psi PHSP; + 0.00038 anti-Omega+ psi(2S) PHSP; +# aaa 10/19/05 Do not if this is correct but filling out with inclusives +0.68192 u anti-d anti-s anti-cs_0 PYTHIA 43; +0.10910 u anti-d anti-d anti-cs_0 PYTHIA 43; +0.02728 u anti-d anti-s anti-su_0 PYTHIA 43; +Enddecay + + +Decay Sigma_b+ + 1.00000 Lambda_b0 pi+ PHSP; +Enddecay +Decay anti-Sigma_b- + 1.00000 anti-Lambda_b0 pi- PHSP; +Enddecay + +Decay Sigma_b- + 1.00000 Lambda_b0 pi- PHSP; +Enddecay +Decay anti-Sigma_b+ + 1.00000 anti-Lambda_b0 pi+ PHSP; +Enddecay + +Decay Sigma_b0 + 1.00000 Lambda_b0 gamma PHOTOS PHSP; +Enddecay +Decay anti-Sigma_b0 + 1.00000 anti-Lambda_b0 gamma PHOTOS PHSP; +Enddecay + + +# +# B_c Mesons +# + +Decay B_c- +0.01600 tau- anti-nu_tau SLN; +# +# SemiLeptonic Decays +0.01900 J/psi e- anti-nu_e PHOTOS PHSP; +0.00094 psi(2S) e- anti-nu_e PHOTOS PHSP; +0.00750 eta_c e- anti-nu_e PHOTOS PHSP; +0.00020 eta_c(2S) e- anti-nu_e PHOTOS PHSP; +0.00004 anti-D0 e- anti-nu_e PHOTOS PHSP; +0.00018 anti-D*0 e- anti-nu_e PHOTOS PHSP; +0.04030 anti-B_s0 e- anti-nu_e PHOTOS PHSP; +0.05060 anti-B_s*0 e- anti-nu_e PHOTOS PHSP; +0.00340 anti-B0 e- anti-nu_e PHOTOS PHSP; +0.00580 anti-B*0 e- anti-nu_e PHOTOS PHSP; +# +0.01900 J/psi mu- anti-nu_mu PHOTOS PHSP; +0.00094 psi(2S) mu- anti-nu_mu PHOTOS PHSP; +0.00750 eta_c mu- anti-nu_mu PHOTOS PHSP; +0.00020 eta_c(2S) mu- anti-nu_mu PHOTOS PHSP; +0.00004 anti-D0 mu- anti-nu_mu PHOTOS PHSP; +0.00018 anti-D*0 mu- anti-nu_mu PHOTOS PHSP; +0.04030 anti-B_s0 mu- anti-nu_mu PHOTOS PHSP; +0.05060 anti-B_s*0 mu- anti-nu_mu PHOTOS PHSP; +0.00340 anti-B0 mu- anti-nu_mu PHOTOS PHSP; +0.00580 anti-B*0 mu- anti-nu_mu PHOTOS PHSP; +# +0.00480 J/psi tau- anti-nu_tau PHSP; +0.00008 psi(2S) tau- anti-nu_tau PHSP; +0.00230 eta_c tau- anti-nu_tau PHSP; +0.000016 eta_c(2S) tau- anti-nu_tau PHSP; +0.00002 anti-D0 tau- anti-nu_tau PHSP; +0.00008 anti-D*0 tau- anti-nu_tau PHSP; +# +# +# Hadronic Decays +0.00200 eta_c pi- PHSP; +0.00420 rho- eta_c SVS; +0.00013 eta_c K- PHSP; +0.00020 K*- eta_c SVS; +0.00130 J/psi pi- SVS; +0.00400 J/psi rho- SVV_HELAMP 1.0 0.0 1.0 0.0 1.0 0.0; +0.00011 J/psi K- SVS; +0.00022 J/psi K*- SVV_HELAMP 1.0 0.0 1.0 0.0 1.0 0.0; +# +0.000053 D- D0 PHSP; +0.000075 D*0 D- SVS; +0.000049 D*- D0 SVS; +0.00033 D*- D*0 SVV_HELAMP 1.0 0.0 1.0 0.0 1.0 0.0; +0.0000048 D_s- D0 PHSP; +0.0000071 D*0 D_s- SVS; +0.0000045 D_s*- D0 SVS; +0.000026 D_s*- D*0 SVV_HELAMP 1.0 0.0 1.0 0.0 1.0 0.0; +# +0.0000003 D- anti-D0 PHSP; +0.0000003 anti-D*0 D- SVS; +0.0000004 D*- anti-D0 SVS; +0.0000016 anti-D*0 D*- SVV_HELAMP 1.0 0.0 1.0 0.0 1.0 0.0; +0.0000066 D_s- anti-D0 PHSP; +0.0000063 anti-D*0 D_s- SVS; +0.0000085 D_s*- anti-D0 SVS; +0.0000404 D_s*- anti-D*0 SVV_HELAMP 1.0 0.0 1.0 0.0 1.0 0.0; +# +0.00280 eta_c D_s- PHSP; +0.00270 D_s*- eta_c SVS; +0.00015 eta_c D- PHSP; +0.00010 D*- eta_c SVS; +0.00170 J/psi D_s- SVS; +0.00670 J/psi D_s*- SVV_HELAMP 1.0 0.0 1.0 0.0 1.0 0.0; +0.00009 J/psi D- SVS; +0.00028 J/psi D*- SVV_HELAMP 1.0 0.0 1.0 0.0 1.0 0.0; +# +0.16400 anti-B_s0 pi- PHSP; +0.07200 rho- anti-B_s0 SVS; +0.01060 anti-B_s0 K- PHSP; +0.00000 K*- anti-B_s0 SVS; +0.06500 anti-B_s*0 pi- SVS; +0.20200 anti-B_s*0 rho- SVV_HELAMP 1.0 0.0 1.0 0.0 1.0 0.0; +0.00370 anti-B_s*0 K- SVS; +0.00000 anti-B_s*0 K*- SVV_HELAMP 1.0 0.0 1.0 0.0 1.0 0.0; +0.01060 anti-B0 pi- PHSP; +0.00960 rho- anti-B0 SVS; +0.00070 anti-B0 K- PHSP; +0.00015 K*- anti-B0 SVS; +0.00950 anti-B*0 pi- SVS; +0.02570 anti-B*0 rho- SVV_HELAMP 1.0 0.0 1.0 0.0 1.0 0.0; +0.00055 anti-B*0 K- SVS; +0.00058 anti-B*0 K*- SVV_HELAMP 1.0 0.0 1.0 0.0 1.0 0.0; +0.00037 B- pi0 PHSP; +0.00034 rho0 B- SVS; +0.01980 B- K0 PHSP; +0.00430 K*0 B- SVS; +0.00033 B*- pi0 SVS; +0.00090 B*- rho0 SVV_HELAMP 1.0 0.0 1.0 0.0 1.0 0.0; +0.01600 B*- K0 SVS; +0.01670 B*- K*0 SVV_HELAMP 1.0 0.0 1.0 0.0 1.0 0.0; +# PR LHCb 09 Apr 2004 Add B_c+ -> rho0 pi+ + 0.00002 rho0 pi- SVS; +0.06005 anti-c s PYTHIA 42; +# PR LHCb 27 Apr 2004 Add Pythia Modes +# +# ash 10/27/03 Do not if this is correct but filling out with unknown cs +#0.0200235 cs_0 anti-uu_0 PYTHIA 63; +#0.0400470 cs_1 anti-uu_1 PYTHIA 63; +# +Enddecay + +# 09/15/03 A. Sanchez: Using BR's from hep-ph/0308214 +# +# Total Br = 0.939865 + .06135 cs +Decay B_c+ +0.01600 tau+ nu_tau SLN; +# +# SemiLeptonic Decays +0.01900 J/psi e+ nu_e PHOTOS PHSP; +0.00094 psi(2S) e+ nu_e PHOTOS PHSP; +0.00750 eta_c e+ nu_e PHOTOS PHSP; +0.00020 eta_c(2S) e+ nu_e PHOTOS PHSP; +0.00004 D0 e+ nu_e PHOTOS PHSP; +0.00018 D*0 e+ nu_e PHOTOS PHSP; +0.04030 B_s0 e+ nu_e PHOTOS PHSP; +0.05060 B_s*0 e+ nu_e PHOTOS PHSP; +0.00340 B0 e+ nu_e PHOTOS PHSP; +0.00580 B*0 e+ nu_e PHOTOS PHSP; +# +0.01900 J/psi mu+ nu_mu PHOTOS PHSP; +0.00094 psi(2S) mu+ nu_mu PHOTOS PHSP; +0.00750 eta_c mu+ nu_mu PHOTOS PHSP; +0.00020 eta_c(2S) mu+ nu_mu PHOTOS PHSP; +0.00004 D0 mu+ nu_mu PHOTOS PHSP; +0.00018 D*0 mu+ nu_mu PHOTOS PHSP; +0.04030 B_s0 mu+ nu_mu PHOTOS PHSP; +0.05060 B_s*0 mu+ nu_mu PHOTOS PHSP; +0.00340 B0 mu+ nu_mu PHOTOS PHSP; +0.00580 B*0 mu+ nu_mu PHOTOS PHSP; +# +0.00480 J/psi tau+ nu_tau PHSP; +0.00008 psi(2S) tau+ nu_tau PHSP; +0.00230 eta_c tau+ nu_tau PHSP; +0.000016 eta_c(2S) tau+ nu_tau PHSP; +0.00002 D0 tau+ nu_tau PHSP; +0.00008 D*0 tau+ nu_tau PHSP; +# +# Hadronic Decays +0.00200 eta_c pi+ PHSP; +0.00420 rho+ eta_c SVS; +0.00013 eta_c K+ PHSP; +0.00020 K*+ eta_c SVS; +0.00130 J/psi pi+ SVS; +0.00400 J/psi rho+ SVV_HELAMP 1.0 0.0 1.0 0.0 1.0 0.0; +0.00011 J/psi K+ SVS; +0.00022 J/psi K*+ SVV_HELAMP 1.0 0.0 1.0 0.0 1.0 0.0; +# +0.000053 D+ anti-D0 PHSP; +0.000075 anti-D*0 D+ SVS; +0.000049 D*+ anti-D0 SVS; +0.00033 D*+ anti-D*0 SVV_HELAMP 1.0 0.0 1.0 0.0 1.0 0.0; +0.0000048 D_s+ anti-D0 PHSP; +0.0000071 anti-D*0 D_s+ SVS; +0.0000045 D_s*+ anti-D0 SVS; +0.000026 D_s*+ anti-D*0 SVV_HELAMP 1.0 0.0 1.0 0.0 1.0 0.0; +# +0.0000003 D+ D0 PHSP; +0.0000003 D*0 D+ SVS; +0.0000004 D*+ D0 SVS; +0.0000016 D*0 D*+ SVV_HELAMP 1.0 0.0 1.0 0.0 1.0 0.0; +0.0000066 D_s+ D0 PHSP; +0.0000063 D*0 D_s+ SVS; +0.0000085 D_s*+ D0 SVS; +0.0000404 D_s*+ D*0 SVV_HELAMP 1.0 0.0 1.0 0.0 1.0 0.0; +# +0.00280 eta_c D_s+ PHSP; +0.00270 D_s*+ eta_c SVS; +0.00015 eta_c D+ PHSP; +0.00010 D*+ eta_c SVS; +0.00170 J/psi D_s+ SVS; +0.00670 J/psi D_s*+ SVV_HELAMP 1.0 0.0 1.0 0.0 1.0 0.0; +0.00009 J/psi D+ SVS; +0.00028 J/psi D*+ SVV_HELAMP 1.0 0.0 1.0 0.0 1.0 0.0; +# +0.16400 B_s0 pi+ PHSP; +0.07200 rho+ B_s0 SVS; +0.01060 B_s0 K+ PHSP; +0.00000 K*+ B_s0 SVS; +0.06500 B_s*0 pi+ SVS; +0.20200 B_s*0 rho+ SVV_HELAMP 1.0 0.0 1.0 0.0 1.0 0.0; +0.00370 B_s*0 K+ SVS; +0.00000 B_s*0 K*+ SVV_HELAMP 1.0 0.0 1.0 0.0 1.0 0.0; +0.01060 B0 pi+ PHSP; +0.00960 rho+ B0 SVS; +0.00070 B0 K+ PHSP; +0.00015 K*+ B0 SVS; +0.00950 B*0 pi+ SVS; +0.02570 B*0 rho+ SVV_HELAMP 1.0 0.0 1.0 0.0 1.0 0.0; +0.00055 B*0 K+ SVS; +0.00058 B*0 K*+ SVV_HELAMP 1.0 0.0 1.0 0.0 1.0 0.0; +0.00037 B+ pi0 PHSP; +0.00034 rho0 B+ SVS; +0.01980 B+ anti-K0 PHSP; +0.00430 K*0 B+ SVS; +0.00033 B*+ pi0 SVS; +0.00090 B*+ rho0 SVV_HELAMP 1.0 0.0 1.0 0.0 1.0 0.0; +0.01600 B*+ anti-K0 SVS; +0.01670 B*+ K*0 SVV_HELAMP 1.0 0.0 1.0 0.0 1.0 0.0; +# PR LHCb 09 Apr 2004 Add B_c+ -> rho0 pi+ + 0.00002 rho0 pi+ SVS; +0.06005 c anti-s PYTHIA 42; +# PR LHCb 27 Apr 2004 Add Pythia Modes +# +# ash 10/27/03 Do not if this is correct but filling out with unknown cs +#0.0200235 anti-cs_0 uu_0 PYTHIA 63; +#0.0400470 anti-cs_1 uu_1 PYTHIA 63; +# +Enddecay + + +# Add excited Lambda_c decays (R.J. Tesarek 12/9/03) +# Just a guess for the last two BR [Lambda_c(2593)+]. +Decay Lambda_c(2593)+ +0.096585366 Sigma_c++ pi- PHSP; #[Reconstructed PDG2011] +0.096585366 Sigma_c0 pi+ PHSP; #[Reconstructed PDG2011] +0.190000000 Lambda_c+ pi+ pi- PHSP; #[Reconstructed PDG2011] +0.096585366 Sigma_c+ pi0 PHSP; #[Reconstructed PDG2011] +0.036219512 Lambda_c+ pi0 pi0 PHSP; #[Reconstructed PDG2011] +0.004024390 Lambda_c+ gamma PHSP; #[Reconstructed PDG2011] +0.240000000 Sigma_c*++ pi- PHSP; #[New mode added] #[Reconstructed PDG2011] +0.240000000 Sigma_c*0 pi+ PHSP; #[New mode added] #[Reconstructed PDG2011] +Enddecay +Decay anti-Lambda_c(2593)- +0.096585366 anti-Sigma_c-- pi+ PHSP; #[Reconstructed PDG2011] +0.096585366 anti-Sigma_c0 pi- PHSP; #[Reconstructed PDG2011] +0.190000000 anti-Lambda_c- pi- pi+ PHSP; #[Reconstructed PDG2011] +0.096585366 anti-Sigma_c- pi0 PHSP; #[Reconstructed PDG2011] +0.036219512 anti-Lambda_c- pi0 pi0 PHSP; #[Reconstructed PDG2011] +0.004024390 anti-Lambda_c- gamma PHSP; #[Reconstructed PDG2011] +0.240000000 anti-Sigma_c*-- pi+ PHSP; #[New mode added] #[Reconstructed PDG2011] +0.240000000 anti-Sigma_c*0 pi- PHSP; #[New mode added] #[Reconstructed PDG2011] +Enddecay +# +Decay Lambda_c(2625)+ +0.670000000 Lambda_c+ pi+ pi- PHSP; #[Reconstructed PDG2011] +0.320294118 Lambda_c+ pi0 PHSP; #[Reconstructed PDG2011] +0.009705882 Lambda_c+ gamma PHSP; #[Reconstructed PDG2011] +Enddecay +Decay anti-Lambda_c(2625)- +0.670000000 anti-Lambda_c- pi- pi+ PHSP; #[Reconstructed PDG2011] +0.320294118 anti-Lambda_c- pi0 PHSP; #[Reconstructed PDG2011] +0.009705882 anti-Lambda_c- gamma PHSP; #[Reconstructed PDG2011] +Enddecay + +# Add excited B hadrons (LHCb PR 10/03/04) +# Reference : Pythia 6.205 decay table +# PDG Id = 5312 +Decay Xi'_b- + 1.0000 Xi_b- gamma PHSP; +Enddecay +Decay anti-Xi'_b+ + 1.0000 anti-Xi_b+ gamma PHSP; +Enddecay +# PDG Id = 5322 +Decay Xi'_b0 + 1.0000 Xi_b0 gamma PHSP; +Enddecay +Decay anti-Xi'_b0 + 1.0000 anti-Xi_b0 gamma PHSP; +Enddecay +# PDG Id = 10521 +Decay B_0*+ + 0.6670 B0 pi+ PHSP; + 0.3330 B+ pi0 PHSP; +Enddecay +Decay B_0*- + 0.6670 anti-B0 pi- PHSP; + 0.3330 B- pi0 PHSP; +Enddecay +# PDG Id = 20523 Broad : S wave +Decay B'_1+ + 0.6670 B*0 pi+ VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; + 0.3330 B*+ pi0 VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +Enddecay +Decay B'_1- + 0.6670 anti-B*0 pi- VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; + 0.3330 B*- pi0 VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +Enddecay +# PDG Id = 10523 Narrow : D wave +Decay B_1+ + 0.6670 B*0 pi+ VVS_PWAVE 0.0 0.0 0.0 0.0 1.0 0.0; + 0.3330 B*+ pi0 VVS_PWAVE 0.0 0.0 0.0 0.0 1.0 0.0; +Enddecay +Decay B_1- + 0.6670 anti-B*0 pi- VVS_PWAVE 0.0 0.0 0.0 0.0 1.0 0.0; + 0.3330 B*- pi0 VVS_PWAVE 0.0 0.0 0.0 0.0 1.0 0.0; +Enddecay +# PDG Id = 525 Narrow : D wave +Decay B_2*+ + 0.3000 B0 pi+ TSS; + 0.1500 B+ pi0 TSS; + 0.1600 B*0 pi+ TVS_PWAVE 0.0 0.0 1.0 0.0 0.0 0.0; + 0.0800 B*+ pi0 TVS_PWAVE 0.0 0.0 1.0 0.0 0.0 0.0; + 0.1300 B*0 pi+ pi0 PHSP; + 0.0600 B*+ pi+ pi- PHSP; + 0.0800 B0 pi+ pi0 PHSP; + 0.0400 B+ pi+ pi- PHSP; +Enddecay +Decay B_2*- + 0.3000 anti-B0 pi- TSS; + 0.1500 B- pi0 TSS; + 0.1600 anti-B*0 pi- TVS_PWAVE 0.0 0.0 1.0 0.0 0.0 0.0; + 0.0800 B*- pi0 TVS_PWAVE 0.0 0.0 1.0 0.0 0.0 0.0; + 0.1300 anti-B*0 pi- pi0 PHSP; + 0.0600 B*- pi- pi+ PHSP; + 0.0800 anti-B0 pi- pi0 PHSP; + 0.0400 B- pi- pi+ PHSP; +Enddecay +# PDG Id = 10511 +Decay B_0*0 + 0.6670 B+ pi- PHSP; + 0.3330 B0 pi0 PHSP; +Enddecay +Decay anti-B_0*0 + 0.6670 B- pi+ PHSP; + 0.3330 anti-B0 pi0 PHSP; +Enddecay +# PDG Id = 20513 Broad : S wave +Decay B'_10 + 0.6670 B*+ pi- VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; + 0.3330 B*0 pi0 VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +Enddecay +Decay anti-B'_10 + 0.6670 B*- pi+ VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; + 0.3330 anti-B*0 pi0 VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +Enddecay +# PDG Id = 10513 Narrow : D wave +Decay B_10 + 0.6670 B*+ pi- VVS_PWAVE 0.0 0.0 0.0 0.0 1.0 0.0; + 0.3330 B*0 pi0 VVS_PWAVE 0.0 0.0 0.0 0.0 1.0 0.0; +Enddecay +Decay anti-B_10 + 0.6670 B*- pi+ VVS_PWAVE 0.0 0.0 0.0 0.0 1.0 0.0; + 0.3330 anti-B*0 pi0 VVS_PWAVE 0.0 0.0 0.0 0.0 1.0 0.0; +Enddecay +# PDG Id = 515 +Decay B_2*0 + 0.3000 B+ pi- TSS; + 0.1500 B0 pi0 TSS; + 0.1600 B*+ pi- TVS_PWAVE 0.0 0.0 1.0 0.0 0.0 0.0; + 0.0800 B*0 pi0 TVS_PWAVE 0.0 0.0 1.0 0.0 0.0 0.0; + 0.1300 B*+ pi- pi0 PHSP; + 0.0600 B*0 pi+ pi- PHSP; + 0.0800 B+ pi- pi0 PHSP; + 0.0400 B0 pi+ pi- PHSP; +Enddecay +Decay anti-B_2*0 + 0.3000 B- pi+ TSS; + 0.1500 anti-B0 pi0 TSS; + 0.1600 B*- pi+ TVS_PWAVE 0.0 0.0 1.0 0.0 0.0 0.0; + 0.0800 anti-B*0 pi0 TVS_PWAVE 0.0 0.0 1.0 0.0 0.0 0.0; + 0.1300 B*- pi+ pi0 PHSP; + 0.0600 anti-B*0 pi- pi+ PHSP; + 0.0800 B- pi+ pi0 PHSP; + 0.0400 anti-B0 pi- pi+ PHSP; +Enddecay +# PDG Id = 10531 +Decay B_s0*0 + 0.5000 B+ K- PHSP; + 0.5000 B0 anti-K0 PHSP; +Enddecay +Decay anti-B_s0*0 + 0.5000 B- K+ PHSP; + 0.5000 anti-B0 K0 PHSP; +Enddecay +# PDG Id = 20533 Broad : S wave +Decay B'_s10 + 0.5000 B*+ K- VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; + 0.5000 B*0 anti-K0 VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +Enddecay +Decay anti-B'_s10 + 0.5000 B*- K+ VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; + 0.5000 anti-B*0 K0 VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +Enddecay +# PDG Id = 10533 Narrow : D wave +Decay B_s10 + 0.5000 B*+ K- VVS_PWAVE 0.0 0.0 0.0 0.0 1.0 0.0; + 0.5000 B*0 anti-K0 VVS_PWAVE 0.0 0.0 0.0 0.0 1.0 0.0; +Enddecay +Decay anti-B_s10 + 0.5000 B*- K+ VVS_PWAVE 0.0 0.0 0.0 0.0 1.0 0.0; + 0.5000 anti-B*0 K0 VVS_PWAVE 0.0 0.0 0.0 0.0 1.0 0.0; +Enddecay +# PDG Id = 535 Narrow : D wave +Decay B_s2*0 + 0.3000 B+ K- TSS; + 0.3000 B0 anti-K0 TSS; + 0.2000 B*+ K- TVS_PWAVE 0.0 0.0 1.0 0.0 0.0 0.0; + 0.2000 B*0 anti-K0 TVS_PWAVE 0.0 0.0 1.0 0.0 0.0 0.0; +Enddecay +Decay anti-B_s2*0 + 0.3000 B- K+ TSS; + 0.3000 anti-B0 K0 TSS; + 0.2000 B*- K+ TVS_PWAVE 0.0 0.0 1.0 0.0 0.0 0.0; + 0.2000 anti-B*0 K0 TVS_PWAVE 0.0 0.0 1.0 0.0 0.0 0.0; +Enddecay +# PDG Id = 543 +Decay B_c*+ + 1.0000 B_c+ gamma VSP_PWAVE; +Enddecay +Decay B_c*- + 1.0000 B_c- gamma VSP_PWAVE; +Enddecay +# PDG Id = 545 Narrow : D wave +Decay B_c2*+ + 0.3000 B0 D+ TSS; + 0.3000 B+ D0 TSS; + 0.2000 B*0 D+ TVS_PWAVE 0.0 0.0 1.0 0.0 0.0 0.0; + 0.2000 B*+ D0 TVS_PWAVE 0.0 0.0 1.0 0.0 0.0 0.0; +Enddecay +Decay B_c2*- + 0.3000 anti-B0 D- TSS; + 0.3000 B- anti-D0 TSS; + 0.2000 anti-B*0 D- TVS_PWAVE 0.0 0.0 1.0 0.0 0.0 0.0; + 0.2000 B*- anti-D0 TVS_PWAVE 0.0 0.0 1.0 0.0 0.0 0.0; +Enddecay +# PDG Id = 5114 +Decay Sigma_b*- + 1.0000 Lambda_b0 pi- PHSP; +Enddecay +Decay anti-Sigma_b*+ + 1.0000 anti-Lambda_b0 pi+ PHSP; +Enddecay +# PDG Id = 5224 +Decay Sigma_b*+ + 1.0000 Lambda_b0 pi+ PHSP; +Enddecay +Decay anti-Sigma_b*- + 1.0000 anti-Lambda_b0 pi- PHSP; +Enddecay +# PDG Id = 5214 +Decay Sigma_b*0 + 1.0000 Lambda_b0 pi0 PHSP; +Enddecay +Decay anti-Sigma_b*0 + 1.0000 anti-Lambda_b0 pi0 PHSP; +Enddecay +# PDG Id = 5314 +Decay Xi_b*- + 1.0000 Xi_b- gamma PHSP; +Enddecay +Decay anti-Xi_b*+ + 1.0000 anti-Xi_b+ gamma PHSP; +Enddecay +# PDG Id = 5324 +Decay Xi_b*0 + 1.0000 Xi_b0 gamma PHSP; +Enddecay +Decay anti-Xi_b*0 + 1.0000 anti-Xi_b0 gamma PHSP; +Enddecay +# PDG Id = 5334 +Decay Omega_b*- + 1.0000 Omega_b- gamma PHSP; +Enddecay +Decay anti-Omega_b*+ + 1.0000 anti-Omega_b+ gamma PHSP; +Enddecay +# PDG Id = 10541 +Decay B_c0*+ + 0.5000 B0 D+ PHSP; + 0.5000 B+ D0 PHSP; +Enddecay +Decay B_c0*- + 0.5000 anti-B0 D- PHSP; + 0.5000 B- anti-D0 PHSP; +Enddecay +# PDG Id = 10543 Narrow : D wave +Decay B_c1+ + 0.5000 B*0 D+ VVS_PWAVE 0.0 0.0 0.0 0.0 1.0 0.0; + 0.5000 B*+ D0 VVS_PWAVE 0.0 0.0 0.0 0.0 1.0 0.0; +Enddecay +Decay B_c1- + 0.5000 anti-B*0 D- VVS_PWAVE 0.0 0.0 0.0 0.0 1.0 0.0; + 0.5000 B*- anti-D0 VVS_PWAVE 0.0 0.0 0.0 0.0 1.0 0.0; +Enddecay +# PDG Id = 20543 Broad : S wave +Decay B'_c1+ + 0.5000 B*0 D+ VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; + 0.5000 B*+ D0 VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +Enddecay +Decay B'_c1- + 0.5000 anti-B*0 D- VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; + 0.5000 B*- anti-D0 VVS_PWAVE 1.0 0.0 0.0 0.0 0.0 0.0; +Enddecay + +# Excited charmed Baryons LHCb PR 3-05-2006 +Decay Xi_cc*++ + 0.08 e+ nu_e Xi_c+ PHSP ; + 0.08 mu+ nu_mu Xi_c+ PHSP ; +0.51 u anti-d s cu_0 PYTHIA 42; +0.25 u anti-d s cu_1 PYTHIA 42; +0.05 u anti-s s cu_0 PYTHIA 42; +0.03 u anti-s s cu_1 PYTHIA 42; +Enddecay + +Decay anti-Xi_cc*-- + 0.08 e- anti-nu_e anti-Xi_c- PHSP; + 0.08 mu- anti-nu_mu anti-Xi_c- PHSP; +0.51 anti-u d anti-s anti-cu_0 PYTHIA 42; +0.25 anti-u d anti-s anti-cu_0 PYTHIA 42; +0.05 anti-u s anti-s anti-cu_1 PYTHIA 42; +0.03 anti-u s anti-s anti-cu_1 PYTHIA 42; +Enddecay + +Decay Xi_cc++ + 0.08 e+ nu_e Xi_c+ PHSP ; + 0.08 mu+ nu_mu Xi_c+ PHSP ; +0.51 u anti-d s cu_0 PYTHIA 42; +0.25 u anti-d s cu_1 PYTHIA 42; +0.05 u anti-s s cu_0 PYTHIA 42; +0.03 u anti-s s cu_1 PYTHIA 42; +Enddecay + +Decay anti-Xi_cc-- + 0.08 e- anti-nu_e anti-Xi_c- PHSP; + 0.08 mu- anti-nu_mu anti-Xi_c- PHSP; +0.51 anti-u d anti-s anti-cu_0 PYTHIA 42; +0.25 anti-u d anti-s anti-cu_0 PYTHIA 42; +0.05 anti-u s anti-s anti-cu_1 PYTHIA 42; +0.03 anti-u s anti-s anti-cu_1 PYTHIA 42; +Enddecay + +Decay Xi_cc*+ + 0.08 e+ nu_e Xi_c0 PHSP; + 0.08 mu+ nu_mu Xi_c0 PHSP; +0.51 u anti-d s cd_0 PYTHIA 42; +0.25 u anti-d s cd_1 PYTHIA 42; +0.05 u anti-s s cd_0 PYTHIA 42; +0.03 u anti-s s cd_1 PYTHIA 42; +Enddecay + +Decay anti-Xi_cc*- + 0.08 e- anti-nu_e anti-Xi_c0 PHSP; + 0.08 mu- anti-nu_mu anti-Xi_c0 PHSP; +0.51 anti-u d anti-s anti-cd_0 PYTHIA 42; +0.25 anti-u d anti-s anti-cd_1 PYTHIA 42; +0.05 anti-u s anti-s anti-cd_0 PYTHIA 42; +0.03 anti-u s anti-s anti-cd_1 PYTHIA 42; +Enddecay + +Decay Xi_cc+ + 0.08 e+ nu_e Xi_c0 PHSP; + 0.08 mu+ nu_mu Xi_c0 PHSP; +0.51 u anti-d s cd_0 PYTHIA 42; +0.25 u anti-d s cd_1 PYTHIA 42; +0.05 u anti-s s cd_0 PYTHIA 42; +0.03 u anti-s s cd_1 PYTHIA 42; +Enddecay + +Decay anti-Xi_cc- + 0.08 e- anti-nu_e anti-Xi_c0 PHSP; + 0.08 mu- anti-nu_mu anti-Xi_c0 PHSP; +0.51 anti-u d anti-s anti-cd_0 PYTHIA 42; +0.25 anti-u d anti-s anti-cd_1 PYTHIA 42; +0.05 anti-u s anti-s anti-cd_0 PYTHIA 42; +0.03 anti-u s anti-s anti-cd_1 PYTHIA 42; +Enddecay + +Decay Omega_cc+ + 0.08 e+ nu_e Omega_c0 PHSP; + 0.08 mu+ nu_mu Omega_c0 PHSP; +0.51 u anti-d s cs_0 PYTHIA 42; +0.25 u anti-d s cs_1 PYTHIA 42; +0.05 u anti-s s cs_0 PYTHIA 42; +0.03 u anti-s s cs_1 PYTHIA 42; +Enddecay + +Decay anti-Omega_cc- + 0.08 e- anti-nu_e anti-Omega_c0 PHSP; + 0.08 mu- anti-nu_mu anti-Omega_c0 PHSP; +0.51 anti-u d anti-s anti-cs_0 PYTHIA 42; +0.25 anti-u d anti-s anti-cs_1 PYTHIA 42; +0.05 anti-u s anti-s anti-cs_0 PYTHIA 42; +0.03 anti-u s anti-s anti-cs_1 PYTHIA 42; +Enddecay + +Decay Omega_cc*+ + 0.08 e+ nu_e Omega_c0 PHSP; + 0.08 mu+ nu_mu Omega_c0 PHSP; +0.51 u anti-d s cs_0 PYTHIA 42; +0.25 u anti-d s cs_1 PYTHIA 42; +0.05 u anti-s s cs_0 PYTHIA 42; +0.03 u anti-s s cs_1 PYTHIA 42; +Enddecay + +Decay anti-Omega_cc*- + 0.08 e- anti-nu_e anti-Omega_c0 PHSP; + 0.08 mu- anti-nu_mu anti-Omega_c0 PHSP; +0.51 anti-u d anti-s anti-cs_0 PYTHIA 42; +0.25 anti-u d anti-s anti-cs_1 PYTHIA 42; +0.05 anti-u s anti-s anti-cs_0 PYTHIA 42; +0.03 anti-u s anti-s anti-cs_1 PYTHIA 42; +Enddecay + +Decay K_L0 +#0.202464226 pi+ e- anti-nu_e PHSP; #[New mode added] #[Reconstructed PDG2011] +#0.202464226 pi- e+ nu_e PHSP; #[New mode added] #[Reconstructed PDG2011] +#0.135033299 pi+ mu- anti-nu_mu PHSP; #[New mode added] #[Reconstructed PDG2011] +#0.135033299 pi- mu+ nu_mu PHSP; #[New mode added] #[Reconstructed PDG2011] +#0.000025738 pi0 pi+ e- anti-nu_e PHSP; #[New mode added] #[Reconstructed PDG2011] +#0.000025738 pi0 pi- e+ nu_e PHSP; #[New mode added] #[Reconstructed PDG2011] +#0.000006205 pi+ e- anti-nu_e e+ e- PHSP; #[New mode added] #[Reconstructed PDG2011] +#0.000006205 pi- e+ nu_e e+ e- PHSP; #[New mode added] #[Reconstructed PDG2011] +#0.194795855 pi0 pi0 pi0 PHSP; #[New mode added] #[Reconstructed PDG2011] +#0.125231606 pi+ pi- pi0 PHSP; #[New mode added] #[Reconstructed PDG2011] +#0.001880711 pi+ e- anti-nu_e gamma PHSP; #[New mode added] #[Reconstructed PDG2011] +#0.001880711 pi- e+ nu_e gamma PHSP; #[New mode added] #[Reconstructed PDG2011] +#0.000277023 pi+ mu- anti-nu_mu gamma PHSP; #[New mode added] #[Reconstructed PDG2011] +#0.000277023 pi- mu+ nu_mu gamma PHSP; #[New mode added] #[Reconstructed PDG2011] +#0.000040995 pi+ pi- gamma PHSP; #[New mode added] #[Reconstructed PDG2011] +#0.000001262 pi0 gamma gamma PHSP; #[New mode added] #[Reconstructed PDG2011] +#0.000000016 pi0 gamma e+ e- PHSP; #[New mode added] #[Reconstructed PDG2011] +#0.000545653 gamma gamma PHSP; #[New mode added] #[Reconstructed PDG2011] +#0.000009265 e+ e- gamma PHSP; #[New mode added] #[Reconstructed PDG2011] +#0.000000355 mu+ mu- gamma PHSP; #[New mode added] #[Reconstructed PDG2011] +#0.000000584 e+ e- gamma gamma PHSP; #[New mode added] #[Reconstructed PDG2011] +#0.000000007 mu+ mu- gamma gamma PHSP; #[New mode added] #[Reconstructed PDG2011] +Enddecay + +End diff --git a/MC/config/PWGHF/pythia8/decayer/eventdecayer_charmbaryon.cfg b/MC/config/PWGHF/pythia8/decayer/eventdecayer_charmbaryon.cfg new file mode 100644 index 000000000..eb6fb800c --- /dev/null +++ b/MC/config/PWGHF/pythia8/decayer/eventdecayer_charmbaryon.cfg @@ -0,0 +1,6 @@ +### Lets the EventDecayer (EventDecayer.enable=true) decay the charm baryons that base.cfg keeps stable. +### Use it in a free EventDecayer.config slot next to the force_hadronic_charmbaryon*.cfg decay channels, +### e.g. DecayerPythia8.config[1]=force_hadronic_charmbaryon.cfg;EventDecayer.enable=true;EventDecayer.config[2]=eventdecayer_charmbaryon.cfg +4132:mayDecay = on # Xi_c0 +4232:mayDecay = on # Xi_c+ +4332:mayDecay = on # Omega_c0 diff --git a/MC/config/PWGHF/pythia8/decayer/force_hadronic_D.cfg b/MC/config/PWGHF/pythia8/decayer/force_hadronic_D.cfg index 369b8f234..62087061a 100644 --- a/MC/config/PWGHF/pythia8/decayer/force_hadronic_D.cfg +++ b/MC/config/PWGHF/pythia8/decayer/force_hadronic_D.cfg @@ -14,7 +14,7 @@ ## add Lc decays absent in PYTHIA8 decay table and set BRs from PDG for other 4122:oneChannel = 1 0.0196 100 2212 -313 4122:addChannel = 1 0.0108 100 2224 -321 -4122:addChannel = 1 0.022 100 3124 211 +4122:addChannel = 1 0.022 100 102134 211 4122:addChannel = 1 0.035 0 2212 -321 211 4122:addChannel = 1 0.0159 0 2212 311 4122:addChannel = 1 0.0130 0 3122 211 @@ -50,7 +50,6 @@ 411:onMode = off 421:onMode = off 431:onMode = off -4112:onMode = off 4122:onMode = off 4232:onMode = off 4132:onMode = off @@ -75,7 +74,7 @@ ### Lambda_c -> Delta K 4122:onIfMatch = 2224 321 ### Lambda_c -> Lambda(1520) pi -4122:onIfMatch = 3124 211 +4122:onIfMatch = 102134 211 ### Lambda_c -> p K pi 4122:onIfMatch = 2212 321 211 ### Lambda_c -> Lambda pi diff --git a/MC/config/PWGHF/pythia8/decayer/force_hadronic_Omegac_forceOmegaChannels.cfg b/MC/config/PWGHF/pythia8/decayer/force_hadronic_Omegac_forceOmegaChannels.cfg new file mode 100644 index 000000000..fbce30b80 --- /dev/null +++ b/MC/config/PWGHF/pythia8/decayer/force_hadronic_Omegac_forceOmegaChannels.cfg @@ -0,0 +1,26 @@ +Init:showChangedParticleData = on + +4332:mayDecay on # OmegaC +4332:tau0 = 0.0803 +4332:addChannel = 1 0.1062323 0 211 3334 +4332:addChannel = 1 0.0531161 0 111 211 3334 +4332:addChannel = 1 0.1381020 0 213 3334 +4332:addChannel = 1 0.0318697 0 -211 211 211 3334 +4332:addChannel = 1 0.2103399 0 -13 14 3334 +4332:addChannel = 1 0.2103399 0 -11 12 3334 +4332:addChannel = 1 0.0421960 0 113 211 3334 +4332:addChannel = 1 0.0435572 0 111 213 3334 +4332:addChannel = 1 0.0671506 0 211 223 3334 +4332:addChannel = 1 0.0453721 0 211 221 3334 +4332:addChannel = 1 0.0335753 0 -311 321 3334 +4332:addChannel = 1 0.0054446 0 111 211 221 3334 +4332:addChannel = 1 0.0070327 0 111 111 211 3334 +4332:addChannel = 1 0.0013612 0 111 113 211 3334 +4332:addChannel = 1 0.0015880 0 -211 211 213 3334 +4332:addChannel = 1 0.0004537 0 -211 211 211 221 3334 +4332:addChannel = 1 0.0002269 0 -211 111 111 211 211 3334 +4332:addChannel = 1 0.0002269 0 213 221 3334 +4332:addChannel = 1 0.0004537 0 111 111 213 3334 +4332:addChannel = 1 0.0009074 0 -213 211 211 3334 +4332:addChannel = 1 0.0002269 0 111 111 211 221 3334 +4332:addChannel = 1 0.0002269 0 -211 111 211 211 3334 diff --git a/MC/config/PWGHF/pythia8/decayer/force_hadronic_Xic0_Omegac.cfg b/MC/config/PWGHF/pythia8/decayer/force_hadronic_Xic0_Omegac.cfg new file mode 100644 index 000000000..457b52847 --- /dev/null +++ b/MC/config/PWGHF/pythia8/decayer/force_hadronic_Xic0_Omegac.cfg @@ -0,0 +1,44 @@ +################################################### +### author: Tiantian Cheng (tiantian.cheng@cern.ch) +### last update: March 2025 +################################################### + +Init:showChangedParticleData = on + +4332:tau0 = 0.0803 # Omega_c0 +4132:tau0 = 0.0455 # Xi_c0 + +4332:onMode = off +4132:onMode = off + +# Omega_c0 (4332) +4332:oneChannel = 1 0.4 0 3334 211 # Omega- pi+ +4332:addChannel = 1 0.4 0 3312 211 # Xi- pi+ +4332:addChannel = 1 0.2 0 3312 321 # Xi- K+ + +# Xi_c0 (4132) +4132:oneChannel = 1 0.4 0 3312 211 # Xi- pi+ +4132:addChannel = 1 0.4 0 3334 321 # Omega- K+ +4132:addChannel = 1 0.2 0 3312 321 # Xi- K+ + +# Xi- -> Lambda pi- +3312:onMode = off +3312:onIfAll = 3122 -211 + +# Omega- -> Lambda K- +3334:onMode = off +3334:onIfAll = 3122 -321 + +# Lambda -> p pi- +3122:onMode = off +3122:onIfAll = 2212 -211 + +# Omega_c0 +4332:onIfMatch = 3334 211 +4332:onIfMatch = 3312 211 +4332:onIfMatch = 3334 321 +4332:onIfMatch = 3312 321 + +# Xi_c0 +4132:onIfMatch = 3312 211 +4132:onIfMatch = 3334 321 diff --git a/MC/config/PWGHF/pythia8/decayer/force_hadronic_Xic_trigger.cfg b/MC/config/PWGHF/pythia8/decayer/force_hadronic_Xic_trigger.cfg new file mode 100644 index 000000000..c2d45d92e --- /dev/null +++ b/MC/config/PWGHF/pythia8/decayer/force_hadronic_Xic_trigger.cfg @@ -0,0 +1,37 @@ +# Decay configuration taken from MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_with_decays_Mode2_pp_ref.cfg +Init:showChangedParticleData = on +4132:tau0 = 0.0455 # Xic0 + +# switch off all decay channels +4232:onMode = off +4132:onMode = off + +## Xic decays +### Ξc+ -> Ξ- π+ π+ (90%) +4232:oneChannel = 1 0.029000 0 3312 211 211 ### Ξc+ -> Ξ- π+ π+ 2.86% +### Ξc+ -> Ξ*0 π+ (10%) +4232:addChannel = 1 0.002900 0 3324 211 + +### Ξc+ -> Ξ*0 π+, Ξ*0 -> Ξ- π+ +4232:onIfMatch = 3324 211 +### Ξc+ -> Ξ- π+ π+ +4232:onIfMatch = 3312 211 211 + +## Xic0 decays +### add Xic0 decays absent in PYTHIA8 decay table +4132:oneChannel = 1 0.7 0 3312 211 ### Xi_c()0 --> Xi- pi+ +4132:addChannel = 1 0.3 0 3334 321 ### Xi_c()0 --> Omega- K+ + +### Xic0 -> Xi- pi+ +4132:onIfMatch = 3312 211 +# Matching Exclusive Decay Channels: +# ------------------------------------------------------- +# Xi_c()0 --> Xi- pi+ 0.01432524810113947 +# -> PDG Codes: 3312, 211 +# Xi_c()0 --> Xi- pi+ pi+ pi- 0.04775082700379833 +# -> PDG Codes: 3312, 211, 211, -211 +4132:onIfMatch = 3334 321 + +### Ξc+ -> Ξ*0 π+, Ξ*0 -> Ξ- π+ +3324:onMode = off +3324:onIfMatch = 3312 211 diff --git a/MC/config/PWGHF/pythia8/decayer/force_hadronic_charmbaryon.cfg b/MC/config/PWGHF/pythia8/decayer/force_hadronic_charmbaryon.cfg new file mode 100644 index 000000000..0089831bc --- /dev/null +++ b/MC/config/PWGHF/pythia8/decayer/force_hadronic_charmbaryon.cfg @@ -0,0 +1,97 @@ +# Decay configuration taken from MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_with_decays_Mode2_pp_ref.cfg +Init:showChangedParticleData = on +4332:tau0 = 0.0803 # OmegaC +4132:tau0 = 0.0455 # Xic0 + +# switch off all decay channels +4232:onMode = off +4332:onMode = off +4132:onMode = off + +## Xic decays +### Ξc+ -> p K- π+ (30%) +4232:oneChannel = 1 0.1500 0 2212 -321 211 ### Ξc+ -> p K- π+ 6.18e-3 +4232:addChannel = 1 0.1500 0 2212 -313 ### Ξc+ -> p antiK*0(892) +### Ξc+ -> Ξ- π+ π+ (45%) +4232:addChannel = 1 0.4500 0 3312 211 211 ### Ξc+ -> Ξ- π+ π+ 2.9% +### Ξc+ -> p φ (10%) +4232:addChannel = 1 0.10000 0 2212 333 ### Ξc+ -> p φ +### Ξc+ -> sigma+ π+ π- (10%) +4232:addChannel = 1 0.10000 0 3222 -211 211 ### Ξc+ -> sigma+ π- π+ 1.37% +### Ξc+ -> Ξ*0 π+ (4.5%) +4232:addChannel = 1 0.045000 0 3324 211 + +### Ξc+ -> p K- π+ +4232:onIfMatch = 2212 321 211 +### Ξc+ -> p antiK*0(892) +4232:onIfMatch = 2212 313 +### Ξc+ -> p φ +4232:onIfMatch = 2212 333 +### Ξc+ -> sigma+ π+ π- (10%) +4232:onIfMatch = 3222 211 211 +### Ξc+ -> Ξ*0 π+, Ξ*0 -> Ξ- π+ +4232:onIfMatch = 3324 211 +### Ξc+ -> Ξ- π+ π+ +4232:onIfMatch = 3312 211 211 + +## Xic0 decays +### add Xic0 decays absent in PYTHIA8 decay table +4132:oneChannel = 1 0.7 0 3312 211 ### Xi_c()0 --> Xi- pi+ +4132:addChannel = 1 0.3 0 3334 321 ### Xi_c()0 --> Omega- K+ + +### Xic0 -> Xi- pi+ +4132:onIfMatch = 3312 211 +# Matching Exclusive Decay Channels: +# ------------------------------------------------------- +# Xi_c()0 --> Xi- pi+ 0.01432524810113947 +# -> PDG Codes: 3312, 211 +# Xi_c()0 --> Xi- pi+ pi+ pi- 0.04775082700379833 +# -> PDG Codes: 3312, 211, 211, -211 +4132:onIfMatch = 3334 321 + +## OmegaC decays +### add custom OmegaC decays absent in PYTHIA8 decay table +4332:oneChannel = 1 0.8 0 3334 211 +4332:addChannel = 1 0.1 0 3312 211 +4332:addChannel = 1 0.1 0 3334 321 + +### Omega_c -> Omega pi +4332:onIfMatch = 3334 211 +# Matching Exclusive Decay Channels: +# ------------------------------------------------------- +# Omega_c()0 --> Omega- pi+ None +# -> PDG Codes: 3334, 211 +# Omega_c()0 --> Omega- pi+ pi0 1.79939678284182 +# -> PDG Codes: 3334, 211, 111 +# Omega_c()0 --> Omega- pi- 2 pi+ 0.30954954954955 +# -> PDG Codes: 3334, -211, [2], 211 + +### Omega_c -> Xi pi +4332:onIfMatch = 3312 211 +# Matching Exclusive Decay Channels: +# ------------------------------------------------------- +# Omega_c()0 --> Xi- Kbar0 pi+ 2.12 +# -> PDG Codes: 3312, -311, 211 +# Omega_c()0 --> Xi- K- 2 pi+ 0.625932203389831 +# -> PDG Codes: 3312, -321, [2], 211 + +### Omega_c -> Omega Kaon +4332:onIfMatch = 3334 321 + +## Allow the decay of resonances in the decay chain +### K*0(892) -> K- π+ +313:onMode = off +313:onIfAll = 321 211 +### φ -> K+ K- +333:onMode = off +333:onIfAll = 321 321 +### for Xic0 -> pi Xi -> pi pi Lambda -> pi pi pi p +### and Omega_c -> pi Xi -> pi pi Lambda -> pi pi pi p +3312:onMode = off +3312:onIfAll = 3122 -211 +### for Omega_c -> pi Omega -> pi K Lambda -> pi K pi p +3334:onMode = off +3334:onIfAll = 3122 -321 +### Ξc+ -> Ξ*0 π+, Ξ*0 -> Ξ- π+ +3324:onMode = off +3324:onIfMatch = 3312 211 diff --git a/MC/config/PWGHF/pythia8/decayer/force_hadronic_charmbaryon_trigger.cfg b/MC/config/PWGHF/pythia8/decayer/force_hadronic_charmbaryon_trigger.cfg new file mode 100644 index 000000000..30b1f218c --- /dev/null +++ b/MC/config/PWGHF/pythia8/decayer/force_hadronic_charmbaryon_trigger.cfg @@ -0,0 +1,75 @@ +# Decay configuration taken from MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_with_decays_Mode2_pp_ref.cfg +Init:showChangedParticleData = on +4332:tau0 = 0.0803 # OmegaC +4132:tau0 = 0.0455 # Xic0 + +# switch off all decay channels +4232:onMode = off +4332:onMode = off +4132:onMode = off + +## Xic decays +### Ξc+ -> Ξ- π+ π+ (90%) +4232:oneChannel = 1 0.029000 0 3312 211 211 ### Ξc+ -> Ξ- π+ π+ 2.9% +### Ξc+ -> Ξ*0 π+ (10%) +4232:addChannel = 1 0.002900 0 3324 211 + +### Ξc+ -> Ξ*0 π+, Ξ*0 -> Ξ- π+ +4232:onIfMatch = 3324 211 +### Ξc+ -> Ξ- π+ π+ +4232:onIfMatch = 3312 211 211 + +## Xic0 decays +### add Xic0 decays absent in PYTHIA8 decay table +4132:oneChannel = 1 0.7 0 3312 211 ### Xi_c()0 --> Xi- pi+ +4132:addChannel = 1 0.3 0 3334 321 ### Xi_c()0 --> Omega- K+ + +### Xic0 -> Xi- pi+ +4132:onIfMatch = 3312 211 +# Matching Exclusive Decay Channels: +# ------------------------------------------------------- +# Xi_c()0 --> Xi- pi+ 0.01432524810113947 +# -> PDG Codes: 3312, 211 +# Xi_c()0 --> Xi- pi+ pi+ pi- 0.04775082700379833 +# -> PDG Codes: 3312, 211, 211, -211 +4132:onIfMatch = 3334 321 + +## OmegaC decays +### add custom OmegaC decays absent in PYTHIA8 decay table +4332:oneChannel = 1 0.8 0 3334 211 +4332:addChannel = 1 0.1 0 3312 211 +4332:addChannel = 1 0.1 0 3334 321 + +### Omega_c -> Omega pi +4332:onIfMatch = 3334 211 +# Matching Exclusive Decay Channels: +# ------------------------------------------------------- +# Omega_c()0 --> Omega- pi+ None +# -> PDG Codes: 3334, 211 +# Omega_c()0 --> Omega- pi+ pi0 1.79939678284182 +# -> PDG Codes: 3334, 211, 111 +# Omega_c()0 --> Omega- pi- 2 pi+ 0.30954954954955 +# -> PDG Codes: 3334, -211, [2], 211 + +### Omega_c -> Xi pi +4332:onIfMatch = 3312 211 +# Matching Exclusive Decay Channels: +# ------------------------------------------------------- +# Omega_c()0 --> Xi- Kbar0 pi+ 2.12 +# -> PDG Codes: 3312, -311, 211 +# Omega_c()0 --> Xi- K- 2 pi+ 0.625932203389831 +# -> PDG Codes: 3312, -321, [2], 211 + +### Omega_c -> Omega Kaon +4332:onIfMatch = 3334 321 + +## Allow the decay of resonances in the decay chain +### and Omega_c -> pi Xi -> pi pi Lambda -> pi pi pi p +3312:onMode = off +3312:onIfAll = 3122 -211 +### for Omega_c -> pi Omega -> pi K Lambda -> pi K pi p +3334:onMode = off +3334:onIfAll = 3122 -321 +### Ξc+ -> Ξ*0 π+ Ξ*0 -> Ξ- π+ +3324:onMode = off +3324:onIfMatch = 3312 211 diff --git a/MC/config/PWGHF/pythia8/decayer/geant4_externaldecayer_Xic_trigger.in b/MC/config/PWGHF/pythia8/decayer/geant4_externaldecayer_Xic_trigger.in new file mode 100644 index 000000000..3ba380d5f --- /dev/null +++ b/MC/config/PWGHF/pythia8/decayer/geant4_externaldecayer_Xic_trigger.in @@ -0,0 +1,68 @@ + +/control/verbose 2 +/mcVerbose/all 1 +/mcVerbose/geometryManager 1 +/mcVerbose/opGeometryManager 1 +/mcTracking/loopVerbose 1 +/mcVerbose/composedPhysicsList 2 +/mcVerbose/runAction 2 # For looping thresholds control +#/tracking/verbose 1 +#//control/cout/ignoreThreadsExcept 0 + +/mcPhysics/rangeCuts 0.001 mm +/mcRegions/setRangePrecision 5 +/mcTracking/skipNeutrino true +/mcDet/setIsMaxStepInLowDensityMaterials true +/mcDet/setMaxStepInLowDensityMaterials 10 m +/mcMagField/setConstDistance 1 mm +/mcDet/setIsZeroMagField true +/mcControl/useRootRandom true # couple G4 random seed to gRandom + +# optical + +/process/optical/verbose 0 +/process/optical/processActivation Scintillation 0 +/process/optical/processActivation OpWLS 0 +/process/optical/processActivation OpMieHG 0 +/process/optical/cerenkov/setTrackSecondariesFirst false +/mcMagField/stepperType NystromRK4 + +# PAI for TRD +# Geant4 VMC >= v3.2 +/mcPhysics/emModel/setEmModel PAI +/mcPhysics/emModel/setRegions TRD_Gas-mix +/mcPhysics/emModel/setParticles all +/mcPrimaryGenerator/skipUnknownParticles true # don't crash when seeing unknown ion etc. (issue warning) + +# +# Precise Msc for EMCAL +# +# Geant4 VMC >= v3.2 +/mcPhysics/emModel/setEmModel SpecialUrbanMsc +/mcPhysics/emModel/setRegions EMC_Lead$ EMC_Scintillator$ +/mcPhysics/emModel/setParticles e- e+ + +# combined transportation + Msc mode is currently broken for ALICE (Geant 10.2.0) +/process/em/transportationWithMsc Disabled + +# +# Adding extra lines for fixing tracking bias +# +/mcMagField/setDeltaIntersection 1.0e-05 mm +/mcMagField/setMinimumEpsilonStep 0.5e-05 +/mcMagField/setMaximumEpsilonStep 1.0e-05 +/mcMagField/printParameters + +# Change default parameters for killing looping particles +# +/mcPhysics/useHighLooperThresholds +/mcRun/setLooperThresholdImportantEnergy 100. MeV + +# Define media with the INCLXX physics list; here basically in all ITS media +#/mcVerbose/biasingConfigurationManager 3 +/mcPhysics/biasing/setModel inclxx +/mcPhysics/biasing/setRegions ITS_AIR$ ITS_WATER$ ITS_COPPER$ ITS_KAPTON(POLYCH2)$ ITS_GLUE_IBFPC$ ITS_CERAMIC$ ITS_K13D2U2k$ ITS_K13D2U120$ ITS_F6151B05M$ ITS_M60J3K$ ITS_M55J6K$ ITS_FGS003$ ITS_CarbonFleece$ ITS_PEEKCF30$ ITS_GLUE$ ITS_ALUMINUM$ ITS_INOX304$ ALPIDE_METALSTACK$ ALPIDE_SI$ +/mcPhysics/biasing/setParticles proton neutron pi+ pi- + +# external decayer +/mcPhysics/setExtDecayerSelection xi_c0 anti_xi_c0 xi_c+ anti_xi_c+ diff --git a/MC/config/PWGHF/pythia8/decayer/geant4_externaldecayer_charmbaryon.in b/MC/config/PWGHF/pythia8/decayer/geant4_externaldecayer_charmbaryon.in new file mode 100644 index 000000000..d82da4a38 --- /dev/null +++ b/MC/config/PWGHF/pythia8/decayer/geant4_externaldecayer_charmbaryon.in @@ -0,0 +1,68 @@ + +/control/verbose 2 +/mcVerbose/all 1 +/mcVerbose/geometryManager 1 +/mcVerbose/opGeometryManager 1 +/mcTracking/loopVerbose 1 +/mcVerbose/composedPhysicsList 2 +/mcVerbose/runAction 2 # For looping thresholds control +#/tracking/verbose 1 +#//control/cout/ignoreThreadsExcept 0 + +/mcPhysics/rangeCuts 0.001 mm +/mcRegions/setRangePrecision 5 +/mcTracking/skipNeutrino true +/mcDet/setIsMaxStepInLowDensityMaterials true +/mcDet/setMaxStepInLowDensityMaterials 10 m +/mcMagField/setConstDistance 1 mm +/mcDet/setIsZeroMagField true +/mcControl/useRootRandom true # couple G4 random seed to gRandom + +# optical + +/process/optical/verbose 0 +/process/optical/processActivation Scintillation 0 +/process/optical/processActivation OpWLS 0 +/process/optical/processActivation OpMieHG 0 +/process/optical/cerenkov/setTrackSecondariesFirst false +/mcMagField/stepperType NystromRK4 + +# PAI for TRD +# Geant4 VMC >= v3.2 +/mcPhysics/emModel/setEmModel PAI +/mcPhysics/emModel/setRegions TRD_Gas-mix +/mcPhysics/emModel/setParticles all +/mcPrimaryGenerator/skipUnknownParticles true # don't crash when seeing unknown ion etc. (issue warning) + +# +# Precise Msc for EMCAL +# +# Geant4 VMC >= v3.2 +/mcPhysics/emModel/setEmModel SpecialUrbanMsc +/mcPhysics/emModel/setRegions EMC_Lead$ EMC_Scintillator$ +/mcPhysics/emModel/setParticles e- e+ + +# combined transportation + Msc mode is currently broken for ALICE (Geant 10.2.0) +/process/em/transportationWithMsc Disabled + +# +# Adding extra lines for fixing tracking bias +# +/mcMagField/setDeltaIntersection 1.0e-05 mm +/mcMagField/setMinimumEpsilonStep 0.5e-05 +/mcMagField/setMaximumEpsilonStep 1.0e-05 +/mcMagField/printParameters + +# Change default parameters for killing looping particles +# +/mcPhysics/useHighLooperThresholds +/mcRun/setLooperThresholdImportantEnergy 100. MeV + +# Define media with the INCLXX physics list; here basically in all ITS media +#/mcVerbose/biasingConfigurationManager 3 +/mcPhysics/biasing/setModel inclxx +/mcPhysics/biasing/setRegions ITS_AIR$ ITS_WATER$ ITS_COPPER$ ITS_KAPTON(POLYCH2)$ ITS_GLUE_IBFPC$ ITS_CERAMIC$ ITS_K13D2U2k$ ITS_K13D2U120$ ITS_F6151B05M$ ITS_M60J3K$ ITS_M55J6K$ ITS_FGS003$ ITS_CarbonFleece$ ITS_PEEKCF30$ ITS_GLUE$ ITS_ALUMINUM$ ITS_INOX304$ ALPIDE_METALSTACK$ ALPIDE_SI$ +/mcPhysics/biasing/setParticles proton neutron pi+ pi- + +# external decayer +/mcPhysics/setExtDecayerSelection omega_c0 anti_omega_c0 xi_c0 anti_xi_c0 xi_c+ anti_xi_c+ diff --git a/MC/config/PWGHF/pythia8/decayer/geant4_externaldecayer_omegac.in b/MC/config/PWGHF/pythia8/decayer/geant4_externaldecayer_omegac.in new file mode 100644 index 000000000..0ea5f6938 --- /dev/null +++ b/MC/config/PWGHF/pythia8/decayer/geant4_externaldecayer_omegac.in @@ -0,0 +1,68 @@ + +/control/verbose 2 +/mcVerbose/all 1 +/mcVerbose/geometryManager 1 +/mcVerbose/opGeometryManager 1 +/mcTracking/loopVerbose 1 +/mcVerbose/composedPhysicsList 2 +/mcVerbose/runAction 2 # For looping thresholds control +#/tracking/verbose 1 +#//control/cout/ignoreThreadsExcept 0 + +/mcPhysics/rangeCuts 0.001 mm +/mcRegions/setRangePrecision 5 +/mcTracking/skipNeutrino true +/mcDet/setIsMaxStepInLowDensityMaterials true +/mcDet/setMaxStepInLowDensityMaterials 10 m +/mcMagField/setConstDistance 1 mm +/mcDet/setIsZeroMagField true +/mcControl/useRootRandom true # couple G4 random seed to gRandom + +# optical + +/process/optical/verbose 0 +/process/optical/processActivation Scintillation 0 +/process/optical/processActivation OpWLS 0 +/process/optical/processActivation OpMieHG 0 +/process/optical/cerenkov/setTrackSecondariesFirst false +/mcMagField/stepperType NystromRK4 + +# PAI for TRD +# Geant4 VMC >= v3.2 +/mcPhysics/emModel/setEmModel PAI +/mcPhysics/emModel/setRegions TRD_Gas-mix +/mcPhysics/emModel/setParticles all +/mcPrimaryGenerator/skipUnknownParticles true # don't crash when seeing unknown ion etc. (issue warning) + +# +# Precise Msc for EMCAL +# +# Geant4 VMC >= v3.2 +/mcPhysics/emModel/setEmModel SpecialUrbanMsc +/mcPhysics/emModel/setRegions EMC_Lead$ EMC_Scintillator$ +/mcPhysics/emModel/setParticles e- e+ + +# combined transportation + Msc mode is currently broken for ALICE (Geant 10.2.0) +/process/em/transportationWithMsc Disabled + +# +# Adding extra lines for fixing tracking bias +# +/mcMagField/setDeltaIntersection 1.0e-05 mm +/mcMagField/setMinimumEpsilonStep 0.5e-05 +/mcMagField/setMaximumEpsilonStep 1.0e-05 +/mcMagField/printParameters + +# Change default parameters for killing looping particles +# +/mcPhysics/useHighLooperThresholds +/mcRun/setLooperThresholdImportantEnergy 100. MeV + +# Define media with the INCLXX physics list; here basically in all ITS media +#/mcVerbose/biasingConfigurationManager 3 +/mcPhysics/biasing/setModel inclxx +/mcPhysics/biasing/setRegions ITS_AIR$ ITS_WATER$ ITS_COPPER$ ITS_KAPTON(POLYCH2)$ ITS_GLUE_IBFPC$ ITS_CERAMIC$ ITS_K13D2U2k$ ITS_K13D2U120$ ITS_F6151B05M$ ITS_M60J3K$ ITS_M55J6K$ ITS_FGS003$ ITS_CarbonFleece$ ITS_PEEKCF30$ ITS_GLUE$ ITS_ALUMINUM$ ITS_INOX304$ ALPIDE_METALSTACK$ ALPIDE_SI$ +/mcPhysics/biasing/setParticles proton neutron pi+ pi- + +# external decayer +/mcPhysics/setExtDecayerSelection omega_c0 anti_omega_c0 diff --git a/MC/config/PWGHF/pythia8/decayer/geant4_externaldecayer_omegac_xic.in b/MC/config/PWGHF/pythia8/decayer/geant4_externaldecayer_omegac_xic.in new file mode 100644 index 000000000..40fea08d1 --- /dev/null +++ b/MC/config/PWGHF/pythia8/decayer/geant4_externaldecayer_omegac_xic.in @@ -0,0 +1,68 @@ + +/control/verbose 2 +/mcVerbose/all 1 +/mcVerbose/geometryManager 1 +/mcVerbose/opGeometryManager 1 +/mcTracking/loopVerbose 1 +/mcVerbose/composedPhysicsList 2 +/mcVerbose/runAction 2 # For looping thresholds control +#/tracking/verbose 1 +#//control/cout/ignoreThreadsExcept 0 + +/mcPhysics/rangeCuts 0.001 mm +/mcRegions/setRangePrecision 5 +/mcTracking/skipNeutrino true +/mcDet/setIsMaxStepInLowDensityMaterials true +/mcDet/setMaxStepInLowDensityMaterials 10 m +/mcMagField/setConstDistance 1 mm +/mcDet/setIsZeroMagField true +/mcControl/useRootRandom true # couple G4 random seed to gRandom + +# optical + +/process/optical/verbose 0 +/process/optical/processActivation Scintillation 0 +/process/optical/processActivation OpWLS 0 +/process/optical/processActivation OpMieHG 0 +/process/optical/cerenkov/setTrackSecondariesFirst false +/mcMagField/stepperType NystromRK4 + +# PAI for TRD +# Geant4 VMC >= v3.2 +/mcPhysics/emModel/setEmModel PAI +/mcPhysics/emModel/setRegions TRD_Gas-mix +/mcPhysics/emModel/setParticles all +/mcPrimaryGenerator/skipUnknownParticles true # don't crash when seeing unknown ion etc. (issue warning) + +# +# Precise Msc for EMCAL +# +# Geant4 VMC >= v3.2 +/mcPhysics/emModel/setEmModel SpecialUrbanMsc +/mcPhysics/emModel/setRegions EMC_Lead$ EMC_Scintillator$ +/mcPhysics/emModel/setParticles e- e+ + +# combined transportation + Msc mode is currently broken for ALICE (Geant 10.2.0) +/process/em/transportationWithMsc Disabled + +# +# Adding extra lines for fixing tracking bias +# +/mcMagField/setDeltaIntersection 1.0e-05 mm +/mcMagField/setMinimumEpsilonStep 0.5e-05 +/mcMagField/setMaximumEpsilonStep 1.0e-05 +/mcMagField/printParameters + +# Change default parameters for killing looping particles +# +/mcPhysics/useHighLooperThresholds +/mcRun/setLooperThresholdImportantEnergy 100. MeV + +# Define media with the INCLXX physics list; here basically in all ITS media +#/mcVerbose/biasingConfigurationManager 3 +/mcPhysics/biasing/setModel inclxx +/mcPhysics/biasing/setRegions ITS_AIR$ ITS_WATER$ ITS_COPPER$ ITS_KAPTON(POLYCH2)$ ITS_GLUE_IBFPC$ ITS_CERAMIC$ ITS_K13D2U2k$ ITS_K13D2U120$ ITS_F6151B05M$ ITS_M60J3K$ ITS_M55J6K$ ITS_FGS003$ ITS_CarbonFleece$ ITS_PEEKCF30$ ITS_GLUE$ ITS_ALUMINUM$ ITS_INOX304$ ALPIDE_METALSTACK$ ALPIDE_SI$ +/mcPhysics/biasing/setParticles proton neutron pi+ pi- + +# external decayer +/mcPhysics/setExtDecayerSelection omega_c0 anti_omega_c0 xi_c0 anti_xi_c0 diff --git a/MC/config/PWGHF/pythia8/generator/pythia8_HFe_Mode2.cfg b/MC/config/PWGHF/pythia8/generator/pythia8_HFe_Mode2.cfg new file mode 100644 index 000000000..4f4dcb7bf --- /dev/null +++ b/MC/config/PWGHF/pythia8/generator/pythia8_HFe_Mode2.cfg @@ -0,0 +1,97 @@ +### authors: Fabrizio Grosa (fabrizio.grosa@cern.ch) +### Grazia Luparello (Grazia.Luparello@cern.ch) +### Antonio Palasciano (antonio.palasciano@cern.ch) +### Jonghan Park (jonghan@cern.ch) +### electrons from heavy-flavour hadrons and from light neutral mesons +### last update: August 2025 + +### beams +Beams:idA 2212 # proton +Beams:idB 2212 # proton +Beams:eCM 13600. # GeV + +### processes +SoftQCD:inelastic on # all inelastic processes + +### decays +ParticleDecays:limitTau0 on +ParticleDecays:tau0Max 10. + +### switching on Pythia Mode2 +ColourReconnection:mode 1 +ColourReconnection:allowDoubleJunRem off +ColourReconnection:m0 0.3 +ColourReconnection:allowJunctions on +ColourReconnection:junctionCorrection 1.20 +ColourReconnection:timeDilationMode 2 +ColourReconnection:timeDilationPar 0.18 +StringPT:sigma 0.335 +StringZ:aLund 0.36 +StringZ:bLund 0.56 +StringFlav:probQQtoQ 0.078 +StringFlav:ProbStoUD 0.2 +StringFlav:probQQ1toQQ0join 0.0275,0.0275,0.0275,0.0275 +MultiPartonInteractions:pT0Ref 2.15 +BeamRemnants:remnantMode 1 +BeamRemnants:saturation 5 + +# Correct decay lengths (wrong in PYTHIA8 decay table) +# Lb +5122:tau0 = 0.4390 +# Xic0 +4132:tau0 = 0.0455 +# OmegaC +4332:tau0 = 0.0803 + + +### switch off all decay channels +111:onMode = off +221:onMode = off + +411:onMode = off +421:onMode = off +431:onMode = off +4122:onMode = off +4132:onMode = off +4232:onMode = off +4332:onMode = off + +511:onMode = off +521:onMode = off +531:onMode = off +5122:onMode = off +5132:onMode = off +5232:onMode = off +5332:onMode = off + +###Semimuonic decays of charm + +### D+/- -> e + X +411:onIfAny = 11 +### D0 -> e + X +421:onIfAny = 11 +### D_s -> e + X +431:onIfAny = 11 +### Lambda_c -> e + X +4122:onIfAny = 11 +### Xsi0_c -> e + X +4132:onIfAny = 11 +### Xsi+_c -> e + X +4232:onIfAny = 11 +### Omega_c -> e + X +4332:onIfAny = 11 + +### B0 -> e + X +511:onIfAny = 11 +### B+/- -> e + X +521:onIfAny = 11 +### B_s -> e + X +531:onIfAny = 11 +### Lambda_b -> e + X +5122:onIfAny = 11 +### Xsi_b -> e + X +5132:onIfAny = 11 +### Xsi0_b -> e + X +5232:onIfAny = 11 +### Omega_b -> e + X +5332:onIfAny = 11 diff --git a/MC/config/PWGHF/pythia8/generator/pythia8_HFe_Mode2_pp_ref.cfg b/MC/config/PWGHF/pythia8/generator/pythia8_HFe_Mode2_pp_ref.cfg new file mode 100644 index 000000000..c13834cd5 --- /dev/null +++ b/MC/config/PWGHF/pythia8/generator/pythia8_HFe_Mode2_pp_ref.cfg @@ -0,0 +1,97 @@ +### authors: Fabrizio Grosa (fabrizio.grosa@cern.ch) +### Grazia Luparello (Grazia.Luparello@cern.ch) +### Antonio Palasciano (antonio.palasciano@cern.ch) +### Jonghan Park (jonghan@cern.ch) +### electrons from heavy-flavour hadrons and from light neutral mesons +### last update: August 2025 + +### beams +Beams:idA 2212 # proton +Beams:idB 2212 # proton +Beams:eCM 5360. # GeV + +### processes +SoftQCD:inelastic on # all inelastic processes + +### decays +ParticleDecays:limitTau0 on +ParticleDecays:tau0Max 10. + +### switching on Pythia Mode2 +ColourReconnection:mode 1 +ColourReconnection:allowDoubleJunRem off +ColourReconnection:m0 0.3 +ColourReconnection:allowJunctions on +ColourReconnection:junctionCorrection 1.20 +ColourReconnection:timeDilationMode 2 +ColourReconnection:timeDilationPar 0.18 +StringPT:sigma 0.335 +StringZ:aLund 0.36 +StringZ:bLund 0.56 +StringFlav:probQQtoQ 0.078 +StringFlav:ProbStoUD 0.2 +StringFlav:probQQ1toQQ0join 0.0275,0.0275,0.0275,0.0275 +MultiPartonInteractions:pT0Ref 2.15 +BeamRemnants:remnantMode 1 +BeamRemnants:saturation 5 + +# Correct decay lengths (wrong in PYTHIA8 decay table) +# Lb +5122:tau0 = 0.4390 +# Xic0 +4132:tau0 = 0.0455 +# OmegaC +4332:tau0 = 0.0803 + + +### switch off all decay channels +111:onMode = off +221:onMode = off + +411:onMode = off +421:onMode = off +431:onMode = off +4122:onMode = off +4132:onMode = off +4232:onMode = off +4332:onMode = off + +511:onMode = off +521:onMode = off +531:onMode = off +5122:onMode = off +5132:onMode = off +5232:onMode = off +5332:onMode = off + +###Semimuonic decays of charm + +### D+/- -> e + X +411:onIfAny = 11 +### D0 -> e + X +421:onIfAny = 11 +### D_s -> e + X +431:onIfAny = 11 +### Lambda_c -> e + X +4122:onIfAny = 11 +### Xsi0_c -> e + X +4132:onIfAny = 11 +### Xsi+_c -> e + X +4232:onIfAny = 11 +### Omega_c -> e + X +4332:onIfAny = 11 + +### B0 -> e + X +511:onIfAny = 11 +### B+/- -> e + X +521:onIfAny = 11 +### B_s -> e + X +531:onIfAny = 11 +### Lambda_b -> e + X +5122:onIfAny = 11 +### Xsi_b -> e + X +5132:onIfAny = 11 +### Xsi0_b -> e + X +5232:onIfAny = 11 +### Omega_b -> e + X +5332:onIfAny = 11 diff --git a/MC/config/PWGHF/pythia8/generator/pythia8_Mode2_noforcedecays.cfg b/MC/config/PWGHF/pythia8/generator/pythia8_Mode2_noforcedecays.cfg new file mode 100644 index 000000000..8109ace4c --- /dev/null +++ b/MC/config/PWGHF/pythia8/generator/pythia8_Mode2_noforcedecays.cfg @@ -0,0 +1,39 @@ +### authors: Fabrizio Grosa (fabrizio.grosa@cern.ch) + +### beams +Beams:idA 2212 # proton +Beams:idB 2212 # proton +Beams:eCM 13600. # GeV + +### processes +SoftQCD:inelastic on # all inelastic processes + +### decays +ParticleDecays:limitTau0 on +ParticleDecays:tau0Max 10. + +### switching on Pythia Mode2 +ColourReconnection:mode 1 +ColourReconnection:allowDoubleJunRem off +ColourReconnection:m0 0.3 +ColourReconnection:allowJunctions on +ColourReconnection:junctionCorrection 1.20 +ColourReconnection:timeDilationMode 2 +ColourReconnection:timeDilationPar 0.18 +StringPT:sigma 0.335 +StringZ:aLund 0.36 +StringZ:bLund 0.56 +StringFlav:probQQtoQ 0.078 +StringFlav:ProbStoUD 0.2 +StringFlav:probQQ1toQQ0join 0.0275,0.0275,0.0275,0.0275 +MultiPartonInteractions:pT0Ref 2.15 +BeamRemnants:remnantMode 1 +BeamRemnants:saturation 5 + +# Correct decay lengths (wrong in PYTHIA8 decay table) +# Lb +5122:tau0 = 0.4390 +# Xic0 +4132:tau0 = 0.0455 +# OmegaC +4332:tau0 = 0.0803 diff --git a/MC/config/PWGHF/pythia8/generator/pythia8_NonHfe.cfg b/MC/config/PWGHF/pythia8/generator/pythia8_NonHfe.cfg new file mode 100644 index 000000000..f16d04171 --- /dev/null +++ b/MC/config/PWGHF/pythia8/generator/pythia8_NonHfe.cfg @@ -0,0 +1,25 @@ +### authors: Rashi Gupta (rashi.gupta@cern.ch) +### authors: Ravindra Singh (ravindra.singh@cern.ch) +### electrons from pi0 and eta enhancement + +### beams +Beams:idA 2212 # proton +Beams:idB 2212 # proton +Beams:eCM 13600. # GeV + +### processes + + +SoftQCD:inelastic = on +### decays +ParticleDecays:limitTau0 on +ParticleDecays:tau0Max 10. +### processes + + +### switch off all decay channels +111:onMode = off +221:onMode = off + +111:onIfAny = 11 # pi0 -> e+ e- gamma +221:onIfAny = 11 # eta -> e+ e- gamma diff --git a/MC/config/PWGHF/pythia8/generator/pythia8_beauty_with_mudecays_Mode2.cfg b/MC/config/PWGHF/pythia8/generator/pythia8_beauty_with_mudecays_Mode2.cfg new file mode 100644 index 000000000..592ec72e8 --- /dev/null +++ b/MC/config/PWGHF/pythia8/generator/pythia8_beauty_with_mudecays_Mode2.cfg @@ -0,0 +1,71 @@ +### authors: Fabrizio Grosa (fabrizio.grosa@cern.ch) +### Cristina Terrevoli (cristina.terrevoli@cern.ch) +### Fabio Catalano (fabio.catalano@cern.ch) +### Nicole Bastid (nicole.bastid@cern.ch) +### Beauty decay muons +### last update: November 2024 + +### beams +Beams:idA 2212 # proton +Beams:idB 2212 # proton +Beams:eCM 13600. # GeV + +### processes +SoftQCD:inelastic on # all inelastic processes + +### decays +ParticleDecays:limitTau0 on +ParticleDecays:tau0Max 10. + +### switching on Pythia Mode2 +ColourReconnection:mode 1 +ColourReconnection:allowDoubleJunRem off +ColourReconnection:m0 0.3 +ColourReconnection:allowJunctions on +ColourReconnection:junctionCorrection 1.20 +ColourReconnection:timeDilationMode 2 +ColourReconnection:timeDilationPar 0.18 +StringPT:sigma 0.335 +StringZ:aLund 0.36 +StringZ:bLund 0.56 +StringFlav:probQQtoQ 0.078 +StringFlav:ProbStoUD 0.2 +StringFlav:probQQ1toQQ0join 0.0275,0.0275,0.0275,0.0275 +MultiPartonInteractions:pT0Ref 2.15 +BeamRemnants:remnantMode 1 +BeamRemnants:saturation 5 + +# Correct decay lengths (wrong in PYTHIA8 decay table) +# Lb +5122:tau0 = 0.4390 +# Xic0 +4132:tau0 = 0.0455 +# OmegaC +4332:tau0 = 0.0803 + + +### switch off all decay channels for charm +511:onMode = off +521:onMode = off +531:onMode = off +5122:onMode = off +5132:onMode = off +5232:onMode = off +5332:onMode = off + +###Semimuonic decays of charm + +### B0 -> mu X +511:onIfAny = 13 +### B+/- -> mu X +521:onIfAny = 13 +### B_s -> mu X +531:onIfAny = 13 +### Lambda_b -> mu X +5122:onIfAny = 13 +### Xsi_b -> mu X +5132:onIfAny = 13 +### Xsi0_b -> mu X +5232:onIfAny = 13 +### Omega_b -> mu X +5332:onIfAny = 13 diff --git a/MC/config/PWGHF/pythia8/generator/pythia8_beautyhadronic_with_BtoDK_Mode2.cfg b/MC/config/PWGHF/pythia8/generator/pythia8_beautyhadronic_with_BtoDK_Mode2.cfg new file mode 100644 index 000000000..4150664af --- /dev/null +++ b/MC/config/PWGHF/pythia8/generator/pythia8_beautyhadronic_with_BtoDK_Mode2.cfg @@ -0,0 +1,183 @@ +### authors: Fabrizio Grosa (fabrizio.grosa@cern.ch) +### Cristina Terrevoli (cristina.terrevoli@cern.ch) +### Fabio Catalano (fabio.catalano@cern.ch) +### last update: November 2023 + +### beams +Beams:idA 2212 # proton +Beams:idB 2212 # proton +Beams:eCM 13600. # GeV + +### processes +SoftQCD:inelastic on # all inelastic processes + +### decays +ParticleDecays:limitTau0 on +ParticleDecays:tau0Max 10. + +### switching on Pythia Mode2 +ColourReconnection:mode 1 +ColourReconnection:allowDoubleJunRem off +ColourReconnection:m0 0.3 +ColourReconnection:allowJunctions on +ColourReconnection:junctionCorrection 1.20 +ColourReconnection:timeDilationMode 2 +ColourReconnection:timeDilationPar 0.18 +StringPT:sigma 0.335 +StringZ:aLund 0.36 +StringZ:bLund 0.56 +StringFlav:probQQtoQ 0.078 +StringFlav:ProbStoUD 0.2 +StringFlav:probQQ1toQQ0join 0.0275,0.0275,0.0275,0.0275 +MultiPartonInteractions:pT0Ref 2.15 +BeamRemnants:remnantMode 1 +BeamRemnants:saturation 5 + +# Correct decay lengths (wrong in PYTHIA8 decay table) +# Lb +5122:tau0 = 0.4390 +# Xic0 +4132:tau0 = 0.0455 +# OmegaC +4332:tau0 = 0.0803 + +### Force golden charm hadrons decay modes for D2H studies +### add D0 decays absent in PYTHIA8 decay table and set BRs from PDG for other +421:oneChannel = 1 0.0389 0 -321 211 +421:addChannel = 1 0.00389 0 -321 211 111 +### add D+ decays absent in PYTHIA8 decay table and set BRs from PDG for other +411:oneChannel = 1 0.0752 0 -321 211 211 +411:addChannel = 1 0.0104 0 -313 211 +411:addChannel = 1 0.0156 0 311 211 +411:addChannel = 1 0.0752 0 333 211 # to have the same amount of D+->KKpi and D+->Kpipi +## add Lc decays absent in PYTHIA8 decay table and set BRs from PDG for other +4122:oneChannel = 1 0.0196 100 2212 -313 +4122:addChannel = 1 0.0108 100 2224 -321 +4122:addChannel = 1 0.022 100 102134 211 +4122:addChannel = 1 0.035 0 2212 -321 211 +4122:addChannel = 1 0.0159 0 2212 311 +### add Xic+ decays absent in PYTHIA8 decay table +4232:addChannel = 1 0.2 0 2212 -313 +4232:addChannel = 1 0.2 0 2212 -321 211 +4232:addChannel = 1 0.2 0 3324 211 +4232:addChannel = 1 0.2 0 3312 211 211 +### add Xic0 decays absent in PYTHIA8 decay table +4132:addChannel = 1 0.0143 0 3312 211 +### add OmegaC decays absent in PYTHIA8 decay table +4332:addChannel = 1 0.5 0 3334 211 +4332:addChannel = 1 0.5 0 3312 211 + +### K* -> K pi +313:onMode = off +313:onIfAll = 321 211 +### for Ds -> Phi pi+ +333:onMode = off +333:onIfAll = 321 321 +### for D0 -> rho0 pi+ k- +113:onMode = off +113:onIfAll = 211 211 +### for Lambda_c -> Delta++ K- +2224:onMode = off +2224:onIfAll = 2212 211 +### for Lambda_c -> Lambda(1520) K- +102134:onMode = off +102134:onIfAll = 2212 321 +### for Xic0 -> pi Xi -> pi pi Lambda -> pi pi pi p +### and Omega_c -> pi Xi -> pi pi Lambda -> pi pi pi p +3312:onMode = off +3312:onIfAll = 3122 -211 +3122:onMode = off +3122:onIfAll = 2212 -211 +### for Omega_c -> pi Omega -> pi K Lambda -> pi K pi p +3334:onMode = off +3334:onIfAll = 3122 -321 + +### switch off all decay channels +411:onMode = off +421:onMode = off +431:onMode = off +4122:onMode = off +4232:onMode = off +4132:onMode = off +443:onMode = off +4332:onMode = off +511:onMode = off +521:onMode = off +531:onMode = off +5122:onMode = off + +### D0 -> K pi +421:onIfMatch = 321 211 +### D0 -> K pi pi0 +421:onIfMatch = 321 211 111 + +### D+/- -> K pi pi +411:onIfMatch = 321 211 211 +### D+/- -> K* pi +411:onIfMatch = 313 211 +### D+/- -> phi pi +411:onIfMatch = 333 211 + +### D_s -> K K* +431:onIfMatch = 321 313 +### D_s -> Phi pi +431:onIfMatch = 333 211 + +### Lambda_c -> p K* +4122:onIfMatch = 2212 313 +### Lambda_c -> Delta K +4122:onIfMatch = 2224 321 +### Lambda_c -> Lambda(1520) pi +4122:onIfMatch = 102134 211 +### Lambda_c -> p K pi +4122:onIfMatch = 2212 321 211 +### Lambda_c -> pK0s +4122:onIfMatch = 2212 311 + +### Xic+ -> pK*0 +4232:onIfMatch = 2212 313 +### Xic+ -> p K- pi+ +4232:onIfMatch = 2212 321 211 +### Xic+ -> Xi*0 pi+, Xi*->Xi- pi+ +4232:onIfMatch = 3324 211 +### Xic+ -> Xi- pi+ pi+ +4232:onIfMatch = 3312 211 211 + +### Xic0 -> Xi- pi+ +4132:onIfMatch = 3312 211 + +### Omega_c -> Omega pi +4332:onIfMatch = 3334 211 +### Omega_c -> Xi pi +4332:onIfMatch = 3312 211 + +### Force also golden beauty hadrons decay modes for D2H studies +### add B0 decays +511:oneChannel = 1 0.5 0 -411 211 +511:addChannel = 1 0.5 0 -411 321 +### add B+ decays +521:oneChannel = 1 0.5 0 -421 211 +521:addChannel = 1 0.5 0 -421 321 +### add Bs decays +531:oneChannel = 1 0.5 0 -431 211 +531:addChannel = 1 0.5 0 -431 321 +### add Lb decays +5122:oneChannel = 1 0.5 0 4122 -211 +5122:addChannel = 1 0.5 0 4122 -321 + +### B0 -> D pi +511:onIfMatch = 411 211 +### B0 -> D K +511:onIfMatch = 411 321 +### B+ -> D0 pi +521:onIfMatch = 421 211 +### B+ -> D0 K +521:onIfMatch = 421 321 +### Bs -> Ds pi +531:onIfMatch = 431 211 +### Bs -> Ds rho +531:onIfMatch = 431 321 +### Lb -> Lc pi +5122:onIfMatch = 4122 211 +### Lb -> Lc K +5122:onIfMatch = 4122 321 diff --git a/MC/config/PWGHF/pythia8/generator/pythia8_beautyhadronic_with_decays_Mode2.cfg b/MC/config/PWGHF/pythia8/generator/pythia8_beautyhadronic_with_decays_Mode2.cfg new file mode 100644 index 000000000..0cb1bbaf5 --- /dev/null +++ b/MC/config/PWGHF/pythia8/generator/pythia8_beautyhadronic_with_decays_Mode2.cfg @@ -0,0 +1,171 @@ +### authors: Fabrizio Grosa (fabrizio.grosa@cern.ch) +### Cristina Terrevoli (cristina.terrevoli@cern.ch) +### Fabio Catalano (fabio.catalano@cern.ch) +### last update: November 2023 + +### beams +Beams:idA 2212 # proton +Beams:idB 2212 # proton +Beams:eCM 13600. # GeV + +### processes +SoftQCD:inelastic on # all inelastic processes + +### decays +ParticleDecays:limitTau0 on +ParticleDecays:tau0Max 10. + +### switching on Pythia Mode2 +ColourReconnection:mode 1 +ColourReconnection:allowDoubleJunRem off +ColourReconnection:m0 0.3 +ColourReconnection:allowJunctions on +ColourReconnection:junctionCorrection 1.20 +ColourReconnection:timeDilationMode 2 +ColourReconnection:timeDilationPar 0.18 +StringPT:sigma 0.335 +StringZ:aLund 0.36 +StringZ:bLund 0.56 +StringFlav:probQQtoQ 0.078 +StringFlav:ProbStoUD 0.2 +StringFlav:probQQ1toQQ0join 0.0275,0.0275,0.0275,0.0275 +MultiPartonInteractions:pT0Ref 2.15 +BeamRemnants:remnantMode 1 +BeamRemnants:saturation 5 + +# Correct decay lengths (wrong in PYTHIA8 decay table) +# Lb +5122:tau0 = 0.4390 +# Xic0 +4132:tau0 = 0.0455 +# OmegaC +4332:tau0 = 0.0803 + +### Force golden charm hadrons decay modes for D2H studies +### add D0 decays absent in PYTHIA8 decay table and set BRs from PDG for other +421:oneChannel = 1 0.0389 0 -321 211 +421:addChannel = 1 0.00389 0 -321 211 111 +### add D+ decays absent in PYTHIA8 decay table and set BRs from PDG for other +411:oneChannel = 1 0.0752 0 -321 211 211 +411:addChannel = 1 0.0104 0 -313 211 +411:addChannel = 1 0.0100 0 333 211 # to have the same amount of D+->KKpi and D+->Kpipi +## add Lc decays absent in PYTHIA8 decay table and set BRs from PDG for other +4122:oneChannel = 1 0.0196 100 2212 -313 +4122:addChannel = 1 0.0108 100 2224 -321 +4122:addChannel = 1 0.022 100 102134 211 +4122:addChannel = 1 0.035 0 2212 -321 211 + +### K* -> K pi +313:onMode = off +313:onIfAll = 321 211 +### for Ds -> Phi pi+ +333:onMode = off +333:onIfAll = 321 321 +### for D0 -> rho0 pi+ k- +113:onMode = off +113:onIfAll = 211 211 +### for Lambda_c -> Delta++ K- +2224:onMode = off +2224:onIfAll = 2212 211 +### for Lambda_c -> Lambda(1520) K- +102134:onMode = off +102134:onIfAll = 2212 321 + +### switch off all decay channels +411:onMode = off +421:onMode = off +431:onMode = off +4122:onMode = off +511:onMode = off +521:onMode = off +531:onMode = off +5122:onMode = off + +### D0 -> K pi +421:onIfMatch = 321 211 +### D0 -> K pi pi0 +421:onIfMatch = 321 211 111 + +### D+/- -> K pi pi +411:onIfMatch = 321 211 211 +### D+/- -> K* pi +411:onIfMatch = 313 211 +### D+/- -> phi pi +411:onIfMatch = 333 211 + +### D_s -> K K* +431:onIfMatch = 321 313 +### D_s -> Phi pi +431:onIfMatch = 333 211 + +### Lambda_c -> p K* +4122:onIfMatch = 2212 313 +### Lambda_c -> Delta K +4122:onIfMatch = 2224 321 +### Lambda_c -> Lambda(1520) pi +4122:onIfMatch = 102134 211 +### Lambda_c -> p K pi +4122:onIfMatch = 2212 321 211 + +### Force also golden beauty hadrons decay modes for D2H studies +### add B0 decays +511:oneChannel = 1 0.35 0 -411 211 +511:addChannel = 1 0.35 0 -413 211 +### add B+ decays +521:oneChannel = 1 0.7 0 -421 211 +### add Bs0 decays +531:oneChannel = 1 0.7 0 -431 211 +### add Lb decays +5122:oneChannel = 1 0.6 0 4122 -211 + +### we also add channels useful for studies of partly reconstructed decays / correlated backgrounds +### add B0 decays +511:addChannel = 1 0.1 0 -411 213 +511:addChannel = 1 0.1 0 -413 213 +511:addChannel = 1 0.05 0 -411 321 +511:addChannel = 1 0.05 0 -413 321 +### add B+ decays +521:addChannel = 1 0.1 0 -421 213 +521:addChannel = 1 0.1 0 -423 211 +521:addChannel = 1 0.1 0 -421 321 +### add Bs0 decays +531:addChannel = 1 0.1 0 -431 213 +531:addChannel = 1 0.1 0 -433 211 +531:addChannel = 1 0.1 0 -433 321 +### add Lb decays +5122:addChannel = 1 0.10 0 14122 -211 +5122:addChannel = 1 0.10 0 4124 -211 +5122:addChannel = 1 0.10 0 4212 -211 +5122:addChannel = 1 0.10 0 4122 -321 + +### B0 -> D pi +511:onIfMatch = 411 211 +### B0 -> D* pi +511:onIfMatch = 413 211 +### B0 -> D rho +511:onIfMatch = 411 213 +### B0 -> D rho +511:onIfMatch = 411 321 + +### B+ -> D0 pi +521:onIfMatch = 421 211 +### B+ -> D0 rho +521:onIfMatch = 421 213 +### B+ -> D0* pi +521:onIfMatch = 423 211 + +### Bs -> Ds pi +531:onIfMatch = 431 211 +### Bs -> Ds rho +531:onIfMatch = 431 213 +### Bs -> Ds* pi +531:onIfMatch = 433 211 + +### Lb -> Lc pi +5122:onIfMatch = 4122 211 +### Lb -> Lc(2595)+ pi +5122:onIfMatch = 14122 211 +### Lb -> Lc(2625)+ pi +5122:onIfMatch = 4124 211 +### Lb -> Sc(2455)+ pi +5122:onIfMatch = 4212 211 diff --git a/MC/config/PWGHF/pythia8/generator/pythia8_bminus_CRmode2.cfg b/MC/config/PWGHF/pythia8/generator/pythia8_bminus_CRmode2.cfg new file mode 100644 index 000000000..285f42a79 --- /dev/null +++ b/MC/config/PWGHF/pythia8/generator/pythia8_bminus_CRmode2.cfg @@ -0,0 +1,81 @@ +### beams +Beams:idA 2212 # proton +Beams:idB 2212 # proton +Beams:eCM 13600. # GeV + +### processes +SoftQCD:inelastic on # all inelastic processes + +### decays +ParticleDecays:limitTau0 on +ParticleDecays:tau0Max 10. + +### switching on Pythia Mode2 +ColourReconnection:mode 1 +ColourReconnection:allowDoubleJunRem off +ColourReconnection:m0 0.3 +ColourReconnection:allowJunctions on +ColourReconnection:junctionCorrection 1.20 +ColourReconnection:timeDilationMode 2 +ColourReconnection:timeDilationPar 0.18 +StringPT:sigma 0.335 +StringZ:aLund 0.36 +StringZ:bLund 0.56 +StringFlav:probQQtoQ 0.078 +StringFlav:ProbStoUD 0.2 +StringFlav:probQQ1toQQ0join 0.0275,0.0275,0.0275,0.0275 +MultiPartonInteractions:pT0Ref 2.15 +BeamRemnants:remnantMode 1 +BeamRemnants:saturation 5 +BeamRemnants:allowBeamJunction = off +BeamRemnants:beamJunction = off +ColourReconnection:allowDiquarkJunctionCR = off + +# Correct decay lengths +# B+ +521:tau0 = 0.4914 # PDG: 1.638 ps -> c*tau = 0.4914 mm + +## B+ (521) decay modes – at least 2 nucleons in the final states +521:onMode = off +521:oneChannel = 1 3e-07 0 -2112 -2112 111 2112 2212 +521:addChannel = 1 3e-07 0 -3122 -2212 213 2112 2212 +521:addChannel = 1 3e-07 0 -2212 -2112 111 323 2112 2212 +521:addChannel = 1 3e-07 0 -2212 -2112 -211 211 321 2112 2212 +521:addChannel = 1 2e-07 0 -3122 -2212 211 2112 2212 +521:addChannel = 1 2e-07 0 -3122 -2212 -211 111 111 211 211 221 2112 2212 +521:addChannel = 1 2e-07 0 -3122 -2112 111 2112 2212 +521:addChannel = 1 2e-07 0 -3122 -2112 -211 111 211 2112 2212 +521:addChannel = 1 2e-07 0 -2214 -2212 111 211 321 2112 2212 +521:addChannel = 1 2e-07 0 -2212 -2212 211 211 311 2112 2212 +521:addChannel = 1 2e-07 0 -2212 -2212 113 211 321 2112 2212 +521:addChannel = 1 2e-07 0 -2212 -2212 111 111 211 211 2112 2212 +521:addChannel = 1 2e-07 0 -2212 -2112 211 311 2112 2212 +521:addChannel = 1 2e-07 0 -2212 -2112 113 321 2112 2212 +521:addChannel = 1 2e-07 0 -2212 -2112 111 221 321 2112 2212 +521:addChannel = 1 2e-07 0 -2212 -2112 111 113 211 2112 2212 +521:addChannel = 1 2e-07 0 -2112 -2112 -211 211 311 2112 2212 +521:addChannel = 1 2e-07 0 -2212 -2112 -211 113 211 211 2112 2212 +521:addChannel = 1 2e-07 0 -2212 -2112 -211 111 211 211 2112 2212 +521:addChannel = 1 2e-07 0 -2112 -2112 221 223 2112 2212 +521:addChannel = 1 2e-07 0 -2112 -2112 111 221 2112 2212 +521:onIfMatch = -2112 -2112 111 2112 2212 +521:onIfMatch = -3122 -2212 213 2112 2212 +521:onIfMatch = -2212 -2112 111 323 2112 2212 +521:onIfMatch = -2212 -2112 -211 211 321 2112 2212 +521:onIfMatch = -3122 -2212 211 2112 2212 +521:onIfMatch = -3122 -2212 -211 111 111 211 211 221 2112 2212 +521:onIfMatch = -3122 -2112 111 2112 2212 +521:onIfMatch = -3122 -2112 -211 111 211 2112 2212 +521:onIfMatch = -2214 -2212 111 211 321 2112 2212 +521:onIfMatch = -2212 -2212 211 211 311 2112 2212 +521:onIfMatch = -2212 -2212 113 211 321 2112 2212 +521:onIfMatch = -2212 -2212 111 111 211 211 2112 2212 +521:onIfMatch = -2212 -2112 211 311 2112 2212 +521:onIfMatch = -2212 -2112 113 321 2112 2212 +521:onIfMatch = -2212 -2112 111 221 321 2112 2212 +521:onIfMatch = -2212 -2112 111 113 211 2112 2212 +521:onIfMatch = -2112 -2112 -211 211 311 2112 2212 +521:onIfMatch = -2212 -2112 -211 113 211 211 2112 2212 +521:onIfMatch = -2212 -2112 -211 111 211 211 2112 2212 +521:onIfMatch = -2112 -2112 221 223 2112 2212 +521:onIfMatch = -2112 -2112 111 221 2112 2212 diff --git a/MC/config/PWGHF/pythia8/generator/pythia8_cdeuteron_to_dkpi.cfg b/MC/config/PWGHF/pythia8/generator/pythia8_cdeuteron_to_dkpi.cfg new file mode 100644 index 000000000..788e56fe3 --- /dev/null +++ b/MC/config/PWGHF/pythia8/generator/pythia8_cdeuteron_to_dkpi.cfg @@ -0,0 +1,102 @@ +### author: Fabrizio Grosa (fabrizio.grosa@cern.ch) +### since: May 2026 + +### beams (not relevant) +Beams:idA 2212 # proton +Beams:idB 2212 # proton +Beams:eCM 13600. # GeV + +### processes +SoftQCD:inelastic on # all inelastic processes + +### decays +ParticleDecays:limitTau0 on +ParticleDecays:tau0Max 10. + +### add the c-deuteron +### id:all = name antiName spinType chargeType colType m0 mWidth mMin mMax tau0 +2010010020:all = CDeuteron AntiCDeuteron 1 3 0 3.226 0 3.226 3.226 0.5 +2010010020:mayDecay = on + +# same as Λc+ -> p K- π+ including resonances + a neutron +2010010020:oneChannel = 1 0.14400 0 2112 2212 -321 211 ### cd+ -> n p K- π+ (non-resonant) 3.5% +2010010020:addChannel = 1 0.08100 100 2112 2212 -313 ### cd+ -> n p K*0(892) 1.96% +2010010020:addChannel = 1 0.04500 100 2112 2224 -321 ### cd+ -> n Delta++ K- 1.08% +2010010020:addChannel = 1 0.09000 100 2112 102134 211 ### cd+ -> n Lambda(1520) K- 2.20e-3 + +### K*0(892) -> K- π+ +313:onMode = off +313:onIfAll = 321 211 +### for Λc -> Delta++ K- +2224:onMode = off +2224:onIfAll = 2212 211 +### for Λc -> Lambda(1520) K- +102134:onMode = off +102134:onIfAll = 2212 321 + +# Λb0 -> cd+ decays taken from PYTHIA +# NB: cd+ must always be the last daughter, not to screw up replacements in the generator +# 15% 2 body +5122:oneChannel = 1 0.15 0 2010010020 -2212 ### Λb0 -> cd+ anti-p +# 27% 3 body +5122:addChannel = 1 0.054 100 -2212 111 2010010020 ### Λb0 -> cd+ anti-p pi0 +5122:addChannel = 1 0.027 100 -2212 113 2010010020 ### Λb0 -> cd+ anti-p rho0 +5122:addChannel = 1 0.027 100 -2212 221 2010010020 ### Λb0 -> cd+ anti-p eta +5122:addChannel = 1 0.027 100 -2212 223 2010010020 ### Λb0 -> cd+ anti-p omega +5122:addChannel = 1 0.060 100 -2112 -211 2010010020 ### Λb0 -> cd+ anti-n0 pi- +5122:addChannel = 1 0.030 100 -2112 -213 2010010020 ### Λb0 -> cd+ anti-n0 rho- +5122:addChannel = 1 0.010 100 -2214 111 2010010020 ### Λb0 -> cd+ DeltaBar- pi0 +5122:addChannel = 1 0.005 100 -2214 113 2010010020 ### Λb0 -> cd+ DeltaBar- rho0 +5122:addChannel = 1 0.005 100 -2214 221 2010010020 ### Λb0 -> cd+ DeltaBar- eta +5122:addChannel = 1 0.005 100 -2214 223 2010010020 ### Λb0 -> cd+ DeltaBar- omega +5122:addChannel = 1 0.007 100 -2114 -211 2010010020 ### Λb0 -> cd+ DeltaBar0 pi- +5122:addChannel = 1 0.003 100 -2114 -213 2010010020 ### Λb0 -> cd+ DeltaBar0 rho- +5122:addChannel = 1 0.007 100 -2224 211 2010010020 ### Λb0 -> cd+ DeltaBar-- pi+ +5122:addChannel = 1 0.003 100 -2224 213 2010010020 ### Λb0 -> cd+ DeltaBar-- rho+ +5122:addChannel = 1 0.010 0 -3122 -321 2010010020 ### Λb0 -> cd+ Lambdabar0 K- +# 31% 4 body +5122:addChannel = 1 0.054 100 -2212 111 111 2010010020 ### Λb0 -> cd+ anti-p pi0 pi0 +5122:addChannel = 1 0.027 100 -2212 211 -211 2010010020 ### Λb0 -> cd+ anti-p pi+ pi- +5122:addChannel = 1 0.013 100 -2212 213 -211 2010010020 ### Λb0 -> cd+ anti-p rho+ pi- +5122:addChannel = 1 0.018 100 -2212 113 111 2010010020 ### Λb0 -> cd+ anti-p rho0 pi0 +5122:addChannel = 1 0.009 100 -2212 113 113 2010010020 ### Λb0 -> cd+ anti-p rho0 rho0 +5122:addChannel = 1 0.018 100 -2212 221 111 2010010020 ### Λb0 -> cd+ anti-p eta pi0 +5122:addChannel = 1 0.009 100 -2212 221 113 2010010020 ### Λb0 -> cd+ anti-p eta rho0 +5122:addChannel = 1 0.018 100 -2212 223 111 2010010020 ### Λb0 -> cd+ anti-p omega pi0 +5122:addChannel = 1 0.009 100 -2212 223 113 2010010020 ### Λb0 -> cd+ anti-p omega rho0 +5122:addChannel = 1 0.040 100 -2112 -211 111 2010010020 ### Λb0 -> cd+ anti-n0 pi- pi0 +5122:addChannel = 1 0.020 100 -2112 -211 113 2010010020 ### Λb0 -> cd+ anti-n0 pi- rho0 +5122:addChannel = 1 0.020 100 -2112 -213 111 2010010020 ### Λb0 -> cd+ anti-n0 rho- pi0 +5122:addChannel = 1 0.010 100 -2112 -213 113 2010010020 ### Λb0 -> cd+ anti-n0 rho- rho0 +5122:addChannel = 1 0.010 100 -2214 111 111 2010010020 ### Λb0 -> cd+ DeltaBar- pi0 pi0 +5122:addChannel = 1 0.005 100 -2214 113 111 2010010020 ### Λb0 -> cd+ DeltaBar- rho0 pi0 +5122:addChannel = 1 0.005 100 -2214 221 111 2010010020 ### Λb0 -> cd+ DeltaBar- eta pi0 +5122:addChannel = 1 0.005 100 -2214 223 111 2010010020 ### Λb0 -> cd+ DeltaBar- omega pi0 +5122:addChannel = 1 0.007 100 -2114 -211 111 2010010020 ### Λb0 -> cd+ DeltaBar0 pi- pi0 +5122:addChannel = 1 0.003 100 -2114 -213 111 2010010020 ### Λb0 -> cd+ DeltaBar0 rho- pi0 +5122:addChannel = 1 0.007 100 -2224 211 111 2010010020 ### Λb0 -> cd+ DeltaBar-- pi+ pi0 +5122:addChannel = 1 0.003 100 -2224 213 111 2010010020 ### Λb0 -> cd+ DeltaBar-- rho+ pi0 +5122:addChannel = 1 0.010 0 -3122 -321 111 2010010020 ### Λb0 -> cd+ Lambdabar0 K- pi0 +# 19% 5 body +5122:addChannel = 1 0.033 100 -2212 111 111 111 2010010020 ### Λb0 -> cd+ anti-p pi0 pi0 pi0 +5122:addChannel = 1 0.016 100 -2212 211 -211 111 2010010020 ### Λb0 -> cd+ anti-p pi+ pi- pi0 +5122:addChannel = 1 0.008 100 -2212 213 -211 111 2010010020 ### Λb0 -> cd+ anti-p rho+ pi- pi0 +5122:addChannel = 1 0.012 100 -2212 113 111 111 2010010020 ### Λb0 -> cd+ anti-p rho0 pi0 pi0 +5122:addChannel = 1 0.006 100 -2212 113 113 111 2010010020 ### Λb0 -> cd+ anti-p rho0 rho0 pi0 +5122:addChannel = 1 0.012 100 -2212 221 111 111 2010010020 ### Λb0 -> cd+ anti-p eta pi0 pi0 +5122:addChannel = 1 0.006 100 -2212 221 113 111 2010010020 ### Λb0 -> cd+ anti-p eta rho0 pi0 +5122:addChannel = 1 0.012 100 -2212 223 111 111 2010010020 ### Λb0 -> cd+ anti-p omega pi0 pi0 +5122:addChannel = 1 0.006 100 -2212 223 113 111 2010010020 ### Λb0 -> cd+ anti-p omega rho0 pi0 +5122:addChannel = 1 0.030 100 -2112 -211 111 111 2010010020 ### Λb0 -> cd+ anti-n0 pi- pi0 pi0 +5122:addChannel = 1 0.011 100 -2112 -211 113 111 2010010020 ### Λb0 -> cd+ anti-n0 pi- rho0 pi0 +5122:addChannel = 1 0.011 100 -2112 -213 111 111 2010010020 ### Λb0 -> cd+ anti-n0 rho- pi0 pi0 +5122:addChannel = 1 0.006 100 -2112 -213 113 111 2010010020 ### Λb0 -> cd+ anti-n0 rho- rho0 pi0 +5122:addChannel = 1 0.006 100 -2214 111 111 111 2010010020 ### Λb0 -> cd+ DeltaBar- pi0 pi0 pi0 +5122:addChannel = 1 0.002 100 -2214 113 111 111 2010010020 ### Λb0 -> cd+ DeltaBar- rho0 pi0 pi0 +5122:addChannel = 1 0.002 100 -2214 221 111 111 2010010020 ### Λb0 -> cd+ DeltaBar- eta pi0 pi0 +5122:addChannel = 1 0.002 100 -2214 223 111 111 2010010020 ### Λb0 -> cd+ DeltaBar- omega pi0 pi0 +5122:addChannel = 1 0.005 100 -2114 -211 111 111 2010010020 ### Λb0 -> cd+ DeltaBar0 pi- pi0 pi0 +5122:addChannel = 1 0.001 100 -2114 -213 111 111 2010010020 ### Λb0 -> cd+ DeltaBar0 rho- pi0 pi0 +5122:addChannel = 1 0.005 100 -2224 211 111 111 2010010020 ### Λb0 -> cd+ DeltaBar-- pi+ pi0 pi0 +5122:addChannel = 1 0.001 100 -2224 213 111 111 2010010020 ### Λb0 -> cd+ DeltaBar-- rho+ pi0 pi0 +5122:addChannel = 1 0.006 0 -3122 -321 111 111 2010010020 ### Λb0 -> cd+ Lambdabar0 K- pi0 pi0 diff --git a/MC/config/PWGHF/pythia8/generator/pythia8_charm_with_mudecays_Mode2.cfg b/MC/config/PWGHF/pythia8/generator/pythia8_charm_with_mudecays_Mode2.cfg new file mode 100644 index 000000000..dac759f3b --- /dev/null +++ b/MC/config/PWGHF/pythia8/generator/pythia8_charm_with_mudecays_Mode2.cfg @@ -0,0 +1,70 @@ +### authors: Fabrizio Grosa (fabrizio.grosa@cern.ch) +### Cristina Terrevoli (cristina.terrevoli@cern.ch) +### Fabio Catalano (fabio.catalano@cern.ch) +### Nicole Bastid (nicole.bastid@cern.ch) +### charm decay muons +### last update: November 2024 + +### beams +Beams:idA 2212 # proton +Beams:idB 2212 # proton +Beams:eCM 13600. # GeV + +### processes +SoftQCD:inelastic on # all inelastic processes + +### decays +ParticleDecays:limitTau0 on +ParticleDecays:tau0Max 10. + +### switching on Pythia Mode2 +ColourReconnection:mode 1 +ColourReconnection:allowDoubleJunRem off +ColourReconnection:m0 0.3 +ColourReconnection:allowJunctions on +ColourReconnection:junctionCorrection 1.20 +ColourReconnection:timeDilationMode 2 +ColourReconnection:timeDilationPar 0.18 +StringPT:sigma 0.335 +StringZ:aLund 0.36 +StringZ:bLund 0.56 +StringFlav:probQQtoQ 0.078 +StringFlav:ProbStoUD 0.2 +StringFlav:probQQ1toQQ0join 0.0275,0.0275,0.0275,0.0275 +MultiPartonInteractions:pT0Ref 2.15 +BeamRemnants:remnantMode 1 +BeamRemnants:saturation 5 + +# Correct decay lengths (wrong in PYTHIA8 decay table) +# Lb +5122:tau0 = 0.4390 +# Xic0 +4132:tau0 = 0.0455 +# OmegaC +4332:tau0 = 0.0803 + +### switch off all decay channels for charm +411:onMode = off +421:onMode = off +431:onMode = off +4122:onMode = off +4132:onMode = off +4232:onMode = off +4332:onMode = off + +###Semimuonic decays of charm + +### D+/- -> mu X +411:onIfAny = 13 +### D0 -> mu X +421:onIfAny = 13 +### D_s -> mu X +431:onIfAny = 13 +### Lambda_c -> mu X +4122:onIfAny = 13 +### Xsi0_c -> mu X +4132:onIfAny = 13 +### Xsi+_c +4232:onIfAny = 13 +### Omega_c -> mu X +4332:onIfAny = 13 diff --git a/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_OmegaC_NoDecay.cfg b/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_OmegaC_NoDecay.cfg new file mode 100644 index 000000000..20d4c2fb6 --- /dev/null +++ b/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_OmegaC_NoDecay.cfg @@ -0,0 +1,42 @@ +### configuration for Omega from OmegaC + +### beams +Beams:idA 2212 # proton +Beams:idB 2212 # proton +Beams:eCM 13600. # GeV + +### processes +SoftQCD:inelastic on # all inelastic processes + +### decays +ParticleDecays:limitTau0 on +ParticleDecays:tau0Max 10. + +### switching on Pythia Mode2 +ColourReconnection:mode 1 +ColourReconnection:allowDoubleJunRem off +ColourReconnection:m0 0.3 +ColourReconnection:allowJunctions on +ColourReconnection:junctionCorrection 1.20 +ColourReconnection:timeDilationMode 2 +ColourReconnection:timeDilationPar 0.18 +StringPT:sigma 0.335 +StringZ:aLund 0.36 +StringZ:bLund 0.56 +StringFlav:probQQtoQ 0.078 +StringFlav:ProbStoUD 0.2 +StringFlav:probQQ1toQQ0join 0.0275,0.0275,0.0275,0.0275 +MultiPartonInteractions:pT0Ref 2.15 +BeamRemnants:remnantMode 1 +BeamRemnants:saturation 5 + +# Correct decay lengths (wrong in PYTHIA8 decay table) +# Lb +5122:tau0 = 0.4390 +# Xic0 +4132:tau0 = 0.0455 +# OmegaC +4332:tau0 = 0.0803 + +### switch off OmegaC decays +4332:onMode = off diff --git a/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_OmegaC_NoDecay_Mode2_pp_ref.cfg b/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_OmegaC_NoDecay_Mode2_pp_ref.cfg new file mode 100644 index 000000000..60732359a --- /dev/null +++ b/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_OmegaC_NoDecay_Mode2_pp_ref.cfg @@ -0,0 +1,43 @@ +### author: Tiantian Cheng (tiantian.cheng@cern.ch) +### last update: August 2025 + +### beams +Beams:idA 2212 # proton +Beams:idB 2212 # proton +Beams:eCM 5360. # GeV + +### processes +SoftQCD:inelastic on # all inelastic processes + +### decays +ParticleDecays:limitTau0 on +ParticleDecays:tau0Max 10. + +### switching on Pythia Mode2 +ColourReconnection:mode 1 +ColourReconnection:allowDoubleJunRem off +ColourReconnection:m0 0.3 +ColourReconnection:allowJunctions on +ColourReconnection:junctionCorrection 1.20 +ColourReconnection:timeDilationMode 2 +ColourReconnection:timeDilationPar 0.18 +StringPT:sigma 0.335 +StringZ:aLund 0.36 +StringZ:bLund 0.56 +StringFlav:probQQtoQ 0.078 +StringFlav:ProbStoUD 0.2 +StringFlav:probQQ1toQQ0join 0.0275,0.0275,0.0275,0.0275 +MultiPartonInteractions:pT0Ref 2.15 +BeamRemnants:remnantMode 1 +BeamRemnants:saturation 5 + +# Correct decay lengths (wrong in PYTHIA8 decay table) +# Lb +5122:tau0 = 0.4390 +# Xic0 +4132:tau0 = 0.0455 +# OmegaC +4332:tau0 = 0.0803 + +### switch off OmegaC decays +4332:onMode = off diff --git a/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_XiC_NoDecay.cfg b/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_XiC_NoDecay.cfg new file mode 100644 index 000000000..6ce71ed4f --- /dev/null +++ b/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_XiC_NoDecay.cfg @@ -0,0 +1,42 @@ +### configuration for XiC + +### beams +Beams:idA 2212 # proton +Beams:idB 2212 # proton +Beams:eCM 13600. # GeV + +### processes +SoftQCD:inelastic on # all inelastic processes + +### decays +ParticleDecays:limitTau0 on +ParticleDecays:tau0Max 10. + +### switching on Pythia Mode2 +ColourReconnection:mode 1 +ColourReconnection:allowDoubleJunRem off +ColourReconnection:m0 0.3 +ColourReconnection:allowJunctions on +ColourReconnection:junctionCorrection 1.20 +ColourReconnection:timeDilationMode 2 +ColourReconnection:timeDilationPar 0.18 +StringPT:sigma 0.335 +StringZ:aLund 0.36 +StringZ:bLund 0.56 +StringFlav:probQQtoQ 0.078 +StringFlav:ProbStoUD 0.2 +StringFlav:probQQ1toQQ0join 0.0275,0.0275,0.0275,0.0275 +MultiPartonInteractions:pT0Ref 2.15 +BeamRemnants:remnantMode 1 +BeamRemnants:saturation 5 + +#Correct decay lengths(wrong in PYTHIA8 decay table) +#Lb +5122:tau0 = 0.4390 +#Xic0 +4132:tau0 = 0.0455 +#OmegaC +4332:tau0 = 0.0803 + +### switch off XiC0 decays +4132:onMode = off diff --git a/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_XiC_NoDecay_Mode2_pp_ref.cfg b/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_XiC_NoDecay_Mode2_pp_ref.cfg new file mode 100644 index 000000000..7ca299325 --- /dev/null +++ b/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_XiC_NoDecay_Mode2_pp_ref.cfg @@ -0,0 +1,43 @@ +### author: Tiantian Cheng (tiantian.cheng@cern.ch) +### last update: August 2025 + +### beams +Beams:idA 2212 # proton +Beams:idB 2212 # proton +Beams:eCM 5360. # GeV + +### processes +SoftQCD:inelastic on # all inelastic processes + +### decays +ParticleDecays:limitTau0 on +ParticleDecays:tau0Max 10. + +### switching on Pythia Mode2 +ColourReconnection:mode 1 +ColourReconnection:allowDoubleJunRem off +ColourReconnection:m0 0.3 +ColourReconnection:allowJunctions on +ColourReconnection:junctionCorrection 1.20 +ColourReconnection:timeDilationMode 2 +ColourReconnection:timeDilationPar 0.18 +StringPT:sigma 0.335 +StringZ:aLund 0.36 +StringZ:bLund 0.56 +StringFlav:probQQtoQ 0.078 +StringFlav:ProbStoUD 0.2 +StringFlav:probQQ1toQQ0join 0.0275,0.0275,0.0275,0.0275 +MultiPartonInteractions:pT0Ref 2.15 +BeamRemnants:remnantMode 1 +BeamRemnants:saturation 5 + +# Correct decay lengths (wrong in PYTHIA8 decay table) +# Lb +5122:tau0 = 0.4390 +# Xic0 +4132:tau0 = 0.0455 +# OmegaC +4332:tau0 = 0.0803 + +### switch off Xic0 decays +4132:onMode = off diff --git a/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_XiCplus_NoDecay.cfg b/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_XiCplus_NoDecay.cfg new file mode 100644 index 000000000..51771aee8 --- /dev/null +++ b/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_XiCplus_NoDecay.cfg @@ -0,0 +1,42 @@ +### configuration for XiC + +### beams +Beams:idA 2212 # proton +Beams:idB 2212 # proton +Beams:eCM 13600. # GeV + +### processes +SoftQCD:inelastic on # all inelastic processes + +### decays +ParticleDecays:limitTau0 on +ParticleDecays:tau0Max 10. + +### switching on Pythia Mode2 +ColourReconnection:mode 1 +ColourReconnection:allowDoubleJunRem off +ColourReconnection:m0 0.3 +ColourReconnection:allowJunctions on +ColourReconnection:junctionCorrection 1.20 +ColourReconnection:timeDilationMode 2 +ColourReconnection:timeDilationPar 0.18 +StringPT:sigma 0.335 +StringZ:aLund 0.36 +StringZ:bLund 0.56 +StringFlav:probQQtoQ 0.078 +StringFlav:ProbStoUD 0.2 +StringFlav:probQQ1toQQ0join 0.0275,0.0275,0.0275,0.0275 +MultiPartonInteractions:pT0Ref 2.15 +BeamRemnants:remnantMode 1 +BeamRemnants:saturation 5 + +#Correct decay lengths(wrong in PYTHIA8 decay table) +#Lb +5122:tau0 = 0.4390 +#Xic0 +4132:tau0 = 0.0455 +#OmegaC +4332:tau0 = 0.0803 + +### switch off XiC+ decays +4232:onMode = off diff --git a/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_XiCplus_NoDecay_Mode2_pp_ref.cfg b/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_XiCplus_NoDecay_Mode2_pp_ref.cfg new file mode 100644 index 000000000..34e80be24 --- /dev/null +++ b/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_XiCplus_NoDecay_Mode2_pp_ref.cfg @@ -0,0 +1,43 @@ +### author: Tiantian Cheng (tiantian.cheng@cern.ch) +### last update: August 2025 + +### beams +Beams:idA 2212 # proton +Beams:idB 2212 # proton +Beams:eCM 5360. # GeV + +### processes +SoftQCD:inelastic on # all inelastic processes + +### decays +ParticleDecays:limitTau0 on +ParticleDecays:tau0Max 10. + +### switching on Pythia Mode2 +ColourReconnection:mode 1 +ColourReconnection:allowDoubleJunRem off +ColourReconnection:m0 0.3 +ColourReconnection:allowJunctions on +ColourReconnection:junctionCorrection 1.20 +ColourReconnection:timeDilationMode 2 +ColourReconnection:timeDilationPar 0.18 +StringPT:sigma 0.335 +StringZ:aLund 0.36 +StringZ:bLund 0.56 +StringFlav:probQQtoQ 0.078 +StringFlav:ProbStoUD 0.2 +StringFlav:probQQ1toQQ0join 0.0275,0.0275,0.0275,0.0275 +MultiPartonInteractions:pT0Ref 2.15 +BeamRemnants:remnantMode 1 +BeamRemnants:saturation 5 + +# Correct decay lengths (wrong in PYTHIA8 decay table) +# Lb +5122:tau0 = 0.4390 +# Xic0 +4132:tau0 = 0.0455 +# OmegaC +4332:tau0 = 0.0803 + +### switch off Xic+ decays +4232:onMode = off diff --git a/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_decays_Mode2_hardQCD_5TeV_XicOmegaC.cfg b/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_decays_Mode2_hardQCD_5TeV_XicOmegaC.cfg new file mode 100644 index 000000000..84ac21536 --- /dev/null +++ b/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_decays_Mode2_hardQCD_5TeV_XicOmegaC.cfg @@ -0,0 +1,156 @@ +### authors: Fabrizio Grosa (fabrizio.grosa@cern.ch) +### Ruiqi Yin (ruiqi.yin@cern.ch) +### last update: October 2025 + +### beams +Beams:idA 2212 # proton +Beams:idB 2212 # proton +Beams:eCM 5360. # GeV + +### processes: c-cbar and b-bbar processes +HardQCD:hardccbar on +HardQCD:hardbbbar on +HardQCD:gg2ccbar on +HardQCD:qqbar2ccbar on +HardQCD:gg2bbbar on +HardQCD:qqbar2bbbar on + +### decays +ParticleDecays:limitTau0 on +ParticleDecays:tau0Max 10. + +### switching on Pythia Mode2 +ColourReconnection:mode 1 +ColourReconnection:allowDoubleJunRem off +ColourReconnection:m0 0.3 +ColourReconnection:allowJunctions on +ColourReconnection:junctionCorrection 1.20 +ColourReconnection:timeDilationMode 2 +ColourReconnection:timeDilationPar 0.18 +StringPT:sigma 0.335 +StringZ:aLund 0.36 +StringZ:bLund 0.56 +StringFlav:probQQtoQ 0.078 +StringFlav:ProbStoUD 0.2 +StringFlav:probQQ1toQQ0join 0.0275,0.0275,0.0275,0.0275 +MultiPartonInteractions:pT0Ref 2.15 +BeamRemnants:remnantMode 1 +BeamRemnants:saturation 5 + +# Correct decay lengths (wrong in PYTHIA8 decay table) +# Lb +5122:tau0 = 0.4390 +# Xic0 +4132:tau0 = 0.0455 +# OmegaC +4332:tau0 = 0.0803 + +### Force charm baryon decay modes (Ξc and Ωc) +### D mesons use default PYTHIA8 branching ratios + +## Λc decays (optional, can be kept for reference) +### Λc+ -> p K- π+ (36%) +4122:oneChannel = 1 0.14400 0 2212 -321 211 ### Λc+ -> p K- π+ (non-resonant) 3.5% +4122:addChannel = 1 0.08100 100 2212 -313 ### Λc+ -> p K*0(892) 1.96% +4122:addChannel = 1 0.04500 100 2224 -321 ### Λc+ -> Delta++ K- 1.08% +4122:addChannel = 1 0.09000 100 102134 211 ### Λc+ -> Lambda(1520) K- 2.20e-3 +### Λc+ -> p K0S (36%) +4122:addChannel = 1 0.36000 0 2212 311 ### Λc+ -> p K0S 1.59% +### Λc+ -> p K- π+ π0 (small, 3%) +4122:addChannel = 1 0.03000 0 2212 -321 211 111 ### Λc+ -> p K- π+ π0 (non-resonant) 4.6% +### Λc+ -> p π- π+ (12.50%) +4122:addChannel = 1 0.12500 0 2212 -211 211 ### Λc+ -> p π+ π+ 4.59% +### Λc+ -> p K- K+ (12.50%) +4122:addChannel = 1 0.12500 0 2212 333 ### Λc+ -> p phi 1.06% + +## Xic decays +### Ξc+ -> p K- π+ (35%) +4232:oneChannel = 1 0.17500 0 2212 -321 211 ### Ξc+ -> p K- π+ 6.18e-3 +4232:addChannel = 1 0.17500 0 2212 -313 ### Ξc+ -> p antiK*0(892) +### Ξc+ -> Ξ- π+ π+ (35%) (set the same as Ξc+ -> p K- π+) +4232:addChannel = 1 0.35000 0 3312 211 211 ### Ξc+ -> Ξ- π+ π+ 2.86% +### Ξc+ -> p φ (10%) +4232:addChannel = 1 0.10000 0 2212 333 ### Ξc+ -> p φ +### Ξc+ -> sigma+ π+ π- (10%) +4232:addChannel = 1 0.12500 0 3222 -211 211 ### Ξc+ -> sigma+ π- π+ 1.37% +### Ξc+ -> Ξ*0 π+ (10%) +4232:addChannel = 1 0.12500 0 3324 211 + +### add Xic0 decays absent in PYTHIA8 decay table +4132:oneChannel = 1 0.0143 0 3312 211 + +### add OmegaC decays absent in PYTHIA8 decay table +4332:oneChannel = 1 0.5 0 3334 211 +4332:addChannel = 1 0.5 0 3312 211 + +# Allow the decay of resonances in the decay chain (only for charm baryons) +### for Λc -> Delta++ K- +2224:onMode = off +2224:onIfAll = 2212 211 +### for Λc -> Lambda(1520) K- +102134:onMode = off +102134:onIfAll = 2212 321 +### for Λc -> p K*0(892) +313:onMode = off +313:onIfAll = 321 211 +### for Λc -> p phi +333:onMode = off +333:onIfAll = 321 321 +### for Xic0 -> pi Xi -> pi pi Lambda -> pi pi pi p +### and Omega_c -> pi Xi -> pi pi Lambda -> pi pi pi p +3312:onMode = off +3312:onIfAll = 3122 -211 +3122:onMode = off +3122:onIfAll = 2212 -211 +### for Omega_c -> pi Omega -> pi K Lambda -> pi K pi p +3334:onMode = off +3334:onIfAll = 3122 -321 + +# Switch off charm baryon decay channels (D mesons use PYTHIA8 defaults) +4122:onMode = off +4232:onMode = off +4132:onMode = off +4332:onMode = off + +# Allow the decay of charm baryons +### Λc -> pK0s +4122:onIfMatch = 2212 311 +### Λc -> p K- π+ π0 +4122:onIfMatch = 2212 321 211 +### Λc -> p K* +4122:onIfMatch = 2212 313 +### Λc -> Delta++ K +4122:onIfMatch = 2224 321 +### Λc -> Lambda(1520) π +4122:onIfMatch = 102134 211 +### Λc -> p K- π+ π0 +4122:onIfMatch = 2212 321 211 111 +### Λc -> p π π +4122:onIfMatch = 2212 211 211 +### Λc -> p K K +4122:onIfMatch = 2212 333 + +### Ξc+ -> p K- π+ +4232:onIfMatch = 2212 321 211 +### Ξc+ -> p antiK*0(892) +4232:onIfMatch = 2212 313 +### Ξc+ -> p φ +4232:onIfMatch = 2212 333 +### Ξc+ -> sigma- π+ π+ +4232:onIfMatch = 3222 211 211 +### Ξc+ -> Ξ*0 π+, Ξ*0 -> Ξ- π+ +4232:onIfMatch = 3324 211 +### Ξc+ -> Ξ- π+ π+ +4232:onIfMatch = 3312 211 211 + +### Xic0 -> Xi- pi+ +4132:onIfMatch = 3312 211 +### Xic0 -> Omega- Ka+ +4132:onIfMatch = 3334 321 + +### Omega_c -> Omega pi +4332:onIfMatch = 3334 211 +### Omega_c -> Xi pi +4332:onIfMatch = 3312 211 +### Omega_c -> Omega- Ka+ +4332:onIfMatch = 3334 321 \ No newline at end of file diff --git a/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_with_decays.cfg b/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_with_decays.cfg new file mode 100644 index 000000000..5b0f72979 --- /dev/null +++ b/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_with_decays.cfg @@ -0,0 +1,130 @@ +### authors: Fabrizio Grosa (fabrizio.grosa@cern.ch) +### Cristina Terrevoli (cristina.terrevoli@cern.ch) +### Fabio Catalano (fabio.catalano@cern.ch) +### last update: November 2023 + +### beams +Beams:idA 2212 # proton +Beams:idB 2212 # proton +Beams:eCM 13600. # GeV + +### processes +SoftQCD:inelastic on # all inelastic processes + +### decays +ParticleDecays:limitTau0 on +ParticleDecays:tau0Max 10. + +# Correct decay lengths (wrong in PYTHIA8 decay table) +# Lb +5122:tau0 = 0.4390 +# Xic0 +4132:tau0 = 0.0455 +# OmegaC +4332:tau0 = 0.0803 + +### Force golden charm hadrons decay modes for D2H studies +### add D0 decays absent in PYTHIA8 decay table and set BRs from PDG for other +421:oneChannel = 1 0.0389 0 -321 211 +421:addChannel = 1 0.00389 0 -321 211 111 +### add D+ decays absent in PYTHIA8 decay table and set BRs from PDG for other +411:oneChannel = 1 0.0752 0 -321 211 211 +411:addChannel = 1 0.0104 0 -313 211 +411:addChannel = 1 0.0156 0 311 211 +411:addChannel = 1 0.0752 0 333 211 # to have the same amount of D+->KKpi and D+->Kpipi +## add Lc decays absent in PYTHIA8 decay table and set BRs from PDG for other +4122:oneChannel = 1 0.0196 100 2212 -313 +4122:addChannel = 1 0.0108 100 2224 -321 +4122:addChannel = 1 0.022 100 102134 211 +4122:addChannel = 1 0.035 0 2212 -321 211 +4122:addChannel = 1 0.0159 0 2212 311 +### add Xic+ decays absent in PYTHIA8 decay table +4232:addChannel = 1 0.2 0 2212 -313 +4232:addChannel = 1 0.2 0 2212 -321 211 +4232:addChannel = 1 0.2 0 3324 211 +4232:addChannel = 1 0.2 0 3312 211 211 +### add Xic0 decays absent in PYTHIA8 decay table +4132:addChannel = 1 0.0143 0 3312 211 +### add OmegaC decays absent in PYTHIA8 decay table +4332:addChannel = 1 0.5 0 3334 211 +4332:addChannel = 1 0.5 0 3312 211 + +### K* -> K pi +313:onMode = off +313:onIfAll = 321 211 +### for Ds -> Phi pi+ +333:onMode = off +333:onIfAll = 321 321 +### for D0 -> rho0 pi+ k- +113:onMode = off +113:onIfAll = 211 211 +### for Lambda_c -> Delta++ K- +2224:onMode = off +2224:onIfAll = 2212 211 +### for Lambda_c -> Lambda(1520) K- +102134:onMode = off +102134:onIfAll = 2212 321 +### for Xic0 -> pi Xi -> pi pi Lambda -> pi pi pi p +### and Omega_c -> pi Xi -> pi pi Lambda -> pi pi pi p +3312:onMode = off +3312:onIfAll = 3122 -211 +3122:onMode = off +3122:onIfAll = 2212 -211 +### for Omega_c -> pi Omega -> pi K Lambda -> pi K pi p +3334:onMode = off +3334:onIfAll = 3122 -321 + +### switch off all decay channels +411:onMode = off +421:onMode = off +431:onMode = off +4122:onMode = off +4232:onMode = off +4132:onMode = off +443:onMode = off +4332:onMode = off + +### D0 -> K pi +421:onIfMatch = 321 211 +### D0 -> K pi pi0 +421:onIfMatch = 321 211 111 + +### D+/- -> K pi pi +411:onIfMatch = 321 211 211 +### D+/- -> K* pi +411:onIfMatch = 313 211 +### D+/- -> phi pi +411:onIfMatch = 333 211 + +### D_s -> K K* +431:onIfMatch = 321 313 +### D_s -> Phi pi +431:onIfMatch = 333 211 + +### Lambda_c -> p K* +4122:onIfMatch = 2212 313 +### Lambda_c -> Delta K +4122:onIfMatch = 2224 321 +### Lambda_c -> Lambda(1520) pi +4122:onIfMatch = 102134 211 +### Lambda_c -> p K pi +4122:onIfMatch = 2212 321 211 +### Lambda_c -> pK0s +4122:onIfMatch = 2212 311 + +### Xic+ -> pK*0 +4232:onIfMatch = 2212 313 +### Xic+ -> p K- pi+ +4232:onIfMatch = 2212 321 211 +### Xic+ -> Xi*0 pi+, Xi*->Xi- pi+ +4232:onIfMatch = 3324 211 +### Xic+ -> Xi- pi+ pi+ +4232:onIfMatch = 3312 211 211 + +### Xic0 -> Xi- pi+ +4132:onIfMatch = 3312 211 + +### Omega_c -> Omega pi +4332:onIfMatch = 3334 211 +### Omega_c -> Xi pi +4332:onIfMatch = 3312 211 diff --git a/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_with_decays_DReso.cfg b/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_with_decays_DReso.cfg new file mode 100644 index 000000000..7713a5518 --- /dev/null +++ b/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_with_decays_DReso.cfg @@ -0,0 +1,237 @@ +### author: Stefano Politano, Luca Aglietta (stefano.politano@cern.ch, luca.aglietta@cern.ch) +### since: July 2024 + +### beams +Beams:idA 2212 # proton +Beams:idB 2212 # proton +Beams:eCM 13600. # GeV + +### processes +SoftQCD:inelastic on # all inelastic processes + +### decays +ParticleDecays:limitTau0 on +ParticleDecays:tau0Max 10. + +# parameters to boost resonances production +StringFlav:mesonCL1S0J1= 3 +StringFlav:mesonCL1S1J2= 3.2 +StringFlav:mesonCL1S1J0= 0.75 +StringFlav:mesonCL1S1J1= 1. + + +### turn off all charm resonances decays +10433:onMode = off # Ds1(2536) +435:onMode = off # Ds2*(2573) +425:onMode = off # D2*(2460)0 +415:onMode = off # D2*(2460)+ +10411:onMode = off # D0*+ +10421:onMode = off # D0*0 +20433:onMode = off # Ds1 (2460) +20423:onMode = off # D1(2430)0 + +### turn off all beauty hadron decays +531:onMode = off +511:onMode = off +521:onMode = off + +### add D1(2430)0 +20423:oneChannel = 1 1 0 413 -211 +20423:onIfMatch = 413 -211 + +### add Ds1(2536) +10433:oneChannel = 1 1 0 413 311 +10433:onIfMatch = 413 311 + +### Ds1 (2460) +20433:oneChannel = 1 0.5 0 431 22 +20433:addChannel = 1 0.5 0 431 211 -211 +20433:onIfMatch = 431 22 +20433:onIfMatch = 431 211 211 + +### add Ds2*(2573) +435:oneChannel = 1 0.0500000 0 413 311 +435:addChannel = 1 0.4500000 0 411 311 +435:addChannel = 1 0.4500000 0 421 321 +435:onIfMatch = 413 311 +435:onIfMatch = 411 311 +435:onIfMatch = 421 321 + +### add D2*(2460)0 +425:oneChannel = 1 0.5 0 413 -211 +425:addChannel = 1 0.5 0 411 -211 +425:onIfMatch = 413 211 +425:onIfMatch = 411 211 + +### add D2*(2460)+ +415:oneChannel = 1 1 0 421 211 +415:onIfMatch = 421 211 + + +### add D0*+ +10411:oneChannel = 1 1 0 421 211 +10411:onIfMatch = 421 211 + +### add D0*0 +10421:oneChannel = 1 1 0 411 -211 +10421:onIfMatch = 411 211 + + +### add Bs0 +531:oneChannel = 1 0.0070000 0 12 -11 -435 +531:addChannel = 1 0.0070000 0 12 -11 -10433 +531:addChannel = 1 0.0070000 0 14 -13 -435 +531:addChannel = 1 0.0070000 0 14 -13 -10433 +531:addChannel = 1 0.0040000 0 14 -13 -20433 +531:addChannel = 1 0.0160000 0 16 -15 -433 +531:addChannel = 1 0.0028000 0 16 -15 -435 +531:addChannel = 1 0.0028000 0 16 -15 -10433 +531:addChannel = 1 0.0013000 0 -435 211 +531:addChannel = 1 0.0008000 0 -20433 211 +531:addChannel = 1 0.0021000 0 -20433 213 + +531:onIfMatch = 12 11 435 +531:onIfMatch = 12 11 10433 +531:onIfMatch = 14 13 435 +531:onIfMatch = 14 13 10433 +531:onIfMatch = 14 13 20433 +531:onIfMatch = 16 15 433 +531:onIfMatch = 16 15 435 +531:onIfMatch = 16 15 10433 +531:onIfMatch = 435 211 +531:onIfMatch = 20433 211 +531:onIfMatch = 20433 213 + +### add B0 +511:oneChannel = 1 0.0023000 0 12 -11 -415 +511:addChannel = 1 0.0045000 0 12 -11 -10411 +511:addChannel = 1 0.0023000 0 14 -13 -415 +511:addChannel = 1 0.0045000 0 14 -13 -10411 +511:addChannel = 1 0.0020000 0 16 -15 -415 +511:addChannel = 1 0.0013000 0 16 -15 -10411 +511:addChannel = 1 0.0002000 0 -10411 211 +511:addChannel = 1 0.0009100 0 -10421 211 +511:addChannel = 1 0.0040000 0 433 -415 +511:addChannel = 1 0.0042000 0 431 -415 +511:addChannel = 1 0.0009000 0 -415 211 +511:addChannel = 1 0.0022000 0 -415 213 + +511:onIfMatch = 12 11 415 +511:onIfMatch = 12 11 10411 +511:onIfMatch = 14 13 415 +511:onIfMatch = 14 13 10411 +511:onIfMatch = 16 15 415 +511:onIfMatch = 16 15 10411 +511:onIfMatch = 10411 211 +511:onIfMatch = 10421 211 +511:onIfMatch = 433 415 +511:onIfMatch = 431 415 +511:onIfMatch = 415 211 +511:onIfMatch = 415 213 + +### add B+ +521:oneChannel = 1 0.0090000 0 12 -11 -20423 +521:addChannel = 1 0.0090000 0 14 -13 -20423 +521:addChannel = 1 0.0020000 0 16 -15 -20423 +521:addChannel = 1 0.0030000 0 12 -11 -425 +521:addChannel = 1 0.0030000 0 14 -13 -425 +521:addChannel = 1 0.0020000 0 16 -15 -425 +521:addChannel = 1 0.0049000 0 12 -11 -10421 +521:addChannel = 1 0.0049000 0 14 -13 -10421 +521:addChannel = 1 0.0013000 0 16 -15 -10421 +521:addChannel = 1 0.0007500 0 -20423 211 +521:addChannel = 1 0.0022000 0 -20423 213 +521:addChannel = 1 0.0006000 0 -20423 431 +521:addChannel = 1 0.0012000 0 -20423 433 +521:addChannel = 1 0.0008000 0 -425 211 +521:addChannel = 1 0.0038000 0 -425 213 +521:addChannel = 1 0.0042000 0 431 -425 +521:addChannel = 1 0.0040000 0 433 -425 + +521:onIfMatch = 12 11 20423 +521:onIfMatch = 14 13 20423 +521:onIfMatch = 16 15 20423 +521:onIfMatch = 12 11 425 +521:onIfMatch = 14 13 425 +521:onIfMatch = 16 15 425 +521:onIfMatch = 12 11 10421 +521:onIfMatch = 14 13 10421 +521:onIfMatch = 16 15 10421 +521:onIfMatch = 20423 211 +521:onIfMatch = 20423 213 +521:onIfMatch = 20423 431 +521:onIfMatch = 20423 433 +521:onIfMatch = 425 211 +521:onIfMatch = 425 213 +521:onIfMatch = 431 425 +521:onIfMatch = 433 425 + +# Correct decay lengths (wrong in PYTHIA8 decay table) +# Lb +5122:tau0 = 0.4390 +# Xic0 +4132:tau0 = 0.0455 +# OmegaC +4332:tau0 = 0.0803 + +### Force golden charm hadrons decay modes for D2H studies +### add D+ decays absent in PYTHIA8 decay table and set BRs from PDG for other +411:oneChannel = 1 0.0752 0 -321 211 211 +411:addChannel = 1 0.0104 0 -313 211 +411:addChannel = 1 0.0156 0 311 211 +411:addChannel = 1 0.0752 0 333 211 # to have the same amount of D+->KKpi and D+->Kpipi +## add Lc decays absent in PYTHIA8 decay table and set BRs from PDG for other +4122:oneChannel = 1 0.0196 100 2212 -313 +4122:addChannel = 1 0.0108 100 2224 -321 +4122:addChannel = 1 0.022 100 102134 211 +4122:addChannel = 1 0.035 0 2212 -321 211 +4122:addChannel = 1 0.0159 0 2212 311 + +### K* -> K pi +313:onMode = off +313:onIfAll = 321 211 +### for Ds -> Phi pi+ +333:onMode = off +333:onIfAll = 321 321 +### for D0 -> rho0 pi+ k- +113:onMode = off +113:onIfAll = 211 211 +### for Lambda_c -> Delta++ K- +2224:onMode = off +2224:onIfAll = 2212 211 +### for Lambda_c -> Lambda(1520) K- +102134:onMode = off +102134:onIfAll = 2212 321 + +### switch off all decay channels +411:onMode = off +421:onMode = off +431:onMode = off +4122:onMode = off + +### D0 -> K pi +421:onIfMatch = 321 211 + +### D+/- -> K pi pi +411:onIfMatch = 321 211 211 +### D+/- -> K* pi +411:onIfMatch = 313 211 +### D+/- -> phi pi +411:onIfMatch = 333 211 + +### D_s -> K K* +431:onIfMatch = 321 313 +### D_s -> Phi pi +431:onIfMatch = 333 211 + +### Lambda_c -> p K* +4122:onIfMatch = 2212 313 +### Lambda_c -> Delta K +4122:onIfMatch = 2224 321 +### Lambda_c -> Lambda(1520) pi +4122:onIfMatch = 102134 211 +### Lambda_c -> p K pi +4122:onIfMatch = 2212 321 211 +### Lambda_c -> pK0s +4122:onIfMatch = 2212 311 + diff --git a/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_with_decays_DResoTrigger.cfg b/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_with_decays_DResoTrigger.cfg new file mode 100644 index 000000000..a6b132d94 --- /dev/null +++ b/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_with_decays_DResoTrigger.cfg @@ -0,0 +1,159 @@ +### author: Stefano Politano, Luca Aglietta (stefano.politano@cern.ch, luca.aglietta@cern.ch) +### since: July 2024 + +### beams +Beams:idA 2212 # proton +Beams:idB 2212 # proton +Beams:eCM 13600. # GeV + +### processes +SoftQCD:inelastic on # all inelastic processes + +### decays +ParticleDecays:limitTau0 on +ParticleDecays:tau0Max 10. + +# parameters to boost resonances production +StringFlav:mesonCL1S0J1= 3 +StringFlav:mesonCL1S1J2= 5 + +### add resonances not present in PYTHIA +### id:all = name antiName spinType chargeType colType m0 mWidth mMin mMax tau0 +### Ds1*(2700) +30433:all = D*_s1(2700)+ D*_s1(2700)- 3 3 0 2.714 0.122 2.51 10. 0. +30433:mayDecay = on + +### Ds1*(2860) +40433:all = D*_s1(2860)+ D*_s1(2860)- 3 3 0 2.859 0.160 2.51 10. 0. +40433:mayDecay = on + +### Ds3*(2860) +437:all = D*_s3(2860)+ D*_s3(2860)- 7 3 0 2.860 0.053 2.51 10. 0. +437:mayDecay = on + +### Xic(3055)+ +4325:all = Xi_c(3055)+ Xi_cbar(3055)- 4 3 0 3.0559 0.0078 2.986 10. 0. +4325:mayDecay = on + +### Xic(3080)+ +4326:all = Xi_c(3080)+ Xi_cbar(3080)- 2 3 0 3.0772 0.0036 2.986 10. 0. +4326:mayDecay = on + +### Xic(3055)0 +4315:all = Xi_c(3055)0 Xi_cbar(3055)0 4 0 0 3.0590 0.0064 2.981 10. 0. +4315:mayDecay = on + +### Xic(3080)0 +4316:all = Xi_c(3080)0 Xi_cbar(3080)0 2 0 0 3.0799 0.0056 2.981 10. 0. +4316:mayDecay = on + +### turn off all charm resonances decays +10433:onMode = off # Ds1(2536) +435:onMode = off # Ds2*(2573) +30433:onMode = off # Ds1*(2700) +40433:onMode = off # Ds1*(2860) +437:onMode = off # Ds3*(2860) +4325:onMode = off # Xic(3055)+ +4326:onMode = off # Xic(3080)+ +4315:onMode = off # Xic(3055)0 +4316:onMode = off # Xic(3080)0 + +### turn off all beauty hadron decays +531:onMode = off +521:onMode = off + +### add Ds1(2536) +10433:oneChannel = 1 1 0 413 311 +10433:onIfMatch = 413 311 + +### add Ds2*(2573) +435:oneChannel = 1 0.1000000 0 413 311 +435:addChannel = 1 0.9000000 0 411 311 +435:onIfMatch = 413 311 +435:onIfMatch = 411 311 + +### add Ds1*(2700) +30433:oneChannel = 1 1 0 413 311 +30433:onIfMatch = 413 311 + +### add Ds1*(2860) +40433:oneChannel = 1 1 0 413 311 +40433:onIfMatch = 413 311 + +### add Ds3*(2860) +437:oneChannel = 1 1 0 413 311 +437:onIfMatch = 413 311 + +### add Xic(3055)+ +4325:oneChannel = 1 1 0 411 3122 +4325:onIfMatch = 411 3122 + +### add Xic(3080)+ +4326:oneChannel = 1 1 0 411 3122 +4326:onIfMatch = 411 3122 + +### add Xic(3055)0 +4315:oneChannel = 1 1 0 421 3122 +4315:onIfMatch = 421 3122 + +### add Xic(3080)0 +4316:oneChannel = 1 1 0 421 3122 +4316:onIfMatch = 421 3122 + +### add Bs0 +531:oneChannel = 1 0.0070000 0 12 -11 -435 +531:addChannel = 1 0.0070000 0 12 -11 -10433 +531:addChannel = 1 0.0070000 0 14 -13 -435 +531:addChannel = 1 0.0070000 0 14 -13 -10433 +531:addChannel = 1 0.0028000 0 16 -15 -435 +531:addChannel = 1 0.0028000 0 16 -15 -10433 +531:addChannel = 1 0.0013000 0 -435 211 + +531:onIfMatch = 12 11 435 +531:onIfMatch = 12 11 10433 +531:onIfMatch = 14 13 435 +531:onIfMatch = 14 13 10433 +531:onIfMatch = 16 15 435 +531:onIfMatch = 16 15 10433 +531:onIfMatch = 435 211 + +# Correct decay lengths (wrong in PYTHIA8 decay table) +# Lb +5122:tau0 = 0.4390 +# Xic0 +4132:tau0 = 0.0455 +# OmegaC +4332:tau0 = 0.0803 + +### Force golden charm hadrons decay modes for D2H studies +### add D+ decays absent in PYTHIA8 decay table and set BRs from PDG for other +411:oneChannel = 1 0.0752 0 -321 211 211 +411:addChannel = 1 0.0104 0 -313 211 +411:addChannel = 1 0.0156 0 311 211 +411:addChannel = 1 0.0752 0 333 211 # to have the same amount of D+->KKpi and D+->Kpipi +### set D0 BRs +421:oneChannel = 1 0.9 0 -321 211 +421:addChannel = 1 0.1 0 -321 211 111 + +### K* -> K pi +313:onMode = off +313:onIfAll = 321 211 +### for Ds -> Phi pi+ +333:onMode = off +333:onIfAll = 321 321 + +### switch off all decay channels +411:onMode = off +421:onMode = off + +### D0 -> K pi +421:onIfMatch = 321 211 +### D0 -> K pi pi0 +421:onIfMatch = 321 211 111 + +### D+/- -> K pi pi +411:onIfMatch = 321 211 211 +### D+/- -> K* pi +411:onIfMatch = 313 211 +### D+/- -> phi pi +411:onIfMatch = 333 211 diff --git a/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_with_decays_LcResoTrigger.cfg b/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_with_decays_LcResoTrigger.cfg new file mode 100644 index 000000000..274d4b22b --- /dev/null +++ b/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_with_decays_LcResoTrigger.cfg @@ -0,0 +1,118 @@ +### author: Antonio Palasciano (antonio.palasciano@cern.ch) +### since: July 2024 + +### beams +Beams:idA 2212 # proton +Beams:idB 2212 # proton +Beams:eCM 13600. # GeV + +### processes +SoftQCD:inelastic on # all inelastic processes + +### switching on Pythia Mode2 +ColourReconnection:mode 1 +ColourReconnection:allowDoubleJunRem off +ColourReconnection:m0 0.3 +ColourReconnection:allowJunctions on +ColourReconnection:junctionCorrection 1.20 +ColourReconnection:timeDilationMode 2 +ColourReconnection:timeDilationPar 0.18 +StringPT:sigma 0.335 +StringZ:aLund 0.36 +StringZ:bLund 0.56 +StringFlav:probQQtoQ 0.078 +StringFlav:ProbStoUD 0.2 +StringFlav:probQQ1toQQ0join 0.0275,0.0275,0.0275,0.0275 +MultiPartonInteractions:pT0Ref 2.15 +BeamRemnants:remnantMode 1 +BeamRemnants:saturation 5 + +### decays +ParticleDecays:limitTau0 on +ParticleDecays:tau0Max 10. + +### add resonances not present in PYTHIA +### id:all = name antiName spinType chargeType colType m0 mWidth mMin mMax tau0 + +### LambdaC(2860) +24124:all = Lambda_c(2860)+ Lambda_c(2860)- 3 1 0 2.8561 0.0676 2.7 10. 0. +24124:mayDecay = on + +### LambdaC(2880) +24126:all = Lambda_c(2880)+ Lambda_c(2880)- 5 1 0 2.8816 0.0056 2.85 10. 0. +24126:mayDecay = on + +### LambdaC(2940) +4125:all = Lambda_c(2940)+ Lambda_c(2940)- 3 1 0 2.9396 0.020 2.9 10. 0. +4125:mayDecay = on + +### TC(3100) +9422111:all = Theta_c(3100) 3 0 0 3.099 0.0000083 2.95 10. 0. +9422111:mayDecay = on + +### turn off all charm resonances decays +# 24124:onMode = off # LambdaC(2860) +# 24126:onMode = off # LambdaC(2880) +# 4125:onMode = off # LambdaC(2940) +# 9422111:onMode = off # Theta_c(3100) + +### turn off all beauty hadron decays +531:onMode = off +521:onMode = off +5122:onMode = off + +### add LambdaC(2860) +24124:oneChannel = 1 1 0 421 2212 +24124:onIfMatch = 421 2212 + +### add LambdaC(2880) +24126:oneChannel = 1 1 0 421 2212 +24126:onIfMatch = 421 2212 + +### add LambdaC(2940) +4125:oneChannel = 1 1 0 421 2212 +4125:onIfMatch = 421 2212 + +### add ThetaC(3100) +9422111:oneChannel = 1 1 0 413 2212 +9422111:onIfMatch = 413 2212 + +### add Lb +5122:addChannel = 1 0.2 0 421 2212 -211 # Lb -> D0 p pi- non resonant +5122:addChannel = 1 0.4 0 24124 -211 # Lb -> LambdaC(2860)+ pi- +5122:addChannel = 1 0.25 0 24126 -211 # Lb -> LambdaC(2880)+ pi- +5122:addChannel = 1 0.15 0 4125 -211 # Lb -> LambdaC(2940)+ pi- + +5122:onIfMatch = 421 2212 -211 # D0 p pi- +5122:onIfMatch = 24124 -211 # LambdaC(2860)+ pi- +5122:onIfMatch = 24126 -211 # LambdaC(2880)+ pi- +5122:onIfMatch = 4125 -211 # LambdaC(2940)+ pi- + +# Correct decay lengths (wrong in PYTHIA8 decay table) +# Lb +5122:tau0 = 0.4390 +# Xic0 +4132:tau0 = 0.0455 +# OmegaC +4332:tau0 = 0.0803 + +### Force golden charm hadrons decay modes for D2H studies +### set D0 BRs +421:oneChannel = 1 0.9 0 -321 211 +421:addChannel = 1 0.1 0 -321 211 111 + +### K* -> K pi +313:onMode = off +313:onIfAll = 321 211 + +### switch off all decay channels +411:onMode = off +413:onMode = off +421:onMode = off + +### D0 -> K pi +421:onIfMatch = 321 211 +### D0 -> K pi pi0 +421:onIfMatch = 321 211 111 +### D*+0 -> D0 pi +413:onIfMatch = 421 211 diff --git a/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_with_decays_Mode2.cfg b/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_with_decays_Mode2.cfg new file mode 100644 index 000000000..7c03e2d0f --- /dev/null +++ b/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_with_decays_Mode2.cfg @@ -0,0 +1,290 @@ +### authors: Fabrizio Grosa (fabrizio.grosa@cern.ch) +### Cristina Terrevoli (cristina.terrevoli@cern.ch) +### Fabio Catalano (fabio.catalano@cern.ch) +### last update: November 2023 + +### beams +Beams:idA 2212 # proton +Beams:idB 2212 # proton +Beams:eCM 13600. # GeV + +### processes +SoftQCD:inelastic on # all inelastic processes + +### decays +ParticleDecays:limitTau0 on +ParticleDecays:tau0Max 10. + +### switching on Pythia Mode2 +ColourReconnection:mode 1 +ColourReconnection:allowDoubleJunRem off +ColourReconnection:m0 0.3 +ColourReconnection:allowJunctions on +ColourReconnection:junctionCorrection 1.20 +ColourReconnection:timeDilationMode 2 +ColourReconnection:timeDilationPar 0.18 +StringPT:sigma 0.335 +StringZ:aLund 0.36 +StringZ:bLund 0.56 +StringFlav:probQQtoQ 0.078 +StringFlav:ProbStoUD 0.2 +StringFlav:probQQ1toQQ0join 0.0275,0.0275,0.0275,0.0275 +MultiPartonInteractions:pT0Ref 2.15 +BeamRemnants:remnantMode 1 +BeamRemnants:saturation 5 + +# Correct decay lengths (wrong in PYTHIA8 decay table) +# Lb +5122:tau0 = 0.4390 +# Xic0 +4132:tau0 = 0.0455 +# OmegaC +4332:tau0 = 0.0803 + +### Force golden charm hadrons decay modes for D2H studies +# HF decays +### BR are set to yield 50% of signal in golden channel and uniform abundance of corr. bkg channels (weighted by BR from PDG) +### +### D0 decays +### D0 -> K- π+ (50%) +421:oneChannel = 1 0.50000 0 -321 211 ### D0 -> K- π+ 3.94% +### D0 -> K- π+ π0 (12.50%) +421:addChannel = 1 0.00625 0 -321 211 111 ### D0 -> K- π+ π0 (non-resonant) 1.15% (e.g. 115/(115+231+195+1120)*0.2) +421:addChannel = 1 0.08750 0 213 -321 ### D0 -> rho+ K- 11.2% +421:addChannel = 1 0.01250 0 -313 111 ### D0 -> antiK*0(892) π0 1.95% +421:addChannel = 1 0.01875 0 -323 211 ### D0 -> K*-(892) π+ 2.31% +### D0 -> π- π+ (12.50%) +421:addChannel = 1 0.12500 0 -211 211 ### D0 -> π- π+ (non-resonant) 1.0e-4 +### D0 -> π- π+ π0 (12.50%) +421:addChannel = 1 0.08750 0 213 -211 ### D0 -> rho+ π- 1.01% +421:addChannel = 1 0.03750 0 -211 211 111 ### D0 -> π- π+ π0 (non-resonant) 1.3e-4 +### D0 -> K- K+ (12.50%) +421:addChannel = 1 0.12500 0 -321 321 ### D0 -> K- K+ (non-resonant) 4.08e-3 + +### D+ decays +### D+ -> K- π+ π+ (50%) +411:oneChannel = 1 0.40189 0 -321 211 211 ### D+ -> K- π+ π+ 9.38% +411:addChannel = 1 0.05356 0 -10311 211 ### D+ -> antiK*0(1430) π+ 1.25% +411:addChannel = 1 0.04455 0 -313 211 ### D+ -> K*0(892) pi+ 1.04% +### D+ -> K- π+ π+ π0 (small, 3%) +411:addChannel = 1 0.03000 0 -321 211 211 111 ### D+ -> K- π+ π+ π0 6.25% +### D+ -> K- K+ π+ (36.00%, set 25% for D+ -> φ π+, 11% for the rest) +411:addChannel = 1 0.25000 0 333 211 ### D+ -> φ π+ 2.69e-3 !needed for signal +411:addChannel = 1 0.03929 0 -313 321 ### D+ -> K*0(892) K+ 2.49e-3 +411:addChannel = 1 0.02865 0 -10311 321 ### D+ -> antiK*0(1430) K+ 1.82e-3 +411:addChannel = 1 0.04206 0 -321 321 211 ### D+ -> K- K+ π+ (non-resonant) 2.68e-3 +### D+ -> π- π+ π+ (11.00%) +411:addChannel = 1 0.02911 0 113 211 ### D+ -> rho0 π+ 8.4e-4 +411:addChannel = 1 0.01618 0 225 211 ### D+ -> f2(1270) π+ 4.6e-4 +411:addChannel = 1 0.06471 0 -211 211 211 ### D+ -> π- π+ π+ (non-resonant) 1.0e-4 + +### Ds+ decays +### Ds+ -> K- K+ π+ (50%) +431:oneChannel = 1 0.50000 0 333 211 ### Ds+ -> φ(1020) π+ 2.21% +431:addChannel = 1 0.15000 0 -313 321 ### Ds+ -> antiK*(892) K+ 2.58% +### Ds+ -> K- K+ π+ π0 (small, 2%) +431:addChannel = 1 0.02000 0 333 213 ### Ds+ -> φ(1020) rho 5.50% +### Ds+ -> π- π+ π+ (11.00%) +431:addChannel = 1 0.00220 0 113 211 ### Ds+ -> rho π+ 1.1e-4 +431:addChannel = 1 0.00220 0 225 211 ### Ds+ -> f2(1270) π+ 1.4e-3 +431:addChannel = 1 0.10560 0 -211 211 211 ### Ds+ -> π- π+ π+ 9.12e-3 (s-wave) +### Ds+ -> π- K+ π+ (11.00%) +431:addChannel = 1 0.03080 0 313 211 ### Ds+ -> K*(892)0 π+ 1.67e-3 +431:addChannel = 1 0.02200 0 10221 321 ### Ds+ -> f0(1370) K+ 1.2e-3 +431:addChannel = 1 0.03960 0 113 321 ### Ds+ -> rho0 K+ 2.17e-3 +431:addChannel = 1 0.01760 0 -211 321 211 ### Ds+ -> π- K+ π+ (non-resonant) 1.16-3 +### Ds+ -> π+ π- π+ π0 (11.00%) +431:addChannel = 1 0.11000 0 221 211 ### Ds+ -> eta π+ -> π0 π+ π+ π- (affects D+ golden channel) + +## Λc decays +### Λc+ -> p K- π+ (36%) +4122:oneChannel = 1 0.14400 0 2212 -321 211 ### Λc+ -> p K- π+ (non-resonant) 3.5% +4122:addChannel = 1 0.08100 100 2212 -313 ### Λc+ -> p K*0(892) 1.96% +4122:addChannel = 1 0.04500 100 2224 -321 ### Λc+ -> Delta++ K- 1.08% +4122:addChannel = 1 0.09000 100 102134 211 ### Λc+ -> Lambda(1520) K- 2.20e-3 +### Λc+ -> p K0S (36%) +4122:addChannel = 1 0.36000 0 2212 311 ### Λc+ -> p K0S 1.59% +### Λc+ -> p K- π+ π0 (small, 3%) +4122:addChannel = 1 0.03000 0 2212 -321 211 111 ### Λc+ -> p K- π+ π0 (non-resonant) 4.6% +### Λc+ -> p π- π+ (12.50%) +4122:addChannel = 1 0.12500 0 2212 -211 211 ### Λc+ -> p π+ π+ 4.59% +### Λc+ -> p K- K+ (12.50%) +4122:addChannel = 1 0.12500 0 2212 333 ### Λc+ -> p phi 1.06% + +## Xic decays +### Ξc+ -> p K- π+ (35%) +4232:oneChannel = 1 0.17500 0 2212 -321 211 ### Ξc+ -> p K- π+ 6.18e-3 +4232:addChannel = 1 0.17500 0 2212 -313 ### Ξc+ -> p antiK*0(892) +### Ξc+ -> Ξ- π+ π+ (35%) (set the same as Ξc+ -> p K- π+) +4232:addChannel = 1 0.35000 0 3312 211 211 ### Ξc+ -> Ξ- π+ π+ 2.86% +### Ξc+ -> p φ (10%) +4232:addChannel = 1 0.10000 0 2212 333 ### Ξc+ -> p φ +### Ξc+ -> sigma+ π+ π- (10%) +4232:addChannel = 1 0.12500 0 3222 -211 211 ### Ξc+ -> sigma+ π- π+ 1.37% +### Ξc+ -> Ξ*0 π+ (10%) +4232:addChannel = 1 0.12500 0 3324 211 + +### add Xic0 decays absent in PYTHIA8 decay table +4132:oneChannel = 1 0.0143 0 3312 211 + +### add OmegaC decays absent in PYTHIA8 decay table +4332:oneChannel = 1 0.5 0 3334 211 +4332:addChannel = 1 0.5 0 3312 211 + +# Allow the decay of resonances in the decay chain +### K*0(892) -> K- π+ +313:onMode = off +313:onIfAll = 321 211 +### K*(892)+ -> K- π0 +323:onMode = off +323:onIfAll = 321 111 +### K*(1430)0 -> K- π+ +10311:onMode = off +10311:onIfAll = 321 211 +### rho+ -> π+ π0 +213:onMode = off +213:onIfAll = 211 111 +### φ -> K+ K- +333:onMode = off +333:onIfAll = 321 321 +### rho0 -> π+ π- +113:onMode = off +113:onIfAll = 211 211 +### f2(1270) -> π+ π- +225:onMode = off +225:onIfAll = 211 211 +### f0(1370) -> π+ π- +10221:onMode = off +10221:onIfAll = 211 211 +### eta -> π+ π- +221:onMode = off +221:onIfAll = 111 211 211 +### for Λc -> Delta++ K- +2224:onMode = off +2224:onIfAll = 2212 211 +### for Λc -> Lambda(1520) K- +102134:onMode = off +102134:onIfAll = 2212 321 +### for Xic0 -> pi Xi -> pi pi Lambda -> pi pi pi p +### and Omega_c -> pi Xi -> pi pi Lambda -> pi pi pi p +3312:onMode = off +3312:onIfAll = 3122 -211 +3122:onMode = off +3122:onIfAll = 2212 -211 +### for Omega_c -> pi Omega -> pi K Lambda -> pi K pi p +3334:onMode = off +3334:onIfAll = 3122 -321 + +# switch off all decay channels +411:onMode = off +421:onMode = off +431:onMode = off +4122:onMode = off +4232:onMode = off +4132:onMode = off +443:onMode = off +4332:onMode = off + +# Allow the decay of HF +### D0 -> K π +421:onIfMatch = 321 211 +### D0 -> K π π0 +421:onIfMatch = 321 211 111 +### D0 -> rho K +421:onIfMatch = 213 321 +### D0 -> antiK*0 π0 +421:onIfMatch = 313 111 +### D0 -> K*- π+ +421:onIfMatch = 323 211 +### D0 -> π π +421:onIfMatch = 211 211 +### D0 -> rho+ π- +421:onIfMatch = 213 -211 +### D0 -> π π π0 +421:onIfMatch = 211 211 111 +### D0 -> K K +421:onIfMatch = 321 321 + +### D+/- -> K π π +411:onIfMatch = 321 211 211 +### D+/- -> K π π +411:onIfMatch = 321 211 211 111 +### D+/- -> K* π +411:onIfMatch = 313 211 +### D+/- -> K* K +411:onIfMatch = 313 321 +### D+/- -> antiK* π +411:onIfMatch = 10311 211 +### D+/- -> antiK* K +411:onIfMatch = 10311 321 +### D+/- -> φ π +411:onIfMatch = 333 211 +### D+/- -> K K π +411:onIfMatch = 321 321 211 +### D+/- -> f2(1270) π +411:onIfMatch = 225 211 +### D+/- -> rho π +411:onIfMatch = 113 211 +### D+/- -> π π π +411:onIfMatch = 211 211 211 + +### Ds -> φ π +431:onIfMatch = 333 211 +### Ds -> K* K +431:onIfMatch = 313 321 +### Ds -> φ rho+ +431:onIfMatch = 333 213 +### Ds -> rho π +431:onIfMatch = 113 211 +### Ds -> f2 π +431:onIfMatch = 225 211 +### Ds -> π π π +431:onIfMatch = 211 211 211 +### Ds -> K*(892)0 π+ +431:onIfMatch = 313 211 +### Ds -> f0(1370) K+ +431:onIfMatch = 10221 321 +### Ds -> rho0 K+ +431:onIfMatch = 113 321 +### Ds -> K π π +431:onIfMatch = 321 211 211 +### Ds -> eta π+ +431:onIfMatch = 221 211 + +### Λc -> pK0s +4122:onIfMatch = 2212 311 +### Λc -> p K- π+ π0 +4122:onIfMatch = 2212 321 211 +### Λc -> p K* +4122:onIfMatch = 2212 313 +### Λc -> Delta++ K +4122:onIfMatch = 2224 321 +### Λc -> Lambda(1520) π +4122:onIfMatch = 102134 211 +### Λc -> p K- π+ π0 +4122:onIfMatch = 2212 321 211 111 +### Λc -> p π π +4122:onIfMatch = 2212 211 211 +### Λc -> p K K +4122:onIfMatch = 2212 333 + +### Ξc+ -> p K- π+ +4232:onIfMatch = 2212 321 211 +### Ξc+ -> p antiK*0(892) +4232:onIfMatch = 2212 313 +### Ξc+ -> p φ +4232:onIfMatch = 2212 333 +### Ξc+ -> sigma- π+ π+ +4232:onIfMatch = 3222 211 211 +### Ξc+ -> Ξ*0 π+, Ξ*0 -> Ξ- π+ +4232:onIfMatch = 3324 211 +### Ξc+ -> Ξ- π+ π+ +4232:onIfMatch = 3312 211 211 + +### Xic0 -> Xi- pi+ +4132:onIfMatch = 3312 211 + +### Omega_c -> Omega pi +4332:onIfMatch = 3334 211 +### Omega_c -> Xi pi +4332:onIfMatch = 3312 211 \ No newline at end of file diff --git a/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_with_decays_Mode2_CorrBkg.cfg b/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_with_decays_Mode2_CorrBkg.cfg new file mode 100644 index 000000000..9dd73f0ab --- /dev/null +++ b/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_with_decays_Mode2_CorrBkg.cfg @@ -0,0 +1,242 @@ +### authors: Stefano Politano (stefano.politano@cern.ch), Mattia Faggin (mattia.faggin@cern.ch), Fabrizio Grosa (fabrizio.grosa@cern.ch), Marcello Di Costanzo (marcello.di.costanzo@cern.ch) +### last update: April 2025 + +### beams +Beams:idA 2212 # proton +Beams:idB 2212 # proton +Beams:eCM 13600. # GeV + +### processes +SoftQCD:inelastic on # all inelastic processes + +### decays +ParticleDecays:limitTau0 on +ParticleDecays:tau0Max 10. + +### switching on Pythia Mode2 +ColourReconnection:mode 1 +ColourReconnection:allowDoubleJunRem off +ColourReconnection:m0 0.3 +ColourReconnection:allowJunctions on +ColourReconnection:junctionCorrection 1.20 +ColourReconnection:timeDilationMode 2 +ColourReconnection:timeDilationPar 0.18 +StringPT:sigma 0.335 +StringZ:aLund 0.36 +StringZ:bLund 0.56 +StringFlav:probQQtoQ 0.078 +StringFlav:ProbStoUD 0.2 +StringFlav:probQQ1toQQ0join 0.0275,0.0275,0.0275,0.0275 +MultiPartonInteractions:pT0Ref 2.15 +BeamRemnants:remnantMode 1 +BeamRemnants:saturation 5 + +# Correct decay lengths (wrong in PYTHIA8 decay table) +# Lb +5122:tau0 = 0.4390 +# Xic0 +4132:tau0 = 0.0455 +# OmegaC +4332:tau0 = 0.0803 + +### HF decays +### BR are set to yield 20% of signal in golden channel and uniform abundance of corr. bkg channels (weighted by BR from PDG) +### +### D0 decays +### D0 -> K- π+ (20%) +421:oneChannel = 1 0.20 0 -321 211 ### D0 -> K- π+ 3.94% +### D0 -> K- π+ π0 (20%) +421:addChannel = 1 0.01 0 -321 211 111 ### D0 -> K- π+ π0 (non-resonant) 1.15% (e.g. 115/(115+231+195+1120)*0.2) +421:addChannel = 1 0.14 0 213 -321 ### D0 -> rho+ K- 11.2% +421:addChannel = 1 0.02 0 -313 111 ### D0 -> antiK*0(892) π0 1.95% +421:addChannel = 1 0.03 0 -323 211 ### D0 -> K*-(892) π+ 2.31% +### D0 -> π- π+ (20%) +421:addChannel = 1 0.20 0 -211 211 ### D0 -> π- π+ (non-resonant) 1.0e-4 +### D0 -> π- π+ π0 (20%) +421:addChannel = 1 0.14 0 213 -211 ### D0 -> rho+ π- 1.01% +421:addChannel = 1 0.06 0 -211 211 111 ### D0 -> π- π+ π0 (non-resonant) 1.3e-4 +### D0 -> K- K+ (20%) +421:addChannel = 1 0.20 0 -321 321 ### D0 -> K- K+ (non-resonant) 4.08e-3 + +### D+ decays +### D+ -> K- π+ π+ (28%) +411:oneChannel = 1 0.28 0 -321 211 211 ### D+ -> K- π+ π+ 9.38% +### D+ -> K- π+ π+ π0 (small, 5%) +411:addChannel = 1 0.05 0 -321 211 211 111 ### D+ -> K- π+ π+ π0 6.25% +### D+ -> K- K+ π+ (33%) +411:addChannel = 1 0.085 0 -313 321 ### D+ -> K*0(892) K+ 2.49e-3 +411:addChannel = 1 0.062 0 -10311 321 ### D+ -> antiK*0(1430) K+ 1.82e-3 +411:addChannel = 1 0.092 0 333 211 ### D+ -> φ π+ 2.69e-3 +411:addChannel = 1 0.091 0 -321 321 211 ### D+ -> K- K+ π+ (non-resonant) 2.68e-3 +### D+ -> π- π+ π+ (34%) +411:addChannel = 1 0.09 0 113 211 ### D+ -> rho0 π+ 8.4e-4 +411:addChannel = 1 0.05 0 225 211 ### D+ -> f2(1270) π+ 4.6e-4 +411:addChannel = 1 0.20 0 -211 211 211 ### D+ -> π- π+ π+ (non-resonant) 1.0e-4 + +### Ds+ decays +### Ds+ -> K- K+ π+ (20%) +431:oneChannel = 1 0.100 0 333 211 ### Ds+ -> φ(1020) π+ 2.21% +431:addChannel = 1 0.100 0 -313 321 ### Ds+ -> antiK*(892) K+ 2.58% +### Ds+ -> K- K+ π+ π0 (small, 5%) +431:addChannel = 1 0.050 0 333 213 ### Ds+ -> φ(1020) rho 5.50% +### Ds+ -> π- π+ π+ (25%) +431:addChannel = 1 0.005 0 113 211 ### Ds+ -> rho π+ 1.1e-4 +431:addChannel = 1 0.005 0 225 211 ### Ds+ -> f2(1270) π+ 1.4e-3 +431:addChannel = 1 0.240 0 -211 211 211 ### Ds+ -> π- π+ π+ 9.12e-3 (s-wave) +### Ds+ -> π- K+ π+ (25%) +431:addChannel = 1 0.07 0 313 211 ### Ds+ -> K*(892)0 π+ 1.67e-3 +431:addChannel = 1 0.05 0 10221 321 ### Ds+ -> f0(1370) K+ 1.2e-3 +431:addChannel = 1 0.09 0 113 321 ### Ds+ -> rho0 K+ 2.17e-3 +431:addChannel = 1 0.04 0 -211 321 211 ### Ds+ -> π- K+ π+ (non-resonant) 1.16-3 +### Ds+ -> π+ π- π+ π0 (25%) +431:addChannel = 1 0.250 0 221 211 ### Ds+ -> eta π+ -> π0 π+ π+ π- (affects D+ golden channel) + +## Λc decays +### Λc+ -> p K- π+ (20%, BR set up to 20%) +4122:oneChannel = 1 0.080 0 2212 -321 211 ### Λc+ -> p K- π+ (non-resonant) 3.5% +4122:addChannel = 1 0.045 100 2212 -313 ### Λc+ -> p K*0(892) 1.96% +4122:addChannel = 1 0.025 100 2224 -321 ### Λc+ -> Delta++ K- 1.08% +4122:addChannel = 1 0.050 100 102134 211 ### Λc+ -> Lambda(1520) K- 2.20e-3 +### Λc+ -> p K- π+ π0 (small, 5%) +4122:addChannel = 1 0.050 0 2212 -321 211 111 ### Λc+ -> p K- π+ π0 (non-resonant) 4.6% +### Λc+ -> p π- π+ (25%) +4122:addChannel = 1 0.250 0 2212 -211 211 ### Λc+ -> p π+ π+ 4.59% +### Λc+ -> p K- K+ (25%) +4122:addChannel = 1 0.250 0 2212 333 ### Λc+ -> p phi 1.06% + +### Ξc+ decays (unfiormly distributed) +### Ξc+ -> p K- π+ (34%) +4232:oneChannel = 1 0.17 0 2212 -321 211 ### Ξc+ -> p K- π+ +4232:addChannel = 1 0.17 0 2212 -313 ### Ξc+ -> p antiK*0(892) +### Ξc+ -> p K- K+ (33%) +4232:addChannel = 1 0.33 0 2212 333 ### Ξc+ -> p φ +### Ξc+ -> sigma+ π+ π- (33%) +4232:addChannel = 1 0.33 0 3222 -211 211 ### Ξc+ -> sigma+ π- π+ + +# Allow the decay of resonances in the decay chain +### K*0(892) -> K- π+ +313:onMode = off +313:onIfAll = 321 211 +### K*(892)+ -> K- π0 +323:onMode = off +323:onIfAll = 321 111 +### K*(1430)0 -> K- π+ +10311:onMode = off +10311:onIfAll = 321 211 +### rho+ -> π+ π0 +213:onMode = off +213:onIfAll = 211 111 +### φ -> K+ K- +333:onMode = off +333:onIfAll = 321 321 +### rho0 -> π+ π- +113:onMode = off +113:onIfAll = 211 211 +### f2(1270) -> π+ π- +225:onMode = off +225:onIfAll = 211 211 +### f0(1370) -> π+ π- +10221:onMode = off +10221:onIfAll = 211 211 +### eta -> π+ π- +221:onMode = off +221:onIfAll = 111 211 211 +### for Λc -> Delta++ K- +2224:onMode = off +2224:onIfAll = 2212 211 +### for Λc -> Lambda(1520) K- +102134:onMode = off +102134:onIfAll = 2212 321 + +### Switch off all decay channels +411:onMode = off +421:onMode = off +431:onMode = off +4122:onMode = off +4232:onMode = off + +# Allow the decay of HF +### D0 -> K π +421:onIfMatch = 321 211 +### D0 -> K π π0 +421:onIfMatch = 321 211 111 +### D0 -> rho K +421:onIfMatch = 213 321 +### D0 -> antiK*0 π0 +421:onIfMatch = 313 111 +### D0 -> K*- π+ +421:onIfMatch = 323 211 +### D0 -> π π +421:onIfMatch = 211 211 +### D0 -> rho+ π- +421:onIfMatch = 213 -211 +### D0 -> π π π0 +421:onIfMatch = 211 211 111 +### D0 -> K K +421:onIfMatch = 321 321 + +### D+/- -> K π π +411:onIfMatch = 321 211 211 +### D+/- -> K π π +411:onIfMatch = 321 211 211 111 +### D+/- -> K* K +411:onIfMatch = 313 321 +### D+/- -> antiK* π +411:onIfMatch = 10311 321 +### D+/- -> φ π +411:onIfMatch = 333 211 +### D+/- -> K K π +411:onIfMatch = 321 321 211 +### D+/- -> f2(1270) π +411:onIfMatch = 225 211 +### D+/- -> rho π +411:onIfMatch = 113 211 +### D+/- -> π π π +411:onIfMatch = 211 211 211 + +### Ds -> φ π +431:onIfMatch = 333 211 +### Ds -> K* K +431:onIfMatch = 313 321 +### Ds -> φ rho+ +431:onIfMatch = 333 213 +### Ds -> rho π +431:onIfMatch = 113 211 +### Ds -> f2 π +431:onIfMatch = 225 211 +### Ds -> π π π +431:onIfMatch = 211 211 211 +### Ds -> K*(892)0 π+ +431:onIfMatch = 313 211 +### Ds -> f0(1370) K+ +431:onIfMatch = 10221 321 +### Ds -> rho0 K+ +431:onIfMatch = 113 321 +### Ds -> K π π +431:onIfMatch = 321 211 211 +### Ds -> eta π+ +431:onIfMatch = 221 211 + +### Λc -> p K π +4122:onIfMatch = 2212 321 211 +### Λc -> p K* +4122:onIfMatch = 2212 313 +### Λc -> Delta++ K +4122:onIfMatch = 2224 321 +### Λc -> Lambda(1520) π +4122:onIfMatch = 102134 211 +### Λc -> p K- π+ π0 +4122:onIfMatch = 2212 321 211 111 +### Λc -> p π π +4122:onIfMatch = 2212 211 211 +### Λc -> p K K +4122:onIfMatch = 2212 333 + +### Ξc+ -> p K- π+ +4232:onIfMatch = 2212 321 211 +### Ξc+ -> p antiK*0(892) +4232:onIfMatch = 2212 313 +### Ξc+ -> p φ +4232:onIfMatch = 2212 333 +### Ξc+ -> sigma- π+ π+ +4232:onIfMatch = 3222 211 211 \ No newline at end of file diff --git a/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_with_decays_Mode2_CorrBkgSigmaC.cfg b/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_with_decays_Mode2_CorrBkgSigmaC.cfg new file mode 100644 index 000000000..0d61dfe3b --- /dev/null +++ b/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_with_decays_Mode2_CorrBkgSigmaC.cfg @@ -0,0 +1,84 @@ +### authors: Mattia Faggin (mattia.faggin@cern.ch) +### last update: July 2025 + +### beams +Beams:idA 2212 # proton +Beams:idB 2212 # proton +Beams:eCM 13600. # GeV + +### processes +SoftQCD:inelastic on # all inelastic processes + +### decays +ParticleDecays:limitTau0 on +ParticleDecays:tau0Max 10. + +### switching on Pythia Mode2 +ColourReconnection:mode 1 +ColourReconnection:allowDoubleJunRem off +ColourReconnection:m0 0.3 +ColourReconnection:allowJunctions on +ColourReconnection:junctionCorrection 1.20 +ColourReconnection:timeDilationMode 2 +ColourReconnection:timeDilationPar 0.18 +StringPT:sigma 0.335 +StringZ:aLund 0.36 +StringZ:bLund 0.56 +StringFlav:probQQtoQ 0.078 +StringFlav:ProbStoUD 0.2 +StringFlav:probQQ1toQQ0join 0.0275,0.0275,0.0275,0.0275 +MultiPartonInteractions:pT0Ref 2.15 +BeamRemnants:remnantMode 1 +BeamRemnants:saturation 5 + +# switch off charm-hadron decays +4122:onMode = off +14122:onMode = off ### Lambda_c(2595)+ already present in PYTHIA +4124:onMode = off ### Lambda_c(2625)+ already present in PYTHIA + +## Λc decays +### Λc+ -> p K- π+ +4122:oneChannel = 1 0.0350 0 2212 -321 211 ### Λc+ -> p K- π+ (non-resonant) 3.5% +4122:addChannel = 1 0.0196 100 2212 -313 ### Λc+ -> p K*0(892) 1.96% +4122:addChannel = 1 0.0108 100 2224 -321 ### Λc+ -> Delta++ K- 1.08% +4122:addChannel = 1 0.0022 100 102134 211 ### Λc+ -> Lambda(1520) K- 2.20e-3 +4122:onIfMatch = 2212 321 211 +4122:onIfMatch = 2212 313 +4122:onIfMatch = 2224 321 +4122:onIfMatch = 102134 211 +### Λc(2595)+ +14122:oneChannel = 1 0.24 0 4222 -211 ### Λc(2595)+ -> Σc(2455)++ π- (24%) +14122:addChannel = 1 0.24 0 4112 211 ### Λc(2595)+ -> Σc(2455)0 π+ (24%) +14122:addChannel = 1 0.18 0 4122 211 -211 ### Λc(2595)+ -> Λc+ π+ π- (18%) +14122:onIfMatch = 4222 211 ### Λc(2595)+ -> Σc(2455)++ π- +14122:onIfMatch = 4112 211 ### Λc(2595)+ -> Σc(2455)0 π+ +14122:onIfMatch = 4122 211 211 ### Λc(2595)+ -> Λc+ π+ π- +### Λc(2625)+ +4124:oneChannel = 1 0.24 0 4222 -211 ### Λc(2592)+ -> Σc(2455)++ π- (24%) +4124:addChannel = 1 0.24 0 4112 211 ### Λc(2592)+ -> Σc(2455)0 π+ (24%) +4124:addChannel = 1 0.18 0 4122 211 -211 ### Λc(2592)+ -> Λc+ π+ π- (18%) +4124:onIfMatch = 4222 211 ### Λc(2625)+ -> Σc(2455)++ π- +4124:onIfMatch = 4112 211 ### Λc(2625)+ -> Σc(2455)0 π+ +4124:onIfMatch = 4122 211 211 ### Λc(2625)+ -> Λc+ π+ π- + +# Correct decay lengths (wrong in PYTHIA8 decay table) +# Lb +5122:tau0 = 0.4390 +# Xic0 +4132:tau0 = 0.0455 +# OmegaC +4332:tau0 = 0.0803 + +# Correct Breit-Wigner width +4124:mWidth = 0.00052 # <0.52 MeV + +# Force the decay of resonances in the decay chain +### K*0(892) -> K- π+ +313:onMode = off +313:onIfAll = 321 211 +### for Λc -> Delta++ K- +2224:onMode = off +2224:onIfAll = 2212 211 +### for Λc -> Lambda(1520) K- +102134:onMode = off +102134:onIfAll = 2212 321 diff --git a/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_with_decays_Mode2_hardQCD_5TeV.cfg b/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_with_decays_Mode2_hardQCD_5TeV.cfg new file mode 100644 index 000000000..f407097af --- /dev/null +++ b/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_with_decays_Mode2_hardQCD_5TeV.cfg @@ -0,0 +1,294 @@ +### authors: Fabrizio Grosa (fabrizio.grosa@cern.ch) +### Mattia Faggin (mattia.faggin@cern.ch) +### last update: June 2024 + +### beams +Beams:idA 2212 # proton +Beams:idB 2212 # proton +Beams:eCM 5360. # GeV + +### processes: c-cbar and b-bbar processes +HardQCD:hardccbar on +HardQCD:hardbbbar on +HardQCD:gg2ccbar on +HardQCD:qqbar2ccbar on +HardQCD:gg2bbbar on +HardQCD:qqbar2bbbar on + +### decays +ParticleDecays:limitTau0 on +ParticleDecays:tau0Max 10. + +### switching on Pythia Mode2 +ColourReconnection:mode 1 +ColourReconnection:allowDoubleJunRem off +ColourReconnection:m0 0.3 +ColourReconnection:allowJunctions on +ColourReconnection:junctionCorrection 1.20 +ColourReconnection:timeDilationMode 2 +ColourReconnection:timeDilationPar 0.18 +StringPT:sigma 0.335 +StringZ:aLund 0.36 +StringZ:bLund 0.56 +StringFlav:probQQtoQ 0.078 +StringFlav:ProbStoUD 0.2 +StringFlav:probQQ1toQQ0join 0.0275,0.0275,0.0275,0.0275 +MultiPartonInteractions:pT0Ref 2.15 +BeamRemnants:remnantMode 1 +BeamRemnants:saturation 5 + +# Correct decay lengths (wrong in PYTHIA8 decay table) +# Lb +5122:tau0 = 0.4390 +# Xic0 +4132:tau0 = 0.0455 +# OmegaC +4332:tau0 = 0.0803 + +### Force golden charm hadrons decay modes for D2H studies +# HF decays +### BR are set to yield 50% of signal in golden channel and uniform abundance of corr. bkg channels (weighted by BR from PDG) +### +### D0 decays +### D0 -> K- π+ (50%) +421:oneChannel = 1 0.50000 0 -321 211 ### D0 -> K- π+ 3.94% +### D0 -> K- π+ π0 (12.50%) +421:addChannel = 1 0.00625 0 -321 211 111 ### D0 -> K- π+ π0 (non-resonant) 1.15% (e.g. 115/(115+231+195+1120)*0.2) +421:addChannel = 1 0.08750 0 213 -321 ### D0 -> rho+ K- 11.2% +421:addChannel = 1 0.01250 0 -313 111 ### D0 -> antiK*0(892) π0 1.95% +421:addChannel = 1 0.01875 0 -323 211 ### D0 -> K*-(892) π+ 2.31% +### D0 -> π- π+ (12.50%) +421:addChannel = 1 0.12500 0 -211 211 ### D0 -> π- π+ (non-resonant) 1.0e-4 +### D0 -> π- π+ π0 (12.50%) +421:addChannel = 1 0.08750 0 213 -211 ### D0 -> rho+ π- 1.01% +421:addChannel = 1 0.03750 0 -211 211 111 ### D0 -> π- π+ π0 (non-resonant) 1.3e-4 +### D0 -> K- K+ (12.50%) +421:addChannel = 1 0.12500 0 -321 321 ### D0 -> K- K+ (non-resonant) 4.08e-3 + +### D+ decays +### D+ -> K- π+ π+ (50%) +411:oneChannel = 1 0.40189 0 -321 211 211 ### D+ -> K- π+ π+ 9.38% +411:addChannel = 1 0.05356 0 -10311 211 ### D+ -> antiK*0(1430) π+ 1.25% +411:addChannel = 1 0.04455 0 -313 211 ### D+ -> K*0(892) pi+ 1.04% +### D+ -> K- π+ π+ π0 (small, 3%) +411:addChannel = 1 0.03000 0 -321 211 211 111 ### D+ -> K- π+ π+ π0 6.25% +### D+ -> K- K+ π+ (36.00%, set 25% for D+ -> φ π+, 11% for the rest) +411:addChannel = 1 0.25000 0 333 211 ### D+ -> φ π+ 2.69e-3 !needed for signal +411:addChannel = 1 0.03929 0 -313 321 ### D+ -> K*0(892) K+ 2.49e-3 +411:addChannel = 1 0.02865 0 -10311 321 ### D+ -> antiK*0(1430) K+ 1.82e-3 +411:addChannel = 1 0.04206 0 -321 321 211 ### D+ -> K- K+ π+ (non-resonant) 2.68e-3 +### D+ -> π- π+ π+ (11.00%) +411:addChannel = 1 0.02911 0 113 211 ### D+ -> rho0 π+ 8.4e-4 +411:addChannel = 1 0.01618 0 225 211 ### D+ -> f2(1270) π+ 4.6e-4 +411:addChannel = 1 0.06471 0 -211 211 211 ### D+ -> π- π+ π+ (non-resonant) 1.0e-4 + +### Ds+ decays +### Ds+ -> K- K+ π+ (50%) +431:oneChannel = 1 0.50000 0 333 211 ### Ds+ -> φ(1020) π+ 2.21% +431:addChannel = 1 0.15000 0 -313 321 ### Ds+ -> antiK*(892) K+ 2.58% +### Ds+ -> K- K+ π+ π0 (small, 2%) +431:addChannel = 1 0.02000 0 333 213 ### Ds+ -> φ(1020) rho 5.50% +### Ds+ -> π- π+ π+ (11.00%) +431:addChannel = 1 0.00220 0 113 211 ### Ds+ -> rho π+ 1.1e-4 +431:addChannel = 1 0.00220 0 225 211 ### Ds+ -> f2(1270) π+ 1.4e-3 +431:addChannel = 1 0.10560 0 -211 211 211 ### Ds+ -> π- π+ π+ 9.12e-3 (s-wave) +### Ds+ -> π- K+ π+ (11.00%) +431:addChannel = 1 0.03080 0 313 211 ### Ds+ -> K*(892)0 π+ 1.67e-3 +431:addChannel = 1 0.02200 0 10221 321 ### Ds+ -> f0(1370) K+ 1.2e-3 +431:addChannel = 1 0.03960 0 113 321 ### Ds+ -> rho0 K+ 2.17e-3 +431:addChannel = 1 0.01760 0 -211 321 211 ### Ds+ -> π- K+ π+ (non-resonant) 1.16-3 +### Ds+ -> π+ π- π+ π0 (11.00%) +431:addChannel = 1 0.11000 0 221 211 ### Ds+ -> eta π+ -> π0 π+ π+ π- (affects D+ golden channel) + +## Λc decays +### Λc+ -> p K- π+ (36%) +4122:oneChannel = 1 0.14400 0 2212 -321 211 ### Λc+ -> p K- π+ (non-resonant) 3.5% +4122:addChannel = 1 0.08100 100 2212 -313 ### Λc+ -> p K*0(892) 1.96% +4122:addChannel = 1 0.04500 100 2224 -321 ### Λc+ -> Delta++ K- 1.08% +4122:addChannel = 1 0.09000 100 102134 211 ### Λc+ -> Lambda(1520) K- 2.20e-3 +### Λc+ -> p K0S (36%) +4122:addChannel = 1 0.36000 0 2212 310 ### Λc+ -> p K0S 1.59% +### Λc+ -> p K- π+ π0 (small, 3%) +4122:addChannel = 1 0.03000 0 2212 -321 211 111 ### Λc+ -> p K- π+ π0 (non-resonant) 4.6% +### Λc+ -> p π- π+ (12.50%) +4122:addChannel = 1 0.12500 0 2212 -211 211 ### Λc+ -> p π+ π+ 4.59% +### Λc+ -> p K- K+ (12.50%) +4122:addChannel = 1 0.12500 0 2212 333 ### Λc+ -> p phi 1.06% + +## Xic decays +### Ξc+ -> p K- π+ (35%) +4232:oneChannel = 1 0.17500 0 2212 -321 211 ### Ξc+ -> p K- π+ 6.18e-3 +4232:addChannel = 1 0.17500 0 2212 -313 ### Ξc+ -> p antiK*0(892) +### Ξc+ -> Ξ- π+ π+ (35%) (set the same as Ξc+ -> p K- π+) +4232:addChannel = 1 0.35000 0 3312 211 211 ### Ξc+ -> Ξ- π+ π+ 2.86% +### Ξc+ -> p φ (10%) +4232:addChannel = 1 0.10000 0 2212 333 ### Ξc+ -> p φ +### Ξc+ -> sigma+ π+ π- (10%) +4232:addChannel = 1 0.12500 0 3222 -211 211 ### Ξc+ -> sigma+ π- π+ 1.37% +### Ξc+ -> Ξ*0 π+ (10%) +4232:addChannel = 1 0.12500 0 3324 211 + +### add Xic0 decays absent in PYTHIA8 decay table +4132:oneChannel = 1 0.0143 0 3312 211 + +### add OmegaC decays absent in PYTHIA8 decay table +4332:oneChannel = 1 0.5 0 3334 211 +4332:addChannel = 1 0.5 0 3312 211 + +# Allow the decay of resonances in the decay chain +### K*0(892) -> K- π+ +313:onMode = off +313:onIfAll = 321 211 +### K*(892)+ -> K- π0 +323:onMode = off +323:onIfAll = 321 111 +### K*(1430)0 -> K- π+ +10311:onMode = off +10311:onIfAll = 321 211 +### rho+ -> π+ π0 +213:onMode = off +213:onIfAll = 211 111 +### φ -> K+ K- +333:onMode = off +333:onIfAll = 321 321 +### rho0 -> π+ π- +113:onMode = off +113:onIfAll = 211 211 +### f2(1270) -> π+ π- +225:onMode = off +225:onIfAll = 211 211 +### f0(1370) -> π+ π- +10221:onMode = off +10221:onIfAll = 211 211 +### eta -> π+ π- +221:onMode = off +221:onIfAll = 111 211 211 +### for Λc -> Delta++ K- +2224:onMode = off +2224:onIfAll = 2212 211 +### for Λc -> Lambda(1520) K- +102134:onMode = off +102134:onIfAll = 2212 321 +### for Xic0 -> pi Xi -> pi pi Lambda -> pi pi pi p +### and Omega_c -> pi Xi -> pi pi Lambda -> pi pi pi p +3312:onMode = off +3312:onIfAll = 3122 -211 +3122:onMode = off +3122:onIfAll = 2212 -211 +### for Omega_c -> pi Omega -> pi K Lambda -> pi K pi p +3334:onMode = off +3334:onIfAll = 3122 -321 + +# Switch off all decay channels +411:onMode = off +421:onMode = off +431:onMode = off +4122:onMode = off +4232:onMode = off +4132:onMode = off +443:onMode = off +4332:onMode = off + +# Allow the decay of HF +### D0 -> K π +421:onIfMatch = 321 211 +### D0 -> K π π0 +421:onIfMatch = 321 211 111 +### D0 -> rho K +421:onIfMatch = 213 321 +### D0 -> antiK*0 π0 +421:onIfMatch = 313 111 +### D0 -> K*- π+ +421:onIfMatch = 323 211 +### D0 -> π π +421:onIfMatch = 211 211 +### D0 -> rho+ π- +421:onIfMatch = 213 -211 +### D0 -> π π π0 +421:onIfMatch = 211 211 111 +### D0 -> K K +421:onIfMatch = 321 321 + +### D+/- -> K π π +411:onIfMatch = 321 211 211 +### D+/- -> K π π +411:onIfMatch = 321 211 211 111 +### D+/- -> K* π +411:onIfMatch = 313 211 +### D+/- -> K* K +411:onIfMatch = 313 321 +### D+/- -> antiK* π +411:onIfMatch = 10311 211 +### D+/- -> antiK* K +411:onIfMatch = 10311 321 +### D+/- -> φ π +411:onIfMatch = 333 211 +### D+/- -> K K π +411:onIfMatch = 321 321 211 +### D+/- -> f2(1270) π +411:onIfMatch = 225 211 +### D+/- -> rho π +411:onIfMatch = 113 211 +### D+/- -> π π π +411:onIfMatch = 211 211 211 + +### Ds -> φ π +431:onIfMatch = 333 211 +### Ds -> K* K +431:onIfMatch = 313 321 +### Ds -> φ rho+ +431:onIfMatch = 333 213 +### Ds -> rho π +431:onIfMatch = 113 211 +### Ds -> f2 π +431:onIfMatch = 225 211 +### Ds -> π π π +431:onIfMatch = 211 211 211 +### Ds -> K*(892)0 π+ +431:onIfMatch = 313 211 +### Ds -> f0(1370) K+ +431:onIfMatch = 10221 321 +### Ds -> rho0 K+ +431:onIfMatch = 113 321 +### Ds -> K π π +431:onIfMatch = 321 211 211 +### Ds -> eta π+ +431:onIfMatch = 221 211 + +### Λc -> pK0s +4122:onIfMatch = 2212 310 +### Λc -> p K- π+ π0 +4122:onIfMatch = 2212 321 211 +### Λc -> p K* +4122:onIfMatch = 2212 313 +### Λc -> Delta++ K +4122:onIfMatch = 2224 321 +### Λc -> Lambda(1520) π +4122:onIfMatch = 102134 211 +### Λc -> p K- π+ π0 +4122:onIfMatch = 2212 321 211 111 +### Λc -> p π π +4122:onIfMatch = 2212 211 211 +### Λc -> p K K +4122:onIfMatch = 2212 333 + +### Ξc+ -> p K- π+ +4232:onIfMatch = 2212 321 211 +### Ξc+ -> p antiK*0(892) +4232:onIfMatch = 2212 313 +### Ξc+ -> p φ +4232:onIfMatch = 2212 333 +### Ξc+ -> sigma- π+ π+ +4232:onIfMatch = 3222 211 211 +### Ξc+ -> Ξ*0 π+, Ξ*0 -> Ξ- π+ +4232:onIfMatch = 3324 211 +### Ξc+ -> Ξ- π+ π+ +4232:onIfMatch = 3312 211 211 + +### Xic0 -> Xi- pi+ +4132:onIfMatch = 3312 211 + +### Omega_c -> Omega pi +4332:onIfMatch = 3334 211 +### Omega_c -> Xi pi +4332:onIfMatch = 3312 211 \ No newline at end of file diff --git a/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_with_decays_Mode2_hardQCD_5TeV_CorrBkg.cfg b/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_with_decays_Mode2_hardQCD_5TeV_CorrBkg.cfg new file mode 100644 index 000000000..7280c9338 --- /dev/null +++ b/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_with_decays_Mode2_hardQCD_5TeV_CorrBkg.cfg @@ -0,0 +1,250 @@ +### authors: Fabrizio Grosa (fabrizio.grosa@cern.ch) +### Mattia Faggin (mattia.faggin@cern.ch) +### Stefano Politano (stefano.politano@cern.ch) +###50 Marcello Di Costanzo (marcello.di.costanzo@cern.ch) +### last update: June 2024 + +### beams +Beams:idA 2212 # proton +Beams:idB 2212 # proton +Beams:eCM 5360. # GeV + +### processes: c-cbar and b-bbar processes +HardQCD:hardccbar on +HardQCD:hardbbbar on +HardQCD:gg2ccbar on +HardQCD:qqbar2ccbar on +HardQCD:gg2bbbar on +HardQCD:qqbar2bbbar on + +### decays +ParticleDecays:limitTau0 on +ParticleDecays:tau0Max 10. + +### switching on Pythia Mode2 +ColourReconnection:mode 1 +ColourReconnection:allowDoubleJunRem off +ColourReconnection:m0 0.3 +ColourReconnection:allowJunctions on +ColourReconnection:junctionCorrection 1.20 +ColourReconnection:timeDilationMode 2 +ColourReconnection:timeDilationPar 0.18 +StringPT:sigma 0.335 +StringZ:aLund 0.36 +StringZ:bLund 0.56 +StringFlav:probQQtoQ 0.078 +StringFlav:ProbStoUD 0.2 +StringFlav:probQQ1toQQ0join 0.0275,0.0275,0.0275,0.0275 +MultiPartonInteractions:pT0Ref 2.15 +BeamRemnants:remnantMode 1 +BeamRemnants:saturation 5 + +# Correct decay lengths (wrong in PYTHIA8 decay table) +# Lb +5122:tau0 = 0.4390 +# Xic0 +4132:tau0 = 0.0455 +# OmegaC +4332:tau0 = 0.0803 + +### HF decays +### BR are set to yield 20% of signal in golden channel and uniform abundance of corr. bkg channels (weighted by BR from PDG) +### +### D0 decays +### D0 -> K- π+ (20%) +421:oneChannel = 1 0.20 0 -321 211 ### D0 -> K- π+ 3.94% +### D0 -> K- π+ π0 (20%) +421:addChannel = 1 0.01 0 -321 211 111 ### D0 -> K- π+ π0 (non-resonant) 1.15% (e.g. 115/(115+231+195+1120)*0.2) +421:addChannel = 1 0.14 0 213 -321 ### D0 -> rho+ K- 11.2% +421:addChannel = 1 0.02 0 -313 111 ### D0 -> antiK*0(892) π0 1.95% +421:addChannel = 1 0.03 0 -323 211 ### D0 -> K*-(892) π+ 2.31% +### D0 -> π- π+ (20%) +421:addChannel = 1 0.20 0 -211 211 ### D0 -> π- π+ (non-resonant) 1.0e-4 +### D0 -> π- π+ π0 (20%) +421:addChannel = 1 0.14 0 213 -211 ### D0 -> rho+ π- 1.01% +421:addChannel = 1 0.06 0 -211 211 111 ### D0 -> π- π+ π0 (non-resonant) 1.3e-4 +### D0 -> K- K+ (20%) +421:addChannel = 1 0.20 0 -321 321 ### D0 -> K- K+ (non-resonant) 4.08e-3 + +### D+ decays +### D+ -> K- π+ π+ (28%) +411:oneChannel = 1 0.28 0 -321 211 211 ### D+ -> K- π+ π+ 9.38% +### D+ -> K- π+ π+ π0 (small, 5%) +411:addChannel = 1 0.05 0 -321 211 211 111 ### D+ -> K- π+ π+ π0 6.25% +### D+ -> K- K+ π+ (33%) +411:addChannel = 1 0.085 0 -313 321 ### D+ -> K*0(892) K+ 2.49e-3 +411:addChannel = 1 0.062 0 -10311 321 ### D+ -> antiK*0(1430) K+ 1.82e-3 +411:addChannel = 1 0.092 0 333 211 ### D+ -> φ π+ 2.69e-3 +411:addChannel = 1 0.091 0 -321 321 211 ### D+ -> K- K+ π+ (non-resonant) 2.68e-3 +### D+ -> π- π+ π+ (34%) +411:addChannel = 1 0.09 0 113 211 ### D+ -> rho0 π+ 8.4e-4 +411:addChannel = 1 0.05 0 225 211 ### D+ -> f2(1270) π+ 4.6e-4 +411:addChannel = 1 0.20 0 -211 211 211 ### D+ -> π- π+ π+ (non-resonant) 1.0e-4 + +### Ds+ decays +### Ds+ -> K- K+ π+ (20%) +431:oneChannel = 1 0.100 0 333 211 ### Ds+ -> φ(1020) π+ 2.21% +431:addChannel = 1 0.100 0 -313 321 ### Ds+ -> antiK*(892) K+ 2.58% +### Ds+ -> K- K+ π+ π0 (small, 5%) +431:addChannel = 1 0.050 0 333 213 ### Ds+ -> φ(1020) rho 5.50% +### Ds+ -> π- π+ π+ (25%) +431:addChannel = 1 0.005 0 113 211 ### Ds+ -> rho π+ 1.1e-4 +431:addChannel = 1 0.005 0 225 211 ### Ds+ -> f2(1270) π+ 1.4e-3 +431:addChannel = 1 0.240 0 -211 211 211 ### Ds+ -> π- π+ π+ 9.12e-3 (s-wave) +### Ds+ -> π- K+ π+ (25%) +431:addChannel = 1 0.07 0 313 211 ### Ds+ -> K*(892)0 π+ 1.67e-3 +431:addChannel = 1 0.05 0 10221 321 ### Ds+ -> f0(1370) K+ 1.2e-3 +431:addChannel = 1 0.09 0 113 321 ### Ds+ -> rho0 K+ 2.17e-3 +431:addChannel = 1 0.04 0 -211 321 211 ### Ds+ -> π- K+ π+ (non-resonant) 1.16-3 +### Ds+ -> π+ π- π+ π0 (25%) +431:addChannel = 1 0.250 0 221 211 ### Ds+ -> eta π+ -> π0 π+ π+ π- (affects D+ golden channel) + +## Λc decays +### Λc+ -> p K- π+ (20%, BR set up to 20%) +4122:oneChannel = 1 0.080 0 2212 -321 211 ### Λc+ -> p K- π+ (non-resonant) 3.5% +4122:addChannel = 1 0.045 100 2212 -313 ### Λc+ -> p K*0(892) 1.96% +4122:addChannel = 1 0.025 100 2224 -321 ### Λc+ -> Delta++ K- 1.08% +4122:addChannel = 1 0.050 100 102134 211 ### Λc+ -> Lambda(1520) K- 2.20e-3 +### Λc+ -> p K- π+ π0 (small, 5%) +4122:addChannel = 1 0.050 0 2212 -321 211 111 ### Λc+ -> p K- π+ π0 (non-resonant) 4.6% +### Λc+ -> p π- π+ (25%) +4122:addChannel = 1 0.250 0 2212 -211 211 ### Λc+ -> p π+ π+ 4.59% +### Λc+ -> p K- K+ (25%) +4122:addChannel = 1 0.250 0 2212 333 ### Λc+ -> p phi 1.06% + +### Ξc+ decays (unfiormly distributed) +### Ξc+ -> p K- π+ (34%) +4232:oneChannel = 1 0.17 0 2212 -321 211 ### Ξc+ -> p K- π+ +4232:addChannel = 1 0.17 0 2212 -313 ### Ξc+ -> p antiK*0(892) +### Ξc+ -> p K- K+ (33%) +4232:addChannel = 1 0.33 0 2212 333 ### Ξc+ -> p φ +### Ξc+ -> sigma+ π+ π- (33%) +4232:addChannel = 1 0.33 0 3222 -211 211 ### Ξc+ -> sigma+ π- π+ + +# Allow the decay of resonances in the decay chain +### K*0(892) -> K- π+ +313:onMode = off +313:onIfAll = 321 211 +### K*(892)+ -> K- π0 +323:onMode = off +323:onIfAll = 321 111 +### K*(1430)0 -> K- π+ +10311:onMode = off +10311:onIfAll = 321 211 +### rho+ -> π+ π0 +213:onMode = off +213:onIfAll = 211 111 +### φ -> K+ K- +333:onMode = off +333:onIfAll = 321 321 +### rho0 -> π+ π- +113:onMode = off +113:onIfAll = 211 211 +### f2(1270) -> π+ π- +225:onMode = off +225:onIfAll = 211 211 +### f0(1370) -> π+ π- +10221:onMode = off +10221:onIfAll = 211 211 +### eta -> π+ π- +221:onMode = off +221:onIfAll = 111 211 211 +### for Λc -> Delta++ K- +2224:onMode = off +2224:onIfAll = 2212 211 +### for Λc -> Lambda(1520) K- +102134:onMode = off +102134:onIfAll = 2212 321 + +### Switch off all decay channels +411:onMode = off +421:onMode = off +431:onMode = off +4122:onMode = off +4232:onMode = off + +# Allow the decay of HF +### D0 -> K π +421:onIfMatch = 321 211 +### D0 -> K π π0 +421:onIfMatch = 321 211 111 +### D0 -> rho K +421:onIfMatch = 213 321 +### D0 -> antiK*0 π0 +421:onIfMatch = 313 111 +### D0 -> K*- π+ +421:onIfMatch = 323 211 +### D0 -> π π +421:onIfMatch = 211 211 +### D0 -> rho+ π- +421:onIfMatch = 213 -211 +### D0 -> π π π0 +421:onIfMatch = 211 211 111 +### D0 -> K K +421:onIfMatch = 321 321 + +### D+/- -> K π π +411:onIfMatch = 321 211 211 +### D+/- -> K π π +411:onIfMatch = 321 211 211 111 +### D+/- -> K* K +411:onIfMatch = 313 321 +### D+/- -> antiK* π +411:onIfMatch = 10311 321 +### D+/- -> φ π +411:onIfMatch = 333 211 +### D+/- -> K K π +411:onIfMatch = 321 321 211 +### D+/- -> f2(1270) π +411:onIfMatch = 225 211 +### D+/- -> rho π +411:onIfMatch = 113 211 +### D+/- -> π π π +411:onIfMatch = 211 211 211 + +### Ds -> φ π +431:onIfMatch = 333 211 +### Ds -> K* K +431:onIfMatch = 313 321 +### Ds -> φ rho+ +431:onIfMatch = 333 213 +### Ds -> rho π +431:onIfMatch = 113 211 +### Ds -> f2 π +431:onIfMatch = 225 211 +### Ds -> π π π +431:onIfMatch = 211 211 211 +### Ds -> K*(892)0 π+ +431:onIfMatch = 313 211 +### Ds -> f0(1370) K+ +431:onIfMatch = 10221 321 +### Ds -> rho0 K+ +431:onIfMatch = 113 321 +### Ds -> K π π +431:onIfMatch = 321 211 211 +### Ds -> eta π+ +431:onIfMatch = 221 211 + +### Λc -> p K π +4122:onIfMatch = 2212 321 211 +### Λc -> p K* +4122:onIfMatch = 2212 313 +### Λc -> Delta++ K +4122:onIfMatch = 2224 321 +### Λc -> Lambda(1520) π +4122:onIfMatch = 102134 211 +### Λc -> p K- π+ π0 +4122:onIfMatch = 2212 321 211 111 +### Λc -> p π π +4122:onIfMatch = 2212 211 211 +### Λc -> p K K +4122:onIfMatch = 2212 333 + +### Ξc+ -> p K- π+ +4232:onIfMatch = 2212 321 211 +### Ξc+ -> p antiK*0(892) +4232:onIfMatch = 2212 313 +### Ξc+ -> p φ +4232:onIfMatch = 2212 333 +### Ξc+ -> sigma- π+ π+ +4232:onIfMatch = 3222 211 211 \ No newline at end of file diff --git a/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_with_decays_Mode2_pp_ref.cfg b/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_with_decays_Mode2_pp_ref.cfg new file mode 100644 index 000000000..16d19709f --- /dev/null +++ b/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_with_decays_Mode2_pp_ref.cfg @@ -0,0 +1,290 @@ +### authors: Fabrizio Grosa (fabrizio.grosa@cern.ch) +### Cristina Terrevoli (cristina.terrevoli@cern.ch) +### Fabio Catalano (fabio.catalano@cern.ch) +### last update: November 2023 + +### beams +Beams:idA 2212 # proton +Beams:idB 2212 # proton +Beams:eCM 5360. # GeV + +### processes +SoftQCD:inelastic on # all inelastic processes + +### decays +ParticleDecays:limitTau0 on +ParticleDecays:tau0Max 10. + +### switching on Pythia Mode2 +ColourReconnection:mode 1 +ColourReconnection:allowDoubleJunRem off +ColourReconnection:m0 0.3 +ColourReconnection:allowJunctions on +ColourReconnection:junctionCorrection 1.20 +ColourReconnection:timeDilationMode 2 +ColourReconnection:timeDilationPar 0.18 +StringPT:sigma 0.335 +StringZ:aLund 0.36 +StringZ:bLund 0.56 +StringFlav:probQQtoQ 0.078 +StringFlav:ProbStoUD 0.2 +StringFlav:probQQ1toQQ0join 0.0275,0.0275,0.0275,0.0275 +MultiPartonInteractions:pT0Ref 2.15 +BeamRemnants:remnantMode 1 +BeamRemnants:saturation 5 + +# Correct decay lengths (wrong in PYTHIA8 decay table) +# Lb +5122:tau0 = 0.4390 +# Xic0 +4132:tau0 = 0.0455 +# OmegaC +4332:tau0 = 0.0803 + +### Force golden charm hadrons decay modes for D2H studies +# HF decays +### BR are set to yield 50% of signal in golden channel and uniform abundance of corr. bkg channels (weighted by BR from PDG) +### +### D0 decays +### D0 -> K- π+ (50%) +421:oneChannel = 1 0.50000 0 -321 211 ### D0 -> K- π+ 3.94% +### D0 -> K- π+ π0 (12.50%) +421:addChannel = 1 0.00625 0 -321 211 111 ### D0 -> K- π+ π0 (non-resonant) 1.15% (e.g. 115/(115+231+195+1120)*0.2) +421:addChannel = 1 0.08750 0 213 -321 ### D0 -> rho+ K- 11.2% +421:addChannel = 1 0.01250 0 -313 111 ### D0 -> antiK*0(892) π0 1.95% +421:addChannel = 1 0.01875 0 -323 211 ### D0 -> K*-(892) π+ 2.31% +### D0 -> π- π+ (12.50%) +421:addChannel = 1 0.12500 0 -211 211 ### D0 -> π- π+ (non-resonant) 1.0e-4 +### D0 -> π- π+ π0 (12.50%) +421:addChannel = 1 0.08750 0 213 -211 ### D0 -> rho+ π- 1.01% +421:addChannel = 1 0.03750 0 -211 211 111 ### D0 -> π- π+ π0 (non-resonant) 1.3e-4 +### D0 -> K- K+ (12.50%) +421:addChannel = 1 0.12500 0 -321 321 ### D0 -> K- K+ (non-resonant) 4.08e-3 + +### D+ decays +### D+ -> K- π+ π+ (50%) +411:oneChannel = 1 0.40189 0 -321 211 211 ### D+ -> K- π+ π+ 9.38% +411:addChannel = 1 0.05356 0 -10311 211 ### D+ -> antiK*0(1430) π+ 1.25% +411:addChannel = 1 0.04455 0 -313 211 ### D+ -> K*0(892) pi+ 1.04% +### D+ -> K- π+ π+ π0 (small, 3%) +411:addChannel = 1 0.03000 0 -321 211 211 111 ### D+ -> K- π+ π+ π0 6.25% +### D+ -> K- K+ π+ (36.00%, set 25% for D+ -> φ π+, 11% for the rest) +411:addChannel = 1 0.25000 0 333 211 ### D+ -> φ π+ 2.69e-3 !needed for signal +411:addChannel = 1 0.03929 0 -313 321 ### D+ -> K*0(892) K+ 2.49e-3 +411:addChannel = 1 0.02865 0 -10311 321 ### D+ -> antiK*0(1430) K+ 1.82e-3 +411:addChannel = 1 0.04206 0 -321 321 211 ### D+ -> K- K+ π+ (non-resonant) 2.68e-3 +### D+ -> π- π+ π+ (11.00%) +411:addChannel = 1 0.02911 0 113 211 ### D+ -> rho0 π+ 8.4e-4 +411:addChannel = 1 0.01618 0 225 211 ### D+ -> f2(1270) π+ 4.6e-4 +411:addChannel = 1 0.06471 0 -211 211 211 ### D+ -> π- π+ π+ (non-resonant) 1.0e-4 + +### Ds+ decays +### Ds+ -> K- K+ π+ (50%) +431:oneChannel = 1 0.50000 0 333 211 ### Ds+ -> φ(1020) π+ 2.21% +431:addChannel = 1 0.15000 0 -313 321 ### Ds+ -> antiK*(892) K+ 2.58% +### Ds+ -> K- K+ π+ π0 (small, 2%) +431:addChannel = 1 0.02000 0 333 213 ### Ds+ -> φ(1020) rho 5.50% +### Ds+ -> π- π+ π+ (11.00%) +431:addChannel = 1 0.00220 0 113 211 ### Ds+ -> rho π+ 1.1e-4 +431:addChannel = 1 0.00220 0 225 211 ### Ds+ -> f2(1270) π+ 1.4e-3 +431:addChannel = 1 0.10560 0 -211 211 211 ### Ds+ -> π- π+ π+ 9.12e-3 (s-wave) +### Ds+ -> π- K+ π+ (11.00%) +431:addChannel = 1 0.03080 0 313 211 ### Ds+ -> K*(892)0 π+ 1.67e-3 +431:addChannel = 1 0.02200 0 10221 321 ### Ds+ -> f0(1370) K+ 1.2e-3 +431:addChannel = 1 0.03960 0 113 321 ### Ds+ -> rho0 K+ 2.17e-3 +431:addChannel = 1 0.01760 0 -211 321 211 ### Ds+ -> π- K+ π+ (non-resonant) 1.16-3 +### Ds+ -> π+ π- π+ π0 (11.00%) +431:addChannel = 1 0.11000 0 221 211 ### Ds+ -> eta π+ -> π0 π+ π+ π- (affects D+ golden channel) + +## Λc decays +### Λc+ -> p K- π+ (36%) +4122:oneChannel = 1 0.14400 0 2212 -321 211 ### Λc+ -> p K- π+ (non-resonant) 3.5% +4122:addChannel = 1 0.08100 100 2212 -313 ### Λc+ -> p K*0(892) 1.96% +4122:addChannel = 1 0.04500 100 2224 -321 ### Λc+ -> Delta++ K- 1.08% +4122:addChannel = 1 0.09000 100 102134 211 ### Λc+ -> Lambda(1520) K- 2.20e-3 +### Λc+ -> p K0S (36%) +4122:addChannel = 1 0.36000 0 2212 311 ### Λc+ -> p K0S 1.59% +### Λc+ -> p K- π+ π0 (small, 3%) +4122:addChannel = 1 0.03000 0 2212 -321 211 111 ### Λc+ -> p K- π+ π0 (non-resonant) 4.6% +### Λc+ -> p π- π+ (12.50%) +4122:addChannel = 1 0.12500 0 2212 -211 211 ### Λc+ -> p π+ π+ 4.59% +### Λc+ -> p K- K+ (12.50%) +4122:addChannel = 1 0.12500 0 2212 333 ### Λc+ -> p phi 1.06% + +## Xic decays +### Ξc+ -> p K- π+ (35%) +4232:oneChannel = 1 0.17500 0 2212 -321 211 ### Ξc+ -> p K- π+ 6.18e-3 +4232:addChannel = 1 0.17500 0 2212 -313 ### Ξc+ -> p antiK*0(892) +### Ξc+ -> Ξ- π+ π+ (35%) (set the same as Ξc+ -> p K- π+) +4232:addChannel = 1 0.35000 0 3312 211 211 ### Ξc+ -> Ξ- π+ π+ 2.86% +### Ξc+ -> p φ (10%) +4232:addChannel = 1 0.10000 0 2212 333 ### Ξc+ -> p φ +### Ξc+ -> sigma+ π+ π- (10%) +4232:addChannel = 1 0.12500 0 3222 -211 211 ### Ξc+ -> sigma+ π- π+ 1.37% +### Ξc+ -> Ξ*0 π+ (10%) +4232:addChannel = 1 0.12500 0 3324 211 + +### add Xic0 decays absent in PYTHIA8 decay table +4132:oneChannel = 1 0.0143 0 3312 211 + +### add OmegaC decays absent in PYTHIA8 decay table +4332:oneChannel = 1 0.5 0 3334 211 +4332:addChannel = 1 0.5 0 3312 211 + +# Allow the decay of resonances in the decay chain +### K*0(892) -> K- π+ +313:onMode = off +313:onIfAll = 321 211 +### K*(892)+ -> K- π0 +323:onMode = off +323:onIfAll = 321 111 +### K*(1430)0 -> K- π+ +10311:onMode = off +10311:onIfAll = 321 211 +### rho+ -> π+ π0 +213:onMode = off +213:onIfAll = 211 111 +### φ -> K+ K- +333:onMode = off +333:onIfAll = 321 321 +### rho0 -> π+ π- +113:onMode = off +113:onIfAll = 211 211 +### f2(1270) -> π+ π- +225:onMode = off +225:onIfAll = 211 211 +### f0(1370) -> π+ π- +10221:onMode = off +10221:onIfAll = 211 211 +### eta -> π+ π- +221:onMode = off +221:onIfAll = 111 211 211 +### for Λc -> Delta++ K- +2224:onMode = off +2224:onIfAll = 2212 211 +### for Λc -> Lambda(1520) K- +102134:onMode = off +102134:onIfAll = 2212 321 +### for Xic0 -> pi Xi -> pi pi Lambda -> pi pi pi p +### and Omega_c -> pi Xi -> pi pi Lambda -> pi pi pi p +3312:onMode = off +3312:onIfAll = 3122 -211 +3122:onMode = off +3122:onIfAll = 2212 -211 +### for Omega_c -> pi Omega -> pi K Lambda -> pi K pi p +3334:onMode = off +3334:onIfAll = 3122 -321 + +# switch off all decay channels +411:onMode = off +421:onMode = off +431:onMode = off +4122:onMode = off +4232:onMode = off +4132:onMode = off +443:onMode = off +4332:onMode = off + +# Allow the decay of HF +### D0 -> K π +421:onIfMatch = 321 211 +### D0 -> K π π0 +421:onIfMatch = 321 211 111 +### D0 -> rho K +421:onIfMatch = 213 321 +### D0 -> antiK*0 π0 +421:onIfMatch = 313 111 +### D0 -> K*- π+ +421:onIfMatch = 323 211 +### D0 -> π π +421:onIfMatch = 211 211 +### D0 -> rho+ π- +421:onIfMatch = 213 -211 +### D0 -> π π π0 +421:onIfMatch = 211 211 111 +### D0 -> K K +421:onIfMatch = 321 321 + +### D+/- -> K π π +411:onIfMatch = 321 211 211 +### D+/- -> K π π +411:onIfMatch = 321 211 211 111 +### D+/- -> K* π +411:onIfMatch = 313 211 +### D+/- -> K* K +411:onIfMatch = 313 321 +### D+/- -> antiK* π +411:onIfMatch = 10311 211 +### D+/- -> antiK* K +411:onIfMatch = 10311 321 +### D+/- -> φ π +411:onIfMatch = 333 211 +### D+/- -> K K π +411:onIfMatch = 321 321 211 +### D+/- -> f2(1270) π +411:onIfMatch = 225 211 +### D+/- -> rho π +411:onIfMatch = 113 211 +### D+/- -> π π π +411:onIfMatch = 211 211 211 + +### Ds -> φ π +431:onIfMatch = 333 211 +### Ds -> K* K +431:onIfMatch = 313 321 +### Ds -> φ rho+ +431:onIfMatch = 333 213 +### Ds -> rho π +431:onIfMatch = 113 211 +### Ds -> f2 π +431:onIfMatch = 225 211 +### Ds -> π π π +431:onIfMatch = 211 211 211 +### Ds -> K*(892)0 π+ +431:onIfMatch = 313 211 +### Ds -> f0(1370) K+ +431:onIfMatch = 10221 321 +### Ds -> rho0 K+ +431:onIfMatch = 113 321 +### Ds -> K π π +431:onIfMatch = 321 211 211 +### Ds -> eta π+ +431:onIfMatch = 221 211 + +### Λc -> pK0s +4122:onIfMatch = 2212 311 +### Λc -> p K- π+ π0 +4122:onIfMatch = 2212 321 211 +### Λc -> p K* +4122:onIfMatch = 2212 313 +### Λc -> Delta++ K +4122:onIfMatch = 2224 321 +### Λc -> Lambda(1520) π +4122:onIfMatch = 102134 211 +### Λc -> p K- π+ π0 +4122:onIfMatch = 2212 321 211 111 +### Λc -> p π π +4122:onIfMatch = 2212 211 211 +### Λc -> p K K +4122:onIfMatch = 2212 333 + +### Ξc+ -> p K- π+ +4232:onIfMatch = 2212 321 211 +### Ξc+ -> p antiK*0(892) +4232:onIfMatch = 2212 313 +### Ξc+ -> p φ +4232:onIfMatch = 2212 333 +### Ξc+ -> sigma- π+ π+ +4232:onIfMatch = 3222 211 211 +### Ξc+ -> Ξ*0 π+, Ξ*0 -> Ξ- π+ +4232:onIfMatch = 3324 211 +### Ξc+ -> Ξ- π+ π+ +4232:onIfMatch = 3312 211 211 + +### Xic0 -> Xi- pi+ +4132:onIfMatch = 3312 211 + +### Omega_c -> Omega pi +4332:onIfMatch = 3334 211 +### Omega_c -> Xi pi +4332:onIfMatch = 3312 211 \ No newline at end of file diff --git a/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_with_decays_Mode2_ptHardBins.cfg b/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_with_decays_Mode2_ptHardBins.cfg new file mode 100644 index 000000000..aa0a9d444 --- /dev/null +++ b/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_with_decays_Mode2_ptHardBins.cfg @@ -0,0 +1,157 @@ +### authors: Fabrizio Grosa (fabrizio.grosa@cern.ch) +### Cristina Terrevoli (cristina.terrevoli@cern.ch) +### Fabio Catalano (fabio.catalano@cern.ch) +### last update: March 2024 + +### beams +Beams:idA 2212 # proton +Beams:idB 2212 # proton +Beams:eCM 13600. # GeV + +### processes: c-cbar and b-bbar processes +HardQCD:hardccbar on +HardQCD:hardbbbar on +HardQCD:gg2ccbar on +HardQCD:qqbar2ccbar on +HardQCD:gg2bbbar on +HardQCD:qqbar2bbbar on + +### decays +ParticleDecays:limitTau0 on +ParticleDecays:tau0Max 10. + +### switching on Pythia Mode2 +ColourReconnection:mode 1 +ColourReconnection:allowDoubleJunRem off +ColourReconnection:m0 0.3 +ColourReconnection:allowJunctions on +ColourReconnection:junctionCorrection 1.20 +ColourReconnection:timeDilationMode 2 +ColourReconnection:timeDilationPar 0.18 +StringPT:sigma 0.335 +StringZ:aLund 0.36 +StringZ:bLund 0.56 +StringFlav:probQQtoQ 0.078 +StringFlav:ProbStoUD 0.2 +StringFlav:probQQ1toQQ0join 0.0275,0.0275,0.0275,0.0275 +MultiPartonInteractions:pT0Ref 2.15 +BeamRemnants:remnantMode 1 +BeamRemnants:saturation 5 + +### pT-hard bins +PhaseSpace:pTHatMin = 20 +PhaseSpace:pTHatMax = 200 + +# Correct decay lengths (wrong in PYTHIA8 decay table) +# Lb +5122:tau0 = 0.4390 +# Xic0 +4132:tau0 = 0.0455 +# OmegaC +4332:tau0 = 0.0803 + +### Force golden charm hadrons decay modes for D2H studies +### add D0 decays absent in PYTHIA8 decay table and set BRs from PDG for other +421:oneChannel = 1 0.0389 0 -321 211 +421:addChannel = 1 0.00389 0 -321 211 111 +### add D+ decays absent in PYTHIA8 decay table and set BRs from PDG for other +411:oneChannel = 1 0.0752 0 -321 211 211 +411:addChannel = 1 0.0104 0 -313 211 +411:addChannel = 1 0.0156 0 311 211 +411:addChannel = 1 0.0752 0 333 211 # to have the same amount of D+->KKpi and D+->Kpipi +## add Lc decays absent in PYTHIA8 decay table and set BRs from PDG for other +4122:oneChannel = 1 0.0196 100 2212 -313 +4122:addChannel = 1 0.0108 100 2224 -321 +4122:addChannel = 1 0.022 100 102134 211 +4122:addChannel = 1 0.035 0 2212 -321 211 +4122:addChannel = 1 0.0159 0 2212 311 +### add Xic+ decays absent in PYTHIA8 decay table +4232:addChannel = 1 0.2 0 2212 -313 +4232:addChannel = 1 0.2 0 2212 -321 211 +4232:addChannel = 1 0.2 0 3324 211 +4232:addChannel = 1 0.2 0 3312 211 211 +### add Xic0 decays absent in PYTHIA8 decay table +4132:addChannel = 1 0.0143 0 3312 211 +### add OmegaC decays absent in PYTHIA8 decay table +4332:addChannel = 1 0.5 0 3334 211 +4332:addChannel = 1 0.5 0 3312 211 + +### K* -> K pi +313:onMode = off +313:onIfAll = 321 211 +### for Ds -> Phi pi+ +333:onMode = off +333:onIfAll = 321 321 +### for D0 -> rho0 pi+ k- +113:onMode = off +113:onIfAll = 211 211 +### for Lambda_c -> Delta++ K- +2224:onMode = off +2224:onIfAll = 2212 211 +### for Lambda_c -> Lambda(1520) K- +102134:onMode = off +102134:onIfAll = 2212 321 +### for Xic0 -> pi Xi -> pi pi Lambda -> pi pi pi p +### and Omega_c -> pi Xi -> pi pi Lambda -> pi pi pi p +3312:onMode = off +3312:onIfAll = 3122 -211 +3122:onMode = off +3122:onIfAll = 2212 -211 +### for Omega_c -> pi Omega -> pi K Lambda -> pi K pi p +3334:onMode = off +3334:onIfAll = 3122 -321 + +### switch off all decay channels +411:onMode = off +421:onMode = off +431:onMode = off +4122:onMode = off +4232:onMode = off +4132:onMode = off +443:onMode = off +4332:onMode = off + +### D0 -> K pi +421:onIfMatch = 321 211 +### D0 -> K pi pi0 +421:onIfMatch = 321 211 111 + +### D+/- -> K pi pi +411:onIfMatch = 321 211 211 +### D+/- -> K* pi +411:onIfMatch = 313 211 +### D+/- -> phi pi +411:onIfMatch = 333 211 + +### D_s -> K K* +431:onIfMatch = 321 313 +### D_s -> Phi pi +431:onIfMatch = 333 211 + +### Lambda_c -> p K* +4122:onIfMatch = 2212 313 +### Lambda_c -> Delta K +4122:onIfMatch = 2224 321 +### Lambda_c -> Lambda(1520) pi +4122:onIfMatch = 102134 211 +### Lambda_c -> p K pi +4122:onIfMatch = 2212 321 211 +### Lambda_c -> pK0s +4122:onIfMatch = 2212 311 + +### Xic+ -> pK*0 +4232:onIfMatch = 2212 313 +### Xic+ -> p K- pi+ +4232:onIfMatch = 2212 321 211 +### Xic+ -> Xi*0 pi+, Xi*->Xi- pi+ +4232:onIfMatch = 3324 211 +### Xic+ -> Xi- pi+ pi+ +4232:onIfMatch = 3312 211 211 + +### Xic0 -> Xi- pi+ +4132:onIfMatch = 3312 211 + +### Omega_c -> Omega pi +4332:onIfMatch = 3334 211 +### Omega_c -> Xi pi +4332:onIfMatch = 3312 211 diff --git a/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_with_decays_QCDCR_NeNe.cfg b/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_with_decays_QCDCR_NeNe.cfg new file mode 100644 index 000000000..e059ebc8c --- /dev/null +++ b/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_with_decays_QCDCR_NeNe.cfg @@ -0,0 +1,299 @@ +# Pythia8 configuration file for charm hadronic production in OO collisions at 5.36 TeV + +### NeNe beams +Beams:idA = 1000100200 # Neon +Beams:idB = 1000100200 # Neon +Beams:eCM = 5360.0 ### energy + +### processes +SoftQCD:inelastic on # all inelastic processes + +### decays +Beams:frameType = 1 +ParticleDecays:limitTau0 = on +ParticleDecays:tau0Max = 10. ### match alice: 1cm/c = 10.0mm/c + +### Save some CPU at init of jobs +### To avoid refitting, add the following lines to your configuration file: +HeavyIon:SigFitNGen 0 +HeavyIon:SigFitDefPar 2.15,18.42,0.33 + +Random:setSeed on + +### switching on QCD colour reconnection mode +StringPT:sigma = 0.335 +StringZ:aLund = 0.36 +StringZ:bLund = 0.56 +StringFlav:probQQtoQ = 0.078 +StringFlav:ProbStoUD = 0.2 +StringFlav:probQQ1toQQ0join = 0.0275,0.0275,0.0275,0.0275 +BeamRemnants:remnantMode = 1 +BeamRemnants:saturation = 5 +ColourReconnection:mode = 1 +ColourReconnection:allowDoubleJunRem = off +ColourReconnection:allowJunctions = on +ColourReconnection:timeDilationMode = 0 +ColourReconnection:reconnect = on +ColourReconnection:forceHadronLevelCR = off +MultiPartonInteractions:pT0Ref = 2.12 +ColourReconnection:m0 = 2.9 +ColourReconnection:junctionCorrection = 1.43 +HIMultipartonInteractions:pT0Ref = 2.12 +BeamRemnants:beamJunction = off +HIBeamRemnants:beamJunction = off + +# Correct decay lengths (wrong in PYTHIA8 decay table) +# Lb +5122:tau0 = 0.4390 +# Xic0 +4132:tau0 = 0.0455 +# OmegaC +4332:tau0 = 0.0803 + +### Force golden charm hadrons decay modes for D2H studies +# HF decays +### BR are set to yield 50% of signal in golden channel and uniform abundance of corr. bkg channels (weighted by BR from PDG) +### +### D0 decays +### D0 -> K- π+ (50%) +421:oneChannel = 1 0.50000 0 -321 211 ### D0 -> K- π+ 3.94% +### D0 -> K- π+ π0 (12.50%) +421:addChannel = 1 0.00625 0 -321 211 111 ### D0 -> K- π+ π0 (non-resonant) 1.15% (e.g. 115/(115+231+195+1120)*0.2) +421:addChannel = 1 0.08750 0 213 -321 ### D0 -> rho+ K- 11.2% +421:addChannel = 1 0.01250 0 -313 111 ### D0 -> antiK*0(892) π0 1.95% +421:addChannel = 1 0.01875 0 -323 211 ### D0 -> K*-(892) π+ 2.31% +### D0 -> π- π+ (12.50%) +421:addChannel = 1 0.12500 0 -211 211 ### D0 -> π- π+ (non-resonant) 1.0e-4 +### D0 -> π- π+ π0 (12.50%) +421:addChannel = 1 0.08750 0 213 -211 ### D0 -> rho+ π- 1.01% +421:addChannel = 1 0.03750 0 -211 211 111 ### D0 -> π- π+ π0 (non-resonant) 1.3e-4 +### D0 -> K- K+ (12.50%) +421:addChannel = 1 0.12500 0 -321 321 ### D0 -> K- K+ (non-resonant) 4.08e-3 + +### D+ decays +### D+ -> K- π+ π+ (50%) +411:oneChannel = 1 0.40189 0 -321 211 211 ### D+ -> K- π+ π+ 9.38% +411:addChannel = 1 0.05356 0 -10311 211 ### D+ -> antiK*0(1430) π+ 1.25% +411:addChannel = 1 0.04455 0 -313 211 ### D+ -> K*0(892) pi+ 1.04% +### D+ -> K- π+ π+ π0 (small, 3%) +411:addChannel = 1 0.03000 0 -321 211 211 111 ### D+ -> K- π+ π+ π0 6.25% +### D+ -> K- K+ π+ (36.00%, set 25% for D+ -> φ π+, 11% for the rest) +411:addChannel = 1 0.25000 0 333 211 ### D+ -> φ π+ 2.69e-3 !needed for signal +411:addChannel = 1 0.03929 0 -313 321 ### D+ -> K*0(892) K+ 2.49e-3 +411:addChannel = 1 0.02865 0 -10311 321 ### D+ -> antiK*0(1430) K+ 1.82e-3 +411:addChannel = 1 0.04206 0 -321 321 211 ### D+ -> K- K+ π+ (non-resonant) 2.68e-3 +### D+ -> π- π+ π+ (11.00%) +411:addChannel = 1 0.02911 0 113 211 ### D+ -> rho0 π+ 8.4e-4 +411:addChannel = 1 0.01618 0 225 211 ### D+ -> f2(1270) π+ 4.6e-4 +411:addChannel = 1 0.06471 0 -211 211 211 ### D+ -> π- π+ π+ (non-resonant) 1.0e-4 + +### Ds+ decays +### Ds+ -> K- K+ π+ (50%) +431:oneChannel = 1 0.50000 0 333 211 ### Ds+ -> φ(1020) π+ 2.21% +431:addChannel = 1 0.15000 0 -313 321 ### Ds+ -> antiK*(892) K+ 2.58% +### Ds+ -> K- K+ π+ π0 (small, 2%) +431:addChannel = 1 0.02000 0 333 213 ### Ds+ -> φ(1020) rho 5.50% +### Ds+ -> π- π+ π+ (11.00%) +431:addChannel = 1 0.00220 0 113 211 ### Ds+ -> rho π+ 1.1e-4 +431:addChannel = 1 0.00220 0 225 211 ### Ds+ -> f2(1270) π+ 1.4e-3 +431:addChannel = 1 0.10560 0 -211 211 211 ### Ds+ -> π- π+ π+ 9.12e-3 (s-wave) +### Ds+ -> π- K+ π+ (11.00%) +431:addChannel = 1 0.03080 0 313 211 ### Ds+ -> K*(892)0 π+ 1.67e-3 +431:addChannel = 1 0.02200 0 10221 321 ### Ds+ -> f0(1370) K+ 1.2e-3 +431:addChannel = 1 0.03960 0 113 321 ### Ds+ -> rho0 K+ 2.17e-3 +431:addChannel = 1 0.01760 0 -211 321 211 ### Ds+ -> π- K+ π+ (non-resonant) 1.16-3 +### Ds+ -> π+ π- π+ π0 (11.00%) +431:addChannel = 1 0.11000 0 221 211 ### Ds+ -> eta π+ -> π0 π+ π+ π- (affects D+ golden channel) + +## Λc decays +### Λc+ -> p K- π+ (36%) +4122:oneChannel = 1 0.14400 0 2212 -321 211 ### Λc+ -> p K- π+ (non-resonant) 3.5% +4122:addChannel = 1 0.08100 100 2212 -313 ### Λc+ -> p K*0(892) 1.96% +4122:addChannel = 1 0.04500 100 2224 -321 ### Λc+ -> Delta++ K- 1.08% +4122:addChannel = 1 0.09000 100 102134 211 ### Λc+ -> Lambda(1520) K- 2.20e-3 +### Λc+ -> p K0S (36%) +4122:addChannel = 1 0.36000 0 2212 311 ### Λc+ -> p K0S 1.59% +### Λc+ -> p K- π+ π0 (small, 3%) +4122:addChannel = 1 0.03000 0 2212 -321 211 111 ### Λc+ -> p K- π+ π0 (non-resonant) 4.6% +### Λc+ -> p π- π+ (12.50%) +4122:addChannel = 1 0.12500 0 2212 -211 211 ### Λc+ -> p π+ π+ 4.59% +### Λc+ -> p K- K+ (12.50%) +4122:addChannel = 1 0.12500 0 2212 333 ### Λc+ -> p phi 1.06% + +## Xic decays +### Ξc+ -> p K- π+ (35%) +4232:oneChannel = 1 0.17500 0 2212 -321 211 ### Ξc+ -> p K- π+ 6.18e-3 +4232:addChannel = 1 0.17500 0 2212 -313 ### Ξc+ -> p antiK*0(892) +### Ξc+ -> Ξ- π+ π+ (35%) (set the same as Ξc+ -> p K- π+) +4232:addChannel = 1 0.35000 0 3312 211 211 ### Ξc+ -> Ξ- π+ π+ 2.86% +### Ξc+ -> p φ (10%) +4232:addChannel = 1 0.10000 0 2212 333 ### Ξc+ -> p φ +### Ξc+ -> sigma+ π+ π- (10%) +4232:addChannel = 1 0.12500 0 3222 -211 211 ### Ξc+ -> sigma+ π- π+ 1.37% +### Ξc+ -> Ξ*0 π+ (10%) +4232:addChannel = 1 0.12500 0 3324 211 + +### add Xic0 decays absent in PYTHIA8 decay table +4132:oneChannel = 1 0.0143 0 3312 211 + +### add OmegaC decays absent in PYTHIA8 decay table +4332:oneChannel = 1 0.5 0 3334 211 +4332:addChannel = 1 0.5 0 3312 211 + +# Allow the decay of resonances in the decay chain +### K*0(892) -> K- π+ +313:onMode = off +313:onIfAll = 321 211 +### K*(892)+ -> K- π0 +323:onMode = off +323:onIfAll = 321 111 +### K*(1430)0 -> K- π+ +10311:onMode = off +10311:onIfAll = 321 211 +### rho+ -> π+ π0 +213:onMode = off +213:onIfAll = 211 111 +### φ -> K+ K- +333:onMode = off +333:onIfAll = 321 321 +### rho0 -> π+ π- +113:onMode = off +113:onIfAll = 211 211 +### f2(1270) -> π+ π- +225:onMode = off +225:onIfAll = 211 211 +### f0(1370) -> π+ π- +10221:onMode = off +10221:onIfAll = 211 211 +### eta -> π+ π- +221:onMode = off +221:onIfAll = 111 211 211 +### for Λc -> Delta++ K- +2224:onMode = off +2224:onIfAll = 2212 211 +### for Λc -> Lambda(1520) K- +102134:onMode = off +102134:onIfAll = 2212 321 +### for Xic0 -> pi Xi -> pi pi Lambda -> pi pi pi p +### and Omega_c -> pi Xi -> pi pi Lambda -> pi pi pi p +3312:onMode = off +3312:onIfAll = 3122 -211 +3122:onMode = off +3122:onIfAll = 2212 -211 +### for Omega_c -> pi Omega -> pi K Lambda -> pi K pi p +3334:onMode = off +3334:onIfAll = 3122 -321 + +# switch off all decay channels +411:onMode = off +421:onMode = off +431:onMode = off +4122:onMode = off +4232:onMode = off +4132:onMode = off +443:onMode = off +4332:onMode = off + +# Allow the decay of HF +### D0 -> K π +421:onIfMatch = 321 211 +### D0 -> K π π0 +421:onIfMatch = 321 211 111 +### D0 -> rho K +421:onIfMatch = 213 321 +### D0 -> antiK*0 π0 +421:onIfMatch = 313 111 +### D0 -> K*- π+ +421:onIfMatch = 323 211 +### D0 -> π π +421:onIfMatch = 211 211 +### D0 -> rho+ π- +421:onIfMatch = 213 -211 +### D0 -> π π π0 +421:onIfMatch = 211 211 111 +### D0 -> K K +421:onIfMatch = 321 321 + +### D+/- -> K π π +411:onIfMatch = 321 211 211 +### D+/- -> K π π +411:onIfMatch = 321 211 211 111 +### D+/- -> K* π +411:onIfMatch = 313 211 +### D+/- -> K* K +411:onIfMatch = 313 321 +### D+/- -> antiK* π +411:onIfMatch = 10311 211 +### D+/- -> antiK* K +411:onIfMatch = 10311 321 +### D+/- -> φ π +411:onIfMatch = 333 211 +### D+/- -> K K π +411:onIfMatch = 321 321 211 +### D+/- -> f2(1270) π +411:onIfMatch = 225 211 +### D+/- -> rho π +411:onIfMatch = 113 211 +### D+/- -> π π π +411:onIfMatch = 211 211 211 + +### Ds -> φ π +431:onIfMatch = 333 211 +### Ds -> K* K +431:onIfMatch = 313 321 +### Ds -> φ rho+ +431:onIfMatch = 333 213 +### Ds -> rho π +431:onIfMatch = 113 211 +### Ds -> f2 π +431:onIfMatch = 225 211 +### Ds -> π π π +431:onIfMatch = 211 211 211 +### Ds -> K*(892)0 π+ +431:onIfMatch = 313 211 +### Ds -> f0(1370) K+ +431:onIfMatch = 10221 321 +### Ds -> rho0 K+ +431:onIfMatch = 113 321 +### Ds -> K π π +431:onIfMatch = 321 211 211 +### Ds -> eta π+ +431:onIfMatch = 221 211 + +### Λc -> pK0s +4122:onIfMatch = 2212 311 +### Λc -> p K- π+ π0 +4122:onIfMatch = 2212 321 211 +### Λc -> p K* +4122:onIfMatch = 2212 313 +### Λc -> Delta++ K +4122:onIfMatch = 2224 321 +### Λc -> Lambda(1520) π +4122:onIfMatch = 102134 211 +### Λc -> p K- π+ π0 +4122:onIfMatch = 2212 321 211 111 +### Λc -> p π π +4122:onIfMatch = 2212 211 211 +### Λc -> p K K +4122:onIfMatch = 2212 333 + +### Ξc+ -> p K- π+ +4232:onIfMatch = 2212 321 211 +### Ξc+ -> p antiK*0(892) +4232:onIfMatch = 2212 313 +### Ξc+ -> p φ +4232:onIfMatch = 2212 333 +### Ξc+ -> sigma- π+ π+ +4232:onIfMatch = 3222 211 211 +### Ξc+ -> Ξ*0 π+, Ξ*0 -> Ξ- π+ +4232:onIfMatch = 3324 211 +### Ξc+ -> Ξ- π+ π+ +4232:onIfMatch = 3312 211 211 + +### Xic0 -> Xi- pi+ +4132:onIfMatch = 3312 211 + +### Omega_c -> Omega pi +4332:onIfMatch = 3334 211 +### Omega_c -> Xi pi +4332:onIfMatch = 3312 211 diff --git a/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_with_decays_QCDCR_OO.cfg b/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_with_decays_QCDCR_OO.cfg new file mode 100644 index 000000000..255cdfbd4 --- /dev/null +++ b/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_with_decays_QCDCR_OO.cfg @@ -0,0 +1,299 @@ +# Pythia8 configuration file for charm hadronic production in OO collisions at 5.36 TeV + +### beams +Beams:idA 1000080160 # Oxygen +Beams:idB 1000080160 # Oxygen +Beams:eCM 5360. # GeV + +### processes +SoftQCD:inelastic on # all inelastic processes + +### decays +Beams:frameType 1 +ParticleDecays:limitTau0 on +ParticleDecays:tau0Max 10. + +### Save some CPU at init of jobs +### To avoid refitting, add the following lines to your configuration file: +HeavyIon:SigFitNGen 0 +HeavyIon:SigFitDefPar 2.15,18.42,0.33 + +Random:setSeed on + +### switching on QCD colour reconnection mode +StringPT:sigma = 0.335 +StringZ:aLund = 0.36 +StringZ:bLund = 0.56 +StringFlav:probQQtoQ = 0.078 +StringFlav:ProbStoUD = 0.2 +StringFlav:probQQ1toQQ0join = 0.0275,0.0275,0.0275,0.0275 +BeamRemnants:remnantMode = 1 +BeamRemnants:saturation = 5 +ColourReconnection:mode = 1 +ColourReconnection:allowDoubleJunRem = off +ColourReconnection:allowJunctions = on +ColourReconnection:timeDilationMode = 0 +ColourReconnection:reconnect = on +ColourReconnection:forceHadronLevelCR = off +MultiPartonInteractions:pT0Ref = 2.12 +ColourReconnection:m0 = 2.9 +ColourReconnection:junctionCorrection = 1.43 +HIMultipartonInteractions:pT0Ref = 2.12 +BeamRemnants:beamJunction = off +HIBeamRemnants:beamJunction = off + +# Correct decay lengths (wrong in PYTHIA8 decay table) +# Lb +5122:tau0 = 0.4390 +# Xic0 +4132:tau0 = 0.0455 +# OmegaC +4332:tau0 = 0.0803 + +### Force golden charm hadrons decay modes for D2H studies +# HF decays +### BR are set to yield 50% of signal in golden channel and uniform abundance of corr. bkg channels (weighted by BR from PDG) +### +### D0 decays +### D0 -> K- π+ (50%) +421:oneChannel = 1 0.50000 0 -321 211 ### D0 -> K- π+ 3.94% +### D0 -> K- π+ π0 (12.50%) +421:addChannel = 1 0.00625 0 -321 211 111 ### D0 -> K- π+ π0 (non-resonant) 1.15% (e.g. 115/(115+231+195+1120)*0.2) +421:addChannel = 1 0.08750 0 213 -321 ### D0 -> rho+ K- 11.2% +421:addChannel = 1 0.01250 0 -313 111 ### D0 -> antiK*0(892) π0 1.95% +421:addChannel = 1 0.01875 0 -323 211 ### D0 -> K*-(892) π+ 2.31% +### D0 -> π- π+ (12.50%) +421:addChannel = 1 0.12500 0 -211 211 ### D0 -> π- π+ (non-resonant) 1.0e-4 +### D0 -> π- π+ π0 (12.50%) +421:addChannel = 1 0.08750 0 213 -211 ### D0 -> rho+ π- 1.01% +421:addChannel = 1 0.03750 0 -211 211 111 ### D0 -> π- π+ π0 (non-resonant) 1.3e-4 +### D0 -> K- K+ (12.50%) +421:addChannel = 1 0.12500 0 -321 321 ### D0 -> K- K+ (non-resonant) 4.08e-3 + +### D+ decays +### D+ -> K- π+ π+ (50%) +411:oneChannel = 1 0.40189 0 -321 211 211 ### D+ -> K- π+ π+ 9.38% +411:addChannel = 1 0.05356 0 -10311 211 ### D+ -> antiK*0(1430) π+ 1.25% +411:addChannel = 1 0.04455 0 -313 211 ### D+ -> K*0(892) pi+ 1.04% +### D+ -> K- π+ π+ π0 (small, 3%) +411:addChannel = 1 0.03000 0 -321 211 211 111 ### D+ -> K- π+ π+ π0 6.25% +### D+ -> K- K+ π+ (36.00%, set 25% for D+ -> φ π+, 11% for the rest) +411:addChannel = 1 0.25000 0 333 211 ### D+ -> φ π+ 2.69e-3 !needed for signal +411:addChannel = 1 0.03929 0 -313 321 ### D+ -> K*0(892) K+ 2.49e-3 +411:addChannel = 1 0.02865 0 -10311 321 ### D+ -> antiK*0(1430) K+ 1.82e-3 +411:addChannel = 1 0.04206 0 -321 321 211 ### D+ -> K- K+ π+ (non-resonant) 2.68e-3 +### D+ -> π- π+ π+ (11.00%) +411:addChannel = 1 0.02911 0 113 211 ### D+ -> rho0 π+ 8.4e-4 +411:addChannel = 1 0.01618 0 225 211 ### D+ -> f2(1270) π+ 4.6e-4 +411:addChannel = 1 0.06471 0 -211 211 211 ### D+ -> π- π+ π+ (non-resonant) 1.0e-4 + +### Ds+ decays +### Ds+ -> K- K+ π+ (50%) +431:oneChannel = 1 0.50000 0 333 211 ### Ds+ -> φ(1020) π+ 2.21% +431:addChannel = 1 0.15000 0 -313 321 ### Ds+ -> antiK*(892) K+ 2.58% +### Ds+ -> K- K+ π+ π0 (small, 2%) +431:addChannel = 1 0.02000 0 333 213 ### Ds+ -> φ(1020) rho 5.50% +### Ds+ -> π- π+ π+ (11.00%) +431:addChannel = 1 0.00220 0 113 211 ### Ds+ -> rho π+ 1.1e-4 +431:addChannel = 1 0.00220 0 225 211 ### Ds+ -> f2(1270) π+ 1.4e-3 +431:addChannel = 1 0.10560 0 -211 211 211 ### Ds+ -> π- π+ π+ 9.12e-3 (s-wave) +### Ds+ -> π- K+ π+ (11.00%) +431:addChannel = 1 0.03080 0 313 211 ### Ds+ -> K*(892)0 π+ 1.67e-3 +431:addChannel = 1 0.02200 0 10221 321 ### Ds+ -> f0(1370) K+ 1.2e-3 +431:addChannel = 1 0.03960 0 113 321 ### Ds+ -> rho0 K+ 2.17e-3 +431:addChannel = 1 0.01760 0 -211 321 211 ### Ds+ -> π- K+ π+ (non-resonant) 1.16-3 +### Ds+ -> π+ π- π+ π0 (11.00%) +431:addChannel = 1 0.11000 0 221 211 ### Ds+ -> eta π+ -> π0 π+ π+ π- (affects D+ golden channel) + +## Λc decays +### Λc+ -> p K- π+ (36%) +4122:oneChannel = 1 0.14400 0 2212 -321 211 ### Λc+ -> p K- π+ (non-resonant) 3.5% +4122:addChannel = 1 0.08100 100 2212 -313 ### Λc+ -> p K*0(892) 1.96% +4122:addChannel = 1 0.04500 100 2224 -321 ### Λc+ -> Delta++ K- 1.08% +4122:addChannel = 1 0.09000 100 102134 211 ### Λc+ -> Lambda(1520) K- 2.20e-3 +### Λc+ -> p K0S (36%) +4122:addChannel = 1 0.36000 0 2212 311 ### Λc+ -> p K0S 1.59% +### Λc+ -> p K- π+ π0 (small, 3%) +4122:addChannel = 1 0.03000 0 2212 -321 211 111 ### Λc+ -> p K- π+ π0 (non-resonant) 4.6% +### Λc+ -> p π- π+ (12.50%) +4122:addChannel = 1 0.12500 0 2212 -211 211 ### Λc+ -> p π+ π+ 4.59% +### Λc+ -> p K- K+ (12.50%) +4122:addChannel = 1 0.12500 0 2212 333 ### Λc+ -> p phi 1.06% + +## Xic decays +### Ξc+ -> p K- π+ (35%) +4232:oneChannel = 1 0.17500 0 2212 -321 211 ### Ξc+ -> p K- π+ 6.18e-3 +4232:addChannel = 1 0.17500 0 2212 -313 ### Ξc+ -> p antiK*0(892) +### Ξc+ -> Ξ- π+ π+ (35%) (set the same as Ξc+ -> p K- π+) +4232:addChannel = 1 0.35000 0 3312 211 211 ### Ξc+ -> Ξ- π+ π+ 2.86% +### Ξc+ -> p φ (10%) +4232:addChannel = 1 0.10000 0 2212 333 ### Ξc+ -> p φ +### Ξc+ -> sigma+ π+ π- (10%) +4232:addChannel = 1 0.12500 0 3222 -211 211 ### Ξc+ -> sigma+ π- π+ 1.37% +### Ξc+ -> Ξ*0 π+ (10%) +4232:addChannel = 1 0.12500 0 3324 211 + +### add Xic0 decays absent in PYTHIA8 decay table +4132:oneChannel = 1 0.0143 0 3312 211 + +### add OmegaC decays absent in PYTHIA8 decay table +4332:oneChannel = 1 0.5 0 3334 211 +4332:addChannel = 1 0.5 0 3312 211 + +# Allow the decay of resonances in the decay chain +### K*0(892) -> K- π+ +313:onMode = off +313:onIfAll = 321 211 +### K*(892)+ -> K- π0 +323:onMode = off +323:onIfAll = 321 111 +### K*(1430)0 -> K- π+ +10311:onMode = off +10311:onIfAll = 321 211 +### rho+ -> π+ π0 +213:onMode = off +213:onIfAll = 211 111 +### φ -> K+ K- +333:onMode = off +333:onIfAll = 321 321 +### rho0 -> π+ π- +113:onMode = off +113:onIfAll = 211 211 +### f2(1270) -> π+ π- +225:onMode = off +225:onIfAll = 211 211 +### f0(1370) -> π+ π- +10221:onMode = off +10221:onIfAll = 211 211 +### eta -> π+ π- +221:onMode = off +221:onIfAll = 111 211 211 +### for Λc -> Delta++ K- +2224:onMode = off +2224:onIfAll = 2212 211 +### for Λc -> Lambda(1520) K- +102134:onMode = off +102134:onIfAll = 2212 321 +### for Xic0 -> pi Xi -> pi pi Lambda -> pi pi pi p +### and Omega_c -> pi Xi -> pi pi Lambda -> pi pi pi p +3312:onMode = off +3312:onIfAll = 3122 -211 +3122:onMode = off +3122:onIfAll = 2212 -211 +### for Omega_c -> pi Omega -> pi K Lambda -> pi K pi p +3334:onMode = off +3334:onIfAll = 3122 -321 + +# switch off all decay channels +411:onMode = off +421:onMode = off +431:onMode = off +4122:onMode = off +4232:onMode = off +4132:onMode = off +443:onMode = off +4332:onMode = off + +# Allow the decay of HF +### D0 -> K π +421:onIfMatch = 321 211 +### D0 -> K π π0 +421:onIfMatch = 321 211 111 +### D0 -> rho K +421:onIfMatch = 213 321 +### D0 -> antiK*0 π0 +421:onIfMatch = 313 111 +### D0 -> K*- π+ +421:onIfMatch = 323 211 +### D0 -> π π +421:onIfMatch = 211 211 +### D0 -> rho+ π- +421:onIfMatch = 213 -211 +### D0 -> π π π0 +421:onIfMatch = 211 211 111 +### D0 -> K K +421:onIfMatch = 321 321 + +### D+/- -> K π π +411:onIfMatch = 321 211 211 +### D+/- -> K π π +411:onIfMatch = 321 211 211 111 +### D+/- -> K* π +411:onIfMatch = 313 211 +### D+/- -> K* K +411:onIfMatch = 313 321 +### D+/- -> antiK* π +411:onIfMatch = 10311 211 +### D+/- -> antiK* K +411:onIfMatch = 10311 321 +### D+/- -> φ π +411:onIfMatch = 333 211 +### D+/- -> K K π +411:onIfMatch = 321 321 211 +### D+/- -> f2(1270) π +411:onIfMatch = 225 211 +### D+/- -> rho π +411:onIfMatch = 113 211 +### D+/- -> π π π +411:onIfMatch = 211 211 211 + +### Ds -> φ π +431:onIfMatch = 333 211 +### Ds -> K* K +431:onIfMatch = 313 321 +### Ds -> φ rho+ +431:onIfMatch = 333 213 +### Ds -> rho π +431:onIfMatch = 113 211 +### Ds -> f2 π +431:onIfMatch = 225 211 +### Ds -> π π π +431:onIfMatch = 211 211 211 +### Ds -> K*(892)0 π+ +431:onIfMatch = 313 211 +### Ds -> f0(1370) K+ +431:onIfMatch = 10221 321 +### Ds -> rho0 K+ +431:onIfMatch = 113 321 +### Ds -> K π π +431:onIfMatch = 321 211 211 +### Ds -> eta π+ +431:onIfMatch = 221 211 + +### Λc -> pK0s +4122:onIfMatch = 2212 311 +### Λc -> p K- π+ π0 +4122:onIfMatch = 2212 321 211 +### Λc -> p K* +4122:onIfMatch = 2212 313 +### Λc -> Delta++ K +4122:onIfMatch = 2224 321 +### Λc -> Lambda(1520) π +4122:onIfMatch = 102134 211 +### Λc -> p K- π+ π0 +4122:onIfMatch = 2212 321 211 111 +### Λc -> p π π +4122:onIfMatch = 2212 211 211 +### Λc -> p K K +4122:onIfMatch = 2212 333 + +### Ξc+ -> p K- π+ +4232:onIfMatch = 2212 321 211 +### Ξc+ -> p antiK*0(892) +4232:onIfMatch = 2212 313 +### Ξc+ -> p φ +4232:onIfMatch = 2212 333 +### Ξc+ -> sigma- π+ π+ +4232:onIfMatch = 3222 211 211 +### Ξc+ -> Ξ*0 π+, Ξ*0 -> Ξ- π+ +4232:onIfMatch = 3324 211 +### Ξc+ -> Ξ- π+ π+ +4232:onIfMatch = 3312 211 211 + +### Xic0 -> Xi- pi+ +4132:onIfMatch = 3312 211 + +### Omega_c -> Omega pi +4332:onIfMatch = 3334 211 +### Omega_c -> Xi pi +4332:onIfMatch = 3312 211 \ No newline at end of file diff --git a/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_with_decays_QCDCR_pO.cfg b/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_with_decays_QCDCR_pO.cfg new file mode 100644 index 000000000..89c1c5d9e --- /dev/null +++ b/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_with_decays_QCDCR_pO.cfg @@ -0,0 +1,300 @@ +# Pythia8 configuration file for charm hadronic production in pO collisions at 9.62 TeV + +### beams +Beams:frameType 2 # back-to-back beams of different energies and particles +Beams:idA 2212 # proton +Beams:idB 1000080160 # Oxygen +Beams:eA 6800. # Energy of proton beam in GeV moving in the +z direction +Beams:eB 3400. # Energy in GeV per Oxygen nucleon (6.8 Z TeV) moving in the -z direction + +### processes +SoftQCD:inelastic on # all inelastic processes + +### decays +ParticleDecays:limitTau0 on +ParticleDecays:tau0Max 10. + +### Save some CPU at init of jobs +### To avoid refitting, add the following lines to your configuration file: +HeavyIon:SigFitNGen 0 +HeavyIon:SigFitDefPar 2.15,18.42,0.33 + +Random:setSeed on + +### switching on QCD colour reconnection mode +StringPT:sigma = 0.335 +StringZ:aLund = 0.36 +StringZ:bLund = 0.56 +StringFlav:probQQtoQ = 0.078 +StringFlav:ProbStoUD = 0.2 +StringFlav:probQQ1toQQ0join = 0.0275,0.0275,0.0275,0.0275 +BeamRemnants:remnantMode = 1 +BeamRemnants:saturation = 5 +ColourReconnection:mode = 1 +ColourReconnection:allowDoubleJunRem = off +ColourReconnection:allowJunctions = on +ColourReconnection:timeDilationMode = 0 +ColourReconnection:reconnect = on +ColourReconnection:forceHadronLevelCR = off +MultiPartonInteractions:pT0Ref = 2.12 +ColourReconnection:m0 = 2.9 +ColourReconnection:junctionCorrection = 1.43 +HIMultipartonInteractions:pT0Ref = 2.12 +BeamRemnants:beamJunction = off +HIBeamRemnants:beamJunction = off + +# Correct decay lengths (wrong in PYTHIA8 decay table) +# Lb +5122:tau0 = 0.4390 +# Xic0 +4132:tau0 = 0.0455 +# OmegaC +4332:tau0 = 0.0803 + +### Force golden charm hadrons decay modes for D2H studies +# HF decays +### BR are set to yield 50% of signal in golden channel and uniform abundance of corr. bkg channels (weighted by BR from PDG) +### +### D0 decays +### D0 -> K- π+ (50%) +421:oneChannel = 1 0.50000 0 -321 211 ### D0 -> K- π+ 3.94% +### D0 -> K- π+ π0 (12.50%) +421:addChannel = 1 0.00625 0 -321 211 111 ### D0 -> K- π+ π0 (non-resonant) 1.15% (e.g. 115/(115+231+195+1120)*0.2) +421:addChannel = 1 0.08750 0 213 -321 ### D0 -> rho+ K- 11.2% +421:addChannel = 1 0.01250 0 -313 111 ### D0 -> antiK*0(892) π0 1.95% +421:addChannel = 1 0.01875 0 -323 211 ### D0 -> K*-(892) π+ 2.31% +### D0 -> π- π+ (12.50%) +421:addChannel = 1 0.12500 0 -211 211 ### D0 -> π- π+ (non-resonant) 1.0e-4 +### D0 -> π- π+ π0 (12.50%) +421:addChannel = 1 0.08750 0 213 -211 ### D0 -> rho+ π- 1.01% +421:addChannel = 1 0.03750 0 -211 211 111 ### D0 -> π- π+ π0 (non-resonant) 1.3e-4 +### D0 -> K- K+ (12.50%) +421:addChannel = 1 0.12500 0 -321 321 ### D0 -> K- K+ (non-resonant) 4.08e-3 + +### D+ decays +### D+ -> K- π+ π+ (50%) +411:oneChannel = 1 0.40189 0 -321 211 211 ### D+ -> K- π+ π+ 9.38% +411:addChannel = 1 0.05356 0 -10311 211 ### D+ -> antiK*0(1430) π+ 1.25% +411:addChannel = 1 0.04455 0 -313 211 ### D+ -> K*0(892) pi+ 1.04% +### D+ -> K- π+ π+ π0 (small, 3%) +411:addChannel = 1 0.03000 0 -321 211 211 111 ### D+ -> K- π+ π+ π0 6.25% +### D+ -> K- K+ π+ (36.00%, set 25% for D+ -> φ π+, 11% for the rest) +411:addChannel = 1 0.25000 0 333 211 ### D+ -> φ π+ 2.69e-3 !needed for signal +411:addChannel = 1 0.03929 0 -313 321 ### D+ -> K*0(892) K+ 2.49e-3 +411:addChannel = 1 0.02865 0 -10311 321 ### D+ -> antiK*0(1430) K+ 1.82e-3 +411:addChannel = 1 0.04206 0 -321 321 211 ### D+ -> K- K+ π+ (non-resonant) 2.68e-3 +### D+ -> π- π+ π+ (11.00%) +411:addChannel = 1 0.02911 0 113 211 ### D+ -> rho0 π+ 8.4e-4 +411:addChannel = 1 0.01618 0 225 211 ### D+ -> f2(1270) π+ 4.6e-4 +411:addChannel = 1 0.06471 0 -211 211 211 ### D+ -> π- π+ π+ (non-resonant) 1.0e-4 + +### Ds+ decays +### Ds+ -> K- K+ π+ (50%) +431:oneChannel = 1 0.50000 0 333 211 ### Ds+ -> φ(1020) π+ 2.21% +431:addChannel = 1 0.15000 0 -313 321 ### Ds+ -> antiK*(892) K+ 2.58% +### Ds+ -> K- K+ π+ π0 (small, 2%) +431:addChannel = 1 0.02000 0 333 213 ### Ds+ -> φ(1020) rho 5.50% +### Ds+ -> π- π+ π+ (11.00%) +431:addChannel = 1 0.00220 0 113 211 ### Ds+ -> rho π+ 1.1e-4 +431:addChannel = 1 0.00220 0 225 211 ### Ds+ -> f2(1270) π+ 1.4e-3 +431:addChannel = 1 0.10560 0 -211 211 211 ### Ds+ -> π- π+ π+ 9.12e-3 (s-wave) +### Ds+ -> π- K+ π+ (11.00%) +431:addChannel = 1 0.03080 0 313 211 ### Ds+ -> K*(892)0 π+ 1.67e-3 +431:addChannel = 1 0.02200 0 10221 321 ### Ds+ -> f0(1370) K+ 1.2e-3 +431:addChannel = 1 0.03960 0 113 321 ### Ds+ -> rho0 K+ 2.17e-3 +431:addChannel = 1 0.01760 0 -211 321 211 ### Ds+ -> π- K+ π+ (non-resonant) 1.16-3 +### Ds+ -> π+ π- π+ π0 (11.00%) +431:addChannel = 1 0.11000 0 221 211 ### Ds+ -> eta π+ -> π0 π+ π+ π- (affects D+ golden channel) + +## Λc decays +### Λc+ -> p K- π+ (36%) +4122:oneChannel = 1 0.14400 0 2212 -321 211 ### Λc+ -> p K- π+ (non-resonant) 3.5% +4122:addChannel = 1 0.08100 100 2212 -313 ### Λc+ -> p K*0(892) 1.96% +4122:addChannel = 1 0.04500 100 2224 -321 ### Λc+ -> Delta++ K- 1.08% +4122:addChannel = 1 0.09000 100 102134 211 ### Λc+ -> Lambda(1520) K- 2.20e-3 +### Λc+ -> p K0S (36%) +4122:addChannel = 1 0.36000 0 2212 311 ### Λc+ -> p K0S 1.59% +### Λc+ -> p K- π+ π0 (small, 3%) +4122:addChannel = 1 0.03000 0 2212 -321 211 111 ### Λc+ -> p K- π+ π0 (non-resonant) 4.6% +### Λc+ -> p π- π+ (12.50%) +4122:addChannel = 1 0.12500 0 2212 -211 211 ### Λc+ -> p π+ π+ 4.59% +### Λc+ -> p K- K+ (12.50%) +4122:addChannel = 1 0.12500 0 2212 333 ### Λc+ -> p phi 1.06% + +## Xic decays +### Ξc+ -> p K- π+ (35%) +4232:oneChannel = 1 0.17500 0 2212 -321 211 ### Ξc+ -> p K- π+ 6.18e-3 +4232:addChannel = 1 0.17500 0 2212 -313 ### Ξc+ -> p antiK*0(892) +### Ξc+ -> Ξ- π+ π+ (35%) (set the same as Ξc+ -> p K- π+) +4232:addChannel = 1 0.35000 0 3312 211 211 ### Ξc+ -> Ξ- π+ π+ 2.86% +### Ξc+ -> p φ (10%) +4232:addChannel = 1 0.10000 0 2212 333 ### Ξc+ -> p φ +### Ξc+ -> sigma+ π+ π- (10%) +4232:addChannel = 1 0.12500 0 3222 -211 211 ### Ξc+ -> sigma+ π- π+ 1.37% +### Ξc+ -> Ξ*0 π+ (10%) +4232:addChannel = 1 0.12500 0 3324 211 + +### add Xic0 decays absent in PYTHIA8 decay table +4132:oneChannel = 1 0.0143 0 3312 211 + +### add OmegaC decays absent in PYTHIA8 decay table +4332:oneChannel = 1 0.5 0 3334 211 +4332:addChannel = 1 0.5 0 3312 211 + +# Allow the decay of resonances in the decay chain +### K*0(892) -> K- π+ +313:onMode = off +313:onIfAll = 321 211 +### K*(892)+ -> K- π0 +323:onMode = off +323:onIfAll = 321 111 +### K*(1430)0 -> K- π+ +10311:onMode = off +10311:onIfAll = 321 211 +### rho+ -> π+ π0 +213:onMode = off +213:onIfAll = 211 111 +### φ -> K+ K- +333:onMode = off +333:onIfAll = 321 321 +### rho0 -> π+ π- +113:onMode = off +113:onIfAll = 211 211 +### f2(1270) -> π+ π- +225:onMode = off +225:onIfAll = 211 211 +### f0(1370) -> π+ π- +10221:onMode = off +10221:onIfAll = 211 211 +### eta -> π+ π- +221:onMode = off +221:onIfAll = 111 211 211 +### for Λc -> Delta++ K- +2224:onMode = off +2224:onIfAll = 2212 211 +### for Λc -> Lambda(1520) K- +102134:onMode = off +102134:onIfAll = 2212 321 +### for Xic0 -> pi Xi -> pi pi Lambda -> pi pi pi p +### and Omega_c -> pi Xi -> pi pi Lambda -> pi pi pi p +3312:onMode = off +3312:onIfAll = 3122 -211 +3122:onMode = off +3122:onIfAll = 2212 -211 +### for Omega_c -> pi Omega -> pi K Lambda -> pi K pi p +3334:onMode = off +3334:onIfAll = 3122 -321 + +# switch off all decay channels +411:onMode = off +421:onMode = off +431:onMode = off +4122:onMode = off +4232:onMode = off +4132:onMode = off +443:onMode = off +4332:onMode = off + +# Allow the decay of HF +### D0 -> K π +421:onIfMatch = 321 211 +### D0 -> K π π0 +421:onIfMatch = 321 211 111 +### D0 -> rho K +421:onIfMatch = 213 321 +### D0 -> antiK*0 π0 +421:onIfMatch = 313 111 +### D0 -> K*- π+ +421:onIfMatch = 323 211 +### D0 -> π π +421:onIfMatch = 211 211 +### D0 -> rho+ π- +421:onIfMatch = 213 -211 +### D0 -> π π π0 +421:onIfMatch = 211 211 111 +### D0 -> K K +421:onIfMatch = 321 321 + +### D+/- -> K π π +411:onIfMatch = 321 211 211 +### D+/- -> K π π +411:onIfMatch = 321 211 211 111 +### D+/- -> K* π +411:onIfMatch = 313 211 +### D+/- -> K* K +411:onIfMatch = 313 321 +### D+/- -> antiK* π +411:onIfMatch = 10311 211 +### D+/- -> antiK* K +411:onIfMatch = 10311 321 +### D+/- -> φ π +411:onIfMatch = 333 211 +### D+/- -> K K π +411:onIfMatch = 321 321 211 +### D+/- -> f2(1270) π +411:onIfMatch = 225 211 +### D+/- -> rho π +411:onIfMatch = 113 211 +### D+/- -> π π π +411:onIfMatch = 211 211 211 + +### Ds -> φ π +431:onIfMatch = 333 211 +### Ds -> K* K +431:onIfMatch = 313 321 +### Ds -> φ rho+ +431:onIfMatch = 333 213 +### Ds -> rho π +431:onIfMatch = 113 211 +### Ds -> f2 π +431:onIfMatch = 225 211 +### Ds -> π π π +431:onIfMatch = 211 211 211 +### Ds -> K*(892)0 π+ +431:onIfMatch = 313 211 +### Ds -> f0(1370) K+ +431:onIfMatch = 10221 321 +### Ds -> rho0 K+ +431:onIfMatch = 113 321 +### Ds -> K π π +431:onIfMatch = 321 211 211 +### Ds -> eta π+ +431:onIfMatch = 221 211 + +### Λc -> pK0s +4122:onIfMatch = 2212 311 +### Λc -> p K- π+ π0 +4122:onIfMatch = 2212 321 211 +### Λc -> p K* +4122:onIfMatch = 2212 313 +### Λc -> Delta++ K +4122:onIfMatch = 2224 321 +### Λc -> Lambda(1520) π +4122:onIfMatch = 102134 211 +### Λc -> p K- π+ π0 +4122:onIfMatch = 2212 321 211 111 +### Λc -> p π π +4122:onIfMatch = 2212 211 211 +### Λc -> p K K +4122:onIfMatch = 2212 333 + +### Ξc+ -> p K- π+ +4232:onIfMatch = 2212 321 211 +### Ξc+ -> p antiK*0(892) +4232:onIfMatch = 2212 313 +### Ξc+ -> p φ +4232:onIfMatch = 2212 333 +### Ξc+ -> sigma- π+ π+ +4232:onIfMatch = 3222 211 211 +### Ξc+ -> Ξ*0 π+, Ξ*0 -> Ξ- π+ +4232:onIfMatch = 3324 211 +### Ξc+ -> Ξ- π+ π+ +4232:onIfMatch = 3312 211 211 + +### Xic0 -> Xi- pi+ +4132:onIfMatch = 3312 211 + +### Omega_c -> Omega pi +4332:onIfMatch = 3334 211 +### Omega_c -> Xi pi +4332:onIfMatch = 3312 211 \ No newline at end of file diff --git a/MC/config/PWGHF/pythia8/generator/pythia8_charmtriggers_omegactoxipi.cfg b/MC/config/PWGHF/pythia8/generator/pythia8_charmtriggers_omegactoxipi.cfg index 1c72afe9b..198d11cfe 100644 --- a/MC/config/PWGHF/pythia8/generator/pythia8_charmtriggers_omegactoxipi.cfg +++ b/MC/config/PWGHF/pythia8/generator/pythia8_charmtriggers_omegactoxipi.cfg @@ -15,6 +15,24 @@ ParticleDecays:tau0Max 100. # Correct OmegaC decay length (wrong in PYTHIA8 decay table) (mm/c) 4332:tau0 = 0.08000000000 +### switching on Pythia Mode2 +ColourReconnection:mode 1 +ColourReconnection:allowDoubleJunRem off +ColourReconnection:m0 0.3 +ColourReconnection:allowJunctions on +ColourReconnection:junctionCorrection 1.20 +ColourReconnection:timeDilationMode 2 +ColourReconnection:timeDilationPar 0.18 +StringPT:sigma 0.335 +StringZ:aLund 0.36 +StringZ:bLund 0.56 +StringFlav:probQQtoQ 0.078 +StringFlav:ProbStoUD 0.2 +StringFlav:probQQ1toQQ0join 0.0275,0.0275,0.0275,0.0275 +MultiPartonInteractions:pT0Ref 2.15 +BeamRemnants:remnantMode 1 +BeamRemnants:saturation 5 + ### add OmegaC decay absent in PYTHIA8 decay table 4332:addChannel = 1 0.02 0 3312 211 diff --git a/MC/config/PWGHF/pythia8/generator/pythia8_charmtriggers_with_Bforced_decay.cfg b/MC/config/PWGHF/pythia8/generator/pythia8_charmtriggers_with_Bforced_decay.cfg index 5298cc9e9..7210ad680 100644 --- a/MC/config/PWGHF/pythia8/generator/pythia8_charmtriggers_with_Bforced_decay.cfg +++ b/MC/config/PWGHF/pythia8/generator/pythia8_charmtriggers_with_Bforced_decay.cfg @@ -14,7 +14,9 @@ ParticleDecays:limitTau0 on ParticleDecays:tau0Max 10. ### Force golden charm hadrons decay modes for trigger studies - +### add D0 decays absent in PYTHIA8 decay table and set BRs from PDG for other +421:oneChannel = 1 0.0389 0 -321 211 +421:addChannel = 1 0.00389 0 -321 211 111 ### add D+ decays absent in PYTHIA8 decay table and set BRs from PDG for other 411:oneChannel = 1 0.0752 0 -321 211 211 411:addChannel = 1 0.0104 0 -313 211 @@ -23,7 +25,7 @@ ParticleDecays:tau0Max 10. ## add Lc decays absent in PYTHIA8 decay table and set BRs from PDG for other 4122:oneChannel = 1 0.0196 100 2212 -313 4122:addChannel = 1 0.0108 100 2224 -321 -4122:addChannel = 1 0.022 100 3124 211 +4122:addChannel = 1 0.022 100 102134 211 4122:addChannel = 1 0.035 0 2212 -321 211 ### add Xic+ decays absent in PYTHIA8 decay table 4232:addChannel = 1 0.2 0 2212 -313 @@ -64,7 +66,6 @@ ParticleDecays:tau0Max 10. 411:onMode = off 421:onMode = off 431:onMode = off -4112:onMode = off 4122:onMode = off 4232:onMode = off 4132:onMode = off @@ -77,6 +78,8 @@ ParticleDecays:tau0Max 10. ### D0 -> K pi 421:onIfMatch = 321 211 +### D0 -> K pi pi0 +421:onIfMatch = 321 211 111 ### D+/- -> K pi pi 411:onIfMatch = 321 211 211 @@ -93,7 +96,7 @@ ParticleDecays:tau0Max 10. ### Lambda_c -> Delta K 4122:onIfMatch = 2224 321 ### Lambda_c -> Lambda(1520) pi -4122:onIfMatch = 3124 211 +4122:onIfMatch = 102134 211 ### Lambda_c -> p K pi 4122:onIfMatch = 2212 321 211 diff --git a/MC/config/PWGHF/pythia8/generator/pythia8_charmtriggers_with_decays.cfg b/MC/config/PWGHF/pythia8/generator/pythia8_charmtriggers_with_decays.cfg index e4272e8aa..d57d03392 100644 --- a/MC/config/PWGHF/pythia8/generator/pythia8_charmtriggers_with_decays.cfg +++ b/MC/config/PWGHF/pythia8/generator/pythia8_charmtriggers_with_decays.cfg @@ -14,7 +14,9 @@ ParticleDecays:limitTau0 on ParticleDecays:tau0Max 10. ### Force golden charm hadrons decay modes for trigger studies - +### add D0 decays absent in PYTHIA8 decay table and set BRs from PDG for other +421:oneChannel = 1 0.0389 0 -321 211 +421:addChannel = 1 0.00389 0 -321 211 111 ### add D+ decays absent in PYTHIA8 decay table and set BRs from PDG for other 411:oneChannel = 1 0.0752 0 -321 211 211 411:addChannel = 1 0.0104 0 -313 211 @@ -23,7 +25,7 @@ ParticleDecays:tau0Max 10. ## add Lc decays absent in PYTHIA8 decay table and set BRs from PDG for other 4122:oneChannel = 1 0.0196 100 2212 -313 4122:addChannel = 1 0.0108 100 2224 -321 -4122:addChannel = 1 0.022 100 3124 211 +4122:addChannel = 1 0.022 100 102134 211 4122:addChannel = 1 0.035 0 2212 -321 211 ### add Xic+ decays absent in PYTHIA8 decay table 4232:addChannel = 1 0.2 0 2212 -313 @@ -53,7 +55,6 @@ ParticleDecays:tau0Max 10. 411:onMode = off 421:onMode = off 431:onMode = off -4112:onMode = off 4122:onMode = off 4232:onMode = off 4132:onMode = off @@ -62,6 +63,8 @@ ParticleDecays:tau0Max 10. ### D0 -> K pi 421:onIfMatch = 321 211 +### D0 -> K pi pi0 +421:onIfMatch = 321 211 111 ### D+/- -> K pi pi 411:onIfMatch = 321 211 211 @@ -78,7 +81,7 @@ ParticleDecays:tau0Max 10. ### Lambda_c -> Delta K 4122:onIfMatch = 2224 321 ### Lambda_c -> Lambda(1520) pi -4122:onIfMatch = 3124 211 +4122:onIfMatch = 102134 211 ### Lambda_c -> p K pi 4122:onIfMatch = 2212 321 211 diff --git a/MC/config/PWGHF/pythia8/generator/pythia8_charmtriggers_xicomegac.cfg b/MC/config/PWGHF/pythia8/generator/pythia8_charmtriggers_xicomegac.cfg new file mode 100644 index 000000000..7bb053f28 --- /dev/null +++ b/MC/config/PWGHF/pythia8/generator/pythia8_charmtriggers_xicomegac.cfg @@ -0,0 +1,32 @@ +### Xi_c + +### beams +Beams:idA 2212 # proton +Beams:idB 2212 # proton +Beams:eCM 13600. # GeV + +### processes +SoftQCD:inelastic on # all inelastic processes + +ParticleDecays:limitTau0 on +ParticleDecays:tau0Max 10. + +# Correct XiC decay length (mm/c) +4132:tau0 = 0.04557000000 + +### add XiC decay absent in PYTHIA8 decay table +4132:addChannel = 1 0.02 0 3312 211 + +### force the XiC to decay in the XiC -> Xi pi channel +4132:onMode = off +4132:onIfMatch = 3312 211 + +# Correct OmegaC decay length (wrong in PYTHIA8 decay table) (mm/c) +4332:tau0 = 0.08034000000 + +### add OmegaC decay absent in PYTHIA8 decay table +4332:addChannel = 1 0.02 0 3334 211 + +### force the OmegaC to decay in the Omega_c -> Omega pi channel +4332:onMode = off +4332:onIfMatch = 3334 211 \ No newline at end of file diff --git a/MC/config/PWGHF/pythia8/generator/pythia8_charmtriggers_xictoxipi.cfg b/MC/config/PWGHF/pythia8/generator/pythia8_charmtriggers_xictoxipi.cfg index bed2ee9ee..e19ce491b 100644 --- a/MC/config/PWGHF/pythia8/generator/pythia8_charmtriggers_xictoxipi.cfg +++ b/MC/config/PWGHF/pythia8/generator/pythia8_charmtriggers_xictoxipi.cfg @@ -14,6 +14,24 @@ ParticleDecays:tau0Max 100. # Correct XiC decay length (mm/c) 4132:tau0 = 0.04557000000 +### switching on Pythia Mode2 +ColourReconnection:mode 1 +ColourReconnection:allowDoubleJunRem off +ColourReconnection:m0 0.3 +ColourReconnection:allowJunctions on +ColourReconnection:junctionCorrection 1.20 +ColourReconnection:timeDilationMode 2 +ColourReconnection:timeDilationPar 0.18 +StringPT:sigma 0.335 +StringZ:aLund 0.36 +StringZ:bLund 0.56 +StringFlav:probQQtoQ 0.078 +StringFlav:ProbStoUD 0.2 +StringFlav:probQQ1toQQ0join 0.0275,0.0275,0.0275,0.0275 +MultiPartonInteractions:pT0Ref 2.15 +BeamRemnants:remnantMode 1 +BeamRemnants:saturation 5 + ### add XiC decay absent in PYTHIA8 decay table 4132:addChannel = 1 0.02 0 3312 211 diff --git a/MC/config/PWGHF/pythia8/generator/pythia8_inel_Mode2.cfg b/MC/config/PWGHF/pythia8/generator/pythia8_inel_Mode2.cfg new file mode 100644 index 000000000..b022e3612 --- /dev/null +++ b/MC/config/PWGHF/pythia8/generator/pythia8_inel_Mode2.cfg @@ -0,0 +1,38 @@ +### beams +Beams:idA 2212 # proton +Beams:idB 2212 # proton +Beams:eCM 13600. # GeV + +### processes +SoftQCD:inelastic on # all inelastic processes + +### decays +ParticleDecays:limitTau0 on +ParticleDecays:tau0Max 10. + +### switching on Pythia Mode2 +ColourReconnection:mode 1 +ColourReconnection:allowDoubleJunRem off +ColourReconnection:m0 0.3 +ColourReconnection:allowJunctions on +ColourReconnection:junctionCorrection 1.20 +ColourReconnection:timeDilationMode 2 +ColourReconnection:timeDilationPar 0.18 +StringPT:sigma 0.335 +StringZ:aLund 0.36 +StringZ:bLund 0.56 +StringFlav:probQQtoQ 0.078 +StringFlav:ProbStoUD 0.2 +StringFlav:probQQ1toQQ0join 0.0275,0.0275,0.0275,0.0275 +MultiPartonInteractions:pT0Ref 2.15 +BeamRemnants:remnantMode 1 +BeamRemnants:saturation 5 + +# Correct decay lengths (wrong in PYTHIA8 decay table) +# Lb +5122:tau0 = 0.4390 +# Xic0 +4132:tau0 = 0.0455 +# OmegaC +4332:tau0 = 0.0803 + diff --git a/MC/config/PWGHF/pythia8/generator/pythia8_lambdab_probQQtoQ.cfg b/MC/config/PWGHF/pythia8/generator/pythia8_lambdab_probQQtoQ.cfg new file mode 100644 index 000000000..f59bc78da --- /dev/null +++ b/MC/config/PWGHF/pythia8/generator/pythia8_lambdab_probQQtoQ.cfg @@ -0,0 +1,71 @@ +### beams +Beams:idA 2212 # proton +Beams:idB 2212 # proton +Beams:eCM 13600. # GeV + +### processes +SoftQCD:inelastic on # all inelastic processes + +### decays +ParticleDecays:limitTau0 on +ParticleDecays:tau0Max 10. + +### switching on Pythia Mode2 +ColourReconnection:mode 1 +ColourReconnection:allowDoubleJunRem off +ColourReconnection:m0 0.3 +ColourReconnection:allowJunctions on +ColourReconnection:junctionCorrection 1.20 +ColourReconnection:timeDilationMode 2 +ColourReconnection:timeDilationPar 0.18 +StringPT:sigma 0.335 +StringZ:aLund 0.36 +StringZ:bLund 0.56 +StringFlav:probQQtoQ 0.078 +StringFlav:ProbStoUD 0.2 +StringFlav:probQQ1toQQ0join 0.0275,0.0275,0.0275,0.0275 +MultiPartonInteractions:pT0Ref 2.15 +BeamRemnants:remnantMode 1 +BeamRemnants:saturation 5 +BeamRemnants:allowBeamJunction = off +BeamRemnants:beamJunction = off +ColourReconnection:allowDiquarkJunctionCR = off + +# Correct decay lengths (wrong in PYTHIA8 decay table) +# Lb +5122:tau0 = 0.4390 +# Xic0 +4132:tau0 = 0.0455 +# OmegaC +4332:tau0 = 0.0803 + +## Lb decay modes +5122:onMode = off +5122:oneChannel = 1 9.03e-05 0 2212 2212 -2212 -2212 2112 +5122:addChannel = 1 0.00181 0 2212 2212 -2212 -2212 2112 111 +5122:addChannel = 1 0.0181 0 2212 2212 -2212 -2212 2112 111 111 +5122:addChannel = 1 0.0271 0 2212 2212 -2212 -2212 2112 211 -211 +5122:addChannel = 1 0.0181 0 2212 2212 -2212 -2212 2112 211 211 -211 -211 +5122:addChannel = 1 0.181 0 2212 2212 -2212 -2212 2112 211 -211 111 +5122:addChannel = 1 0.199 0 2212 2212 -2212 -2212 2112 211 -211 111 111 +5122:addChannel = 1 0.0271 0 2212 2212 -2212 -2212 2112 211 211 -211 -211 111 +5122:addChannel = 1 0.00452 0 2212 2212 -2212 -2212 2112 211 111 +5122:addChannel = 1 0.0361 0 2212 2212 2112 -2212 -2112 -211 111 +5122:addChannel = 1 0.0452 0 2212 2212 2112 -2212 -2112 -211 111 111 +5122:addChannel = 1 0.0542 0 2212 2212 2112 -2212 -2112 -211 -211 211 +5122:addChannel = 1 0.361 0 2212 2212 2112 -2212 -2112 -211 -211 211 111 +5122:addChannel = 1 0.0271 0 2212 2212 2112 -2212 -2112 -211 -211 211 111 111 +5122:onIfMatch = 2212 2212 2212 2212 2112 +5122:onIfMatch = 2212 2212 2212 2212 2112 111 +5122:onIfMatch = 2212 2212 2212 2212 2112 111 111 +5122:onIfMatch = 2212 2212 2212 2212 2112 211 211 +5122:onIfMatch = 2212 2212 2212 2212 2112 211 211 211 211 +5122:onIfMatch = 2212 2212 2212 2212 2112 211 211 111 +5122:onIfMatch = 2212 2212 2212 2212 2112 211 211 111 111 +5122:onIfMatch = 2212 2212 2212 2212 2112 211 211 211 211 111 +5122:onIfMatch = 2212 2212 2212 2212 2112 211 111 +5122:onIfMatch = 2212 2212 2112 2212 2112 211 111 +5122:onIfMatch = 2212 2212 2112 2212 2112 211 111 111 +5122:onIfMatch = 2212 2212 2112 2212 2112 211 211 211 +5122:onIfMatch = 2212 2212 2112 2212 2112 211 211 211 111 +5122:onIfMatch = 2212 2212 2112 2212 2112 211 211 211 111 111 diff --git a/MC/config/PWGHF/pythia8/generator/pythia8_lambdab_with_decays_Mode2.cfg b/MC/config/PWGHF/pythia8/generator/pythia8_lambdab_with_decays_Mode2.cfg new file mode 100644 index 000000000..8243c2fbe --- /dev/null +++ b/MC/config/PWGHF/pythia8/generator/pythia8_lambdab_with_decays_Mode2.cfg @@ -0,0 +1,73 @@ +### authors: Fabrizio Grosa (fabrizio.grosa@cern.ch) +### Cristina Terrevoli (cristina.terrevoli@cern.ch) +### Fabio Catalano (fabio.catalano@cern.ch) +### last update: November 2023 + +### beams +Beams:idA 2212 # proton +Beams:idB 2212 # proton +Beams:eCM 13600. # GeV + +### processes +SoftQCD:inelastic on # all inelastic processes + +### decays +ParticleDecays:limitTau0 on +ParticleDecays:tau0Max 10. + +### switching on Pythia Mode2 +ColourReconnection:mode 1 +ColourReconnection:allowDoubleJunRem off +ColourReconnection:m0 0.3 +ColourReconnection:allowJunctions on +ColourReconnection:junctionCorrection 1.20 +ColourReconnection:timeDilationMode 2 +ColourReconnection:timeDilationPar 0.18 +StringPT:sigma 0.335 +StringZ:aLund 0.36 +StringZ:bLund 0.56 +StringFlav:probQQtoQ 0.078 +StringFlav:ProbStoUD 0.2 +StringFlav:probQQ1toQQ0join 0.0275,0.0275,0.0275,0.0275 +MultiPartonInteractions:pT0Ref 2.15 +BeamRemnants:remnantMode 1 +BeamRemnants:saturation 5 + +# Correct decay lengths (wrong in PYTHIA8 decay table) +# Lb +5122:tau0 = 0.4390 +# Xic0 +4132:tau0 = 0.0455 +# OmegaC +4332:tau0 = 0.0803 + +## Lb decay modes +5122:onMode = off +5122:oneChannel = 1 9.03e-05 0 2212 2212 -2212 -2212 2112 +5122:addChannel = 1 0.00181 0 2212 2212 -2212 -2212 2112 111 +5122:addChannel = 1 0.0181 0 2212 2212 -2212 -2212 2112 111 111 +5122:addChannel = 1 0.0271 0 2212 2212 -2212 -2212 2112 211 -211 +5122:addChannel = 1 0.0181 0 2212 2212 -2212 -2212 2112 211 211 -211 -211 +5122:addChannel = 1 0.181 0 2212 2212 -2212 -2212 2112 211 -211 111 +5122:addChannel = 1 0.199 0 2212 2212 -2212 -2212 2112 211 -211 111 111 +5122:addChannel = 1 0.0271 0 2212 2212 -2212 -2212 2112 211 211 -211 -211 111 +5122:addChannel = 1 0.00452 0 2212 2212 -2212 -2212 2112 211 111 +5122:addChannel = 1 0.0361 0 2212 2212 2112 -2212 -2112 -211 111 +5122:addChannel = 1 0.0452 0 2212 2212 2112 -2212 -2112 -211 111 111 +5122:addChannel = 1 0.0542 0 2212 2212 2112 -2212 -2112 -211 -211 211 +5122:addChannel = 1 0.361 0 2212 2212 2112 -2212 -2112 -211 -211 211 111 +5122:addChannel = 1 0.0271 0 2212 2212 2112 -2212 -2112 -211 -211 211 111 111 +5122:onIfMatch = 2212 2212 2212 2212 2112 +5122:onIfMatch = 2212 2212 2212 2212 2112 111 +5122:onIfMatch = 2212 2212 2212 2212 2112 111 111 +5122:onIfMatch = 2212 2212 2212 2212 2112 211 211 +5122:onIfMatch = 2212 2212 2212 2212 2112 211 211 211 211 +5122:onIfMatch = 2212 2212 2212 2212 2112 211 211 111 +5122:onIfMatch = 2212 2212 2212 2212 2112 211 211 111 111 +5122:onIfMatch = 2212 2212 2212 2212 2112 211 211 211 211 111 +5122:onIfMatch = 2212 2212 2212 2212 2112 211 111 +5122:onIfMatch = 2212 2212 2112 2212 2112 211 111 +5122:onIfMatch = 2212 2212 2112 2212 2112 211 111 111 +5122:onIfMatch = 2212 2212 2112 2212 2112 211 211 211 +5122:onIfMatch = 2212 2212 2112 2212 2112 211 211 211 111 +5122:onIfMatch = 2212 2212 2112 2212 2112 211 211 211 111 111 \ No newline at end of file diff --git a/MC/config/PWGHF/pythia8/hooks/pythia8_userhooks_qqbar.C b/MC/config/PWGHF/pythia8/hooks/pythia8_userhooks_qqbar.C index 9cbf6fe27..8dcf2b4cd 100644 --- a/MC/config/PWGHF/pythia8/hooks/pythia8_userhooks_qqbar.C +++ b/MC/config/PWGHF/pythia8/hooks/pythia8_userhooks_qqbar.C @@ -16,6 +16,10 @@ class UserHooks_qqbar : public Pythia8::UserHooks bool canVetoPartonLevel() override { return true; }; bool doVetoPartonLevel(const Pythia8::Event& event) override { + // Veto runtime condition + if (!mActive) { + return false; + } // search for c-cbar mother with at least one c at midrapidity for (int ipa = 0; ipa < event.size(); ++ipa) { auto daughterList = event[ipa].daughterList(); @@ -40,11 +44,15 @@ class UserHooks_qqbar : public Pythia8::UserHooks mRapidityMin = valMin; mRapidityMax = valMax; }; + // Allows the veto to be switched on/off at run time, e.g. so that a generator + // alternating between biased and unbiased events can apply the bias selectively. + void setActive(bool val) { mActive = val; }; private: int mPDG = 4; double mRapidityMin = -1.5; double mRapidityMax = 1.5; + bool mActive = true; }; Pythia8::UserHooks* diff --git a/MC/config/PWGHF/pythia8_gun/configOmegaAnd20Pions_randomCharge.ini b/MC/config/PWGHF/pythia8_gun/configOmegaAnd20Pions_randomCharge.ini index 0a60a1236..1d4a4e123 100755 --- a/MC/config/PWGHF/pythia8_gun/configOmegaAnd20Pions_randomCharge.ini +++ b/MC/config/PWGHF/pythia8_gun/configOmegaAnd20Pions_randomCharge.ini @@ -1,6 +1,6 @@ [GeneratorExternal] -fileName=${O2DPG_ROOT}/MC/config/PWGHF/pythia8_gun/generator_pythia8_gun.C +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8_gun/generator_pythia8_gun.C funcName=generateOmegaAndPions_RandomCharge(20) [GeneratorPythia8] -config=${O2DPG_ROOT}/MC/config/PWGHF/pythia8_gun/config_custom_OmegaC.cfg +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8_gun/config_custom_OmegaC.cfg diff --git a/MC/config/PWGHF/pythia8_gun/generator_pythia8_gun.C b/MC/config/PWGHF/pythia8_gun/generator_pythia8_gun.C index 3e5965e4d..692608116 100644 --- a/MC/config/PWGHF/pythia8_gun/generator_pythia8_gun.C +++ b/MC/config/PWGHF/pythia8_gun/generator_pythia8_gun.C @@ -135,6 +135,8 @@ class GeneratorPythia8Gun : public o2::eventgen::GeneratorPythia8 /// generate uniform eta and uniform momentum void genUniformMomentumEta(double minP, double maxP, double minEta, double maxEta) { + // Warning: this generator samples randomly in p and not in pT. Care is advised + // random generator std::unique_ptr ranGenerator{new TRandom3()}; ranGenerator->SetSeed(0); @@ -146,15 +148,11 @@ class GeneratorPythia8Gun : public o2::eventgen::GeneratorPythia8 // z-component momentum from eta const double cosTheta = (exp(2 * gen_eta) - 1) / (exp(2 * gen_eta) + 1); // starting from eta = -ln(tan(theta/2)) = 1/2*ln( (1+cos(theta))/(1-cos(theta)) ) ---> NB: valid for cos(theta)!=1 const double gen_pz = gen_p * cosTheta; - // y-component: random uniform - const double maxVal = sqrt(gen_p * gen_p - gen_pz * gen_pz); - double sign_py = ranGenerator->Uniform(0, 1); - sign_py = (sign_py > 0.5) ? 1. : -1.; - const double gen_py = ranGenerator->Uniform(0., maxVal) * sign_py; - // x-component momentum - double sign_px = ranGenerator->Uniform(0, 1); - sign_px = (sign_px > 0.5) ? 1. : -1.; - const double gen_px = sqrt(gen_p * gen_p - gen_pz * gen_pz - gen_py * gen_py) * sign_px; + // phi: random uniform, X, Y conform + const double pT = sqrt(gen_p * gen_p - gen_pz * gen_pz); + double phi = ranGenerator->Uniform(0., 2.0f*TMath::Pi()); + const double gen_px = pT*TMath::Cos(phi); + const double gen_py = pT*TMath::Sin(phi); set4momentum(gen_px, gen_py, gen_pz); } @@ -288,4 +286,4 @@ FairGenerator* generateOmegaAndPions_RandomCharge(const int nPions) myGen->setAddFurtherPrimaries(-211, nPions / 2); // pi- return myGen; -} \ No newline at end of file +} diff --git a/MC/config/PWGHF/trigger/trigger_ccbar.C b/MC/config/PWGHF/trigger/trigger_ccbar.C index 5ef9b52e5..4c71702e4 100644 --- a/MC/config/PWGHF/trigger/trigger_ccbar.C +++ b/MC/config/PWGHF/trigger/trigger_ccbar.C @@ -1,4 +1,4 @@ -R__ADD_INCLUDE_PATH($O2DPG_ROOT) +R__ADD_INCLUDE_PATH($O2DPG_MC_CONFIG_ROOT) #include "Generators/Trigger.h" #include "TParticle.h" #include "TParticlePDG.h" diff --git a/MC/config/PWGLF/epos/pp_536TeV_hydro_cascade.optns b/MC/config/PWGLF/epos/pp_536TeV_hydro_cascade.optns new file mode 100644 index 000000000..502cdc217 --- /dev/null +++ b/MC/config/PWGLF/epos/pp_536TeV_hydro_cascade.optns @@ -0,0 +1,32 @@ +!-------------------------------------------------------------------- +! proton-proton collisions at 5.36 TeV with hydro and hadronic cascade +!-------------------------------------------------------------------- + +!--------------------------------------- +! Define run +!--------------------------------------- + +application hadron !hadron-hadron, hadron-nucleus, or nucleus-nucleus +set laproj 1 !projectile atomic number +set maproj 1 !projectile mass number +set latarg 1 !target atomic number +set matarg 1 !target mass number +set ecms 5360 !sqrt(s)_pp +set istmax 25 !max status considered for storage + +ftime on !string formation time non-zero +!suppressed decays: +nodecays + 110 20 2130 -2130 2230 -2230 1130 -1130 1330 -1330 2330 -2330 3331 -3331 +end + +set ninicon 1 !number of initial conditions used for hydro evolution +core full !core/corona activated +hydro hlle !hydro activated +eos x3ff !eos activated (epos standard EoS) +hacas full !hadronic cascade activated (UrQMD) +set nfreeze 1 !number of freeze out events per hydro event +set modsho 1 !printout every modsho events +set centrality 0 !0=min bias +set ihepmc 2 !HepMC output enabled on stdout +set nfull 10 diff --git a/MC/config/PWGLF/hybrid/hybrid_coalescence.json b/MC/config/PWGLF/hybrid/hybrid_coalescence.json new file mode 100644 index 000000000..32c3f54ae --- /dev/null +++ b/MC/config/PWGLF/hybrid/hybrid_coalescence.json @@ -0,0 +1,24 @@ + +{ + "mode": "sequential", + "generators": [ + { + "name": "pythia8", + "config": { + "config": "${O2_ROOT}/share/Generators/egconfig/pythia8_inel.cfg", + "hooksFileName": "", + "hooksFuncName": "", + "includePartonEvent": false, + "particleFilter": "", + "verbose": 0 + } + }, + { + "name": "external", + "config": { + "iniFile": "${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/ini/GeneratorAntideuteronCoalescenceJet.ini" + } + } + ], + "fractions": [4, 1] +} diff --git a/MC/config/PWGLF/ini/GeneratorAntideuteronCoalescenceJet.ini b/MC/config/PWGLF/ini/GeneratorAntideuteronCoalescenceJet.ini new file mode 100644 index 000000000..1a6394c65 --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorAntideuteronCoalescenceJet.ini @@ -0,0 +1,6 @@ +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_antideuteron_injection.C +funcName = generateAntideuterons(0.8) + +[GeneratorPythia8] +config = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/pythia8/generator/pythia8_jet.cfg \ No newline at end of file diff --git a/MC/config/PWGLF/ini/GeneratorDoubleLambdaTriggered.ini b/MC/config/PWGLF/ini/GeneratorDoubleLambdaTriggered.ini new file mode 100644 index 000000000..5051b84a9 --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorDoubleLambdaTriggered.ini @@ -0,0 +1,9 @@ +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_doubleLambdas.C +funcName=generateDoubleLambda(1, 0.6, 10, 0.8) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_ropes_136tev.cfg + +[DecayerPythia8] +config[0]=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/decayer/base.cfg diff --git a/MC/config/PWGLF/ini/GeneratorEPOS4_pp536TeV.ini b/MC/config/PWGLF/ini/GeneratorEPOS4_pp536TeV.ini new file mode 100644 index 000000000..5958fb64c --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorEPOS4_pp536TeV.ini @@ -0,0 +1,11 @@ +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/examples/epos4/generator_EPOS4.C +funcName=generateEPOS4("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/epos/pp_536TeV_hydro_cascade.optns", 2147483647) + +[GeneratorFileOrCmd] +cmd=${O2DPG_MC_CONFIG_ROOT}/MC/config/examples/epos4/epos.sh +bMaxSwitch=none + +# Set to version 2 if EPOS4.0.0 is used +[HepMC] +version=3 diff --git a/MC/config/PWGLF/ini/GeneratorLFAntideuteronInJets.ini b/MC/config/PWGLF/ini/GeneratorLFAntideuteronInJets.ini new file mode 100644 index 000000000..31463851b --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLFAntideuteronInJets.ini @@ -0,0 +1,7 @@ +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_longlived_gaptriggered.C +funcName=generateLongLivedGapTriggered("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/antideuteron.gun", 2, false) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/pythia8/generator/pythia8_jet.cfg +includePartonEvent=false \ No newline at end of file diff --git a/MC/config/PWGLF/ini/GeneratorLFCascadesInJets_pp536TeV_gap2.ini b/MC/config/PWGLF/ini/GeneratorLFCascadesInJets_pp536TeV_gap2.ini new file mode 100644 index 000000000..5bdd49711 --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLFCascadesInJets_pp536TeV_gap2.ini @@ -0,0 +1,16 @@ +#---> GeneratorLFCascadesInJets_pp536TeV +### Cascades in jets - pp collisions at 5.36 TeV + +[GeneratorExternal] +# Generate jet-biased Pythia8 events and require the presence of one cascade species +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_LF.C +funcName=generateLFTriggered("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/strangeparticlelist.gun", 2) + +[GeneratorPythia8] +# Underlying pp event: HardQCD production with jet-pT bias and rope hadronization +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_jet_ropes_536tev.cfg +includePartonEvent=false + +[DecayerPythia8] +# Standard Pythia8 decay configuration +config[0]=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/decayer/base.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLFCascadesInJets_pp536TeV_gap3.ini b/MC/config/PWGLF/ini/GeneratorLFCascadesInJets_pp536TeV_gap3.ini new file mode 100644 index 000000000..dbd1d8f33 --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLFCascadesInJets_pp536TeV_gap3.ini @@ -0,0 +1,16 @@ +#---> GeneratorLFCascadesInJets_pp536TeV +### Cascades in jets - pp collisions at 5.36 TeV + +[GeneratorExternal] +# Generate jet-biased Pythia8 events and require the presence of one cascade species +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_LF.C +funcName=generateLFTriggered("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/strangeparticlelist.gun", 3) + +[GeneratorPythia8] +# Underlying pp event: HardQCD production with jet-pT bias and rope hadronization +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_jet_ropes_536tev.cfg +includePartonEvent=false + +[DecayerPythia8] +# Standard Pythia8 decay configuration +config[0]=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/decayer/base.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLFCascadesInJets_pp536TeV_gap4.ini b/MC/config/PWGLF/ini/GeneratorLFCascadesInJets_pp536TeV_gap4.ini new file mode 100644 index 000000000..0554bb10a --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLFCascadesInJets_pp536TeV_gap4.ini @@ -0,0 +1,16 @@ +#---> GeneratorLFCascadesInJets_pp536TeV +### Cascades in jets - pp collisions at 5.36 TeV + +[GeneratorExternal] +# Generate jet-biased Pythia8 events and require the presence of one cascade species +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_LF.C +funcName=generateLFTriggered("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/strangeparticlelist.gun", 4) + +[GeneratorPythia8] +# Underlying pp event: HardQCD production with jet-pT bias and rope hadronization +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_jet_ropes_536tev.cfg +includePartonEvent=false + +[DecayerPythia8] +# Standard Pythia8 decay configuration +config[0]=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/decayer/base.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLFCascadesInJets_pp536TeV_gap5.ini b/MC/config/PWGLF/ini/GeneratorLFCascadesInJets_pp536TeV_gap5.ini new file mode 100644 index 000000000..b5672f10b --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLFCascadesInJets_pp536TeV_gap5.ini @@ -0,0 +1,16 @@ +#---> GeneratorLFCascadesInJets_pp536TeV +### Cascades in jets - pp collisions at 5.36 TeV + +[GeneratorExternal] +# Generate jet-biased Pythia8 events and require the presence of one cascade species +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_LF.C +funcName=generateLFTriggered("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/strangeparticlelist.gun", 5) + +[GeneratorPythia8] +# Underlying pp event: HardQCD production with jet-pT bias and rope hadronization +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_jet_ropes_536tev.cfg +includePartonEvent=false + +[DecayerPythia8] +# Standard Pythia8 decay configuration +config[0]=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/decayer/base.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLFDeTrHe.ini b/MC/config/PWGLF/ini/GeneratorLFDeTrHe.ini index 48c26d771..191ff404a 100644 --- a/MC/config/PWGLF/ini/GeneratorLFDeTrHe.ini +++ b/MC/config/PWGLF/ini/GeneratorLFDeTrHe.ini @@ -1,13 +1,13 @@ [GeneratorExternal] -fileName=${O2DPG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_LF.C -funcName=generateLF("${O2DPG_ROOT}/MC/config/PWGLF/pythia8/generator/nuclei.gun") +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_LF.C +funcName=generateLF("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/nuclei.gun") ; funcName=generateLF({{1000010020, 10, 0.2, 10}, {-1000010020, 10, 0.2, 10}, {1000010030, 10, 0.2, 10}, {-1000010030, 10, 0.2, 10}, {1000020030, 10, 0.2, 10}, {-1000020030, 10, 0.2, 10}}) # Deuteron Anti-Deuteron Triton Anti-Triton Helium3 Anti-Helium3 [GeneratorPythia8] # config=${O2_ROOT}/share/Generators/egconfig/pythia8_inel.cfg -; config=${O2DPG_ROOT}/MC/config/PWGLF/pythia8/generator/nuclei.cfg +; config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/nuclei.cfg [DecayerPythia8] -config[0]=${O2DPG_ROOT}/MC/config/common/pythia8/decayer/base.cfg -config[1]=${O2DPG_ROOT}/MC/config/PWGLF/pythia8/generator/nuclei.cfg +config[0]=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/decayer/base.cfg +config[1]=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/nuclei.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLFDeuteronOOGap.ini b/MC/config/PWGLF/ini/GeneratorLFDeuteronOOGap.ini new file mode 100644 index 000000000..5fd1a11dc --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLFDeuteronOOGap.ini @@ -0,0 +1,6 @@ +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_longlived_gaptriggered.C +funcName=generateLongLivedGapTriggered({1000010020}, 1, 4, 0.4, 8.0) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/generator/pythia8_OO_536.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLFDoubleOmegaPbPb.ini b/MC/config/PWGLF/ini/GeneratorLFDoubleOmegaPbPb.ini new file mode 100644 index 000000000..445dbf7c5 --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLFDoubleOmegaPbPb.ini @@ -0,0 +1,6 @@ +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_longlived_gaptriggered.C +funcName=generateLongLivedGapTriggered({1060020020}, 1, 20, 0.2, 10, -1, 1, true, false) + +[GeneratorPythia8] +config=${O2_ROOT}/share/Generators/egconfig/pythia8_hi.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLFExoticNucleiPbPb.ini b/MC/config/PWGLF/ini/GeneratorLFExoticNucleiPbPb.ini new file mode 100644 index 000000000..eca344f65 --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLFExoticNucleiPbPb.ini @@ -0,0 +1,6 @@ +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_longlived_gaptriggered.C +funcName=generateLongLivedGapTriggered("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/exotic_nuclei_pbpb.gun", 1) + +[GeneratorPythia8] +config=${O2_ROOT}/share/Generators/egconfig/pythia8_hi.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLFHyperNucleiOOGap.ini b/MC/config/PWGLF/ini/GeneratorLFHyperNucleiOOGap.ini new file mode 100644 index 000000000..2c2477aab --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLFHyperNucleiOOGap.ini @@ -0,0 +1,6 @@ +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_longlived_gaptriggered.C +funcName=generateLongLivedGapTriggered("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/hypernuclei_oo.gun", 1) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/generator/pythia8_OO_536.cfg \ No newline at end of file diff --git a/MC/config/PWGLF/ini/GeneratorLFHyperNucleiPbPbGap.ini b/MC/config/PWGLF/ini/GeneratorLFHyperNucleiPbPbGap.ini index f548c57cf..122d50884 100644 --- a/MC/config/PWGLF/ini/GeneratorLFHyperNucleiPbPbGap.ini +++ b/MC/config/PWGLF/ini/GeneratorLFHyperNucleiPbPbGap.ini @@ -1,6 +1,6 @@ [GeneratorExternal] -fileName=${O2DPG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_longlived_gaptriggered.C -funcName=generateLongLivedGapTriggered({1000010020, 1000010030, 1000020030, 1000020040, 1010010030}, 5, 10) +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_longlived_gaptriggered.C +funcName=generateLongLivedGapTriggered("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/hypernuclei_pbpb.gun", 1) [GeneratorPythia8] config=${O2_ROOT}/share/Generators/egconfig/pythia8_hi.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLFHyperNucleiPbPbGapWithFlow.ini b/MC/config/PWGLF/ini/GeneratorLFHyperNucleiPbPbGapWithFlow.ini new file mode 100644 index 000000000..37d3320d3 --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLFHyperNucleiPbPbGapWithFlow.ini @@ -0,0 +1,7 @@ +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_longlived_gaptriggered.C +funcName=generateLongLivedGapTriggered("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/hypernuclei_flow.gun", 1, 1, 1) + +[GeneratorPythia8] +config=${O2_ROOT}/share/Generators/egconfig/pythia8_hi.cfg + diff --git a/MC/config/PWGLF/ini/GeneratorLFHyperNucleippGap.ini b/MC/config/PWGLF/ini/GeneratorLFHyperNucleippGap.ini index 42235a311..d996cab59 100644 --- a/MC/config/PWGLF/ini/GeneratorLFHyperNucleippGap.ini +++ b/MC/config/PWGLF/ini/GeneratorLFHyperNucleippGap.ini @@ -1,6 +1,6 @@ [GeneratorExternal] -fileName=${O2DPG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_longlived_gaptriggered.C -funcName=generateLongLivedGapTriggered("${O2DPG_ROOT}/MC/config/PWGLF/pythia8/generator/hypernuclei.gun", 5) +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_longlived_gaptriggered.C +funcName=generateLongLivedGapTriggered("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/hypernuclei.gun", 5) [GeneratorPythia8] config=${O2_ROOT}/share/Generators/egconfig/pythia8_inel.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLFHyperNucleippGapXSection.ini b/MC/config/PWGLF/ini/GeneratorLFHyperNucleippGapXSection.ini new file mode 100644 index 000000000..916832354 --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLFHyperNucleippGapXSection.ini @@ -0,0 +1,9 @@ +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_longlived_gaptriggered.C +funcName=generateLongLivedGapTriggered("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/hypernuclei.gun", 5) + +[GeneratorPythia8] +config=${O2_ROOT}/share/Generators/egconfig/pythia8_inel.cfg + +[G4] +configMacroFile=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/xsection/g4config_had_x2.in \ No newline at end of file diff --git a/MC/config/PWGLF/ini/GeneratorLFHyperppGap.ini b/MC/config/PWGLF/ini/GeneratorLFHyperppGap.ini index e27b67891..d17f2933e 100644 --- a/MC/config/PWGLF/ini/GeneratorLFHyperppGap.ini +++ b/MC/config/PWGLF/ini/GeneratorLFHyperppGap.ini @@ -1,6 +1,6 @@ [GeneratorExternal] -fileName=${O2DPG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_longlived_gaptriggered.C -funcName=generateLongLivedGapTriggered("${O2DPG_ROOT}/MC/config/PWGLF/pythia8/generator/hyper.gun", 5) +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_longlived_gaptriggered.C +funcName=generateLongLivedGapTriggered("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/hyper.gun", 5) [GeneratorPythia8] config=${O2_ROOT}/share/Generators/egconfig/pythia8_inel.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLFHypertritonPbPbGap.ini b/MC/config/PWGLF/ini/GeneratorLFHypertritonPbPbGap.ini index 5c6bb6103..a1d8f67e9 100644 --- a/MC/config/PWGLF/ini/GeneratorLFHypertritonPbPbGap.ini +++ b/MC/config/PWGLF/ini/GeneratorLFHypertritonPbPbGap.ini @@ -1,6 +1,6 @@ [GeneratorExternal] -fileName=${O2DPG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_longlived_gaptriggered.C -funcName=generateLongLivedGapTriggered({1010010030}, 5, 10) +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_longlived_gaptriggered.C +funcName=generateLongLivedGapTriggered({1010010030}, 1, 20) [GeneratorPythia8] config=${O2_ROOT}/share/Generators/egconfig/pythia8_hi.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLFHypertritonppGap.ini b/MC/config/PWGLF/ini/GeneratorLFHypertritonppGap.ini index 18b59b70d..8096d41be 100644 --- a/MC/config/PWGLF/ini/GeneratorLFHypertritonppGap.ini +++ b/MC/config/PWGLF/ini/GeneratorLFHypertritonppGap.ini @@ -1,5 +1,5 @@ [GeneratorExternal] -fileName=${O2DPG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_longlived_gaptriggered.C +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_longlived_gaptriggered.C funcName=generateLongLivedGapTriggered({1010010030}, 5, 1) [GeneratorPythia8] diff --git a/MC/config/PWGLF/ini/GeneratorLFLambdapp.ini b/MC/config/PWGLF/ini/GeneratorLFLambdapp.ini index 1096b9842..7fe2a9dd9 100644 --- a/MC/config/PWGLF/ini/GeneratorLFLambdapp.ini +++ b/MC/config/PWGLF/ini/GeneratorLFLambdapp.ini @@ -1,5 +1,5 @@ [GeneratorExternal] -fileName=${O2DPG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_longlived.C +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_longlived.C funcName=generateLongLived(3122, 10) [GeneratorPythia8] diff --git a/MC/config/PWGLF/ini/GeneratorLFNucleiFwdppGap.ini b/MC/config/PWGLF/ini/GeneratorLFNucleiFwdppGap.ini index 7c14f1cff..019bd9521 100644 --- a/MC/config/PWGLF/ini/GeneratorLFNucleiFwdppGap.ini +++ b/MC/config/PWGLF/ini/GeneratorLFNucleiFwdppGap.ini @@ -1,6 +1,6 @@ [GeneratorExternal] -fileName=${O2DPG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_longlived_gaptriggered.C -funcName=generateLongLivedGapTriggered("${O2DPG_ROOT}/MC/config/PWGLF/pythia8/generator/nuclei_fwd.gun", 5) +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_longlived_gaptriggered.C +funcName=generateLongLivedGapTriggered("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/nuclei_fwd.gun", 5) [GeneratorPythia8] config=${O2_ROOT}/share/Generators/egconfig/pythia8_inel.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLFNucleiPbPbInHMPIDAcceptance.ini b/MC/config/PWGLF/ini/GeneratorLFNucleiPbPbInHMPIDAcceptance.ini new file mode 100644 index 000000000..d0d246004 --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLFNucleiPbPbInHMPIDAcceptance.ini @@ -0,0 +1,6 @@ +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_longlived.C +funcName=generateLongLived(1000010020,10,0.1,3.0,-0.6,0.6,-0.7,1.7,-1) + +[GeneratorPythia8] +config=${O2_ROOT}/share/Generators/egconfig/pythia8_hi.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLFNucleippInHMPIDAcceptance.ini b/MC/config/PWGLF/ini/GeneratorLFNucleippInHMPIDAcceptance.ini new file mode 100644 index 000000000..fe7d7a39b --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLFNucleippInHMPIDAcceptance.ini @@ -0,0 +1,6 @@ +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_longlived.C +funcName=generateLongLived(1000010020,10,0.1,3.0,-0.6,0.6,-0.7,1.7,-1) + +[GeneratorPythia8] +config=${O2_ROOT}/share/Generators/egconfig/pythia8_inel.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLFOmegaEmb.ini b/MC/config/PWGLF/ini/GeneratorLFOmegaEmb.ini index 5623f543d..c5e709966 100644 --- a/MC/config/PWGLF/ini/GeneratorLFOmegaEmb.ini +++ b/MC/config/PWGLF/ini/GeneratorLFOmegaEmb.ini @@ -1,6 +1,6 @@ [GeneratorExternal] -fileName=${O2DPG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_box.C +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_box.C funcName=generatePythia8Box(3334, 3) [GeneratorPythia8] -config=${O2DPG_ROOT}/MC/config/PWGLF/pythia8/generator/omega.cfg +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/omega.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLFSigmaPiResonances.ini b/MC/config/PWGLF/ini/GeneratorLFSigmaPiResonances.ini new file mode 100644 index 000000000..046e95765 --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLFSigmaPiResonances.ini @@ -0,0 +1,10 @@ +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_LF_rapidity.C +funcName=generateLFRapidity("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/sigmapi_resonances_gun.json", true, 4, false, true, "${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_136tev.cfg", "") + +[GeneratorPythia8] # if triggered then this will be used as the background event +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_136tev.cfg + +[DecayerPythia8] # after for transport code! +config[0]=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/decayer/base.cfg +config[1]=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/resonances.cfg \ No newline at end of file diff --git a/MC/config/PWGLF/ini/GeneratorLFSigmapp.ini b/MC/config/PWGLF/ini/GeneratorLFSigmapp.ini index d768a39e2..9df93d0fa 100644 --- a/MC/config/PWGLF/ini/GeneratorLFSigmapp.ini +++ b/MC/config/PWGLF/ini/GeneratorLFSigmapp.ini @@ -1,5 +1,5 @@ [GeneratorExternal] -fileName=${O2DPG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_longlived.C +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_longlived.C funcName=generateLongLived(3112, 5, 1, 9, 3222) [GeneratorPythia8] diff --git a/MC/config/PWGLF/ini/GeneratorLFStrangeness.ini b/MC/config/PWGLF/ini/GeneratorLFStrangeness.ini index c34778f53..10379bdac 100644 --- a/MC/config/PWGLF/ini/GeneratorLFStrangeness.ini +++ b/MC/config/PWGLF/ini/GeneratorLFStrangeness.ini @@ -1,13 +1,13 @@ [GeneratorExternal] -fileName=${O2DPG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_LF.C -# funcName=generateLF("${O2DPG_ROOT}/MC/config/PWGLF/pythia8/generator/strangeparticlelist.gun", true, 0) -funcName=generateLF("${O2DPG_ROOT}/MC/config/PWGLF/pythia8/generator/strangeparticlelist.gun", true, 4) +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_LF.C +# funcName=generateLF("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/strangeparticlelist.gun", true, 0) +funcName=generateLF("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/strangeparticlelist.gun", true, 4) ; [GeneratorPythia8] # If injected the generator must be left empty ; config=${O2_ROOT}/share/Generators/egconfig/pythia8_inel.cfg ; config=/home/njacazio/alice/O2DPG/MC/config/PWGLF/pythia8/decayer/strangeness.cfg [DecayerPythia8] # The only configuration must be given to the decayer to handle particles that are not handled in the transport code -config[0]=${O2DPG_ROOT}/MC/config/common/pythia8/decayer/base.cfg -config[1]=${O2DPG_ROOT}/MC/config/PWGLF/pythia8/decayer/strangeness.cfg +config[0]=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/decayer/base.cfg +config[1]=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/decayer/strangeness.cfg verbose=true \ No newline at end of file diff --git a/MC/config/PWGLF/ini/GeneratorLFStrangenessInJetsTriggered_gap4.ini b/MC/config/PWGLF/ini/GeneratorLFStrangenessInJetsTriggered_gap4.ini new file mode 100644 index 000000000..0e4746183 --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLFStrangenessInJetsTriggered_gap4.ini @@ -0,0 +1,9 @@ +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_LF.C +funcName=generateLFTriggered("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/strangeparticlelist.gun", 4) + +[GeneratorPythia8] # if triggered then this will be used as the background event +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_jet_ropes_136tev.cfg + +[DecayerPythia8] +config[0]=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/decayer/base.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLFStrangenessTriggered.ini b/MC/config/PWGLF/ini/GeneratorLFStrangenessTriggered.ini index d558289b0..e41cf7dcf 100644 --- a/MC/config/PWGLF/ini/GeneratorLFStrangenessTriggered.ini +++ b/MC/config/PWGLF/ini/GeneratorLFStrangenessTriggered.ini @@ -1,11 +1,12 @@ [GeneratorExternal] -fileName=${O2DPG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_LF.C -# funcName=generateLFTriggered("${O2DPG_ROOT}/MC/config/PWGLF/pythia8/generator/strangeparticlelist.gun", 0) -funcName=generateLFTriggered("${O2DPG_ROOT}/MC/config/PWGLF/pythia8/generator/strangeparticlelist.gun", 4) +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_LF.C +# funcName=generateLFTriggered("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/strangeparticlelist.gun", 0) +funcName=generateLFTriggered("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/strangeparticlelist.gun", 4) [GeneratorPythia8] # if triggered then this will be used as the background event # config=${O2_ROOT}/share/Generators/egconfig/pythia8_inel.cfg -config=${O2DPG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_136tev.cfg +; config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_136tev.cfg +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_ropes_136tev.cfg [DecayerPythia8] -config[0]=${O2DPG_ROOT}/MC/config/common/pythia8/decayer/base.cfg +config[0]=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/decayer/base.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLFStrangenessTriggered_5360gev.ini b/MC/config/PWGLF/ini/GeneratorLFStrangenessTriggered_5360gev.ini new file mode 100644 index 000000000..09d05da74 --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLFStrangenessTriggered_5360gev.ini @@ -0,0 +1,10 @@ +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_LF.C +# funcName=generateLFTriggered("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/strangeparticlelist.gun", 0) +funcName=generateLFTriggered("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/strangeparticlelist.gun", 4) + +[GeneratorPythia8] # if triggered then this will be used as the background event +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_pp536tev.cfg + +[DecayerPythia8] +config[0]=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/decayer/base.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLFStrangenessTriggered_900gev.ini b/MC/config/PWGLF/ini/GeneratorLFStrangenessTriggered_900gev.ini index 3447669ac..5d5b10da5 100644 --- a/MC/config/PWGLF/ini/GeneratorLFStrangenessTriggered_900gev.ini +++ b/MC/config/PWGLF/ini/GeneratorLFStrangenessTriggered_900gev.ini @@ -1,10 +1,10 @@ [GeneratorExternal] -fileName=${O2DPG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_LF.C -# funcName=generateLFTriggered("${O2DPG_ROOT}/MC/config/PWGLF/pythia8/generator/strangeparticlelist.gun", 0) -funcName=generateLFTriggered("${O2DPG_ROOT}/MC/config/PWGLF/pythia8/generator/strangeparticlelist.gun", 4) +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_LF.C +# funcName=generateLFTriggered("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/strangeparticlelist.gun", 0) +funcName=generateLFTriggered("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/strangeparticlelist.gun", 4) [GeneratorPythia8] # if triggered then this will be used as the background event -config=${O2DPG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_pp900gev.cfg +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_pp900gev.cfg [DecayerPythia8] -config[0]=${O2DPG_ROOT}/MC/config/common/pythia8/decayer/base.cfg +config[0]=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/decayer/base.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLFStrangenessTriggered_OO.ini b/MC/config/PWGLF/ini/GeneratorLFStrangenessTriggered_OO.ini new file mode 100644 index 000000000..580a3a640 --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLFStrangenessTriggered_OO.ini @@ -0,0 +1,5 @@ +#---> GeneratorLFStrangenessTriggered +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_LF.C +funcName=generateLFTriggered("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/strangeparticlelist.gun", 4, "${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/generator/pythia8_OO_536.cfg", "${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/generator/pythia8_OO_536.cfg") + diff --git a/MC/config/PWGLF/ini/GeneratorLFStrangenessTriggered_gap2.ini b/MC/config/PWGLF/ini/GeneratorLFStrangenessTriggered_gap2.ini new file mode 100644 index 000000000..24ac7286a --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLFStrangenessTriggered_gap2.ini @@ -0,0 +1,12 @@ +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_LF.C +# funcName=generateLFTriggered("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/strangeparticlelist.gun", 0) +funcName=generateLFTriggered("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/strangeparticlelist.gun", 2) + +[GeneratorPythia8] # if triggered then this will be used as the background event +# config=${O2_ROOT}/share/Generators/egconfig/pythia8_inel.cfg +; config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_136tev.cfg +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_ropes_136tev.cfg + +[DecayerPythia8] +config[0]=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/decayer/base.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLFStrangenessTriggered_gap3.ini b/MC/config/PWGLF/ini/GeneratorLFStrangenessTriggered_gap3.ini new file mode 100644 index 000000000..a9dd1d129 --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLFStrangenessTriggered_gap3.ini @@ -0,0 +1,12 @@ +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_LF.C +# funcName=generateLFTriggered("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/strangeparticlelist.gun", 0) +funcName=generateLFTriggered("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/strangeparticlelist.gun", 3) + +[GeneratorPythia8] # if triggered then this will be used as the background event +# config=${O2_ROOT}/share/Generators/egconfig/pythia8_inel.cfg +; config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_136tev.cfg +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_ropes_136tev.cfg + +[DecayerPythia8] +config[0]=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/decayer/base.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLFStrangenessTriggered_gap5.ini b/MC/config/PWGLF/ini/GeneratorLFStrangenessTriggered_gap5.ini new file mode 100644 index 000000000..2e9edb89a --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLFStrangenessTriggered_gap5.ini @@ -0,0 +1,12 @@ +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_LF.C +# funcName=generateLFTriggered("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/strangeparticlelist.gun", 0) +funcName=generateLFTriggered("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/strangeparticlelist.gun", 5) + +[GeneratorPythia8] # if triggered then this will be used as the background event +# config=${O2_ROOT}/share/Generators/egconfig/pythia8_inel.cfg +; config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_136tev.cfg +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_ropes_136tev.cfg + +[DecayerPythia8] +config[0]=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/decayer/base.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLFV0sInJets_pp536TeV_gap2.ini b/MC/config/PWGLF/ini/GeneratorLFV0sInJets_pp536TeV_gap2.ini new file mode 100644 index 000000000..553ad5617 --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLFV0sInJets_pp536TeV_gap2.ini @@ -0,0 +1,16 @@ +#---> GeneratorLFV0sInJets_pp536TeV +### V0s in jets - pp collisions at 5.36 TeV + +[GeneratorExternal] +# Generate jet-biased Pythia8 events and require the presence of one V0 particle +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_LF.C +funcName=generateLFTriggered("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/v0particlelist.gun", 2) + +[GeneratorPythia8] +# Underlying pp event: HardQCD production with jet-pT bias and rope hadronization +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_jet_ropes_536tev.cfg +includePartonEvent=false + +[DecayerPythia8] +# Standard Pythia8 decay configuration +config[0]=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/decayer/base.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLFV0sInJets_pp536TeV_gap3.ini b/MC/config/PWGLF/ini/GeneratorLFV0sInJets_pp536TeV_gap3.ini new file mode 100644 index 000000000..15550b855 --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLFV0sInJets_pp536TeV_gap3.ini @@ -0,0 +1,16 @@ +#---> GeneratorLFV0sInJets_pp536TeV +### V0s in jets - pp collisions at 5.36 TeV + +[GeneratorExternal] +# Generate jet-biased Pythia8 events and require the presence of one V0 particle +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_LF.C +funcName=generateLFTriggered("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/v0particlelist.gun", 3) + +[GeneratorPythia8] +# Underlying pp event: HardQCD production with jet-pT bias and rope hadronization +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_jet_ropes_536tev.cfg +includePartonEvent=false + +[DecayerPythia8] +# Standard Pythia8 decay configuration +config[0]=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/decayer/base.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLFV0sInJets_pp536TeV_gap4.ini b/MC/config/PWGLF/ini/GeneratorLFV0sInJets_pp536TeV_gap4.ini new file mode 100644 index 000000000..88766bf6b --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLFV0sInJets_pp536TeV_gap4.ini @@ -0,0 +1,16 @@ +#---> GeneratorLFV0sInJets_pp536TeV +### V0s in jets - pp collisions at 5.36 TeV + +[GeneratorExternal] +# Generate jet-biased Pythia8 events and require the presence of one V0 particle +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_LF.C +funcName=generateLFTriggered("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/v0particlelist.gun", 4) + +[GeneratorPythia8] +# Underlying pp event: HardQCD production with jet-pT bias and rope hadronization +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_jet_ropes_536tev.cfg +includePartonEvent=false + +[DecayerPythia8] +# Standard Pythia8 decay configuration +config[0]=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/decayer/base.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLFV0sInJets_pp536TeV_gap5.ini b/MC/config/PWGLF/ini/GeneratorLFV0sInJets_pp536TeV_gap5.ini new file mode 100644 index 000000000..ef89de2c7 --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLFV0sInJets_pp536TeV_gap5.ini @@ -0,0 +1,16 @@ +#---> GeneratorLFV0sInJets_pp536TeV +### V0s in jets - pp collisions at 5.36 TeV + +[GeneratorExternal] +# Generate jet-biased Pythia8 events and require the presence of one V0 particle +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_LF.C +funcName=generateLFTriggered("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/v0particlelist.gun", 5) + +[GeneratorPythia8] +# Underlying pp event: HardQCD production with jet-pT bias and rope hadronization +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_jet_ropes_536tev.cfg +includePartonEvent=false + +[DecayerPythia8] +# Standard Pythia8 decay configuration +config[0]=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/decayer/base.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLFXipp.ini b/MC/config/PWGLF/ini/GeneratorLFXipp.ini index e3d883f85..17e6de4b2 100644 --- a/MC/config/PWGLF/ini/GeneratorLFXipp.ini +++ b/MC/config/PWGLF/ini/GeneratorLFXipp.ini @@ -1,5 +1,5 @@ [GeneratorExternal] -fileName=${O2DPG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_longlived.C +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_longlived.C funcName=generateLongLived(3312, 10) [GeneratorPythia8] diff --git a/MC/config/PWGLF/ini/GeneratorLF_Coalescence.ini b/MC/config/PWGLF/ini/GeneratorLF_Coalescence.ini new file mode 100644 index 000000000..3d7c6c7c9 --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLF_Coalescence.ini @@ -0,0 +1,7 @@ +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_coalescence.C +funcName = generateCoalescence({1000010030, 1000020030, 1010010030}, 1, 0.239) + +[GeneratorPythia8] +config=${O2_ROOT}/share/Generators/egconfig/pythia8_inel.cfg + diff --git a/MC/config/PWGLF/ini/GeneratorLF_Coalescence_pp1360TeV.ini b/MC/config/PWGLF/ini/GeneratorLF_Coalescence_pp1360TeV.ini new file mode 100644 index 000000000..84eac76ac --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLF_Coalescence_pp1360TeV.ini @@ -0,0 +1,7 @@ +#---> GeneratorLF_Coalescence_pp536TeV +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_coalescence.C +funcName = generateCoalescence({1000010020, 1000010030, 1000020030}, 1, 0.239) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_136tev.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLF_Coalescence_pp536TeV.ini b/MC/config/PWGLF/ini/GeneratorLF_Coalescence_pp536TeV.ini new file mode 100644 index 000000000..2e8f1e3c5 --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLF_Coalescence_pp536TeV.ini @@ -0,0 +1,7 @@ +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_coalescence.C +funcName = generateCoalescence({1000010020, 1000010030, 1000020030}, 1, 0.239) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_pp536tev.cfg + diff --git a/MC/config/PWGLF/ini/GeneratorLF_ExoticResonances_pp1360.ini b/MC/config/PWGLF/ini/GeneratorLF_ExoticResonances_pp1360.ini new file mode 100644 index 000000000..abf18374a --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLF_ExoticResonances_pp1360.ini @@ -0,0 +1,10 @@ +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_LF_rapidity.C +funcName=generateLFRapidity("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/exoticresonancegun.json", true, 1, false, false, "${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_136tev.cfg", "") + +[GeneratorPythia8] # if triggered then this will be used as the background event +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_136tev.cfg + +[DecayerPythia8] # after for transport code! +config[0]=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/decayer/base.cfg +config[1]=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/resonances.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLF_HighPt.ini b/MC/config/PWGLF/ini/GeneratorLF_HighPt.ini new file mode 100644 index 000000000..4f1f554e3 --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLF_HighPt.ini @@ -0,0 +1,6 @@ +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_highpt.C +funcName = generateHighPt(-2212,5.0) + +[GeneratorPythia8] +config=${O2_ROOT}/share/Generators/egconfig/pythia8_inel.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLF_Lambda1520_pp.ini b/MC/config/PWGLF/ini/GeneratorLF_Lambda1520_pp.ini new file mode 100644 index 000000000..5f2eb7dca --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLF_Lambda1520_pp.ini @@ -0,0 +1,10 @@ +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_LF_rapidity.C +funcName=generateLFRapidity("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/lambda1520gun_inj.json", true, 1, false, false, "${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_136tev.cfg", "") + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_136tev.cfg + +[DecayerPythia8] +config[0]=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/decayer/base.cfg +config[1]=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/lambda1520.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLF_ResonancesBaryonic_NeNe_injection.ini b/MC/config/PWGLF/ini/GeneratorLF_ResonancesBaryonic_NeNe_injection.ini new file mode 100644 index 000000000..2646765a9 --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLF_ResonancesBaryonic_NeNe_injection.ini @@ -0,0 +1,10 @@ +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_LF_rapidity.C +funcName=generateLFRapidity("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/resonancelistgun_baryonic_inj.json", true, 4, false, false, "${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/generator/pythia8_NeNe_536.cfg", "") + +[GeneratorPythia8] # if triggered then this will be used as the background event +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/generator/pythia8_NeNe_536.cfg + +[DecayerPythia8] # after for transport code! +config[0]=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/decayer/base.cfg +config[1]=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/resonances_baryonic.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLF_ResonancesBaryonic_OO_injection.ini b/MC/config/PWGLF/ini/GeneratorLF_ResonancesBaryonic_OO_injection.ini new file mode 100644 index 000000000..ac4000cba --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLF_ResonancesBaryonic_OO_injection.ini @@ -0,0 +1,10 @@ +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_LF_rapidity.C +funcName=generateLFRapidity("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/resonancelistgun_baryonic_inj.json", true, 4, false, false, "${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/generator/pythia8_OO_536.cfg", "") + +[GeneratorPythia8] # if triggered then this will be used as the background event +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/generator/pythia8_OO_536.cfg + +[DecayerPythia8] # after for transport code! +config[0]=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/decayer/base.cfg +config[1]=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/resonances_baryonic.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLF_ResonancesBaryonic_pO_injection.ini b/MC/config/PWGLF/ini/GeneratorLF_ResonancesBaryonic_pO_injection.ini new file mode 100644 index 000000000..b85a88051 --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLF_ResonancesBaryonic_pO_injection.ini @@ -0,0 +1,10 @@ +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_LF_rapidity.C +funcName=generateLFRapidity("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/resonancelistgun_baryonic_inj.json", true, 4, false, false, "${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/generator/pythia8_pO_961.cfg", "") + +[GeneratorPythia8] # if triggered then this will be used as the background event +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/generator/pythia8_pO_961.cfg + +[DecayerPythia8] # after for transport code! +config[0]=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/decayer/base.cfg +config[1]=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/resonances_baryonic.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLF_ResonancesBaryonic_pp1360_injection.ini b/MC/config/PWGLF/ini/GeneratorLF_ResonancesBaryonic_pp1360_injection.ini new file mode 100644 index 000000000..7d9613efa --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLF_ResonancesBaryonic_pp1360_injection.ini @@ -0,0 +1,10 @@ +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_LF_rapidity.C +funcName=generateLFRapidity("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/resonancelistgun_baryonic_inj.json", true, 4, false, false, "${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_136tev.cfg", "") + +[GeneratorPythia8] # this will be used as the background event +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_136tev.cfg + +[DecayerPythia8] # after for transport code! +config[0]=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/decayer/base.cfg +config[1]=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/resonances_baryonic.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLF_ResonancesBaryonic_pp1360_rapidityinjection.ini b/MC/config/PWGLF/ini/GeneratorLF_ResonancesBaryonic_pp1360_rapidityinjection.ini new file mode 100644 index 000000000..3b27a5608 --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLF_ResonancesBaryonic_pp1360_rapidityinjection.ini @@ -0,0 +1,10 @@ +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_LF_rapidity.C +funcName=generateLFRapidity("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/resonancelistgun_baryonic_inj.json", true, 2, false, true, "${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_136tev.cfg", "") + +[GeneratorPythia8] # this will be used as the background event +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_136tev.cfg + +[DecayerPythia8] # after for transport code! +config[0]=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/decayer/base.cfg +config[1]=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/resonances_baryonic.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLF_ResonancesBaryonic_pp5360_injection.ini b/MC/config/PWGLF/ini/GeneratorLF_ResonancesBaryonic_pp5360_injection.ini new file mode 100644 index 000000000..779dfcf51 --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLF_ResonancesBaryonic_pp5360_injection.ini @@ -0,0 +1,10 @@ +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_LF_rapidity.C +funcName=generateLFRapidity("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/resonancelistgun_exoticAll.json", true, 2, false, true, "${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_pp536tev.cfg", "") + +[GeneratorPythia8] # if triggered then this will be used as the background event +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_pp536tev.cfg + +[DecayerPythia8] # after for transport code! +config[0]=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/decayer/base.cfg +config[1]=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/resonances.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLF_ResonancesML_K1Minus_KstarPi_pp1360.ini b/MC/config/PWGLF/ini/GeneratorLF_ResonancesML_K1Minus_KstarPi_pp1360.ini new file mode 100644 index 000000000..fe2129761 --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLF_ResonancesML_K1Minus_KstarPi_pp1360.ini @@ -0,0 +1,13 @@ +#---> GeneratorLF_ResonancesML_K1Plus_KstarPi_pp1360 +# Single-channel ML signal sample: one K1(1270)- per pp 13.6 TeV inelastic event (no gap), flat pT 0-8 GeV/c, +# |y| < 0.7, forced decay chain from resonance_ml_k1_kstarpi.cfg. One stratum (charge x channel) per job. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_LF_rapidity_width.C +funcName=generateLFRapidity("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/resonancegun_ml_k1minus_kstarpi.json", true, 0, false, true, "${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_136tev.cfg", "") + +[GeneratorPythia8] # if triggered then this will be used as the background event +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_136tev.cfg + +[DecayerPythia8] # after for transport code! +config[0]=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/decayer/base.cfg +config[1]=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/resonance_ml_k1_kstarpi.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLF_ResonancesML_K1Minus_KstarPi_pp5360.ini b/MC/config/PWGLF/ini/GeneratorLF_ResonancesML_K1Minus_KstarPi_pp5360.ini new file mode 100644 index 000000000..f886c011b --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLF_ResonancesML_K1Minus_KstarPi_pp5360.ini @@ -0,0 +1,13 @@ +#---> GeneratorLF_ResonancesML_K1Plus_KstarPi_pp1360 +# Single-channel ML signal sample: one K1(1270)- per pp 5.36 TeV inelastic event (no gap), flat pT 0-8 GeV/c, +# |y| < 0.7, forced decay chain from resonance_ml_k1_kstarpi.cfg. One stratum (charge x channel) per job. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_LF_rapidity_width.C +funcName=generateLFRapidity("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/resonancegun_ml_k1minus_kstarpi.json", true, 0, false, true, "${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_pp536tev.cfg", "") + +[GeneratorPythia8] # if triggered then this will be used as the background event +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_pp536tev.cfg + +[DecayerPythia8] # after for transport code! +config[0]=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/decayer/base.cfg +config[1]=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/resonance_ml_k1_kstarpi.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLF_ResonancesML_K1Minus_RhoK_pp1360.ini b/MC/config/PWGLF/ini/GeneratorLF_ResonancesML_K1Minus_RhoK_pp1360.ini new file mode 100644 index 000000000..30793f3c2 --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLF_ResonancesML_K1Minus_RhoK_pp1360.ini @@ -0,0 +1,13 @@ +#---> GeneratorLF_ResonancesML_K1Plus_RhoK_pp1360 +# Single-channel ML signal sample: one K1(1270)- per pp 13.6 TeV inelastic event (no gap), flat pT 0-8 GeV/c, +# |y| < 0.7, forced decay chain from resonance_ml_k1_rhok.cfg. One stratum (charge x channel) per job. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_LF_rapidity_width.C +funcName=generateLFRapidity("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/resonancegun_ml_k1minus_rhok.json", true, 0, false, true, "${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_136tev.cfg", "") + +[GeneratorPythia8] # if triggered then this will be used as the background event +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_136tev.cfg + +[DecayerPythia8] # after for transport code! +config[0]=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/decayer/base.cfg +config[1]=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/resonance_ml_k1_rhok.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLF_ResonancesML_K1Minus_RhoK_pp5360.ini b/MC/config/PWGLF/ini/GeneratorLF_ResonancesML_K1Minus_RhoK_pp5360.ini new file mode 100644 index 000000000..1aecef325 --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLF_ResonancesML_K1Minus_RhoK_pp5360.ini @@ -0,0 +1,13 @@ +#---> GeneratorLF_ResonancesML_K1Plus_RhoK_pp1360 +# Single-channel ML signal sample: one K1(1270)- per pp 5.36 TeV inelastic event (no gap), flat pT 0-8 GeV/c, +# |y| < 0.7, forced decay chain from resonance_ml_k1_rhok.cfg. One stratum (charge x channel) per job. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_LF_rapidity_width.C +funcName=generateLFRapidity("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/resonancegun_ml_k1minus_rhok.json", true, 0, false, true, "${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_pp536tev.cfg", "") + +[GeneratorPythia8] # if triggered then this will be used as the background event +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_pp536tev.cfg + +[DecayerPythia8] # after for transport code! +config[0]=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/decayer/base.cfg +config[1]=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/resonance_ml_k1_rhok.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLF_ResonancesML_K1Plus_KstarPi_pp1360.ini b/MC/config/PWGLF/ini/GeneratorLF_ResonancesML_K1Plus_KstarPi_pp1360.ini new file mode 100644 index 000000000..c3fae3e14 --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLF_ResonancesML_K1Plus_KstarPi_pp1360.ini @@ -0,0 +1,12 @@ +# Single-channel ML signal sample: one K1(1270)+ per pp 13.6 TeV inelastic event (no gap), flat pT 0-8 GeV/c, +# |y| < 0.7, forced decay chain from resonance_ml_k1_kstarpi.cfg. One stratum (charge x channel) per job. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_LF_rapidity_width.C +funcName=generateLFRapidity("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/resonancegun_ml_k1plus_kstarpi.json", true, 0, false, true, "${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_136tev.cfg", "") + +[GeneratorPythia8] # if triggered then this will be used as the background event +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_136tev.cfg + +[DecayerPythia8] # after for transport code! +config[0]=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/decayer/base.cfg +config[1]=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/resonance_ml_k1_kstarpi.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLF_ResonancesML_K1Plus_KstarPi_pp5360.ini b/MC/config/PWGLF/ini/GeneratorLF_ResonancesML_K1Plus_KstarPi_pp5360.ini new file mode 100644 index 000000000..92d9f6131 --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLF_ResonancesML_K1Plus_KstarPi_pp5360.ini @@ -0,0 +1,13 @@ +#---> GeneratorLF_ResonancesML_K1Plus_KstarPi_pp1360 +# Single-channel ML signal sample: one K1(1270)+ per pp 5.36 TeV inelastic event (no gap), flat pT 0-8 GeV/c, +# |y| < 0.7, forced decay chain from resonance_ml_k1_kstarpi.cfg. One stratum (charge x channel) per job. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_LF_rapidity_width.C +funcName=generateLFRapidity("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/resonancegun_ml_k1plus_kstarpi.json", true, 0, false, true, "${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_pp536tev.cfg", "") + +[GeneratorPythia8] # if triggered then this will be used as the background event +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_pp536tev.cfg + +[DecayerPythia8] # after for transport code! +config[0]=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/decayer/base.cfg +config[1]=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/resonance_ml_k1_kstarpi.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLF_ResonancesML_K1Plus_RhoK_pp1360.ini b/MC/config/PWGLF/ini/GeneratorLF_ResonancesML_K1Plus_RhoK_pp1360.ini new file mode 100644 index 000000000..e94f3b17e --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLF_ResonancesML_K1Plus_RhoK_pp1360.ini @@ -0,0 +1,12 @@ +# Single-channel ML signal sample: one K1(1270)+ per pp 13.6 TeV inelastic event (no gap), flat pT 0-8 GeV/c, +# |y| < 0.7, forced decay chain from resonance_ml_k1_rhok.cfg. One stratum (charge x channel) per job. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_LF_rapidity_width.C +funcName=generateLFRapidity("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/resonancegun_ml_k1plus_rhok.json", true, 0, false, true, "${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_136tev.cfg", "") + +[GeneratorPythia8] # if triggered then this will be used as the background event +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_136tev.cfg + +[DecayerPythia8] # after for transport code! +config[0]=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/decayer/base.cfg +config[1]=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/resonance_ml_k1_rhok.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLF_ResonancesML_K1Plus_RhoK_pp5360.ini b/MC/config/PWGLF/ini/GeneratorLF_ResonancesML_K1Plus_RhoK_pp5360.ini new file mode 100644 index 000000000..7f1c12b03 --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLF_ResonancesML_K1Plus_RhoK_pp5360.ini @@ -0,0 +1,13 @@ +#---> GeneratorLF_ResonancesML_K1Plus_RhoK_pp1360 +# Single-channel ML signal sample: one K1(1270)+ per pp 5.36 TeV inelastic event (no gap), flat pT 0-8 GeV/c, +# |y| < 0.7, forced decay chain from resonance_ml_k1_rhok.cfg. One stratum (charge x channel) per job. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_LF_rapidity_width.C +funcName=generateLFRapidity("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/resonancegun_ml_k1plus_rhok.json", true, 0, false, true, "${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_pp536tev.cfg", "") + +[GeneratorPythia8] # if triggered then this will be used as the background event +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_pp536tev.cfg + +[DecayerPythia8] # after for transport code! +config[0]=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/decayer/base.cfg +config[1]=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/resonance_ml_k1_rhok.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLF_ResonancesML_Omega2012Minus_Xi1530K_pp1360.ini b/MC/config/PWGLF/ini/GeneratorLF_ResonancesML_Omega2012Minus_Xi1530K_pp1360.ini new file mode 100644 index 000000000..1fc017a75 --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLF_ResonancesML_Omega2012Minus_Xi1530K_pp1360.ini @@ -0,0 +1,12 @@ +# Single-channel ML signal sample: one Omega(2012)- per pp 13.6 TeV inelastic event (no gap), flat pT 0-15 GeV/c, +# |y| < 0.7, forced decay chain from resonance_ml_omega2012_xi1530k.cfg. One stratum (charge x channel) per job. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_LF_rapidity_width.C +funcName=generateLFRapidity("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/resonancegun_ml_omega2012minus_xi1530k.json", true, 0, false, true, "${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_136tev.cfg", "") + +[GeneratorPythia8] # if triggered then this will be used as the background event +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_136tev.cfg + +[DecayerPythia8] # after for transport code! +config[0]=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/decayer/base.cfg +config[1]=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/resonance_ml_omega2012_xi1530k.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLF_ResonancesML_Omega2012Minus_Xi1530K_pp5360.ini b/MC/config/PWGLF/ini/GeneratorLF_ResonancesML_Omega2012Minus_Xi1530K_pp5360.ini new file mode 100644 index 000000000..cf1849986 --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLF_ResonancesML_Omega2012Minus_Xi1530K_pp5360.ini @@ -0,0 +1,13 @@ +#---> GeneratorLF_ResonancesML_Omega2012Minus_Xi1530K_pp1360 +# Single-channel ML signal sample: one Omega(2012)- per pp 5.36 TeV inelastic event (no gap), flat pT 0-15 GeV/c, +# |y| < 0.7, forced decay chain from resonance_ml_omega2012_xi1530k.cfg. One stratum (charge x channel) per job. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_LF_rapidity_width.C +funcName=generateLFRapidity("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/resonancegun_ml_omega2012minus_xi1530k.json", true, 0, false, true, "${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_pp536tev.cfg", "") + +[GeneratorPythia8] # if triggered then this will be used as the background event +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_pp536tev.cfg + +[DecayerPythia8] # after for transport code! +config[0]=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/decayer/base.cfg +config[1]=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/resonance_ml_omega2012_xi1530k.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLF_ResonancesML_Omega2012Minus_XiK0s_pp1360.ini b/MC/config/PWGLF/ini/GeneratorLF_ResonancesML_Omega2012Minus_XiK0s_pp1360.ini new file mode 100644 index 000000000..c711e9b4f --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLF_ResonancesML_Omega2012Minus_XiK0s_pp1360.ini @@ -0,0 +1,12 @@ +# Single-channel ML signal sample: one Omega(2012)- per pp 13.6 TeV inelastic event (no gap), flat pT 0-15 GeV/c, +# |y| < 0.7, forced decay chain from resonance_ml_omega2012_xik0s.cfg. One stratum (charge x channel) per job. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_LF_rapidity_width.C +funcName=generateLFRapidity("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/resonancegun_ml_omega2012minus_xik0s.json", true, 0, false, true, "${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_136tev.cfg", "") + +[GeneratorPythia8] # if triggered then this will be used as the background event +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_136tev.cfg + +[DecayerPythia8] # after for transport code! +config[0]=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/decayer/base.cfg +config[1]=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/resonance_ml_omega2012_xik0s.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLF_ResonancesML_Omega2012Minus_XiK0s_pp5360.ini b/MC/config/PWGLF/ini/GeneratorLF_ResonancesML_Omega2012Minus_XiK0s_pp5360.ini new file mode 100644 index 000000000..0956fa592 --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLF_ResonancesML_Omega2012Minus_XiK0s_pp5360.ini @@ -0,0 +1,13 @@ +#---> GeneratorLF_ResonancesML_Omega2012Minus_XiK0s_pp1360 +# Single-channel ML signal sample: one Omega(2012)- per pp 5.36 TeV inelastic event (no gap), flat pT 0-15 GeV/c, +# |y| < 0.7, forced decay chain from resonance_ml_omega2012_xik0s.cfg. One stratum (charge x channel) per job. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_LF_rapidity_width.C +funcName=generateLFRapidity("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/resonancegun_ml_omega2012minus_xik0s.json", true, 0, false, true, "${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_pp536tev.cfg", "") + +[GeneratorPythia8] # if triggered then this will be used as the background event +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_pp536tev.cfg + +[DecayerPythia8] # after for transport code! +config[0]=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/decayer/base.cfg +config[1]=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/resonance_ml_omega2012_xik0s.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLF_ResonancesML_Omega2012Plus_Xi1530K_pp1360.ini b/MC/config/PWGLF/ini/GeneratorLF_ResonancesML_Omega2012Plus_Xi1530K_pp1360.ini new file mode 100644 index 000000000..860aad35c --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLF_ResonancesML_Omega2012Plus_Xi1530K_pp1360.ini @@ -0,0 +1,13 @@ +#---> GeneratorLF_ResonancesML_Omega2012Minus_Xi1530K_pp1360 +# Single-channel ML signal sample: one Omega(2012)+ per pp 13.6 TeV inelastic event (no gap), flat pT 0-15 GeV/c, +# |y| < 0.7, forced decay chain from resonance_ml_omega2012_xi1530k.cfg. One stratum (charge x channel) per job. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_LF_rapidity_width.C +funcName=generateLFRapidity("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/resonancegun_ml_omega2012plus_xi1530k.json", true, 0, false, true, "${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_136tev.cfg", "") + +[GeneratorPythia8] # if triggered then this will be used as the background event +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_136tev.cfg + +[DecayerPythia8] # after for transport code! +config[0]=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/decayer/base.cfg +config[1]=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/resonance_ml_omega2012_xi1530k.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLF_ResonancesML_Omega2012Plus_Xi1530K_pp5360.ini b/MC/config/PWGLF/ini/GeneratorLF_ResonancesML_Omega2012Plus_Xi1530K_pp5360.ini new file mode 100644 index 000000000..fb32b1e09 --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLF_ResonancesML_Omega2012Plus_Xi1530K_pp5360.ini @@ -0,0 +1,13 @@ +#---> GeneratorLF_ResonancesML_Omega2012Minus_Xi1530K_pp1360 +# Single-channel ML signal sample: one Omega(2012)+ per pp 5.36 TeV inelastic event (no gap), flat pT 0-15 GeV/c, +# |y| < 0.7, forced decay chain from resonance_ml_omega2012_xi1530k.cfg. One stratum (charge x channel) per job. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_LF_rapidity_width.C +funcName=generateLFRapidity("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/resonancegun_ml_omega2012plus_xi1530k.json", true, 0, false, true, "${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_pp536tev.cfg", "") + +[GeneratorPythia8] # if triggered then this will be used as the background event +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_pp536tev.cfg + +[DecayerPythia8] # after for transport code! +config[0]=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/decayer/base.cfg +config[1]=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/resonance_ml_omega2012_xi1530k.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLF_ResonancesML_Omega2012Plus_XiK0s_pp1360.ini b/MC/config/PWGLF/ini/GeneratorLF_ResonancesML_Omega2012Plus_XiK0s_pp1360.ini new file mode 100644 index 000000000..f033dffee --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLF_ResonancesML_Omega2012Plus_XiK0s_pp1360.ini @@ -0,0 +1,13 @@ +#---> GeneratorLF_ResonancesML_Omega2012Minus_XiK0s_pp1360 +# Single-channel ML signal sample: one Omega(2012)+ per pp 13.6 TeV inelastic event (no gap), flat pT 0-15 GeV/c, +# |y| < 0.7, forced decay chain from resonance_ml_omega2012_xik0s.cfg. One stratum (charge x channel) per job. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_LF_rapidity_width.C +funcName=generateLFRapidity("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/resonancegun_ml_omega2012plus_xik0s.json", true, 0, false, true, "${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_136tev.cfg", "") + +[GeneratorPythia8] # if triggered then this will be used as the background event +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_136tev.cfg + +[DecayerPythia8] # after for transport code! +config[0]=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/decayer/base.cfg +config[1]=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/resonance_ml_omega2012_xik0s.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLF_ResonancesML_Omega2012Plus_XiK0s_pp5360.ini b/MC/config/PWGLF/ini/GeneratorLF_ResonancesML_Omega2012Plus_XiK0s_pp5360.ini new file mode 100644 index 000000000..d3bb9a3cf --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLF_ResonancesML_Omega2012Plus_XiK0s_pp5360.ini @@ -0,0 +1,13 @@ +#---> GeneratorLF_ResonancesML_Omega2012Minus_XiK0s_pp1360 +# Single-channel ML signal sample: one Omega(2012)+ per pp 5.36 TeV inelastic event (no gap), flat pT 0-15 GeV/c, +# |y| < 0.7, forced decay chain from resonance_ml_omega2012_xik0s.cfg. One stratum (charge x channel) per job. +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_LF_rapidity_width.C +funcName=generateLFRapidity("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/resonancegun_ml_omega2012plus_xik0s.json", true, 0, false, true, "${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_pp536tev.cfg", "") + +[GeneratorPythia8] # if triggered then this will be used as the background event +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_pp536tev.cfg + +[DecayerPythia8] # after for transport code! +config[0]=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/decayer/base.cfg +config[1]=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/resonance_ml_omega2012_xik0s.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLF_Resonances_PbPb.ini b/MC/config/PWGLF/ini/GeneratorLF_Resonances_PbPb.ini new file mode 100644 index 000000000..0c568d47c --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLF_Resonances_PbPb.ini @@ -0,0 +1,10 @@ +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_LF_rapidity.C +funcName=generateLFRapidity("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/resonancelistgun_trig.json", true, 4, true, false, "${O2_ROOT}/share/Generators/egconfig/pythia8_hi.cfg", "${O2_ROOT}/share/Generators/egconfig/pythia8_hi.cfg") + +[GeneratorPythia8] +config=${O2_ROOT}/share/Generators/egconfig/pythia8_hi.cfg + +[DecayerPythia8] # after for transport code! +config[0]=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/decayer/base.cfg +config[1]=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/resonances.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLF_Resonances_PbPb5360_injection.ini b/MC/config/PWGLF/ini/GeneratorLF_Resonances_PbPb5360_injection.ini new file mode 100644 index 000000000..7bce5d0b5 --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLF_Resonances_PbPb5360_injection.ini @@ -0,0 +1,10 @@ +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_LF_rapidity.C +funcName=generateLFRapidity("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/resonancelistgun_inj_pbpb.json", true, 1, false, false, "${O2_ROOT}/share/Generators/egconfig/pythia8_hi.cfg", "") + +[GeneratorPythia8] +config=${O2_ROOT}/share/Generators/egconfig/pythia8_hi.cfg + +[DecayerPythia8] # after for transport code! +config[0]=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/decayer/base.cfg +config[1]=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/resonances.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLF_Resonances_PbPb_exotic.ini b/MC/config/PWGLF/ini/GeneratorLF_Resonances_PbPb_exotic.ini new file mode 100644 index 000000000..8c3471b4f --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLF_Resonances_PbPb_exotic.ini @@ -0,0 +1,10 @@ +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_LF_rapidity.C +funcName=generateLFRapidity("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/resonancelistgun_exotic_pbpb.json", true, 4, false, false, "${O2_ROOT}/share/Generators/egconfig/pythia8_hi.cfg", "") + +[GeneratorPythia8] +config=${O2_ROOT}/share/Generators/egconfig/pythia8_hi.cfg + +[DecayerPythia8] # after for transport code! +config[0]=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/decayer/base.cfg +config[1]=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/resonances.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLF_Resonances_pp.ini b/MC/config/PWGLF/ini/GeneratorLF_Resonances_pp.ini index 073adad06..543c0d7b2 100644 --- a/MC/config/PWGLF/ini/GeneratorLF_Resonances_pp.ini +++ b/MC/config/PWGLF/ini/GeneratorLF_Resonances_pp.ini @@ -1,10 +1,10 @@ [GeneratorExternal] -fileName=${O2DPG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_LF.C -funcName=generateLF("${O2DPG_ROOT}/MC/config/PWGLF/pythia8/generator/resonancelist.gun") +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_LF_rapidity.C +funcName=generateLFRapidity("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/resonancelistgun_trig.json", true, 4, true, false, "${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_136tev.cfg", "${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_136tev.cfg") -# [GeneratorPythia8] # before for transport code! -# config=${O2DPG_ROOT}/MC/config/PWGLF/pythia8/generator/resonances.cfg +[GeneratorPythia8] # if triggered then this will be used as the background event +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_136tev.cfg [DecayerPythia8] # after for transport code! -config[0]=${O2DPG_ROOT}/MC/config/common/pythia8/decayer/base.cfg -config[1]=${O2DPG_ROOT}/MC/config/PWGLF/pythia8/generator/resonances.cfg \ No newline at end of file +config[0]=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/decayer/base.cfg +config[1]=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/resonances.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLF_Resonances_pp1360.ini b/MC/config/PWGLF/ini/GeneratorLF_Resonances_pp1360.ini new file mode 100644 index 000000000..543c0d7b2 --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLF_Resonances_pp1360.ini @@ -0,0 +1,10 @@ +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_LF_rapidity.C +funcName=generateLFRapidity("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/resonancelistgun_trig.json", true, 4, true, false, "${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_136tev.cfg", "${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_136tev.cfg") + +[GeneratorPythia8] # if triggered then this will be used as the background event +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_136tev.cfg + +[DecayerPythia8] # after for transport code! +config[0]=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/decayer/base.cfg +config[1]=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/resonances.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLF_Resonances_pp1360_injection.ini b/MC/config/PWGLF/ini/GeneratorLF_Resonances_pp1360_injection.ini new file mode 100644 index 000000000..c8ad07a90 --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLF_Resonances_pp1360_injection.ini @@ -0,0 +1,10 @@ +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_LF_rapidity.C +funcName=generateLFRapidity("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/resonancelistgun_inj.json", true, 1, false, false, "${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_136tev.cfg", "") + +[GeneratorPythia8] # if triggered then this will be used as the background event +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_136tev.cfg + +[DecayerPythia8] # after for transport code! +config[0]=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/decayer/base.cfg +config[1]=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/resonances.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLF_Resonances_pp5360_Rare.ini b/MC/config/PWGLF/ini/GeneratorLF_Resonances_pp5360_Rare.ini new file mode 100644 index 000000000..f39d7ab4f --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLF_Resonances_pp5360_Rare.ini @@ -0,0 +1,10 @@ +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_LF_rapidity_width.C +funcName=generateLFRapidity("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/resonancelistgun_width_inj.json", true, 5, false, true, "${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_pp536tev.cfg", "") + +[GeneratorPythia8] # if triggered then this will be used as the background event +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_pp536tev.cfg + +[DecayerPythia8] # after for transport code! +config[0]=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/decayer/base.cfg +config[1]=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/resonances_width.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLF_Resonances_pp900.ini b/MC/config/PWGLF/ini/GeneratorLF_Resonances_pp900.ini new file mode 100644 index 000000000..e1895740b --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLF_Resonances_pp900.ini @@ -0,0 +1,10 @@ +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_LF_rapidity.C +funcName=generateLFRapidity("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/resonancelistgun_trig.json", true, 4, true, false, "${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_pp900gev.cfg", "${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_pp900gev.cfg") + +[GeneratorPythia8] # if triggered then this will be used as the background event +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_pp900gev.cfg + +[DecayerPythia8] # after for transport code! +config[0]=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/decayer/base.cfg +config[1]=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/resonances.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLF_Resonances_pp900_injection.ini b/MC/config/PWGLF/ini/GeneratorLF_Resonances_pp900_injection.ini new file mode 100644 index 000000000..d172f034f --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLF_Resonances_pp900_injection.ini @@ -0,0 +1,10 @@ +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_LF_rapidity.C +funcName=generateLFRapidity("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/resonancelistgun_inj.json", true, 4, false, false, "${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_pp900gev.cfg", "") + +[GeneratorPythia8] # if triggered then this will be used as the background event +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_pp900gev.cfg + +[DecayerPythia8] # after for transport code! +config[0]=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/decayer/base.cfg +config[1]=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/resonances.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLF_Resonances_pp_Rare.ini b/MC/config/PWGLF/ini/GeneratorLF_Resonances_pp_Rare.ini new file mode 100644 index 000000000..4c9fbc777 --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLF_Resonances_pp_Rare.ini @@ -0,0 +1,10 @@ +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_LF_rapidity_width.C +funcName=generateLFRapidity("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/resonancelistgun_width_inj.json", true, 5, false, true, "${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_136tev.cfg", "") + +[GeneratorPythia8] # if triggered then this will be used as the background event +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_136tev.cfg + +[DecayerPythia8] # after for transport code! +config[0]=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/decayer/base.cfg +config[1]=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/resonances_width.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLF_Resonances_pp_exotic.ini b/MC/config/PWGLF/ini/GeneratorLF_Resonances_pp_exotic.ini new file mode 100644 index 000000000..e520191e7 --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLF_Resonances_pp_exotic.ini @@ -0,0 +1,10 @@ +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_LF_rapidity.C +funcName=generateLFRapidity("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/gluelistgun.json", true, 2, false, true, "${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_136tev.cfg", "") + +[GeneratorPythia8] # if triggered then this will be used as the background event +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_136tev.cfg + +[DecayerPythia8] # after for transport code! +config[0]=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/decayer/base.cfg +config[1]=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/resonances.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLF_Resonances_pp_exoticAll.ini b/MC/config/PWGLF/ini/GeneratorLF_Resonances_pp_exoticAll.ini new file mode 100644 index 000000000..64d174b68 --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLF_Resonances_pp_exoticAll.ini @@ -0,0 +1,10 @@ +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_LF_rapidity.C +funcName=generateLFRapidity("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/resonancelistgun_exoticAll.json", true, 4, false, false, "${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_136tev.cfg", "") + +[GeneratorPythia8] # if triggered then this will be used as the background event +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_136tev.cfg + +[DecayerPythia8] # after for transport code! +config[0]=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/decayer/base.cfg +config[1]=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/resonances.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLF_Resonances_pp_exoticAll_gap2.ini b/MC/config/PWGLF/ini/GeneratorLF_Resonances_pp_exoticAll_gap2.ini new file mode 100644 index 000000000..e55c3195c --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLF_Resonances_pp_exoticAll_gap2.ini @@ -0,0 +1,10 @@ +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_LF_rapidity.C +funcName=generateLFRapidity("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/resonancelistgun_exoticAll.json", true, 2, false, true, "${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_136tev.cfg", "") + +[GeneratorPythia8] # this will be used as the background event +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_136tev.cfg + +[DecayerPythia8] # after for transport code! +config[0]=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/decayer/base.cfg +config[1]=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/resonances.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLF_StrangenessInJets_gap4.ini b/MC/config/PWGLF/ini/GeneratorLF_StrangenessInJets_gap4.ini new file mode 100644 index 000000000..80b0235b8 --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLF_StrangenessInJets_gap4.ini @@ -0,0 +1,6 @@ +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_strangeness_in_jets.C +funcName = generateStrangenessInJets(10.0,0.4,4) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_jet_ropes_136tev.cfg \ No newline at end of file diff --git a/MC/config/PWGLF/ini/GeneratorLF_Strangeness_OO5360_injection.ini b/MC/config/PWGLF/ini/GeneratorLF_Strangeness_OO5360_injection.ini new file mode 100644 index 000000000..33df39f1b --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLF_Strangeness_OO5360_injection.ini @@ -0,0 +1,6 @@ +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_extraStrangeness.C +funcName=generator_extraStrangeness(5,30) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/generator/pythia8_OO_536.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLF_Strangeness_OO5360_injection_omega100.ini b/MC/config/PWGLF/ini/GeneratorLF_Strangeness_OO5360_injection_omega100.ini new file mode 100644 index 000000000..326f7e6c8 --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLF_Strangeness_OO5360_injection_omega100.ini @@ -0,0 +1,6 @@ +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_extraStrangeness.C +funcName=generator_extraStrangeness(10,100) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/generator/pythia8_OO_536.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLF_Strangeness_OO5360_injection_omega30.ini b/MC/config/PWGLF/ini/GeneratorLF_Strangeness_OO5360_injection_omega30.ini new file mode 100644 index 000000000..ea7fa77a8 --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLF_Strangeness_OO5360_injection_omega30.ini @@ -0,0 +1,6 @@ +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_extraStrangeness.C +funcName=generator_extraStrangeness(10,30) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/generator/pythia8_OO_536.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLF_Strangeness_OO5360_injection_omega300.ini b/MC/config/PWGLF/ini/GeneratorLF_Strangeness_OO5360_injection_omega300.ini new file mode 100644 index 000000000..2d7273ab3 --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLF_Strangeness_OO5360_injection_omega300.ini @@ -0,0 +1,6 @@ +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_extraStrangeness.C +funcName=generator_extraStrangeness(10,300) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/generator/pythia8_OO_536.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLF_Strangeness_OO5360_injection_omega50.ini b/MC/config/PWGLF/ini/GeneratorLF_Strangeness_OO5360_injection_omega50.ini new file mode 100644 index 000000000..9b9b041ac --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLF_Strangeness_OO5360_injection_omega50.ini @@ -0,0 +1,6 @@ +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_extraStrangeness.C +funcName=generator_extraStrangeness(10,50) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/generator/pythia8_OO_536.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLF_Strangeness_OO5360_injection_omega500.ini b/MC/config/PWGLF/ini/GeneratorLF_Strangeness_OO5360_injection_omega500.ini new file mode 100644 index 000000000..d3b36a244 --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLF_Strangeness_OO5360_injection_omega500.ini @@ -0,0 +1,6 @@ +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_extraStrangeness.C +funcName=generator_extraStrangeness(10,500) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/generator/pythia8_OO_536.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLF_Strangeness_PbPb5360_injection.ini b/MC/config/PWGLF/ini/GeneratorLF_Strangeness_PbPb5360_injection.ini new file mode 100644 index 000000000..9363209a9 --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLF_Strangeness_PbPb5360_injection.ini @@ -0,0 +1,6 @@ +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_extraStrangeness.C +funcName=generator_extraStrangeness() + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/generator/pythia8_hi.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLF_Strangeness_ppLStar.ini b/MC/config/PWGLF/ini/GeneratorLF_Strangeness_ppLStar.ini new file mode 100644 index 000000000..5f3809903 --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLF_Strangeness_ppLStar.ini @@ -0,0 +1,6 @@ +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_Lstar.C +funcName=generator_Lstar(5) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_forceLStar.cfg \ No newline at end of file diff --git a/MC/config/PWGLF/ini/GeneratorLF_SyntheFlow.ini b/MC/config/PWGLF/ini/GeneratorLF_SyntheFlow.ini new file mode 100644 index 000000000..d37cb4d75 --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLF_SyntheFlow.ini @@ -0,0 +1,6 @@ +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_syntheFlow.C +funcName=generator_syntheFlow() + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/generator/pythia8_hi.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLF_SyntheFlowOO.ini b/MC/config/PWGLF/ini/GeneratorLF_SyntheFlowOO.ini new file mode 100644 index 000000000..ee52a0a61 --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLF_SyntheFlowOO.ini @@ -0,0 +1,6 @@ +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_syntheFlowOO.C +funcName=generator_syntheFlowOO() + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/generator/pythia8_OO_536.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLF_SyntheFlowXi.ini b/MC/config/PWGLF/ini/GeneratorLF_SyntheFlowXi.ini new file mode 100644 index 000000000..4752520bf --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLF_SyntheFlowXi.ini @@ -0,0 +1,6 @@ +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_syntheFlowXi.C +funcName=generator_syntheFlowXi() + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/generator/pythia8_hi.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLF_antid_and_highpt.ini b/MC/config/PWGLF/ini/GeneratorLF_antid_and_highpt.ini new file mode 100644 index 000000000..9e76aba57 --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLF_antid_and_highpt.ini @@ -0,0 +1,6 @@ +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_antid_and_highpt.C +funcName = generateAntidAndHighPt(0.3,5.0) + +[GeneratorPythia8] +config=${O2_ROOT}/share/Generators/egconfig/pythia8_inel.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLF_deuteron_wigner.ini b/MC/config/PWGLF/ini/GeneratorLF_deuteron_wigner.ini new file mode 100644 index 000000000..1f12b9d03 --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLF_deuteron_wigner.ini @@ -0,0 +1,6 @@ +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_deuteron_wigner.C +funcName = generateAntideuteronsWignerCoalescence(1.2) + +[GeneratorPythia8] +config=${O2_ROOT}/share/Generators/egconfig/pythia8_inel.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLF_doublephi_trigger.ini b/MC/config/PWGLF/ini/GeneratorLF_doublephi_trigger.ini new file mode 100644 index 000000000..6f1dcd71a --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLF_doublephi_trigger.ini @@ -0,0 +1,10 @@ +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_twophi_trigger.C +funcName=generateDoublePhi(0, 0.0, 100.0, 0.8) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_136tev.cfg + +[DecayerPythia8] +config[0]=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/decayer/base.cfg +config[1]=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/resonances.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLF_doublephi_triggerMasspTcut.ini b/MC/config/PWGLF/ini/GeneratorLF_doublephi_triggerMasspTcut.ini new file mode 100644 index 000000000..753c01d31 --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLF_doublephi_triggerMasspTcut.ini @@ -0,0 +1,10 @@ +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_twophi_triggerMassCut.C +funcName=generateDoublePhi(0, 0.0, 100.0, 0.8, 2.4, 6.0) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_136tev.cfg + +[DecayerPythia8] +config[0]=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/decayer/base.cfg +config[1]=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/resonances.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLF_highpt_strangeness.ini b/MC/config/PWGLF/ini/GeneratorLF_highpt_strangeness.ini new file mode 100644 index 000000000..e53c6e959 --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLF_highpt_strangeness.ini @@ -0,0 +1,6 @@ +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_highpt_strangeness.C +funcName = generateHighPtAndStrangeHadron(5.0) + +[GeneratorPythia8] +config=${O2_ROOT}/share/Generators/egconfig/pythia8_inel.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLF_phiphiInvMass.ini b/MC/config/PWGLF/ini/GeneratorLF_phiphiInvMass.ini new file mode 100644 index 000000000..e74204392 --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLF_phiphiInvMass.ini @@ -0,0 +1,10 @@ +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_phiphi.C +funcName=generatePhiResonanceGun(999999, 0.0, 50.0, -1.0, 1.0, "${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_136tev.cfg", 0) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_136tev.cfg + +[DecayerPythia8] +config[0]=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/decayer/base.cfg +config[1]=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/resonances.cfg diff --git a/MC/config/PWGLF/ini/GeneratorLF_pp1360_wDeCoal.ini b/MC/config/PWGLF/ini/GeneratorLF_pp1360_wDeCoal.ini new file mode 100644 index 000000000..83f8c26d7 --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLF_pp1360_wDeCoal.ini @@ -0,0 +1,6 @@ +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator_pythia8_ALICE3.C +funcName=generator_pythia8_ALICE3() + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_136tev_wDeCoal.cfg \ No newline at end of file diff --git a/MC/config/PWGLF/ini/GeneratorLF_pp1360_wDeInel.ini b/MC/config/PWGLF/ini/GeneratorLF_pp1360_wDeInel.ini new file mode 100644 index 000000000..cd07f445d --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLF_pp1360_wDeInel.ini @@ -0,0 +1,6 @@ +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator_pythia8_ALICE3.C +funcName=generator_pythia8_ALICE3() + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_136tev_wDeInel.cfg \ No newline at end of file diff --git a/MC/config/PWGLF/ini/GeneratorLF_pp536_Ropes.ini b/MC/config/PWGLF/ini/GeneratorLF_pp536_Ropes.ini new file mode 100644 index 000000000..2bfa5df9f --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLF_pp536_Ropes.ini @@ -0,0 +1,6 @@ +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator_pythia8_ALICE3.C +funcName=generator_pythia8_ALICE3() + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_ropes_536tev.cfg \ No newline at end of file diff --git a/MC/config/PWGLF/ini/GeneratorLF_pp536_wDeCoal.ini b/MC/config/PWGLF/ini/GeneratorLF_pp536_wDeCoal.ini new file mode 100644 index 000000000..126aa66ca --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLF_pp536_wDeCoal.ini @@ -0,0 +1,6 @@ +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator_pythia8_ALICE3.C +funcName=generator_pythia8_ALICE3() + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_536tev_wDeCoal.cfg \ No newline at end of file diff --git a/MC/config/PWGLF/ini/GeneratorLF_pp536_wDeInel.ini b/MC/config/PWGLF/ini/GeneratorLF_pp536_wDeInel.ini new file mode 100644 index 000000000..10d233002 --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorLF_pp536_wDeInel.ini @@ -0,0 +1,6 @@ +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator_pythia8_ALICE3.C +funcName=generator_pythia8_ALICE3() + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_536tev_wDeInel.cfg \ No newline at end of file diff --git a/MC/config/PWGLF/ini/GeneratorSigmaProtonTriggered.ini b/MC/config/PWGLF/ini/GeneratorSigmaProtonTriggered.ini new file mode 100644 index 000000000..4443d2070 --- /dev/null +++ b/MC/config/PWGLF/ini/GeneratorSigmaProtonTriggered.ini @@ -0,0 +1,5 @@ +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_sigma_hadron.C +funcName=generateSigmaHadron(2212, 2, 1, 0.5, 10, 0.5) +[GeneratorPythia8] +config=${O2_ROOT}/share/Generators/egconfig/pythia8_inel.cfg \ No newline at end of file diff --git a/MC/config/PWGLF/ini/generatorLF_Resonances_flat_mass.ini b/MC/config/PWGLF/ini/generatorLF_Resonances_flat_mass.ini new file mode 100644 index 000000000..18d62cb10 --- /dev/null +++ b/MC/config/PWGLF/ini/generatorLF_Resonances_flat_mass.ini @@ -0,0 +1,10 @@ +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_LF_flat_mass.C +funcName=generateLFRapidity("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/flatMCgun.json", true, 0, false, true, "${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_136tev.cfg", "") + +[GeneratorPythia8] # Background event config +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_136tev.cfg + +[DecayerPythia8] # Transport decayer config +config[0]=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/decayer/base.cfg +config[1]=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/resonances.cfg \ No newline at end of file diff --git a/MC/config/PWGLF/ini/tests/GeneratorAntideuteronCoalescenceJet.C b/MC/config/PWGLF/ini/tests/GeneratorAntideuteronCoalescenceJet.C new file mode 100644 index 000000000..f456070a6 --- /dev/null +++ b/MC/config/PWGLF/ini/tests/GeneratorAntideuteronCoalescenceJet.C @@ -0,0 +1,85 @@ + +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include "SimulationDataFormat/MCTrack.h" +#include "TFile.h" +#include "TTree.h" + +#include +#include +#include +#include +#endif // !defined(__CLING__) || defined(__ROOTCLING__) + +int External() +{ + std::string path{"o2sim_Kine.root"}; + + std::vector injectedPDGs = { + -1000010020 // anti-deuteron + }; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto* tree = (TTree*)file.Get("o2sim"); + if (!tree) { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + + std::vector* tracks = nullptr; + tree->SetBranchAddress("MCTrack", &tracks); + + std::vector nSignal(injectedPDGs.size(), 0); + std::vector nEventsWithSignal(injectedPDGs.size(), 0); + + const auto nEvents = tree->GetEntries(); + + for (Long64_t i = 0; i < nEvents; ++i) { + tree->GetEntry(i); + + std::vector found(injectedPDGs.size(), false); + + for (const auto& track : *tracks) { + const int pdg = track.GetPdgCode(); + + auto it = std::find( + injectedPDGs.begin(), + injectedPDGs.end(), + pdg); + + if (it != injectedPDGs.end()) { + const auto index = std::distance(injectedPDGs.begin(), it); + ++nSignal[index]; + found[index] = true; + } + } + + for (size_t j = 0; j < injectedPDGs.size(); ++j) { + if (found[j]) { + ++nEventsWithSignal[j]; + } + } + } + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + + for (size_t i = 0; i < injectedPDGs.size(); ++i) { + std::cout << "PDG: " << injectedPDGs[i] << "\n"; + std::cout << "# Generated particles: " << nSignal[i] << "\n"; + std::cout << "# Events containing particle: " + << nEventsWithSignal[i] << "\n"; + + if (nSignal[i] == 0) { + std::cerr << "No generated particles with PDG " + << injectedPDGs[i] << "\n"; + return 1; + } + } + + return 0; +} diff --git a/MC/config/PWGLF/ini/tests/GeneratorDoubleLambdaTriggered.C b/MC/config/PWGLF/ini/tests/GeneratorDoubleLambdaTriggered.C new file mode 100644 index 000000000..96fcd8bbd --- /dev/null +++ b/MC/config/PWGLF/ini/tests/GeneratorDoubleLambdaTriggered.C @@ -0,0 +1,25 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + auto nLambda = tree->Scan("MCTrack.GetPdgCode()", "TMath::Abs(MCTrack.GetPdgCode()) == 3122"); + + if (nLambda == 0) { + std::cerr << "No event of interest\n"; + return 1; + } + return 0; +} diff --git a/MC/config/PWGLF/ini/tests/GeneratorEPOS4_pp536TeV.C b/MC/config/PWGLF/ini/tests/GeneratorEPOS4_pp536TeV.C new file mode 100644 index 000000000..5f9432f9e --- /dev/null +++ b/MC/config/PWGLF/ini/tests/GeneratorEPOS4_pp536TeV.C @@ -0,0 +1,64 @@ +int External() +{ + std::string path{"o2sim_Kine.root"}; + // Check that file exists, can be opened and has the correct tree + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) + { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) + { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + // Check if all events are filled + auto nEvents = tree->GetEntries(); + for (Long64_t i = 0; i < nEvents; ++i) + { + tree->GetEntry(i); + if (tracks->empty()) + { + std::cerr << "Empty entry found at event " << i << "\n"; + return 1; + } + } + // Check if there are 100 events, as simulated in the o2dpg-test + if (nEvents != 100) + { + std::cerr << "Expected 100 events, got " << nEvents << "\n"; + return 1; + } + // check if each event has two protons with 2680 GeV of energy + // exits if the particle is not a proton + for (int i = 0; i < nEvents; i++) + { + auto check = tree->GetEntry(i); + int count = 0; + for (int idxMCTrack = 0; idxMCTrack < tracks->size(); ++idxMCTrack) + { + auto track = tracks->at(idxMCTrack); + double energy = track.GetEnergy(); + // Check if track energy is approximately equal to 2680 GeV (a tolerance of 65 keV is considered, straight equality does not work due to floating point precision) + if (std::abs(energy - 2680) < 1e-4) + { + if (track.GetPdgCode() != 2212){ + std::cerr << "Found 2680 GeV particle with pdgID " << track.GetPdgCode() << "\n"; + return 1; + } + count++; + } + } + if (count < 2) + { + std::cerr << "Event " << i << " has less than 2 protons at 2680 GeV\n"; + return 1; + } + } + return 0; +} diff --git a/MC/config/PWGLF/ini/tests/GeneratorLFAntideuteronInJets.C b/MC/config/PWGLF/ini/tests/GeneratorLFAntideuteronInJets.C new file mode 100644 index 000000000..148a08c58 --- /dev/null +++ b/MC/config/PWGLF/ini/tests/GeneratorLFAntideuteronInJets.C @@ -0,0 +1,61 @@ +int External() +{ + std::string path{"o2sim_Kine.root"}; + int numberOfInjectedSignalsPerEvent{1}; + + std::vector injectedPDGs = { + -1000010020 // anti-deuteron + }; + + auto nInjection = injectedPDGs.size(); + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree*)file.Get("o2sim"); + if (!tree) { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + std::vector nSignal(nInjection, 0); + + auto nEvents = tree->GetEntries(); + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + + for (int idxMCTrack = 0; idxMCTrack < tracks->size(); ++idxMCTrack) { + auto track = tracks->at(idxMCTrack); + auto pdg = track.GetPdgCode(); + + auto it = std::find(injectedPDGs.begin(), injectedPDGs.end(), pdg); + + if (it != injectedPDGs.end()) { + int index = std::distance(injectedPDGs.begin(), it); + nSignal[index]++; + } + } + } + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + + for (int i = 0; i < nInjection; i++) { + std::cout << "# Injected particles\n"; + std::cout << injectedPDGs[i] << ": " << nSignal[i] << "\n"; + + if (nSignal[i] == 0) { + std::cerr << "No generated: " << injectedPDGs[i] << "\n"; + return 1; + } + } + + return 0; +} diff --git a/MC/config/PWGLF/ini/tests/GeneratorLFCascadesInJets_pp536TeV.C b/MC/config/PWGLF/ini/tests/GeneratorLFCascadesInJets_pp536TeV.C new file mode 100644 index 000000000..49ba5d4e2 --- /dev/null +++ b/MC/config/PWGLF/ini/tests/GeneratorLFCascadesInJets_pp536TeV.C @@ -0,0 +1,58 @@ +int External() +{ + std::string path{"o2sim_Kine.root"}; + int numberOfInjectedSignalsPerEvent{1}; + std::vector injectedPDGs = { + 3334, + -3334, + 3312, + -3312}; + + auto nInjection = injectedPDGs.size(); + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree*)file.Get("o2sim"); + if (!tree) { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + std::vector nSignal; + for (int i = 0; i < nInjection; i++) { + nSignal.push_back(0); + } + + auto nEvents = tree->GetEntries(); + for (int i = 0; i < nEvents; i++) { + auto check = tree->GetEntry(i); + for (int idxMCTrack = 0; idxMCTrack < tracks->size(); ++idxMCTrack) { + auto track = tracks->at(idxMCTrack); + auto pdg = track.GetPdgCode(); + auto it = std::find(injectedPDGs.begin(), injectedPDGs.end(), pdg); + int index = std::distance(injectedPDGs.begin(), it); // index of injected PDG + if (it != injectedPDGs.end()) // found + { + // count signal PDG + nSignal[index]++; + } + } + } + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + for (int i = 0; i < nInjection; i++) { + std::cout << "# Injected nuclei \n"; + std::cout << injectedPDGs[i] << ": " << nSignal[i] << "\n"; + if (nSignal[i] == 0) { + std::cerr << "No generated: " << injectedPDGs[i] << "\n"; + return 1; // At least one of the injected particles should be generated + } + } + return 0; +} \ No newline at end of file diff --git a/MC/config/PWGLF/ini/tests/GeneratorLFDeuteronOOGap.C b/MC/config/PWGLF/ini/tests/GeneratorLFDeuteronOOGap.C new file mode 100644 index 000000000..6d270f811 --- /dev/null +++ b/MC/config/PWGLF/ini/tests/GeneratorLFDeuteronOOGap.C @@ -0,0 +1,53 @@ +int External() +{ + std::string path{"o2sim_Kine.root"}; + std::vector possiblePDGs = {1000010020, -1000010020}; + + int nPossiblePDGs = possiblePDGs.size(); + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) + { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) + { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + std::vector injectedPDGs; + + auto nEvents = tree->GetEntries(); + for (int i = 0; i < nEvents; i++) + { + auto check = tree->GetEntry(i); + for (int idxMCTrack = 0; idxMCTrack < tracks->size(); ++idxMCTrack) + { + auto track = tracks->at(idxMCTrack); + auto pdg = track.GetPdgCode(); + auto it = std::find(possiblePDGs.begin(), possiblePDGs.end(), pdg); + if (it != possiblePDGs.end() && track.isPrimary()) // found + { + injectedPDGs.push_back(pdg); + } + } + } + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + if(injectedPDGs.empty()){ + std::cerr << "No injected particles\n"; + return 1; // At least one of the injected particles should be generated + } + for (int i = 0; i < nPossiblePDGs; i++) + { + std::cout << "# Injected nuclei \n"; + std::cout << possiblePDGs[i] << ": " << std::count(injectedPDGs.begin(), injectedPDGs.end(), possiblePDGs[i]) << "\n"; + } + return 0; +} diff --git a/MC/config/PWGLF/ini/tests/GeneratorLFDoubleOmegaPbPb.C b/MC/config/PWGLF/ini/tests/GeneratorLFDoubleOmegaPbPb.C new file mode 100644 index 000000000..4b9f6f898 --- /dev/null +++ b/MC/config/PWGLF/ini/tests/GeneratorLFDoubleOmegaPbPb.C @@ -0,0 +1,53 @@ +int External() +{ + std::string path{"o2sim_Kine.root"}; + std::vector possiblePDGs = {1060020020, -1060020020}; + + int nPossiblePDGs = possiblePDGs.size(); + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) + { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) + { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + std::vector injectedPDGs; + + auto nEvents = tree->GetEntries(); + for (int i = 0; i < nEvents; i++) + { + auto check = tree->GetEntry(i); + for (int idxMCTrack = 0; idxMCTrack < tracks->size(); ++idxMCTrack) + { + auto track = tracks->at(idxMCTrack); + auto pdg = track.GetPdgCode(); + auto it = std::find(possiblePDGs.begin(), possiblePDGs.end(), pdg); + if (it != possiblePDGs.end() && track.isPrimary()) // found + { + injectedPDGs.push_back(pdg); + } + } + } + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + if(injectedPDGs.empty()){ + std::cerr << "No injected particles\n"; + return 1; // At least one of the injected particles should be generated + } + for (int i = 0; i < nPossiblePDGs; i++) + { + std::cout << "# Injected double omegas \n"; + std::cout << possiblePDGs[i] << ": " << std::count(injectedPDGs.begin(), injectedPDGs.end(), possiblePDGs[i]) << "\n"; + } + return 0; +} diff --git a/MC/config/PWGLF/ini/tests/GeneratorLFExoticNucleiPbPb.C b/MC/config/PWGLF/ini/tests/GeneratorLFExoticNucleiPbPb.C new file mode 100644 index 000000000..90b542677 --- /dev/null +++ b/MC/config/PWGLF/ini/tests/GeneratorLFExoticNucleiPbPb.C @@ -0,0 +1,53 @@ +int External() +{ + std::string path{"o2sim_Kine.root"}; + std::vector possiblePDGs = {1010000030, -1010000030, 1000030040, -1000030040}; + + int nPossiblePDGs = possiblePDGs.size(); + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) + { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) + { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + std::vector injectedPDGs; + + auto nEvents = tree->GetEntries(); + for (int i = 0; i < nEvents; i++) + { + auto check = tree->GetEntry(i); + for (int idxMCTrack = 0; idxMCTrack < tracks->size(); ++idxMCTrack) + { + auto track = tracks->at(idxMCTrack); + auto pdg = track.GetPdgCode(); + auto it = std::find(possiblePDGs.begin(), possiblePDGs.end(), pdg); + if (it != possiblePDGs.end() && track.isPrimary()) // found + { + injectedPDGs.push_back(pdg); + } + } + } + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + if(injectedPDGs.empty()){ + std::cerr << "No injected particles\n"; + return 1; // At least one of the injected particles should be generated + } + for (int i = 0; i < nPossiblePDGs; i++) + { + std::cout << "# Injected nuclei \n"; + std::cout << possiblePDGs[i] << ": " << std::count(injectedPDGs.begin(), injectedPDGs.end(), possiblePDGs[i]) << "\n"; + } + return 0; +} diff --git a/MC/config/PWGLF/ini/tests/GeneratorLFHyperNucleiOOGap.C b/MC/config/PWGLF/ini/tests/GeneratorLFHyperNucleiOOGap.C new file mode 100644 index 000000000..0f51674fb --- /dev/null +++ b/MC/config/PWGLF/ini/tests/GeneratorLFHyperNucleiOOGap.C @@ -0,0 +1,58 @@ +int External() +{ + std::string path{"o2sim_Kine.root"}; + std::vector possiblePDGs = {1000010020, -1000010020, + 1000010030, -1000010030, + 1000020030, -1000020030, + 1000020040, -1000020040, + 1010010030, -1010010030, + 1010010040, -1010010040}; + + int nPossiblePDGs = possiblePDGs.size(); + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) + { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) + { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + std::vector injectedPDGs; + + auto nEvents = tree->GetEntries(); + for (int i = 0; i < nEvents; i++) + { + auto check = tree->GetEntry(i); + for (int idxMCTrack = 0; idxMCTrack < tracks->size(); ++idxMCTrack) + { + auto track = tracks->at(idxMCTrack); + auto pdg = track.GetPdgCode(); + auto it = std::find(possiblePDGs.begin(), possiblePDGs.end(), pdg); + if (it != possiblePDGs.end() && track.isPrimary()) // found + { + injectedPDGs.push_back(pdg); + } + } + } + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + if(injectedPDGs.empty()){ + std::cerr << "No injected particles\n"; + return 1; // At least one of the injected particles should be generated + } + for (int i = 0; i < nPossiblePDGs; i++) + { + std::cout << "# Injected nuclei \n"; + std::cout << possiblePDGs[i] << ": " << std::count(injectedPDGs.begin(), injectedPDGs.end(), possiblePDGs[i]) << "\n"; + } + return 0; +} diff --git a/MC/config/PWGLF/ini/tests/GeneratorLFHyperNucleiPbPbGapWithFlow.C b/MC/config/PWGLF/ini/tests/GeneratorLFHyperNucleiPbPbGapWithFlow.C new file mode 100644 index 000000000..0f51674fb --- /dev/null +++ b/MC/config/PWGLF/ini/tests/GeneratorLFHyperNucleiPbPbGapWithFlow.C @@ -0,0 +1,58 @@ +int External() +{ + std::string path{"o2sim_Kine.root"}; + std::vector possiblePDGs = {1000010020, -1000010020, + 1000010030, -1000010030, + 1000020030, -1000020030, + 1000020040, -1000020040, + 1010010030, -1010010030, + 1010010040, -1010010040}; + + int nPossiblePDGs = possiblePDGs.size(); + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) + { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) + { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + std::vector injectedPDGs; + + auto nEvents = tree->GetEntries(); + for (int i = 0; i < nEvents; i++) + { + auto check = tree->GetEntry(i); + for (int idxMCTrack = 0; idxMCTrack < tracks->size(); ++idxMCTrack) + { + auto track = tracks->at(idxMCTrack); + auto pdg = track.GetPdgCode(); + auto it = std::find(possiblePDGs.begin(), possiblePDGs.end(), pdg); + if (it != possiblePDGs.end() && track.isPrimary()) // found + { + injectedPDGs.push_back(pdg); + } + } + } + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + if(injectedPDGs.empty()){ + std::cerr << "No injected particles\n"; + return 1; // At least one of the injected particles should be generated + } + for (int i = 0; i < nPossiblePDGs; i++) + { + std::cout << "# Injected nuclei \n"; + std::cout << possiblePDGs[i] << ": " << std::count(injectedPDGs.begin(), injectedPDGs.end(), possiblePDGs[i]) << "\n"; + } + return 0; +} diff --git a/MC/config/PWGLF/ini/tests/GeneratorLFHyperNucleippGapXSection.C b/MC/config/PWGLF/ini/tests/GeneratorLFHyperNucleippGapXSection.C new file mode 120000 index 000000000..9bd97e64f --- /dev/null +++ b/MC/config/PWGLF/ini/tests/GeneratorLFHyperNucleippGapXSection.C @@ -0,0 +1 @@ +GeneratorLFHyperNucleippGap.C \ No newline at end of file diff --git a/MC/config/PWGLF/ini/tests/GeneratorLFNucleiPbPbInHMPIDAcceptance.C b/MC/config/PWGLF/ini/tests/GeneratorLFNucleiPbPbInHMPIDAcceptance.C new file mode 100644 index 000000000..6d270f811 --- /dev/null +++ b/MC/config/PWGLF/ini/tests/GeneratorLFNucleiPbPbInHMPIDAcceptance.C @@ -0,0 +1,53 @@ +int External() +{ + std::string path{"o2sim_Kine.root"}; + std::vector possiblePDGs = {1000010020, -1000010020}; + + int nPossiblePDGs = possiblePDGs.size(); + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) + { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) + { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + std::vector injectedPDGs; + + auto nEvents = tree->GetEntries(); + for (int i = 0; i < nEvents; i++) + { + auto check = tree->GetEntry(i); + for (int idxMCTrack = 0; idxMCTrack < tracks->size(); ++idxMCTrack) + { + auto track = tracks->at(idxMCTrack); + auto pdg = track.GetPdgCode(); + auto it = std::find(possiblePDGs.begin(), possiblePDGs.end(), pdg); + if (it != possiblePDGs.end() && track.isPrimary()) // found + { + injectedPDGs.push_back(pdg); + } + } + } + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + if(injectedPDGs.empty()){ + std::cerr << "No injected particles\n"; + return 1; // At least one of the injected particles should be generated + } + for (int i = 0; i < nPossiblePDGs; i++) + { + std::cout << "# Injected nuclei \n"; + std::cout << possiblePDGs[i] << ": " << std::count(injectedPDGs.begin(), injectedPDGs.end(), possiblePDGs[i]) << "\n"; + } + return 0; +} diff --git a/MC/config/PWGLF/ini/tests/GeneratorLFStrangeness.C b/MC/config/PWGLF/ini/tests/GeneratorLFStrangeness.C index 842ba854a..ca670a64e 100644 --- a/MC/config/PWGLF/ini/tests/GeneratorLFStrangeness.C +++ b/MC/config/PWGLF/ini/tests/GeneratorLFStrangeness.C @@ -4,7 +4,7 @@ int External() // std::string path{"bkg_Kine.root"}; std::vector numberOfInjectedSignalsPerEvent = {}; std::vector injectedPDGs = {}; - std::string particleList = "${O2DPG_ROOT}/MC/config/PWGLF/pythia8/generator/strangeparticlelist.gun"; + std::string particleList = "${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/strangeparticlelist.gun"; particleList = gSystem->ExpandPathName(particleList.c_str()); std::ifstream inputFile(particleList.c_str(), ios::in); diff --git a/MC/config/PWGLF/ini/tests/GeneratorLFStrangenessInJetsTriggered_gap4.C b/MC/config/PWGLF/ini/tests/GeneratorLFStrangenessInJetsTriggered_gap4.C new file mode 100644 index 000000000..50b579161 --- /dev/null +++ b/MC/config/PWGLF/ini/tests/GeneratorLFStrangenessInJetsTriggered_gap4.C @@ -0,0 +1,58 @@ +int External() +{ + std::string path{"o2sim_Kine.root"}; + int numberOfInjectedSignalsPerEvent{1}; + std::vector injectedPDGs = { + 3334, + -3334, + 3312, + -3312}; + + auto nInjection = injectedPDGs.size(); + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree*)file.Get("o2sim"); + if (!tree) { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + std::vector nSignal; + for (int i = 0; i < nInjection; i++) { + nSignal.push_back(0); + } + + auto nEvents = tree->GetEntries(); + for (int i = 0; i < nEvents; i++) { + auto check = tree->GetEntry(i); + for (int idxMCTrack = 0; idxMCTrack < tracks->size(); ++idxMCTrack) { + auto track = tracks->at(idxMCTrack); + auto pdg = track.GetPdgCode(); + auto it = std::find(injectedPDGs.begin(), injectedPDGs.end(), pdg); + int index = std::distance(injectedPDGs.begin(), it); // index of injected PDG + if (it != injectedPDGs.end()) // found + { + // count signal PDG + nSignal[index]++; + } + } + } + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + for (int i = 0; i < nInjection; i++) { + std::cout << "# Injected nuclei \n"; + std::cout << injectedPDGs[i] << ": " << nSignal[i] << "\n"; + if (nSignal[i] == 0) { + std::cerr << "No generated: " << injectedPDGs[i] << "\n"; + return 1; // At least one of the injected particles should be generated + } + } + return 0; +} diff --git a/MC/config/PWGLF/ini/tests/GeneratorLFStrangenessTriggered_5360gev.C b/MC/config/PWGLF/ini/tests/GeneratorLFStrangenessTriggered_5360gev.C new file mode 120000 index 000000000..363dcd11d --- /dev/null +++ b/MC/config/PWGLF/ini/tests/GeneratorLFStrangenessTriggered_5360gev.C @@ -0,0 +1 @@ +GeneratorLFStrangenessTriggered.C \ No newline at end of file diff --git a/MC/config/PWGLF/ini/tests/GeneratorLFStrangenessTriggered_900gev.C b/MC/config/PWGLF/ini/tests/GeneratorLFStrangenessTriggered_900gev.C deleted file mode 100644 index 49ba5d4e2..000000000 --- a/MC/config/PWGLF/ini/tests/GeneratorLFStrangenessTriggered_900gev.C +++ /dev/null @@ -1,58 +0,0 @@ -int External() -{ - std::string path{"o2sim_Kine.root"}; - int numberOfInjectedSignalsPerEvent{1}; - std::vector injectedPDGs = { - 3334, - -3334, - 3312, - -3312}; - - auto nInjection = injectedPDGs.size(); - - TFile file(path.c_str(), "READ"); - if (file.IsZombie()) { - std::cerr << "Cannot open ROOT file " << path << "\n"; - return 1; - } - - auto tree = (TTree*)file.Get("o2sim"); - if (!tree) { - std::cerr << "Cannot find tree o2sim in file " << path << "\n"; - return 1; - } - std::vector* tracks{}; - tree->SetBranchAddress("MCTrack", &tracks); - - std::vector nSignal; - for (int i = 0; i < nInjection; i++) { - nSignal.push_back(0); - } - - auto nEvents = tree->GetEntries(); - for (int i = 0; i < nEvents; i++) { - auto check = tree->GetEntry(i); - for (int idxMCTrack = 0; idxMCTrack < tracks->size(); ++idxMCTrack) { - auto track = tracks->at(idxMCTrack); - auto pdg = track.GetPdgCode(); - auto it = std::find(injectedPDGs.begin(), injectedPDGs.end(), pdg); - int index = std::distance(injectedPDGs.begin(), it); // index of injected PDG - if (it != injectedPDGs.end()) // found - { - // count signal PDG - nSignal[index]++; - } - } - } - std::cout << "--------------------------------\n"; - std::cout << "# Events: " << nEvents << "\n"; - for (int i = 0; i < nInjection; i++) { - std::cout << "# Injected nuclei \n"; - std::cout << injectedPDGs[i] << ": " << nSignal[i] << "\n"; - if (nSignal[i] == 0) { - std::cerr << "No generated: " << injectedPDGs[i] << "\n"; - return 1; // At least one of the injected particles should be generated - } - } - return 0; -} \ No newline at end of file diff --git a/MC/config/PWGLF/ini/tests/GeneratorLFStrangenessTriggered_900gev.C b/MC/config/PWGLF/ini/tests/GeneratorLFStrangenessTriggered_900gev.C new file mode 120000 index 000000000..363dcd11d --- /dev/null +++ b/MC/config/PWGLF/ini/tests/GeneratorLFStrangenessTriggered_900gev.C @@ -0,0 +1 @@ +GeneratorLFStrangenessTriggered.C \ No newline at end of file diff --git a/MC/config/PWGLF/ini/tests/GeneratorLFStrangenessTriggered_gap2.C b/MC/config/PWGLF/ini/tests/GeneratorLFStrangenessTriggered_gap2.C new file mode 120000 index 000000000..363dcd11d --- /dev/null +++ b/MC/config/PWGLF/ini/tests/GeneratorLFStrangenessTriggered_gap2.C @@ -0,0 +1 @@ +GeneratorLFStrangenessTriggered.C \ No newline at end of file diff --git a/MC/config/PWGLF/ini/tests/GeneratorLFStrangenessTriggered_gap3.C b/MC/config/PWGLF/ini/tests/GeneratorLFStrangenessTriggered_gap3.C new file mode 120000 index 000000000..363dcd11d --- /dev/null +++ b/MC/config/PWGLF/ini/tests/GeneratorLFStrangenessTriggered_gap3.C @@ -0,0 +1 @@ +GeneratorLFStrangenessTriggered.C \ No newline at end of file diff --git a/MC/config/PWGLF/ini/tests/GeneratorLFStrangenessTriggered_gap5.C b/MC/config/PWGLF/ini/tests/GeneratorLFStrangenessTriggered_gap5.C new file mode 120000 index 000000000..363dcd11d --- /dev/null +++ b/MC/config/PWGLF/ini/tests/GeneratorLFStrangenessTriggered_gap5.C @@ -0,0 +1 @@ +GeneratorLFStrangenessTriggered.C \ No newline at end of file diff --git a/MC/config/PWGLF/ini/tests/GeneratorLFV0sInJets_pp536TeV.C b/MC/config/PWGLF/ini/tests/GeneratorLFV0sInJets_pp536TeV.C new file mode 100644 index 000000000..4fec0a0f0 --- /dev/null +++ b/MC/config/PWGLF/ini/tests/GeneratorLFV0sInJets_pp536TeV.C @@ -0,0 +1,57 @@ +int External() +{ + std::string path{"o2sim_Kine.root"}; + int numberOfInjectedSignalsPerEvent{1}; + std::vector injectedPDGs = { + 310, // K0S + 3122, // Lambda + -3122 // Anti-Lambda + }; + auto nInjection = injectedPDGs.size(); + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree*)file.Get("o2sim"); + if (!tree) { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + std::vector nSignal; + for (int i = 0; i < nInjection; i++) { + nSignal.push_back(0); + } + + auto nEvents = tree->GetEntries(); + for (int i = 0; i < nEvents; i++) { + auto check = tree->GetEntry(i); + for (int idxMCTrack = 0; idxMCTrack < tracks->size(); ++idxMCTrack) { + auto track = tracks->at(idxMCTrack); + auto pdg = track.GetPdgCode(); + auto it = std::find(injectedPDGs.begin(), injectedPDGs.end(), pdg); + int index = std::distance(injectedPDGs.begin(), it); // index of injected PDG + if (it != injectedPDGs.end()) // found + { + // count signal PDG + nSignal[index]++; + } + } + } + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + for (int i = 0; i < nInjection; i++) { + std::cout << "# Injected nuclei \n"; + std::cout << injectedPDGs[i] << ": " << nSignal[i] << "\n"; + if (nSignal[i] == 0) { + std::cerr << "No generated: " << injectedPDGs[i] << "\n"; + return 1; // At least one of the injected particles should be generated + } + } + return 0; +} diff --git a/MC/config/PWGLF/ini/tests/GeneratorLF_Coalescence.C b/MC/config/PWGLF/ini/tests/GeneratorLF_Coalescence.C new file mode 100644 index 000000000..e25d99565 --- /dev/null +++ b/MC/config/PWGLF/ini/tests/GeneratorLF_Coalescence.C @@ -0,0 +1,31 @@ +int External() +{ + std::string path{"o2sim_Kine.root"}; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) + { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) + { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + auto nEvents = tree->GetEntries(); + auto nInjected = tree->Scan("MCTrack.GetPdgCode()", "MCTrack.GetPdgCode() == -1000020030"); /// don't check matter, too many secondaries + nInjected += tree->Scan("MCTrack.GetPdgCode()", "MCTrack.GetPdgCode() == -1000010030"); /// don't check matter, too many secondaries + nInjected += tree->Scan("MCTrack.GetPdgCode()", "TMath::Abs(MCTrack.GetPdgCode()) == 1010010030"); /// don't check matter, too many secondaries + if (nInjected == 0) + { + std::cerr << "Unexpected ratio of events to injected nuclei\n"; + return 1; + } + return 0; +} diff --git a/MC/config/PWGLF/ini/tests/GeneratorLF_Coalescence_pp536TeV.C b/MC/config/PWGLF/ini/tests/GeneratorLF_Coalescence_pp536TeV.C new file mode 100644 index 000000000..ab7eeb695 --- /dev/null +++ b/MC/config/PWGLF/ini/tests/GeneratorLF_Coalescence_pp536TeV.C @@ -0,0 +1,28 @@ +int External() +{ + std::string path{"o2sim_Kine.root"}; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) + { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) + { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + auto nEvents = tree->GetEntries(); + if (nEvents < 1) + { + std::cerr << "No events actually generated: not OK!"; + return 1; + } + return 0; +} diff --git a/MC/config/PWGLF/ini/tests/GeneratorLF_ExoticResonances_pp1360.C b/MC/config/PWGLF/ini/tests/GeneratorLF_ExoticResonances_pp1360.C new file mode 100644 index 000000000..c0a789557 --- /dev/null +++ b/MC/config/PWGLF/ini/tests/GeneratorLF_ExoticResonances_pp1360.C @@ -0,0 +1,144 @@ +int External() +{ + std::string path{"o2sim_Kine.root"}; + int numberOfInjectedSignalsPerEvent{1}; + int numberOfGapEvents{4}; + int numberOfEventsProcessed{0}; + int numberOfEventsProcessedWithoutInjection{0}; + std::vector injectedPDGs = { + 20223, // f_1(1285) + 20333, // f_1(1420) + 9030221, // f_0(1500) + 10331, // f_0(1710) + 335, // f_2(1525) + 10221 // f_0(1370) + }; + std::vector> decayDaughters = { + {310, -321, 211}, // f_1(1285) + {310, -321, 211}, // f_1(1420) + {310, 310}, // f_0(1500) + {310, 310}, // f_0(1710) + {310, 310}, // f_2(1525) + {310, 310} // f_0(1370) + }; + + auto nInjection = injectedPDGs.size(); + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) + { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) + { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + std::vector nSignal; + for (int i = 0; i < nInjection; i++) + { + nSignal.push_back(0); + } + std::vector> nDecays; + std::vector nNotDecayed; + for (int i = 0; i < nInjection; i++) + { + std::vector nDecay; + for (int j = 0; j < decayDaughters[i].size(); j++) + { + nDecay.push_back(0); + } + nDecays.push_back(nDecay); + nNotDecayed.push_back(0); + } + auto nEvents = tree->GetEntries(); + bool hasInjection = false; + for (int i = 0; i < nEvents; i++) + { + hasInjection = false; + numberOfEventsProcessed++; + auto check = tree->GetEntry(i); + for (int idxMCTrack = 0; idxMCTrack < tracks->size(); ++idxMCTrack) + { + auto track = tracks->at(idxMCTrack); + auto pdg = track.GetPdgCode(); + auto it = std::find(injectedPDGs.begin(), injectedPDGs.end(), pdg); + int index = std::distance(injectedPDGs.begin(), it); // index of injected PDG + if (it != injectedPDGs.end()) // found + { + // count signal PDG + nSignal[index]++; + if(track.getFirstDaughterTrackId() < 0) + { + nNotDecayed[index]++; + continue; + } + for (int j{track.getFirstDaughterTrackId()}; j <= track.getLastDaughterTrackId(); ++j) + { + auto pdgDau = tracks->at(j).GetPdgCode(); + bool foundDau= false; + // count decay PDGs + for (int idxDaughter = 0; idxDaughter < decayDaughters[index].size(); ++idxDaughter) + { + if (pdgDau == decayDaughters[index][idxDaughter]) + { + nDecays[index][idxDaughter]++; + foundDau= true; + hasInjection = true; + break; + } + } + if (!foundDau) { + std::cerr << "Decay daughter not found: " << pdg << " -> " << pdgDau << "\n"; + } + } + } + } + if (!hasInjection) + { + numberOfEventsProcessedWithoutInjection++; + } + } + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + for (int i = 0; i < nInjection; i++) + { + std::cout << "# Mother \n"; + std::cout << injectedPDGs[i] << " generated: " << nSignal[i] << ", " << nNotDecayed[i] << " did not decay\n"; + if (nSignal[i] == 0){ + std::cerr << "No generated: " << injectedPDGs[i] << "\n"; + // return 1; // At least one of the injected particles should be generated + } + for (int j = 0; j < decayDaughters[i].size(); j++) + { + std::cout << "# Daughter " << decayDaughters[i][j] << ": " << nDecays[i][j] << "\n"; + } + // if (nSignal[i] != nEvents * numberOfInjectedSignalsPerEvent) + // { + // std::cerr << "Number of generated: " << injectedPDGs[i] << ", lower than expected\n"; + // // return 1; // Don't need to return 1, since the number of generated particles is not the same for each event + // } + } + std::cout << "--------------------------------\n"; + std::cout << "Number of events processed: " << numberOfEventsProcessed << "\n"; + std::cout << "Number of input for the gap events: " << numberOfGapEvents << "\n"; + std::cout << "Number of events processed without injection: " << numberOfEventsProcessedWithoutInjection << "\n"; + // injected event + numberOfGapEvents*gap events + injected event + numberOfGapEvents*gap events + ... + // total fraction of the gap event: numberOfEventsProcessedWithoutInjection/numberOfEventsProcessed + float ratioOfNormalEvents = numberOfEventsProcessedWithoutInjection/numberOfEventsProcessed; + if (ratioOfNormalEvents > 0.75) + { + std::cout << "The number of injected event is loo low!!" << std::endl; + return 1; + } + + return 0; +} + +void GeneratorLF_ExoticResonances_pp1360() { External(); } diff --git a/MC/config/PWGLF/ini/tests/GeneratorLF_HighPt.C b/MC/config/PWGLF/ini/tests/GeneratorLF_HighPt.C new file mode 100644 index 000000000..538591224 --- /dev/null +++ b/MC/config/PWGLF/ini/tests/GeneratorLF_HighPt.C @@ -0,0 +1,25 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + auto nEvents = tree->GetEntries(); + auto nSelected = tree->Scan("MCTrack.GetPdgCode()", "MCTrack.GetPdgCode() == -2212"); + if (nSelected == 0) { + std::cerr << "No event of interest\n"; + return 1; + } + return 0; +} diff --git a/MC/config/PWGLF/ini/tests/GeneratorLF_Lambda1520_pp.C b/MC/config/PWGLF/ini/tests/GeneratorLF_Lambda1520_pp.C new file mode 100644 index 000000000..6b02def64 --- /dev/null +++ b/MC/config/PWGLF/ini/tests/GeneratorLF_Lambda1520_pp.C @@ -0,0 +1,138 @@ +int External() +{ + std::string path{"o2sim_Kine.root"}; + int numberOfInjectedSignalsPerEvent{1}; + int numberOfGapEvents{4}; + int numberOfEventsProcessed{0}; + int numberOfEventsProcessedWithoutInjection{0}; + std::vector injectedPDGs = { + 102134, // Lambda(1520)0 + -102134, // Lambda(1520)0bar + }; + std::vector> decayDaughters = { + {2212, -321}, // Lambda(1520)0 + {-2212, 321}, // Lambda(1520)0bar + }; + + auto nInjection = injectedPDGs.size(); + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) + { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) + { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + std::vector nSignal; + for (int i = 0; i < nInjection; i++) + { + nSignal.push_back(0); + } + std::vector> nDecays; + std::vector nNotDecayed; + for (int i = 0; i < nInjection; i++) + { + std::vector nDecay; + for (int j = 0; j < decayDaughters[i].size(); j++) + { + nDecay.push_back(0); + } + nDecays.push_back(nDecay); + nNotDecayed.push_back(0); + } + auto nEvents = tree->GetEntries(); + bool hasInjection = false; + for (int i = 0; i < nEvents; i++) + { + hasInjection = false; + numberOfEventsProcessed++; + auto check = tree->GetEntry(i); + for (int idxMCTrack = 0; idxMCTrack < tracks->size(); ++idxMCTrack) + { + auto track = tracks->at(idxMCTrack); + auto pdg = track.GetPdgCode(); + auto it = std::find(injectedPDGs.begin(), injectedPDGs.end(), pdg); + int index = std::distance(injectedPDGs.begin(), it); // index of injected PDG + if (it != injectedPDGs.end()) // found + { + // count signal PDG + nSignal[index]++; + if (track.getFirstDaughterTrackId() < 0) + { + nNotDecayed[index]++; + continue; + } + for (int j{track.getFirstDaughterTrackId()}; j <= track.getLastDaughterTrackId(); ++j) + { + auto pdgDau = tracks->at(j).GetPdgCode(); + bool foundDau = false; + // count decay PDGs + for (int idxDaughter = 0; idxDaughter < decayDaughters[index].size(); ++idxDaughter) + { + if (pdgDau == decayDaughters[index][idxDaughter]) + { + nDecays[index][idxDaughter]++; + foundDau = true; + hasInjection = true; + break; + } + } + if (!foundDau) + { + std::cerr << "Decay daughter not found: " << pdg << " -> " << pdgDau << "\n"; + } + } + } + } + if (!hasInjection) + { + numberOfEventsProcessedWithoutInjection++; + } + } + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + for (int i = 0; i < nInjection; i++) + { + std::cout << "# Mother \n"; + std::cout << injectedPDGs[i] << " generated: " << nSignal[i] << ", " << nNotDecayed[i] << " did not decay\n"; + if (nSignal[i] == 0) + { + std::cerr << "No generated: " << injectedPDGs[i] << "\n"; + // return 1; // At least one of the injected particles should be generated + } + for (int j = 0; j < decayDaughters[i].size(); j++) + { + std::cout << "# Daughter " << decayDaughters[i][j] << ": " << nDecays[i][j] << "\n"; + } + // if (nSignal[i] != nEvents * numberOfInjectedSignalsPerEvent) + // { + // std::cerr << "Number of generated: " << injectedPDGs[i] << ", lower than expected\n"; + // // return 1; // Don't need to return 1, since the number of generated particles is not the same for each event + // } + } + std::cout << "--------------------------------\n"; + std::cout << "Number of events processed: " << numberOfEventsProcessed << "\n"; + std::cout << "Number of input for the gap events: " << numberOfGapEvents << "\n"; + std::cout << "Number of events processed without injection: " << numberOfEventsProcessedWithoutInjection << "\n"; + // injected event + numberOfGapEvents*gap events + injected event + numberOfGapEvents*gap events + ... + // total fraction of the gap event: numberOfEventsProcessedWithoutInjection/numberOfEventsProcessed + float ratioOfNormalEvents = numberOfEventsProcessedWithoutInjection / numberOfEventsProcessed; + if (ratioOfNormalEvents > 0.75) + { + std::cout << "The number of injected event is loo low!!" << std::endl; + return 1; + } + + return 0; +} + +void GeneratorLF_Resonances_pp1360_injection() { External(); } diff --git a/MC/config/PWGLF/ini/tests/GeneratorLF_ResonancesBaryonic_NeNe_injection.C b/MC/config/PWGLF/ini/tests/GeneratorLF_ResonancesBaryonic_NeNe_injection.C new file mode 100644 index 000000000..63a9b6922 --- /dev/null +++ b/MC/config/PWGLF/ini/tests/GeneratorLF_ResonancesBaryonic_NeNe_injection.C @@ -0,0 +1,142 @@ +int External() +{ + std::string path{"o2sim_Kine.root"}; + int numberOfInjectedSignalsPerEvent{1}; + int numberOfGapEvents{4}; + int numberOfEventsProcessed{0}; + int numberOfEventsProcessedWithoutInjection{0}; + std::vector injectedPDGs = { + 102134, // Lambda(1520)0 + -102134, // Lambda(1520)0bar + 3324, // Xi(1530)0 + -3324 // Xi(1530)0bar + }; + std::vector> decayDaughters = { + {2212, -321}, // Lambda(1520)0 + {-2212, 321}, // Lambda(1520)0bar + {3312, 211}, // Xi(1530)0 + {-3312, -211} // Xi(1530)0bar + }; + + auto nInjection = injectedPDGs.size(); + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) + { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) + { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + std::vector nSignal; + for (int i = 0; i < nInjection; i++) + { + nSignal.push_back(0); + } + std::vector> nDecays; + std::vector nNotDecayed; + for (int i = 0; i < nInjection; i++) + { + std::vector nDecay; + for (int j = 0; j < decayDaughters[i].size(); j++) + { + nDecay.push_back(0); + } + nDecays.push_back(nDecay); + nNotDecayed.push_back(0); + } + auto nEvents = tree->GetEntries(); + bool hasInjection = false; + for (int i = 0; i < nEvents; i++) + { + hasInjection = false; + numberOfEventsProcessed++; + auto check = tree->GetEntry(i); + for (int idxMCTrack = 0; idxMCTrack < tracks->size(); ++idxMCTrack) + { + auto track = tracks->at(idxMCTrack); + auto pdg = track.GetPdgCode(); + auto it = std::find(injectedPDGs.begin(), injectedPDGs.end(), pdg); + int index = std::distance(injectedPDGs.begin(), it); // index of injected PDG + if (it != injectedPDGs.end()) // found + { + // count signal PDG + nSignal[index]++; + if (track.getFirstDaughterTrackId() < 0) + { + nNotDecayed[index]++; + continue; + } + for (int j{track.getFirstDaughterTrackId()}; j <= track.getLastDaughterTrackId(); ++j) + { + auto pdgDau = tracks->at(j).GetPdgCode(); + bool foundDau = false; + // count decay PDGs + for (int idxDaughter = 0; idxDaughter < decayDaughters[index].size(); ++idxDaughter) + { + if (pdgDau == decayDaughters[index][idxDaughter]) + { + nDecays[index][idxDaughter]++; + foundDau = true; + hasInjection = true; + break; + } + } + if (!foundDau) + { + std::cerr << "Decay daughter not found: " << pdg << " -> " << pdgDau << "\n"; + } + } + } + } + if (!hasInjection) + { + numberOfEventsProcessedWithoutInjection++; + } + } + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + for (int i = 0; i < nInjection; i++) + { + std::cout << "# Mother \n"; + std::cout << injectedPDGs[i] << " generated: " << nSignal[i] << ", " << nNotDecayed[i] << " did not decay\n"; + if (nSignal[i] == 0) + { + std::cerr << "No generated: " << injectedPDGs[i] << "\n"; + // return 1; // At least one of the injected particles should be generated + } + for (int j = 0; j < decayDaughters[i].size(); j++) + { + std::cout << "# Daughter " << decayDaughters[i][j] << ": " << nDecays[i][j] << "\n"; + } + // if (nSignal[i] != nEvents * numberOfInjectedSignalsPerEvent) + // { + // std::cerr << "Number of generated: " << injectedPDGs[i] << ", lower than expected\n"; + // // return 1; // Don't need to return 1, since the number of generated particles is not the same for each event + // } + } + std::cout << "--------------------------------\n"; + std::cout << "Number of events processed: " << numberOfEventsProcessed << "\n"; + std::cout << "Number of input for the gap events: " << numberOfGapEvents << "\n"; + std::cout << "Number of events processed without injection: " << numberOfEventsProcessedWithoutInjection << "\n"; + // injected event + numberOfGapEvents*gap events + injected event + numberOfGapEvents*gap events + ... + // total fraction of the gap event: numberOfEventsProcessedWithoutInjection/numberOfEventsProcessed + float ratioOfNormalEvents = numberOfEventsProcessedWithoutInjection / numberOfEventsProcessed; + if (ratioOfNormalEvents > 0.75) + { + std::cout << "The number of injected event is loo low!!" << std::endl; + return 1; + } + + return 0; +} + +void GeneratorLF_ResonancesBaryonic_NeNe_injection() { External(); } diff --git a/MC/config/PWGLF/ini/tests/GeneratorLF_ResonancesBaryonic_OO_injection.C b/MC/config/PWGLF/ini/tests/GeneratorLF_ResonancesBaryonic_OO_injection.C new file mode 100644 index 000000000..a47ce77ea --- /dev/null +++ b/MC/config/PWGLF/ini/tests/GeneratorLF_ResonancesBaryonic_OO_injection.C @@ -0,0 +1,142 @@ +int External() +{ + std::string path{"o2sim_Kine.root"}; + int numberOfInjectedSignalsPerEvent{1}; + int numberOfGapEvents{4}; + int numberOfEventsProcessed{0}; + int numberOfEventsProcessedWithoutInjection{0}; + std::vector injectedPDGs = { + 102134, // Lambda(1520)0 + -102134, // Lambda(1520)0bar + 3324, // Xi(1530)0 + -3324 // Xi(1530)0bar + }; + std::vector> decayDaughters = { + {2212, -321}, // Lambda(1520)0 + {-2212, 321}, // Lambda(1520)0bar + {3312, 211}, // Xi(1530)0 + {-3312, -211} // Xi(1530)0bar + }; + + auto nInjection = injectedPDGs.size(); + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) + { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) + { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + std::vector nSignal; + for (int i = 0; i < nInjection; i++) + { + nSignal.push_back(0); + } + std::vector> nDecays; + std::vector nNotDecayed; + for (int i = 0; i < nInjection; i++) + { + std::vector nDecay; + for (int j = 0; j < decayDaughters[i].size(); j++) + { + nDecay.push_back(0); + } + nDecays.push_back(nDecay); + nNotDecayed.push_back(0); + } + auto nEvents = tree->GetEntries(); + bool hasInjection = false; + for (int i = 0; i < nEvents; i++) + { + hasInjection = false; + numberOfEventsProcessed++; + auto check = tree->GetEntry(i); + for (int idxMCTrack = 0; idxMCTrack < tracks->size(); ++idxMCTrack) + { + auto track = tracks->at(idxMCTrack); + auto pdg = track.GetPdgCode(); + auto it = std::find(injectedPDGs.begin(), injectedPDGs.end(), pdg); + int index = std::distance(injectedPDGs.begin(), it); // index of injected PDG + if (it != injectedPDGs.end()) // found + { + // count signal PDG + nSignal[index]++; + if (track.getFirstDaughterTrackId() < 0) + { + nNotDecayed[index]++; + continue; + } + for (int j{track.getFirstDaughterTrackId()}; j <= track.getLastDaughterTrackId(); ++j) + { + auto pdgDau = tracks->at(j).GetPdgCode(); + bool foundDau = false; + // count decay PDGs + for (int idxDaughter = 0; idxDaughter < decayDaughters[index].size(); ++idxDaughter) + { + if (pdgDau == decayDaughters[index][idxDaughter]) + { + nDecays[index][idxDaughter]++; + foundDau = true; + hasInjection = true; + break; + } + } + if (!foundDau) + { + std::cerr << "Decay daughter not found: " << pdg << " -> " << pdgDau << "\n"; + } + } + } + } + if (!hasInjection) + { + numberOfEventsProcessedWithoutInjection++; + } + } + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + for (int i = 0; i < nInjection; i++) + { + std::cout << "# Mother \n"; + std::cout << injectedPDGs[i] << " generated: " << nSignal[i] << ", " << nNotDecayed[i] << " did not decay\n"; + if (nSignal[i] == 0) + { + std::cerr << "No generated: " << injectedPDGs[i] << "\n"; + // return 1; // At least one of the injected particles should be generated + } + for (int j = 0; j < decayDaughters[i].size(); j++) + { + std::cout << "# Daughter " << decayDaughters[i][j] << ": " << nDecays[i][j] << "\n"; + } + // if (nSignal[i] != nEvents * numberOfInjectedSignalsPerEvent) + // { + // std::cerr << "Number of generated: " << injectedPDGs[i] << ", lower than expected\n"; + // // return 1; // Don't need to return 1, since the number of generated particles is not the same for each event + // } + } + std::cout << "--------------------------------\n"; + std::cout << "Number of events processed: " << numberOfEventsProcessed << "\n"; + std::cout << "Number of input for the gap events: " << numberOfGapEvents << "\n"; + std::cout << "Number of events processed without injection: " << numberOfEventsProcessedWithoutInjection << "\n"; + // injected event + numberOfGapEvents*gap events + injected event + numberOfGapEvents*gap events + ... + // total fraction of the gap event: numberOfEventsProcessedWithoutInjection/numberOfEventsProcessed + float ratioOfNormalEvents = numberOfEventsProcessedWithoutInjection / numberOfEventsProcessed; + if (ratioOfNormalEvents > 0.75) + { + std::cout << "The number of injected event is loo low!!" << std::endl; + return 1; + } + + return 0; +} + +void GeneratorLF_ResonancesBaryonic_OO_injection() { External(); } diff --git a/MC/config/PWGLF/ini/tests/GeneratorLF_ResonancesBaryonic_pO_injection.C b/MC/config/PWGLF/ini/tests/GeneratorLF_ResonancesBaryonic_pO_injection.C new file mode 100644 index 000000000..38d05d522 --- /dev/null +++ b/MC/config/PWGLF/ini/tests/GeneratorLF_ResonancesBaryonic_pO_injection.C @@ -0,0 +1,142 @@ +int External() +{ + std::string path{"o2sim_Kine.root"}; + int numberOfInjectedSignalsPerEvent{1}; + int numberOfGapEvents{4}; + int numberOfEventsProcessed{0}; + int numberOfEventsProcessedWithoutInjection{0}; + std::vector injectedPDGs = { + 102134, // Lambda(1520)0 + -102134, // Lambda(1520)0bar + 3324, // Xi(1530)0 + -3324 // Xi(1530)0bar + }; + std::vector> decayDaughters = { + {2212, -321}, // Lambda(1520)0 + {-2212, 321}, // Lambda(1520)0bar + {3312, 211}, // Xi(1530)0 + {-3312, -211} // Xi(1530)0bar + }; + + auto nInjection = injectedPDGs.size(); + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) + { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) + { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + std::vector nSignal; + for (int i = 0; i < nInjection; i++) + { + nSignal.push_back(0); + } + std::vector> nDecays; + std::vector nNotDecayed; + for (int i = 0; i < nInjection; i++) + { + std::vector nDecay; + for (int j = 0; j < decayDaughters[i].size(); j++) + { + nDecay.push_back(0); + } + nDecays.push_back(nDecay); + nNotDecayed.push_back(0); + } + auto nEvents = tree->GetEntries(); + bool hasInjection = false; + for (int i = 0; i < nEvents; i++) + { + hasInjection = false; + numberOfEventsProcessed++; + auto check = tree->GetEntry(i); + for (int idxMCTrack = 0; idxMCTrack < tracks->size(); ++idxMCTrack) + { + auto track = tracks->at(idxMCTrack); + auto pdg = track.GetPdgCode(); + auto it = std::find(injectedPDGs.begin(), injectedPDGs.end(), pdg); + int index = std::distance(injectedPDGs.begin(), it); // index of injected PDG + if (it != injectedPDGs.end()) // found + { + // count signal PDG + nSignal[index]++; + if (track.getFirstDaughterTrackId() < 0) + { + nNotDecayed[index]++; + continue; + } + for (int j{track.getFirstDaughterTrackId()}; j <= track.getLastDaughterTrackId(); ++j) + { + auto pdgDau = tracks->at(j).GetPdgCode(); + bool foundDau = false; + // count decay PDGs + for (int idxDaughter = 0; idxDaughter < decayDaughters[index].size(); ++idxDaughter) + { + if (pdgDau == decayDaughters[index][idxDaughter]) + { + nDecays[index][idxDaughter]++; + foundDau = true; + hasInjection = true; + break; + } + } + if (!foundDau) + { + std::cerr << "Decay daughter not found: " << pdg << " -> " << pdgDau << "\n"; + } + } + } + } + if (!hasInjection) + { + numberOfEventsProcessedWithoutInjection++; + } + } + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + for (int i = 0; i < nInjection; i++) + { + std::cout << "# Mother \n"; + std::cout << injectedPDGs[i] << " generated: " << nSignal[i] << ", " << nNotDecayed[i] << " did not decay\n"; + if (nSignal[i] == 0) + { + std::cerr << "No generated: " << injectedPDGs[i] << "\n"; + // return 1; // At least one of the injected particles should be generated + } + for (int j = 0; j < decayDaughters[i].size(); j++) + { + std::cout << "# Daughter " << decayDaughters[i][j] << ": " << nDecays[i][j] << "\n"; + } + // if (nSignal[i] != nEvents * numberOfInjectedSignalsPerEvent) + // { + // std::cerr << "Number of generated: " << injectedPDGs[i] << ", lower than expected\n"; + // // return 1; // Don't need to return 1, since the number of generated particles is not the same for each event + // } + } + std::cout << "--------------------------------\n"; + std::cout << "Number of events processed: " << numberOfEventsProcessed << "\n"; + std::cout << "Number of input for the gap events: " << numberOfGapEvents << "\n"; + std::cout << "Number of events processed without injection: " << numberOfEventsProcessedWithoutInjection << "\n"; + // injected event + numberOfGapEvents*gap events + injected event + numberOfGapEvents*gap events + ... + // total fraction of the gap event: numberOfEventsProcessedWithoutInjection/numberOfEventsProcessed + float ratioOfNormalEvents = numberOfEventsProcessedWithoutInjection / numberOfEventsProcessed; + if (ratioOfNormalEvents > 0.75) + { + std::cout << "The number of injected event is loo low!!" << std::endl; + return 1; + } + + return 0; +} + +void GeneratorLF_ResonancesBaryonic_pO_injection() { External(); } diff --git a/MC/config/PWGLF/ini/tests/GeneratorLF_ResonancesBaryonic_pp1360_injection.C b/MC/config/PWGLF/ini/tests/GeneratorLF_ResonancesBaryonic_pp1360_injection.C new file mode 100644 index 000000000..27069c08f --- /dev/null +++ b/MC/config/PWGLF/ini/tests/GeneratorLF_ResonancesBaryonic_pp1360_injection.C @@ -0,0 +1,142 @@ +int External() +{ + std::string path{"o2sim_Kine.root"}; + int numberOfInjectedSignalsPerEvent{1}; + int numberOfGapEvents{4}; + int numberOfEventsProcessed{0}; + int numberOfEventsProcessedWithoutInjection{0}; + std::vector injectedPDGs = { + 102134, // Lambda(1520)0 + -102134, // Lambda(1520)0bar + 3324, // Xi(1530)0 + -3324 // Xi(1530)0bar + }; + std::vector> decayDaughters = { + {2212, -321}, // Lambda(1520)0 + {-2212, 321}, // Lambda(1520)0bar + {3312, 211}, // Xi(1530)0 + {-3312, -211} // Xi(1530)0bar + }; + + auto nInjection = injectedPDGs.size(); + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) + { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) + { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + std::vector nSignal; + for (int i = 0; i < nInjection; i++) + { + nSignal.push_back(0); + } + std::vector> nDecays; + std::vector nNotDecayed; + for (int i = 0; i < nInjection; i++) + { + std::vector nDecay; + for (int j = 0; j < decayDaughters[i].size(); j++) + { + nDecay.push_back(0); + } + nDecays.push_back(nDecay); + nNotDecayed.push_back(0); + } + auto nEvents = tree->GetEntries(); + bool hasInjection = false; + for (int i = 0; i < nEvents; i++) + { + hasInjection = false; + numberOfEventsProcessed++; + auto check = tree->GetEntry(i); + for (int idxMCTrack = 0; idxMCTrack < tracks->size(); ++idxMCTrack) + { + auto track = tracks->at(idxMCTrack); + auto pdg = track.GetPdgCode(); + auto it = std::find(injectedPDGs.begin(), injectedPDGs.end(), pdg); + int index = std::distance(injectedPDGs.begin(), it); // index of injected PDG + if (it != injectedPDGs.end()) // found + { + // count signal PDG + nSignal[index]++; + if (track.getFirstDaughterTrackId() < 0) + { + nNotDecayed[index]++; + continue; + } + for (int j{track.getFirstDaughterTrackId()}; j <= track.getLastDaughterTrackId(); ++j) + { + auto pdgDau = tracks->at(j).GetPdgCode(); + bool foundDau = false; + // count decay PDGs + for (int idxDaughter = 0; idxDaughter < decayDaughters[index].size(); ++idxDaughter) + { + if (pdgDau == decayDaughters[index][idxDaughter]) + { + nDecays[index][idxDaughter]++; + foundDau = true; + hasInjection = true; + break; + } + } + if (!foundDau) + { + std::cerr << "Decay daughter not found: " << pdg << " -> " << pdgDau << "\n"; + } + } + } + } + if (!hasInjection) + { + numberOfEventsProcessedWithoutInjection++; + } + } + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + for (int i = 0; i < nInjection; i++) + { + std::cout << "# Mother \n"; + std::cout << injectedPDGs[i] << " generated: " << nSignal[i] << ", " << nNotDecayed[i] << " did not decay\n"; + if (nSignal[i] == 0) + { + std::cerr << "No generated: " << injectedPDGs[i] << "\n"; + // return 1; // At least one of the injected particles should be generated + } + for (int j = 0; j < decayDaughters[i].size(); j++) + { + std::cout << "# Daughter " << decayDaughters[i][j] << ": " << nDecays[i][j] << "\n"; + } + // if (nSignal[i] != nEvents * numberOfInjectedSignalsPerEvent) + // { + // std::cerr << "Number of generated: " << injectedPDGs[i] << ", lower than expected\n"; + // // return 1; // Don't need to return 1, since the number of generated particles is not the same for each event + // } + } + std::cout << "--------------------------------\n"; + std::cout << "Number of events processed: " << numberOfEventsProcessed << "\n"; + std::cout << "Number of input for the gap events: " << numberOfGapEvents << "\n"; + std::cout << "Number of events processed without injection: " << numberOfEventsProcessedWithoutInjection << "\n"; + // injected event + numberOfGapEvents*gap events + injected event + numberOfGapEvents*gap events + ... + // total fraction of the gap event: numberOfEventsProcessedWithoutInjection/numberOfEventsProcessed + float ratioOfNormalEvents = numberOfEventsProcessedWithoutInjection / numberOfEventsProcessed; + if (ratioOfNormalEvents > 0.75) + { + std::cout << "The number of injected event is loo low!!" << std::endl; + return 1; + } + + return 0; +} + +void GeneratorLF_Resonances_pp1360_injection() { External(); } diff --git a/MC/config/PWGLF/ini/tests/GeneratorLF_ResonancesBaryonic_pp1360_rapidityinjection.C b/MC/config/PWGLF/ini/tests/GeneratorLF_ResonancesBaryonic_pp1360_rapidityinjection.C new file mode 100644 index 000000000..27069c08f --- /dev/null +++ b/MC/config/PWGLF/ini/tests/GeneratorLF_ResonancesBaryonic_pp1360_rapidityinjection.C @@ -0,0 +1,142 @@ +int External() +{ + std::string path{"o2sim_Kine.root"}; + int numberOfInjectedSignalsPerEvent{1}; + int numberOfGapEvents{4}; + int numberOfEventsProcessed{0}; + int numberOfEventsProcessedWithoutInjection{0}; + std::vector injectedPDGs = { + 102134, // Lambda(1520)0 + -102134, // Lambda(1520)0bar + 3324, // Xi(1530)0 + -3324 // Xi(1530)0bar + }; + std::vector> decayDaughters = { + {2212, -321}, // Lambda(1520)0 + {-2212, 321}, // Lambda(1520)0bar + {3312, 211}, // Xi(1530)0 + {-3312, -211} // Xi(1530)0bar + }; + + auto nInjection = injectedPDGs.size(); + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) + { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) + { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + std::vector nSignal; + for (int i = 0; i < nInjection; i++) + { + nSignal.push_back(0); + } + std::vector> nDecays; + std::vector nNotDecayed; + for (int i = 0; i < nInjection; i++) + { + std::vector nDecay; + for (int j = 0; j < decayDaughters[i].size(); j++) + { + nDecay.push_back(0); + } + nDecays.push_back(nDecay); + nNotDecayed.push_back(0); + } + auto nEvents = tree->GetEntries(); + bool hasInjection = false; + for (int i = 0; i < nEvents; i++) + { + hasInjection = false; + numberOfEventsProcessed++; + auto check = tree->GetEntry(i); + for (int idxMCTrack = 0; idxMCTrack < tracks->size(); ++idxMCTrack) + { + auto track = tracks->at(idxMCTrack); + auto pdg = track.GetPdgCode(); + auto it = std::find(injectedPDGs.begin(), injectedPDGs.end(), pdg); + int index = std::distance(injectedPDGs.begin(), it); // index of injected PDG + if (it != injectedPDGs.end()) // found + { + // count signal PDG + nSignal[index]++; + if (track.getFirstDaughterTrackId() < 0) + { + nNotDecayed[index]++; + continue; + } + for (int j{track.getFirstDaughterTrackId()}; j <= track.getLastDaughterTrackId(); ++j) + { + auto pdgDau = tracks->at(j).GetPdgCode(); + bool foundDau = false; + // count decay PDGs + for (int idxDaughter = 0; idxDaughter < decayDaughters[index].size(); ++idxDaughter) + { + if (pdgDau == decayDaughters[index][idxDaughter]) + { + nDecays[index][idxDaughter]++; + foundDau = true; + hasInjection = true; + break; + } + } + if (!foundDau) + { + std::cerr << "Decay daughter not found: " << pdg << " -> " << pdgDau << "\n"; + } + } + } + } + if (!hasInjection) + { + numberOfEventsProcessedWithoutInjection++; + } + } + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + for (int i = 0; i < nInjection; i++) + { + std::cout << "# Mother \n"; + std::cout << injectedPDGs[i] << " generated: " << nSignal[i] << ", " << nNotDecayed[i] << " did not decay\n"; + if (nSignal[i] == 0) + { + std::cerr << "No generated: " << injectedPDGs[i] << "\n"; + // return 1; // At least one of the injected particles should be generated + } + for (int j = 0; j < decayDaughters[i].size(); j++) + { + std::cout << "# Daughter " << decayDaughters[i][j] << ": " << nDecays[i][j] << "\n"; + } + // if (nSignal[i] != nEvents * numberOfInjectedSignalsPerEvent) + // { + // std::cerr << "Number of generated: " << injectedPDGs[i] << ", lower than expected\n"; + // // return 1; // Don't need to return 1, since the number of generated particles is not the same for each event + // } + } + std::cout << "--------------------------------\n"; + std::cout << "Number of events processed: " << numberOfEventsProcessed << "\n"; + std::cout << "Number of input for the gap events: " << numberOfGapEvents << "\n"; + std::cout << "Number of events processed without injection: " << numberOfEventsProcessedWithoutInjection << "\n"; + // injected event + numberOfGapEvents*gap events + injected event + numberOfGapEvents*gap events + ... + // total fraction of the gap event: numberOfEventsProcessedWithoutInjection/numberOfEventsProcessed + float ratioOfNormalEvents = numberOfEventsProcessedWithoutInjection / numberOfEventsProcessed; + if (ratioOfNormalEvents > 0.75) + { + std::cout << "The number of injected event is loo low!!" << std::endl; + return 1; + } + + return 0; +} + +void GeneratorLF_Resonances_pp1360_injection() { External(); } diff --git a/MC/config/PWGLF/ini/tests/GeneratorLF_ResonancesBaryonic_pp5360_injection.C b/MC/config/PWGLF/ini/tests/GeneratorLF_ResonancesBaryonic_pp5360_injection.C new file mode 100644 index 000000000..f73f13c3d --- /dev/null +++ b/MC/config/PWGLF/ini/tests/GeneratorLF_ResonancesBaryonic_pp5360_injection.C @@ -0,0 +1,142 @@ +int External() +{ + std::string path{"o2sim_Kine.root"}; + int numberOfInjectedSignalsPerEvent{1}; + int numberOfGapEvents{4}; + int numberOfEventsProcessed{0}; + int numberOfEventsProcessedWithoutInjection{0}; + std::vector injectedPDGs = { + 102134, // Lambda(1520)0 + -102134, // Lambda(1520)0bar + 3324, // Xi(1530)0 + -3324 // Xi(1530)0bar + }; + std::vector> decayDaughters = { + {2212, -321}, // Lambda(1520)0 + {-2212, 321}, // Lambda(1520)0bar + {3312, 211}, // Xi(1530)0 + {-3312, -211} // Xi(1530)0bar + }; + + auto nInjection = injectedPDGs.size(); + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) + { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) + { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + std::vector nSignal; + for (int i = 0; i < nInjection; i++) + { + nSignal.push_back(0); + } + std::vector> nDecays; + std::vector nNotDecayed; + for (int i = 0; i < nInjection; i++) + { + std::vector nDecay; + for (int j = 0; j < decayDaughters[i].size(); j++) + { + nDecay.push_back(0); + } + nDecays.push_back(nDecay); + nNotDecayed.push_back(0); + } + auto nEvents = tree->GetEntries(); + bool hasInjection = false; + for (int i = 0; i < nEvents; i++) + { + hasInjection = false; + numberOfEventsProcessed++; + auto check = tree->GetEntry(i); + for (int idxMCTrack = 0; idxMCTrack < tracks->size(); ++idxMCTrack) + { + auto track = tracks->at(idxMCTrack); + auto pdg = track.GetPdgCode(); + auto it = std::find(injectedPDGs.begin(), injectedPDGs.end(), pdg); + int index = std::distance(injectedPDGs.begin(), it); // index of injected PDG + if (it != injectedPDGs.end()) // found + { + // count signal PDG + nSignal[index]++; + if (track.getFirstDaughterTrackId() < 0) + { + nNotDecayed[index]++; + continue; + } + for (int j{track.getFirstDaughterTrackId()}; j <= track.getLastDaughterTrackId(); ++j) + { + auto pdgDau = tracks->at(j).GetPdgCode(); + bool foundDau = false; + // count decay PDGs + for (int idxDaughter = 0; idxDaughter < decayDaughters[index].size(); ++idxDaughter) + { + if (pdgDau == decayDaughters[index][idxDaughter]) + { + nDecays[index][idxDaughter]++; + foundDau = true; + hasInjection = true; + break; + } + } + if (!foundDau) + { + std::cerr << "Decay daughter not found: " << pdg << " -> " << pdgDau << "\n"; + } + } + } + } + if (!hasInjection) + { + numberOfEventsProcessedWithoutInjection++; + } + } + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + for (int i = 0; i < nInjection; i++) + { + std::cout << "# Mother \n"; + std::cout << injectedPDGs[i] << " generated: " << nSignal[i] << ", " << nNotDecayed[i] << " did not decay\n"; + if (nSignal[i] == 0) + { + std::cerr << "No generated: " << injectedPDGs[i] << "\n"; + // return 1; // At least one of the injected particles should be generated + } + for (int j = 0; j < decayDaughters[i].size(); j++) + { + std::cout << "# Daughter " << decayDaughters[i][j] << ": " << nDecays[i][j] << "\n"; + } + // if (nSignal[i] != nEvents * numberOfInjectedSignalsPerEvent) + // { + // std::cerr << "Number of generated: " << injectedPDGs[i] << ", lower than expected\n"; + // // return 1; // Don't need to return 1, since the number of generated particles is not the same for each event + // } + } + std::cout << "--------------------------------\n"; + std::cout << "Number of events processed: " << numberOfEventsProcessed << "\n"; + std::cout << "Number of input for the gap events: " << numberOfGapEvents << "\n"; + std::cout << "Number of events processed without injection: " << numberOfEventsProcessedWithoutInjection << "\n"; + // injected event + numberOfGapEvents*gap events + injected event + numberOfGapEvents*gap events + ... + // total fraction of the gap event: numberOfEventsProcessedWithoutInjection/numberOfEventsProcessed + float ratioOfNormalEvents = numberOfEventsProcessedWithoutInjection / numberOfEventsProcessed; + if (ratioOfNormalEvents > 0.75) + { + std::cout << "The number of injected event is loo low!!" << std::endl; + return 1; + } + + return 0; +} + +void GeneratorLF_ResonancesBaryonic_pp5360_injection() { External(); } diff --git a/MC/config/PWGLF/ini/tests/GeneratorLF_ResonancesML_K1Plus_KstarPi_pp1360.C b/MC/config/PWGLF/ini/tests/GeneratorLF_ResonancesML_K1Plus_KstarPi_pp1360.C new file mode 100644 index 000000000..c46abf9f4 --- /dev/null +++ b/MC/config/PWGLF/ini/tests/GeneratorLF_ResonancesML_K1Plus_KstarPi_pp1360.C @@ -0,0 +1,96 @@ +// Test of the single-channel ML signal samples GeneratorLF_ResonancesML_*.ini of this channel (both charges, pp 13.6 and 5.36 TeV; the others redirect here). +// Every event carries exactly one injected parent (no gap); the parent and the intermediate resonances decayed by the +// generator must follow the forced chain (charge conjugated for the negative-PDG parent). Parents with a mother come +// from the underlying Pythia event, decay with the default table, and are only counted. +int External() +{ + const std::string path{"o2sim_Kine.root"}; + const int parentPDG{10323}; + const std::vector daughtersOfPositive{313, 211}; + const std::map> intermediateDecaysOfPositive{{313, {321, -211}}}; + const std::set selfConjugate{111, 113, 310}; + auto conjugate = [&](int pdg, int sign) { return (sign > 0 || selfConjugate.count(pdg)) ? pdg : -pdg; }; + auto sorted = [](std::vector v) { std::sort(v.begin(), v.end()); return v; }; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + auto tree = (TTree*)file.Get("o2sim"); + if (!tree) { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int nEventsWrongParentCount{0}, nFromUnderlyingEvent{0}, nParents{0}, nNotDecayed{0}, nWrongDecay{0}, nIntermediates{0}, nWrongIntermediate{0}; + std::map nParentsBySign; + for (Long64_t i = 0; i < tree->GetEntries(); ++i) { + tree->GetEntry(i); + int nParentsInEvent{0}; + for (const auto& track : *tracks) { + const int pdg = track.GetPdgCode(); + if (std::abs(pdg) != parentPDG) { + continue; + } + if (track.getMotherTrackId() >= 0) { + ++nFromUnderlyingEvent; + continue; + } + ++nParentsInEvent; + ++nParents; + const int sign = pdg > 0 ? 1 : -1; + ++nParentsBySign[sign]; + if (track.getFirstDaughterTrackId() < 0) { + ++nNotDecayed; + continue; + } + std::vector expected, found; + for (int d : daughtersOfPositive) { + expected.push_back(conjugate(d, sign)); + } + for (int j = track.getFirstDaughterTrackId(); j <= track.getLastDaughterTrackId(); ++j) { + const auto& daughter = tracks->at(j); + found.push_back(daughter.GetPdgCode()); + const auto it = intermediateDecaysOfPositive.find(sign > 0 ? daughter.GetPdgCode() : conjugate(daughter.GetPdgCode(), -1)); + if (it == intermediateDecaysOfPositive.end()) { + continue; + } + ++nIntermediates; + std::vector expectedGrand, foundGrand; + for (int g : it->second) { + expectedGrand.push_back(conjugate(g, sign)); + } + for (int k = daughter.getFirstDaughterTrackId(); k >= 0 && k <= daughter.getLastDaughterTrackId(); ++k) { + foundGrand.push_back(tracks->at(k).GetPdgCode()); + } + if (sorted(expectedGrand) != sorted(foundGrand)) { + ++nWrongIntermediate; + } + } + if (sorted(expected) != sorted(found)) { + ++nWrongDecay; + } + } + if (nParentsInEvent != 1) { + ++nEventsWrongParentCount; + } + } + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << tree->GetEntries() << "\n"; + std::cout << "# Parents " << parentPDG << ": " << nParentsBySign[1] << ", anti: " << nParentsBySign[-1] << "\n"; + std::cout << "# Parents from the underlying event (not checked): " << nFromUnderlyingEvent << "\n"; + std::cout << "# Events without exactly one parent: " << nEventsWrongParentCount << "\n"; + std::cout << "# Parents not decayed: " << nNotDecayed << ", with wrong daughters: " << nWrongDecay << "\n"; + std::cout << "# Intermediate resonances: " << nIntermediates << ", with wrong daughters: " << nWrongIntermediate << "\n"; + std::cout << "--------------------------------\n"; + if (tree->GetEntries() == 0 || nEventsWrongParentCount || nNotDecayed || nWrongDecay || nWrongIntermediate || + (!intermediateDecaysOfPositive.empty() && nIntermediates != nParents)) { + std::cerr << "Forced decay chain not reproduced\n"; + return 1; + } + return 0; +} diff --git a/MC/config/PWGLF/ini/tests/GeneratorLF_ResonancesML_K1Plus_RhoK_pp1360.C b/MC/config/PWGLF/ini/tests/GeneratorLF_ResonancesML_K1Plus_RhoK_pp1360.C new file mode 100644 index 000000000..25d22a966 --- /dev/null +++ b/MC/config/PWGLF/ini/tests/GeneratorLF_ResonancesML_K1Plus_RhoK_pp1360.C @@ -0,0 +1,96 @@ +// Test of the single-channel ML signal samples GeneratorLF_ResonancesML_*.ini of this channel (both charges, pp 13.6 and 5.36 TeV; the others redirect here). +// Every event carries exactly one injected parent (no gap); the parent and the intermediate resonances decayed by the +// generator must follow the forced chain (charge conjugated for the negative-PDG parent). Parents with a mother come +// from the underlying Pythia event, decay with the default table, and are only counted. +int External() +{ + const std::string path{"o2sim_Kine.root"}; + const int parentPDG{10323}; + const std::vector daughtersOfPositive{321, 113}; + const std::map> intermediateDecaysOfPositive{{113, {211, -211}}}; + const std::set selfConjugate{111, 113, 310}; + auto conjugate = [&](int pdg, int sign) { return (sign > 0 || selfConjugate.count(pdg)) ? pdg : -pdg; }; + auto sorted = [](std::vector v) { std::sort(v.begin(), v.end()); return v; }; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + auto tree = (TTree*)file.Get("o2sim"); + if (!tree) { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int nEventsWrongParentCount{0}, nFromUnderlyingEvent{0}, nParents{0}, nNotDecayed{0}, nWrongDecay{0}, nIntermediates{0}, nWrongIntermediate{0}; + std::map nParentsBySign; + for (Long64_t i = 0; i < tree->GetEntries(); ++i) { + tree->GetEntry(i); + int nParentsInEvent{0}; + for (const auto& track : *tracks) { + const int pdg = track.GetPdgCode(); + if (std::abs(pdg) != parentPDG) { + continue; + } + if (track.getMotherTrackId() >= 0) { + ++nFromUnderlyingEvent; + continue; + } + ++nParentsInEvent; + ++nParents; + const int sign = pdg > 0 ? 1 : -1; + ++nParentsBySign[sign]; + if (track.getFirstDaughterTrackId() < 0) { + ++nNotDecayed; + continue; + } + std::vector expected, found; + for (int d : daughtersOfPositive) { + expected.push_back(conjugate(d, sign)); + } + for (int j = track.getFirstDaughterTrackId(); j <= track.getLastDaughterTrackId(); ++j) { + const auto& daughter = tracks->at(j); + found.push_back(daughter.GetPdgCode()); + const auto it = intermediateDecaysOfPositive.find(sign > 0 ? daughter.GetPdgCode() : conjugate(daughter.GetPdgCode(), -1)); + if (it == intermediateDecaysOfPositive.end()) { + continue; + } + ++nIntermediates; + std::vector expectedGrand, foundGrand; + for (int g : it->second) { + expectedGrand.push_back(conjugate(g, sign)); + } + for (int k = daughter.getFirstDaughterTrackId(); k >= 0 && k <= daughter.getLastDaughterTrackId(); ++k) { + foundGrand.push_back(tracks->at(k).GetPdgCode()); + } + if (sorted(expectedGrand) != sorted(foundGrand)) { + ++nWrongIntermediate; + } + } + if (sorted(expected) != sorted(found)) { + ++nWrongDecay; + } + } + if (nParentsInEvent != 1) { + ++nEventsWrongParentCount; + } + } + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << tree->GetEntries() << "\n"; + std::cout << "# Parents " << parentPDG << ": " << nParentsBySign[1] << ", anti: " << nParentsBySign[-1] << "\n"; + std::cout << "# Parents from the underlying event (not checked): " << nFromUnderlyingEvent << "\n"; + std::cout << "# Events without exactly one parent: " << nEventsWrongParentCount << "\n"; + std::cout << "# Parents not decayed: " << nNotDecayed << ", with wrong daughters: " << nWrongDecay << "\n"; + std::cout << "# Intermediate resonances: " << nIntermediates << ", with wrong daughters: " << nWrongIntermediate << "\n"; + std::cout << "--------------------------------\n"; + if (tree->GetEntries() == 0 || nEventsWrongParentCount || nNotDecayed || nWrongDecay || nWrongIntermediate || + (!intermediateDecaysOfPositive.empty() && nIntermediates != nParents)) { + std::cerr << "Forced decay chain not reproduced\n"; + return 1; + } + return 0; +} diff --git a/MC/config/PWGLF/ini/tests/GeneratorLF_ResonancesML_Omega2012Minus_Xi1530K_pp1360.C b/MC/config/PWGLF/ini/tests/GeneratorLF_ResonancesML_Omega2012Minus_Xi1530K_pp1360.C new file mode 100644 index 000000000..82afde70c --- /dev/null +++ b/MC/config/PWGLF/ini/tests/GeneratorLF_ResonancesML_Omega2012Minus_Xi1530K_pp1360.C @@ -0,0 +1,96 @@ +// Test of the single-channel ML signal samples GeneratorLF_ResonancesML_*.ini of this channel (both charges, pp 13.6 and 5.36 TeV; the others redirect here). +// Every event carries exactly one injected parent (no gap); the parent and the intermediate resonances decayed by the +// generator must follow the forced chain (charge conjugated for the negative-PDG parent). Parents with a mother come +// from the underlying Pythia event, decay with the default table, and are only counted. +int External() +{ + const std::string path{"o2sim_Kine.root"}; + const int parentPDG{123334}; + const std::vector daughtersOfPositive{3324, -321}; + const std::map> intermediateDecaysOfPositive{{3324, {3312, 211}}}; + const std::set selfConjugate{111, 113, 310}; + auto conjugate = [&](int pdg, int sign) { return (sign > 0 || selfConjugate.count(pdg)) ? pdg : -pdg; }; + auto sorted = [](std::vector v) { std::sort(v.begin(), v.end()); return v; }; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + auto tree = (TTree*)file.Get("o2sim"); + if (!tree) { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int nEventsWrongParentCount{0}, nFromUnderlyingEvent{0}, nParents{0}, nNotDecayed{0}, nWrongDecay{0}, nIntermediates{0}, nWrongIntermediate{0}; + std::map nParentsBySign; + for (Long64_t i = 0; i < tree->GetEntries(); ++i) { + tree->GetEntry(i); + int nParentsInEvent{0}; + for (const auto& track : *tracks) { + const int pdg = track.GetPdgCode(); + if (std::abs(pdg) != parentPDG) { + continue; + } + if (track.getMotherTrackId() >= 0) { + ++nFromUnderlyingEvent; + continue; + } + ++nParentsInEvent; + ++nParents; + const int sign = pdg > 0 ? 1 : -1; + ++nParentsBySign[sign]; + if (track.getFirstDaughterTrackId() < 0) { + ++nNotDecayed; + continue; + } + std::vector expected, found; + for (int d : daughtersOfPositive) { + expected.push_back(conjugate(d, sign)); + } + for (int j = track.getFirstDaughterTrackId(); j <= track.getLastDaughterTrackId(); ++j) { + const auto& daughter = tracks->at(j); + found.push_back(daughter.GetPdgCode()); + const auto it = intermediateDecaysOfPositive.find(sign > 0 ? daughter.GetPdgCode() : conjugate(daughter.GetPdgCode(), -1)); + if (it == intermediateDecaysOfPositive.end()) { + continue; + } + ++nIntermediates; + std::vector expectedGrand, foundGrand; + for (int g : it->second) { + expectedGrand.push_back(conjugate(g, sign)); + } + for (int k = daughter.getFirstDaughterTrackId(); k >= 0 && k <= daughter.getLastDaughterTrackId(); ++k) { + foundGrand.push_back(tracks->at(k).GetPdgCode()); + } + if (sorted(expectedGrand) != sorted(foundGrand)) { + ++nWrongIntermediate; + } + } + if (sorted(expected) != sorted(found)) { + ++nWrongDecay; + } + } + if (nParentsInEvent != 1) { + ++nEventsWrongParentCount; + } + } + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << tree->GetEntries() << "\n"; + std::cout << "# Parents " << parentPDG << ": " << nParentsBySign[1] << ", anti: " << nParentsBySign[-1] << "\n"; + std::cout << "# Parents from the underlying event (not checked): " << nFromUnderlyingEvent << "\n"; + std::cout << "# Events without exactly one parent: " << nEventsWrongParentCount << "\n"; + std::cout << "# Parents not decayed: " << nNotDecayed << ", with wrong daughters: " << nWrongDecay << "\n"; + std::cout << "# Intermediate resonances: " << nIntermediates << ", with wrong daughters: " << nWrongIntermediate << "\n"; + std::cout << "--------------------------------\n"; + if (tree->GetEntries() == 0 || nEventsWrongParentCount || nNotDecayed || nWrongDecay || nWrongIntermediate || + (!intermediateDecaysOfPositive.empty() && nIntermediates != nParents)) { + std::cerr << "Forced decay chain not reproduced\n"; + return 1; + } + return 0; +} diff --git a/MC/config/PWGLF/ini/tests/GeneratorLF_ResonancesML_Omega2012Minus_XiK0s_pp1360.C b/MC/config/PWGLF/ini/tests/GeneratorLF_ResonancesML_Omega2012Minus_XiK0s_pp1360.C new file mode 100644 index 000000000..a55c402d4 --- /dev/null +++ b/MC/config/PWGLF/ini/tests/GeneratorLF_ResonancesML_Omega2012Minus_XiK0s_pp1360.C @@ -0,0 +1,96 @@ +// Test of the single-channel ML signal samples GeneratorLF_ResonancesML_*.ini of this channel (both charges, pp 13.6 and 5.36 TeV; the others redirect here). +// Every event carries exactly one injected parent (no gap); the parent and the intermediate resonances decayed by the +// generator must follow the forced chain (charge conjugated for the negative-PDG parent). Parents with a mother come +// from the underlying Pythia event, decay with the default table, and are only counted. +int External() +{ + const std::string path{"o2sim_Kine.root"}; + const int parentPDG{123334}; + const std::vector daughtersOfPositive{3312, 310}; + const std::map> intermediateDecaysOfPositive{}; + const std::set selfConjugate{111, 113, 310}; + auto conjugate = [&](int pdg, int sign) { return (sign > 0 || selfConjugate.count(pdg)) ? pdg : -pdg; }; + auto sorted = [](std::vector v) { std::sort(v.begin(), v.end()); return v; }; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + auto tree = (TTree*)file.Get("o2sim"); + if (!tree) { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int nEventsWrongParentCount{0}, nFromUnderlyingEvent{0}, nParents{0}, nNotDecayed{0}, nWrongDecay{0}, nIntermediates{0}, nWrongIntermediate{0}; + std::map nParentsBySign; + for (Long64_t i = 0; i < tree->GetEntries(); ++i) { + tree->GetEntry(i); + int nParentsInEvent{0}; + for (const auto& track : *tracks) { + const int pdg = track.GetPdgCode(); + if (std::abs(pdg) != parentPDG) { + continue; + } + if (track.getMotherTrackId() >= 0) { + ++nFromUnderlyingEvent; + continue; + } + ++nParentsInEvent; + ++nParents; + const int sign = pdg > 0 ? 1 : -1; + ++nParentsBySign[sign]; + if (track.getFirstDaughterTrackId() < 0) { + ++nNotDecayed; + continue; + } + std::vector expected, found; + for (int d : daughtersOfPositive) { + expected.push_back(conjugate(d, sign)); + } + for (int j = track.getFirstDaughterTrackId(); j <= track.getLastDaughterTrackId(); ++j) { + const auto& daughter = tracks->at(j); + found.push_back(daughter.GetPdgCode()); + const auto it = intermediateDecaysOfPositive.find(sign > 0 ? daughter.GetPdgCode() : conjugate(daughter.GetPdgCode(), -1)); + if (it == intermediateDecaysOfPositive.end()) { + continue; + } + ++nIntermediates; + std::vector expectedGrand, foundGrand; + for (int g : it->second) { + expectedGrand.push_back(conjugate(g, sign)); + } + for (int k = daughter.getFirstDaughterTrackId(); k >= 0 && k <= daughter.getLastDaughterTrackId(); ++k) { + foundGrand.push_back(tracks->at(k).GetPdgCode()); + } + if (sorted(expectedGrand) != sorted(foundGrand)) { + ++nWrongIntermediate; + } + } + if (sorted(expected) != sorted(found)) { + ++nWrongDecay; + } + } + if (nParentsInEvent != 1) { + ++nEventsWrongParentCount; + } + } + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << tree->GetEntries() << "\n"; + std::cout << "# Parents " << parentPDG << ": " << nParentsBySign[1] << ", anti: " << nParentsBySign[-1] << "\n"; + std::cout << "# Parents from the underlying event (not checked): " << nFromUnderlyingEvent << "\n"; + std::cout << "# Events without exactly one parent: " << nEventsWrongParentCount << "\n"; + std::cout << "# Parents not decayed: " << nNotDecayed << ", with wrong daughters: " << nWrongDecay << "\n"; + std::cout << "# Intermediate resonances: " << nIntermediates << ", with wrong daughters: " << nWrongIntermediate << "\n"; + std::cout << "--------------------------------\n"; + if (tree->GetEntries() == 0 || nEventsWrongParentCount || nNotDecayed || nWrongDecay || nWrongIntermediate || + (!intermediateDecaysOfPositive.empty() && nIntermediates != nParents)) { + std::cerr << "Forced decay chain not reproduced\n"; + return 1; + } + return 0; +} diff --git a/MC/config/PWGLF/ini/tests/GeneratorLF_Resonances_PbPb.C b/MC/config/PWGLF/ini/tests/GeneratorLF_Resonances_PbPb.C new file mode 100644 index 000000000..fd735962c --- /dev/null +++ b/MC/config/PWGLF/ini/tests/GeneratorLF_Resonances_PbPb.C @@ -0,0 +1,182 @@ +int External() +{ + std::string path{"o2sim_Kine.root"}; + int numberOfInjectedSignalsPerEvent{1}; + int numberOfGapEvents{4}; + int numberOfEventsProcessed{0}; + int numberOfEventsProcessedWithoutInjection{0}; + std::vector injectedPDGs = { + 323, // K*+- + -323, // K*bar+- + 9010221, // f_0(980) + 113, // rho(770)0 + 213, // rho(770)+ + -213, // rho(770)bar- + 3224, // Sigma(1385)+ + -3224, // Sigma(1385)bar- + 3114, // Sigma(1385)- + -3114, // Sigma(1385)bar+ + 3124, // Lambda(1520)0 + -3124, // Lambda(1520)0bar + 3324, // Xi(1530)0 + -3324, // Xi(1530)0bar + 10323, // K1(1270)+ + -10323, // K1(1270)-bar + 2224, // Delta(1232)+ + -2224, // Delta(1232)bar- + 2114, // Delta(1232)0 + -2114, // Delta(1232)0bar + 123314, // Xi(1820)- + -123314, // Xi(1820)+ + 123324, // Xi(1820)0 + -123324 // Xi(1820)0bar + }; + std::vector> decayDaughters = { + {311, 211}, // K*+- + {-311, -211}, // K*bar+- + {211, -211}, // f_0(980) + {211, -211}, // rho(770)0 + {211, 111}, // rho(770)+ + {-211, 111}, // rho(770)bar- + {3122, 211}, // Sigma(1385)+ + {-3122, -211}, // Sigma(1385)bar- + {3122, -211}, // Sigma(1385)- + {-3122, 211}, // Sigma(1385)bar+ + {2212, -321}, // Lambda(1520)0 + {-2212, 321}, // Lambda(1520)0bar + {3312, 211}, // Xi(1530)0 + {-3312, -211}, // Xi(1530)0bar + {321, 211}, // K1(1270)+ + {-321, -211}, // K1(1270)-bar + {2212, 211}, // Delta(1232)+ + {-2212, -211}, // Delta(1232)bar- + {2212, -211}, // Delta(1232)- + {-2212, 211}, // Delta(1232)bar+ + {3122, -311}, // Xi(1820)- + {3122, 311}, // Xi(1820)+ + {3122, 310}, // Xi(1820)0 + {-3122, 310} // Xi(1820)0bar + }; + + auto nInjection = injectedPDGs.size(); + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) + { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) + { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + std::vector nSignal; + for (int i = 0; i < nInjection; i++) + { + nSignal.push_back(0); + } + std::vector> nDecays; + std::vector nNotDecayed; + for (int i = 0; i < nInjection; i++) + { + std::vector nDecay; + for (int j = 0; j < decayDaughters[i].size(); j++) + { + nDecay.push_back(0); + } + nDecays.push_back(nDecay); + nNotDecayed.push_back(0); + } + auto nEvents = tree->GetEntries(); + bool hasInjection = false; + for (int i = 0; i < nEvents; i++) + { + hasInjection = false; + numberOfEventsProcessed++; + auto check = tree->GetEntry(i); + for (int idxMCTrack = 0; idxMCTrack < tracks->size(); ++idxMCTrack) + { + auto track = tracks->at(idxMCTrack); + auto pdg = track.GetPdgCode(); + auto it = std::find(injectedPDGs.begin(), injectedPDGs.end(), pdg); + int index = std::distance(injectedPDGs.begin(), it); // index of injected PDG + if (it != injectedPDGs.end()) // found + { + // count signal PDG + nSignal[index]++; + if (track.getFirstDaughterTrackId() < 0) + { + nNotDecayed[index]++; + continue; + } + for (int j{track.getFirstDaughterTrackId()}; j <= track.getLastDaughterTrackId(); ++j) + { + auto pdgDau = tracks->at(j).GetPdgCode(); + bool foundDau = false; + // count decay PDGs + for (int idxDaughter = 0; idxDaughter < decayDaughters[index].size(); ++idxDaughter) + { + if (pdgDau == decayDaughters[index][idxDaughter]) + { + nDecays[index][idxDaughter]++; + foundDau = true; + hasInjection = true; + break; + } + } + if (!foundDau) + { + std::cerr << "Decay daughter not found: " << pdg << " -> " << pdgDau << "\n"; + } + } + } + } + if (!hasInjection) + { + numberOfEventsProcessedWithoutInjection++; + } + } + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + for (int i = 0; i < nInjection; i++) + { + std::cout << "# Mother \n"; + std::cout << injectedPDGs[i] << " generated: " << nSignal[i] << ", " << nNotDecayed[i] << " did not decay\n"; + if (nSignal[i] == 0) + { + std::cerr << "No generated: " << injectedPDGs[i] << "\n"; + // return 1; // At least one of the injected particles should be generated + } + for (int j = 0; j < decayDaughters[i].size(); j++) + { + std::cout << "# Daughter " << decayDaughters[i][j] << ": " << nDecays[i][j] << "\n"; + } + // if (nSignal[i] != nEvents * numberOfInjectedSignalsPerEvent) + // { + // std::cerr << "Number of generated: " << injectedPDGs[i] << ", lower than expected\n"; + // // return 1; // Don't need to return 1, since the number of generated particles is not the same for each event + // } + } + std::cout << "--------------------------------\n"; + std::cout << "Number of events processed: " << numberOfEventsProcessed << "\n"; + std::cout << "Number of input for the gap events: " << numberOfGapEvents << "\n"; + std::cout << "Number of events processed without injection: " << numberOfEventsProcessedWithoutInjection << "\n"; + // injected event + numberOfGapEvents*gap events + injected event + numberOfGapEvents*gap events + ... + // total fraction of the gap event: numberOfEventsProcessedWithoutInjection/numberOfEventsProcessed + float ratioOfNormalEvents = numberOfEventsProcessedWithoutInjection / numberOfEventsProcessed; + if (ratioOfNormalEvents > 0.75) + { + std::cout << "The number of injected event is loo low!!" << std::endl; + return 1; + } + + return 0; +} + +void GeneratorLF_Resonances_PbPb5360_injection() { External(); } diff --git a/MC/config/PWGLF/ini/tests/GeneratorLF_Resonances_PbPb5360_injection.C b/MC/config/PWGLF/ini/tests/GeneratorLF_Resonances_PbPb5360_injection.C new file mode 100644 index 000000000..e1400261d --- /dev/null +++ b/MC/config/PWGLF/ini/tests/GeneratorLF_Resonances_PbPb5360_injection.C @@ -0,0 +1,180 @@ +int External() +{ + std::string path{"o2sim_Kine.root"}; + int numberOfInjectedSignalsPerEvent{1}; + int numberOfGapEvents{4}; + int numberOfEventsProcessed{0}; + int numberOfEventsProcessedWithoutInjection{0}; + std::vector injectedPDGs = { + 323, // K*+- + -323, // K*bar+- + 9010221, // f_0(980) + 113, // rho(770)0 + 213, // rho(770)+ + -213, // rho(770)bar- + 3224, // Sigma(1385)+ + -3224, // Sigma(1385)bar- + 3114, // Sigma(1385)- + -3114, // Sigma(1385)bar+ + 3124, // Lambda(1520)0 + -3124, // Lambda(1520)0bar + 3324, // Xi(1530)0 + -3324, // Xi(1530)0bar + 10323, // K1(1270)+ + -10323, // K1(1270)-bar + 2224, // Delta(1232)+ + -2224, // Delta(1232)bar- + 2114, // Delta(1232)0 + -2114, // Delta(1232)0bar + 123314, // Xi(1820)- + -123314, // Xi(1820)+ + 123324, // Xi(1820)0 + -123324 // Xi(1820)0bar + }; + std::vector> decayDaughters = { + {311, 211}, // K*+- + {-311, -211}, // K*bar+- + {211, -211}, // f_0(980) + {211, -211}, // rho(770)0 + {211, 111}, // rho(770)+ + {-211, 111}, // rho(770)bar- + {3122, 211}, // Sigma(1385)+ + {-3122, -211}, // Sigma(1385)bar- + {3122, -211}, // Sigma(1385)- + {-3122, 211}, // Sigma(1385)bar+ + {2212, -321}, // Lambda(1520)0 + {-2212, 321}, // Lambda(1520)0bar + {3312, 211}, // Xi(1530)0 + {-3312, -211}, // Xi(1530)0bar + {321, 211}, // K1(1270)+ + {-321, -211}, // K1(1270)-bar + {2212, 211}, // Delta(1232)+ + {-2212, -211}, // Delta(1232)bar- + {2212, -211}, // Delta(1232)- + {-2212, 211}, // Delta(1232)bar+ + {3122, -311}, // Xi(1820)- + {3122, 311}, // Xi(1820)+ + {3122, 310}, // Xi(1820)0 + {-3122, 310} // Xi(1820)0bar + }; + + auto nInjection = injectedPDGs.size(); + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) + { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) + { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + std::vector nSignal; + for (int i = 0; i < nInjection; i++) + { + nSignal.push_back(0); + } + std::vector> nDecays; + std::vector nNotDecayed; + for (int i = 0; i < nInjection; i++) + { + std::vector nDecay; + for (int j = 0; j < decayDaughters[i].size(); j++) + { + nDecay.push_back(0); + } + nDecays.push_back(nDecay); + nNotDecayed.push_back(0); + } + auto nEvents = tree->GetEntries(); + bool hasInjection = false; + for (int i = 0; i < nEvents; i++) + { + hasInjection = false; + numberOfEventsProcessed++; + auto check = tree->GetEntry(i); + for (int idxMCTrack = 0; idxMCTrack < tracks->size(); ++idxMCTrack) + { + auto track = tracks->at(idxMCTrack); + auto pdg = track.GetPdgCode(); + auto it = std::find(injectedPDGs.begin(), injectedPDGs.end(), pdg); + int index = std::distance(injectedPDGs.begin(), it); // index of injected PDG + if (it != injectedPDGs.end()) // found + { + // count signal PDG + nSignal[index]++; + if(track.getFirstDaughterTrackId() < 0) + { + nNotDecayed[index]++; + continue; + } + for (int j{track.getFirstDaughterTrackId()}; j <= track.getLastDaughterTrackId(); ++j) + { + auto pdgDau = tracks->at(j).GetPdgCode(); + bool foundDau= false; + // count decay PDGs + for (int idxDaughter = 0; idxDaughter < decayDaughters[index].size(); ++idxDaughter) + { + if (pdgDau == decayDaughters[index][idxDaughter]) + { + nDecays[index][idxDaughter]++; + foundDau= true; + hasInjection = true; + break; + } + } + if (!foundDau) { + std::cerr << "Decay daughter not found: " << pdg << " -> " << pdgDau << "\n"; + } + } + } + } + if (!hasInjection) + { + numberOfEventsProcessedWithoutInjection++; + } + } + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + for (int i = 0; i < nInjection; i++) + { + std::cout << "# Mother \n"; + std::cout << injectedPDGs[i] << " generated: " << nSignal[i] << ", " << nNotDecayed[i] << " did not decay\n"; + if (nSignal[i] == 0){ + std::cerr << "No generated: " << injectedPDGs[i] << "\n"; + // return 1; // At least one of the injected particles should be generated + } + for (int j = 0; j < decayDaughters[i].size(); j++) + { + std::cout << "# Daughter " << decayDaughters[i][j] << ": " << nDecays[i][j] << "\n"; + } + // if (nSignal[i] != nEvents * numberOfInjectedSignalsPerEvent) + // { + // std::cerr << "Number of generated: " << injectedPDGs[i] << ", lower than expected\n"; + // // return 1; // Don't need to return 1, since the number of generated particles is not the same for each event + // } + } + std::cout << "--------------------------------\n"; + std::cout << "Number of events processed: " << numberOfEventsProcessed << "\n"; + std::cout << "Number of input for the gap events: " << numberOfGapEvents << "\n"; + std::cout << "Number of events processed without injection: " << numberOfEventsProcessedWithoutInjection << "\n"; + // injected event + numberOfGapEvents*gap events + injected event + numberOfGapEvents*gap events + ... + // total fraction of the gap event: numberOfEventsProcessedWithoutInjection/numberOfEventsProcessed + float ratioOfNormalEvents = numberOfEventsProcessedWithoutInjection/numberOfEventsProcessed; + if (ratioOfNormalEvents > 0.75) + { + std::cout << "The number of injected event is loo low!!" << std::endl; + return 1; + } + + return 0; +} + +void GeneratorLF_Resonances_PbPb5360_injection() { External(); } diff --git a/MC/config/PWGLF/ini/tests/GeneratorLF_Resonances_PbPb_exotic.C b/MC/config/PWGLF/ini/tests/GeneratorLF_Resonances_PbPb_exotic.C new file mode 100644 index 000000000..becf5ce3e --- /dev/null +++ b/MC/config/PWGLF/ini/tests/GeneratorLF_Resonances_PbPb_exotic.C @@ -0,0 +1,161 @@ +int External() +{ + std::string path{"o2sim_Kine.root"}; + int numberOfInjectedSignalsPerEvent{1}; + int numberOfGapEvents{4}; + int numberOfEventsProcessed{0}; + int numberOfEventsProcessedWithoutInjection{0}; + std::vector injectedPDGs = { + 9010221, // f_0(980) + 10221, // f_0(1370) + 9030221, // f_0(1500) + 10331, // f_0(1710) + 20223, // f_1(1285) + 20333, // f_1(1420) + 335, // f_2(1525) + 10323, // K1(1270)+ + -10323, // K1(1270)-bar + 123314, // Xi(1820)- + -123314, // Xi(1820)+ + 123324, // Xi(1820)0 + -123324 // Xi(1820)0bar + }; + std::vector> decayDaughters = { + {211, -211}, // f_0(980) + {310, 310}, // f_0(1370) + {310, 310}, // f_0(1500) + {310, 310}, // f_0(1710) + {310, -321, 211}, // f_1(1285) + {310, -321, 211}, // f_1(1420) + {310, 310}, // f_2(1525) + {321, 211}, // K1(1270)+ + {-321, -211}, // K1(1270)-bar + {2212, 211}, // Delta(1232)+ + {3122, -311}, // Xi(1820)- + {3122, 311}, // Xi(1820)+ + {3122, 310}, // Xi(1820)0 + {-3122, 310} // Xi(1820)0bar + }; + + auto nInjection = injectedPDGs.size(); + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) + { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) + { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + std::vector nSignal; + for (int i = 0; i < nInjection; i++) + { + nSignal.push_back(0); + } + std::vector> nDecays; + std::vector nNotDecayed; + for (int i = 0; i < nInjection; i++) + { + std::vector nDecay; + for (int j = 0; j < decayDaughters[i].size(); j++) + { + nDecay.push_back(0); + } + nDecays.push_back(nDecay); + nNotDecayed.push_back(0); + } + auto nEvents = tree->GetEntries(); + bool hasInjection = false; + for (int i = 0; i < nEvents; i++) + { + hasInjection = false; + numberOfEventsProcessed++; + auto check = tree->GetEntry(i); + for (int idxMCTrack = 0; idxMCTrack < tracks->size(); ++idxMCTrack) + { + auto track = tracks->at(idxMCTrack); + auto pdg = track.GetPdgCode(); + auto it = std::find(injectedPDGs.begin(), injectedPDGs.end(), pdg); + int index = std::distance(injectedPDGs.begin(), it); // index of injected PDG + if (it != injectedPDGs.end()) // found + { + // count signal PDG + nSignal[index]++; + if (track.getFirstDaughterTrackId() < 0) + { + nNotDecayed[index]++; + continue; + } + for (int j{track.getFirstDaughterTrackId()}; j <= track.getLastDaughterTrackId(); ++j) + { + auto pdgDau = tracks->at(j).GetPdgCode(); + bool foundDau = false; + // count decay PDGs + for (int idxDaughter = 0; idxDaughter < decayDaughters[index].size(); ++idxDaughter) + { + if (pdgDau == decayDaughters[index][idxDaughter]) + { + nDecays[index][idxDaughter]++; + foundDau = true; + hasInjection = true; + break; + } + } + if (!foundDau) + { + std::cerr << "Decay daughter not found: " << pdg << " -> " << pdgDau << "\n"; + } + } + } + } + if (!hasInjection) + { + numberOfEventsProcessedWithoutInjection++; + } + } + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + for (int i = 0; i < nInjection; i++) + { + std::cout << "# Mother \n"; + std::cout << injectedPDGs[i] << " generated: " << nSignal[i] << ", " << nNotDecayed[i] << " did not decay\n"; + if (nSignal[i] == 0) + { + std::cerr << "No generated: " << injectedPDGs[i] << "\n"; + // return 1; // At least one of the injected particles should be generated + } + for (int j = 0; j < decayDaughters[i].size(); j++) + { + std::cout << "# Daughter " << decayDaughters[i][j] << ": " << nDecays[i][j] << "\n"; + } + // if (nSignal[i] != nEvents * numberOfInjectedSignalsPerEvent) + // { + // std::cerr << "Number of generated: " << injectedPDGs[i] << ", lower than expected\n"; + // // return 1; // Don't need to return 1, since the number of generated particles is not the same for each event + // } + } + std::cout << "--------------------------------\n"; + std::cout << "Number of events processed: " << numberOfEventsProcessed << "\n"; + std::cout << "Number of input for the gap events: " << numberOfGapEvents << "\n"; + std::cout << "Number of events processed without injection: " << numberOfEventsProcessedWithoutInjection << "\n"; + // injected event + numberOfGapEvents*gap events + injected event + numberOfGapEvents*gap events + ... + // total fraction of the gap event: numberOfEventsProcessedWithoutInjection/numberOfEventsProcessed + float ratioOfNormalEvents = numberOfEventsProcessedWithoutInjection / numberOfEventsProcessed; + if (ratioOfNormalEvents > 0.75) + { + std::cout << "The number of injected event is loo low!!" << std::endl; + return 1; + } + + return 0; +} + +void GeneratorLF_Resonances_PbPb5360_injection() { External(); } diff --git a/MC/config/PWGLF/ini/tests/GeneratorLF_Resonances_pp.C b/MC/config/PWGLF/ini/tests/GeneratorLF_Resonances_pp.C index d87ec110c..c8edc36d7 100644 --- a/MC/config/PWGLF/ini/tests/GeneratorLF_Resonances_pp.C +++ b/MC/config/PWGLF/ini/tests/GeneratorLF_Resonances_pp.C @@ -1,13 +1,13 @@ int External() { std::string path{"o2sim_Kine.root"}; - int numberOfInjectedSignalsPerEvent{10}; + int numberOfInjectedSignalsPerEvent{1}; + int numberOfGapEvents{4}; + int numberOfEventsProcessed{0}; + int numberOfEventsProcessedWithoutInjection{0}; std::vector injectedPDGs = { - 313, // K0*0 - -313, // K0*0bar 323, // K*+- -323, // K*bar+- - 333, // phi 9010221, // f_0(980) 113, // rho(770)0 213, // rho(770)+ @@ -21,43 +21,42 @@ int External() 3324, // Xi(1530)0 -3324, // Xi(1530)0bar 10323, // K1(1270)+ - -10323 // K1(1270)-bar + -10323, // K1(1270)-bar + 2224, // Delta(1232)+ + -2224, // Delta(1232)bar- + 2114, // Delta(1232)0 + -2114, // Delta(1232)0bar + 123314, // Xi(1820)- + -123314, // Xi(1820)+ + 123324, // Xi(1820)0 + -123324 // Xi(1820)0bar }; - // TODO: add decay daughters - // 9030221, // f_0(1500) - // 10331, // f_0(1710) - // 123314, // Xi(1820)- - // -123314, // Xi(1820)+ - // 123324, // Xi(1820)0 - // -123324 // Xi(1820)0bar std::vector> decayDaughters = { - {321, -211}, // K0*0 - {-321, 211}, // K0*0bar {311, 211}, // K*+- {-311, -211}, // K*bar+- - {321, 321}, // phi - {211, -211}, // f_0(980) - {211, -211}, // rho(770)0 + {211, -211}, // f_0(980) + {211, -211}, // rho(770)0 {211, 111}, // rho(770)+ - {-211, 111}, // rho(770)bar- + {-211, 111}, // rho(770)bar- {3122, 211}, // Sigma(1385)+ {-3122, -211}, // Sigma(1385)bar- - {3122, -211}, // Sigma(1385)- - {-3122, 211}, // Sigma(1385)bar+ - {2212, -321}, // Lambda(1520)0 - {-2212, 321}, // Lambda(1520)0bar + {3122, -211}, // Sigma(1385)- + {-3122, 211}, // Sigma(1385)bar+ + {2212, -321}, // Lambda(1520)0 + {-2212, 321}, // Lambda(1520)0bar {3312, 211}, // Xi(1530)0 {-3312, -211}, // Xi(1530)0bar - {321, 211}, // K1(1270)+ - {-321, -211} // K1(1270)-bar + {321, 211}, // K1(1270)+ + {-321, -211}, // K1(1270)-bar + {2212, 211}, // Delta(1232)+ + {-2212, -211}, // Delta(1232)bar- + {2212, -211}, // Delta(1232)- + {-2212, 211}, // Delta(1232)bar+ + {3122, -311}, // Xi(1820)- + {3122, 311}, // Xi(1820)+ + {3122, 310}, // Xi(1820)0 + {-3122, 310} // Xi(1820)0bar }; - // TODO: add decay daughters - // {211, 211}, // f_0(1500) - // {211, 211}, // f_0(1710) - // {211, 211, 111}, // Xi(1820)- - // {-211, -211, -111}, // Xi(1820)+ - // {211, 211, 111}, // Xi(1820)0 - // {-211, -211, -111} // Xi(1820)0bar auto nInjection = injectedPDGs.size(); @@ -95,8 +94,11 @@ int External() nNotDecayed.push_back(0); } auto nEvents = tree->GetEntries(); + bool hasInjection = false; for (int i = 0; i < nEvents; i++) { + hasInjection = false; + numberOfEventsProcessed++; auto check = tree->GetEntry(i); for (int idxMCTrack = 0; idxMCTrack < tracks->size(); ++idxMCTrack) { @@ -108,7 +110,7 @@ int External() { // count signal PDG nSignal[index]++; - if(track.getFirstDaughterTrackId() < 0) + if (track.getFirstDaughterTrackId() < 0) { nNotDecayed[index]++; continue; @@ -116,23 +118,29 @@ int External() for (int j{track.getFirstDaughterTrackId()}; j <= track.getLastDaughterTrackId(); ++j) { auto pdgDau = tracks->at(j).GetPdgCode(); - bool foundDau= false; + bool foundDau = false; // count decay PDGs for (int idxDaughter = 0; idxDaughter < decayDaughters[index].size(); ++idxDaughter) { if (pdgDau == decayDaughters[index][idxDaughter]) { nDecays[index][idxDaughter]++; - foundDau= true; + foundDau = true; + hasInjection = true; break; } } - if (!foundDau) { - std::cerr << "Decay daughter not found: " << pdg << " -> " << pdgDau << "\n"; + if (!foundDau) + { + std::cerr << "Decay daughter not found: " << pdg << " -> " << pdgDau << "\n"; } } } } + if (!hasInjection) + { + numberOfEventsProcessedWithoutInjection++; + } } std::cout << "--------------------------------\n"; std::cout << "# Events: " << nEvents << "\n"; @@ -140,21 +148,35 @@ int External() { std::cout << "# Mother \n"; std::cout << injectedPDGs[i] << " generated: " << nSignal[i] << ", " << nNotDecayed[i] << " did not decay\n"; - if (nSignal[i] == 0){ + if (nSignal[i] == 0) + { std::cerr << "No generated: " << injectedPDGs[i] << "\n"; - return 1; // At least one of the injected particles should be generated + // return 1; // At least one of the injected particles should be generated } for (int j = 0; j < decayDaughters[i].size(); j++) { std::cout << "# Daughter " << decayDaughters[i][j] << ": " << nDecays[i][j] << "\n"; } - if (nSignal[i] != nEvents * numberOfInjectedSignalsPerEvent) - { - std::cerr << "Number of generated: " << injectedPDGs[i] << ", lower than expected\n"; - // return 1; // Don't need to return 1, since the number of generated particles is not the same for each event - } + // if (nSignal[i] != nEvents * numberOfInjectedSignalsPerEvent) + // { + // std::cerr << "Number of generated: " << injectedPDGs[i] << ", lower than expected\n"; + // // return 1; // Don't need to return 1, since the number of generated particles is not the same for each event + // } } + std::cout << "--------------------------------\n"; + std::cout << "Number of events processed: " << numberOfEventsProcessed << "\n"; + std::cout << "Number of input for the gap events: " << numberOfGapEvents << "\n"; + std::cout << "Number of events processed without injection: " << numberOfEventsProcessedWithoutInjection << "\n"; + // injected event + numberOfGapEvents*gap events + injected event + numberOfGapEvents*gap events + ... + // total fraction of the gap event: numberOfEventsProcessedWithoutInjection/numberOfEventsProcessed + float ratioOfNormalEvents = numberOfEventsProcessedWithoutInjection / numberOfEventsProcessed; + if (ratioOfNormalEvents > 0.75) + { + std::cout << "The number of injected event is loo low!!" << std::endl; + return 1; + } + return 0; } -void GeneratorLF_Resonances_pp() { External(); } +void GeneratorLF_Resonances_pp1360_injection() { External(); } diff --git a/MC/config/PWGLF/ini/tests/GeneratorLF_Resonances_pp1360.C b/MC/config/PWGLF/ini/tests/GeneratorLF_Resonances_pp1360.C new file mode 100644 index 000000000..facf80ba5 --- /dev/null +++ b/MC/config/PWGLF/ini/tests/GeneratorLF_Resonances_pp1360.C @@ -0,0 +1,180 @@ +int External() +{ + std::string path{"o2sim_Kine.root"}; + int numberOfInjectedSignalsPerEvent{1}; + int numberOfGapEvents{4}; + int numberOfEventsProcessed{0}; + int numberOfEventsProcessedWithoutInjection{0}; + std::vector injectedPDGs = { + 323, // K*+- + -323, // K*bar+- + 9010221, // f_0(980) + 113, // rho(770)0 + 213, // rho(770)+ + -213, // rho(770)bar- + 3224, // Sigma(1385)+ + -3224, // Sigma(1385)bar- + 3114, // Sigma(1385)- + -3114, // Sigma(1385)bar+ + 3124, // Lambda(1520)0 + -3124, // Lambda(1520)0bar + 3324, // Xi(1530)0 + -3324, // Xi(1530)0bar + 10323, // K1(1270)+ + -10323, // K1(1270)-bar + 2224, // Delta(1232)+ + -2224, // Delta(1232)bar- + 2114, // Delta(1232)0 + -2114, // Delta(1232)0bar + 123314, // Xi(1820)- + -123314, // Xi(1820)+ + 123324, // Xi(1820)0 + -123324 // Xi(1820)0bar + }; + std::vector> decayDaughters = { + {311, 211}, // K*+- + {-311, -211}, // K*bar+- + {211, -211}, // f_0(980) + {211, -211}, // rho(770)0 + {211, 111}, // rho(770)+ + {-211, 111}, // rho(770)bar- + {3122, 211}, // Sigma(1385)+ + {-3122, -211}, // Sigma(1385)bar- + {3122, -211}, // Sigma(1385)- + {-3122, 211}, // Sigma(1385)bar+ + {2212, -321}, // Lambda(1520)0 + {-2212, 321}, // Lambda(1520)0bar + {3312, 211}, // Xi(1530)0 + {-3312, -211}, // Xi(1530)0bar + {321, 211}, // K1(1270)+ + {-321, -211}, // K1(1270)-bar + {2212, 211}, // Delta(1232)+ + {-2212, -211}, // Delta(1232)bar- + {2212, -211}, // Delta(1232)- + {-2212, 211}, // Delta(1232)bar+ + {3122, -311}, // Xi(1820)- + {3122, 311}, // Xi(1820)+ + {3122, 310}, // Xi(1820)0 + {-3122, 310} // Xi(1820)0bar + }; + + auto nInjection = injectedPDGs.size(); + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) + { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) + { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + std::vector nSignal; + for (int i = 0; i < nInjection; i++) + { + nSignal.push_back(0); + } + std::vector> nDecays; + std::vector nNotDecayed; + for (int i = 0; i < nInjection; i++) + { + std::vector nDecay; + for (int j = 0; j < decayDaughters[i].size(); j++) + { + nDecay.push_back(0); + } + nDecays.push_back(nDecay); + nNotDecayed.push_back(0); + } + auto nEvents = tree->GetEntries(); + bool hasInjection = false; + for (int i = 0; i < nEvents; i++) + { + hasInjection = false; + numberOfEventsProcessed++; + auto check = tree->GetEntry(i); + for (int idxMCTrack = 0; idxMCTrack < tracks->size(); ++idxMCTrack) + { + auto track = tracks->at(idxMCTrack); + auto pdg = track.GetPdgCode(); + auto it = std::find(injectedPDGs.begin(), injectedPDGs.end(), pdg); + int index = std::distance(injectedPDGs.begin(), it); // index of injected PDG + if (it != injectedPDGs.end()) // found + { + // count signal PDG + nSignal[index]++; + if(track.getFirstDaughterTrackId() < 0) + { + nNotDecayed[index]++; + continue; + } + for (int j{track.getFirstDaughterTrackId()}; j <= track.getLastDaughterTrackId(); ++j) + { + auto pdgDau = tracks->at(j).GetPdgCode(); + bool foundDau= false; + // count decay PDGs + for (int idxDaughter = 0; idxDaughter < decayDaughters[index].size(); ++idxDaughter) + { + if (pdgDau == decayDaughters[index][idxDaughter]) + { + nDecays[index][idxDaughter]++; + foundDau= true; + hasInjection = true; + break; + } + } + if (!foundDau) { + std::cerr << "Decay daughter not found: " << pdg << " -> " << pdgDau << "\n"; + } + } + } + } + if (!hasInjection) + { + numberOfEventsProcessedWithoutInjection++; + } + } + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + for (int i = 0; i < nInjection; i++) + { + std::cout << "# Mother \n"; + std::cout << injectedPDGs[i] << " generated: " << nSignal[i] << ", " << nNotDecayed[i] << " did not decay\n"; + if (nSignal[i] == 0){ + std::cerr << "No generated: " << injectedPDGs[i] << "\n"; + // return 1; // At least one of the injected particles should be generated + } + for (int j = 0; j < decayDaughters[i].size(); j++) + { + std::cout << "# Daughter " << decayDaughters[i][j] << ": " << nDecays[i][j] << "\n"; + } + // if (nSignal[i] != nEvents * numberOfInjectedSignalsPerEvent) + // { + // std::cerr << "Number of generated: " << injectedPDGs[i] << ", lower than expected\n"; + // // return 1; // Don't need to return 1, since the number of generated particles is not the same for each event + // } + } + std::cout << "--------------------------------\n"; + std::cout << "Number of events processed: " << numberOfEventsProcessed << "\n"; + std::cout << "Number of input for the gap events: " << numberOfGapEvents << "\n"; + std::cout << "Number of events processed without injection: " << numberOfEventsProcessedWithoutInjection << "\n"; + // injected event + numberOfGapEvents*gap events + injected event + numberOfGapEvents*gap events + ... + // total fraction of the gap event: numberOfEventsProcessedWithoutInjection/numberOfEventsProcessed + float ratioOfNormalEvents = numberOfEventsProcessedWithoutInjection/numberOfEventsProcessed; + if (ratioOfNormalEvents > 0.75) + { + std::cout << "The number of injected event is loo low!!" << std::endl; + return 1; + } + + return 0; +} + +void GeneratorLF_Resonances_pp1360() { External(); } diff --git a/MC/config/PWGLF/ini/tests/GeneratorLF_Resonances_pp1360_injection.C b/MC/config/PWGLF/ini/tests/GeneratorLF_Resonances_pp1360_injection.C new file mode 100644 index 000000000..b445e9da8 --- /dev/null +++ b/MC/config/PWGLF/ini/tests/GeneratorLF_Resonances_pp1360_injection.C @@ -0,0 +1,146 @@ +int External() +{ + std::string path{"o2sim_Kine.root"}; + int numberOfInjectedSignalsPerEvent{1}; + int numberOfGapEvents{4}; + int numberOfEventsProcessed{0}; + int numberOfEventsProcessedWithoutInjection{0}; + std::vector injectedPDGs = { + 3124, // Lambda(1520)0 + -3124, // Lambda(1520)0bar + 123314, // Xi(1820)- + -123314, // Xi(1820)+ + 123324, // Xi(1820)0 + -123324 // Xi(1820)0bar + }; + std::vector> decayDaughters = { + {2212, -321}, // Lambda(1520)0 + {-2212, 321}, // Lambda(1520)0bar + {3122, -311}, // Xi(1820)- + {3122, 311}, // Xi(1820)+ + {3122, 310}, // Xi(1820)0 + {-3122, 310} // Xi(1820)0bar + }; + + auto nInjection = injectedPDGs.size(); + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) + { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) + { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + std::vector nSignal; + for (int i = 0; i < nInjection; i++) + { + nSignal.push_back(0); + } + std::vector> nDecays; + std::vector nNotDecayed; + for (int i = 0; i < nInjection; i++) + { + std::vector nDecay; + for (int j = 0; j < decayDaughters[i].size(); j++) + { + nDecay.push_back(0); + } + nDecays.push_back(nDecay); + nNotDecayed.push_back(0); + } + auto nEvents = tree->GetEntries(); + bool hasInjection = false; + for (int i = 0; i < nEvents; i++) + { + hasInjection = false; + numberOfEventsProcessed++; + auto check = tree->GetEntry(i); + for (int idxMCTrack = 0; idxMCTrack < tracks->size(); ++idxMCTrack) + { + auto track = tracks->at(idxMCTrack); + auto pdg = track.GetPdgCode(); + auto it = std::find(injectedPDGs.begin(), injectedPDGs.end(), pdg); + int index = std::distance(injectedPDGs.begin(), it); // index of injected PDG + if (it != injectedPDGs.end()) // found + { + // count signal PDG + nSignal[index]++; + if (track.getFirstDaughterTrackId() < 0) + { + nNotDecayed[index]++; + continue; + } + for (int j{track.getFirstDaughterTrackId()}; j <= track.getLastDaughterTrackId(); ++j) + { + auto pdgDau = tracks->at(j).GetPdgCode(); + bool foundDau = false; + // count decay PDGs + for (int idxDaughter = 0; idxDaughter < decayDaughters[index].size(); ++idxDaughter) + { + if (pdgDau == decayDaughters[index][idxDaughter]) + { + nDecays[index][idxDaughter]++; + foundDau = true; + hasInjection = true; + break; + } + } + if (!foundDau) + { + std::cerr << "Decay daughter not found: " << pdg << " -> " << pdgDau << "\n"; + } + } + } + } + if (!hasInjection) + { + numberOfEventsProcessedWithoutInjection++; + } + } + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + for (int i = 0; i < nInjection; i++) + { + std::cout << "# Mother \n"; + std::cout << injectedPDGs[i] << " generated: " << nSignal[i] << ", " << nNotDecayed[i] << " did not decay\n"; + if (nSignal[i] == 0) + { + std::cerr << "No generated: " << injectedPDGs[i] << "\n"; + // return 1; // At least one of the injected particles should be generated + } + for (int j = 0; j < decayDaughters[i].size(); j++) + { + std::cout << "# Daughter " << decayDaughters[i][j] << ": " << nDecays[i][j] << "\n"; + } + // if (nSignal[i] != nEvents * numberOfInjectedSignalsPerEvent) + // { + // std::cerr << "Number of generated: " << injectedPDGs[i] << ", lower than expected\n"; + // // return 1; // Don't need to return 1, since the number of generated particles is not the same for each event + // } + } + std::cout << "--------------------------------\n"; + std::cout << "Number of events processed: " << numberOfEventsProcessed << "\n"; + std::cout << "Number of input for the gap events: " << numberOfGapEvents << "\n"; + std::cout << "Number of events processed without injection: " << numberOfEventsProcessedWithoutInjection << "\n"; + // injected event + numberOfGapEvents*gap events + injected event + numberOfGapEvents*gap events + ... + // total fraction of the gap event: numberOfEventsProcessedWithoutInjection/numberOfEventsProcessed + float ratioOfNormalEvents = numberOfEventsProcessedWithoutInjection / numberOfEventsProcessed; + if (ratioOfNormalEvents > 0.75) + { + std::cout << "The number of injected event is loo low!!" << std::endl; + return 1; + } + + return 0; +} + +void GeneratorLF_Resonances_pp1360_injection() { External(); } diff --git a/MC/config/PWGLF/ini/tests/GeneratorLF_Resonances_pp5360_Rare.C b/MC/config/PWGLF/ini/tests/GeneratorLF_Resonances_pp5360_Rare.C new file mode 100644 index 000000000..b88244335 --- /dev/null +++ b/MC/config/PWGLF/ini/tests/GeneratorLF_Resonances_pp5360_Rare.C @@ -0,0 +1,112 @@ +// Dedicated kinematics test for Rare resonance injection in pp at 5.36 TeV. + +int External() +{ + const std::string path{"o2sim_Kine.root"}; + const int numberOfGapEvents{4}; // generateLFRapidity(..., gap=5) + const std::vector injectedPDGs = { + 9010221, // f0(980) + 3324, -3324, // Xi(1530)0 and anti-Xi(1530)0 + 123324, -123324, // Xi(1820)0 and anti-Xi(1820)0 + 123314, -123314, // Xi(1820)- and Xi(1820)+ + 123334, -123334 // Omega(2012)- and Omega(2012)+ + }; + const std::vector> decayDaughters = { + {211, -211}, + {3312, 211}, {-3312, -211}, + {3122, 310}, {-3122, 310}, + {3122, -321}, {-3122, 321}, + {3312, 310}, {-3312, 310} + }; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + auto tree = (TTree*)file.Get("o2sim"); + if (!tree) { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + std::vector nSignal(injectedPDGs.size(), 0); + std::vector nNotDecayed(injectedPDGs.size(), 0); + std::vector> nDecays; + for (const auto& daughters : decayDaughters) { + nDecays.emplace_back(daughters.size(), 0); + } + + int numberOfEventsProcessed{0}; + int numberOfEventsProcessedWithoutInjection{0}; + for (Long64_t i = 0; i < tree->GetEntries(); ++i) { + tree->GetEntry(i); + ++numberOfEventsProcessed; + bool hasInjection{false}; + for (const auto& track : *tracks) { + const auto pdg = track.GetPdgCode(); + const auto it = std::find(injectedPDGs.begin(), injectedPDGs.end(), pdg); + if (it == injectedPDGs.end()) { + continue; + } + const auto index = static_cast(std::distance(injectedPDGs.begin(), it)); + ++nSignal[index]; + if (track.getFirstDaughterTrackId() < 0) { + ++nNotDecayed[index]; + continue; + } + for (int j = track.getFirstDaughterTrackId(); j <= track.getLastDaughterTrackId(); ++j) { + const auto pdgDau = tracks->at(j).GetPdgCode(); + bool foundDau{false}; + for (size_t k = 0; k < decayDaughters[index].size(); ++k) { + if (pdgDau == decayDaughters[index][k]) { + ++nDecays[index][k]; + foundDau = true; + hasInjection = true; + break; + } + } + if (!foundDau) { + std::cerr << "Decay daughter not found: " << pdg << " -> " << pdgDau + << " (mother=" << track.getMotherTrackId() + << ", secondMother=" << track.getSecondMotherTrackId() << ")\n"; + } + } + } + if (!hasInjection) { + ++numberOfEventsProcessedWithoutInjection; + } + } + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << tree->GetEntries() << "\n"; + for (size_t i = 0; i < injectedPDGs.size(); ++i) { + std::cout << "# Mother\n"; + std::cout << injectedPDGs[i] << " generated: " << nSignal[i] + << ", " << nNotDecayed[i] << " did not decay\n"; + for (size_t j = 0; j < decayDaughters[i].size(); ++j) { + std::cout << "# Daughter " << decayDaughters[i][j] << ": " << nDecays[i][j] << "\n"; + } + } + std::cout << "--------------------------------\n"; + std::cout << "Number of events processed: " << numberOfEventsProcessed << "\n"; + std::cout << "Number of input for the gap events: " << numberOfGapEvents << "\n"; + std::cout << "Number of events processed without injection: " + << numberOfEventsProcessedWithoutInjection << "\n"; + const double ratioOfNormalEvents = numberOfEventsProcessed + ? static_cast(numberOfEventsProcessedWithoutInjection) / + numberOfEventsProcessed + : 0.0; + std::cout << "Fraction without injection: " << ratioOfNormalEvents << "\n"; + const double expectedRatio = static_cast(numberOfGapEvents) / (numberOfGapEvents + 1); + std::cout << "Expected fraction for 1+" << numberOfGapEvents << " pattern: " << expectedRatio << "\n"; + + // Same basic gap sanity check as the referenced O2DPG test. + if (ratioOfNormalEvents > 0.90 || ratioOfNormalEvents < 0.70) { + std::cerr << "The number of injected events is too low or too high\n"; + return 1; + } + return 0; +} diff --git a/MC/config/PWGLF/ini/tests/GeneratorLF_Resonances_pp900.C b/MC/config/PWGLF/ini/tests/GeneratorLF_Resonances_pp900.C new file mode 100644 index 000000000..a7864016f --- /dev/null +++ b/MC/config/PWGLF/ini/tests/GeneratorLF_Resonances_pp900.C @@ -0,0 +1,180 @@ +int External() +{ + std::string path{"o2sim_Kine.root"}; + int numberOfInjectedSignalsPerEvent{1}; + int numberOfGapEvents{4}; + int numberOfEventsProcessed{0}; + int numberOfEventsProcessedWithoutInjection{0}; + std::vector injectedPDGs = { + 323, // K*+- + -323, // K*bar+- + 9010221, // f_0(980) + 113, // rho(770)0 + 213, // rho(770)+ + -213, // rho(770)bar- + 3224, // Sigma(1385)+ + -3224, // Sigma(1385)bar- + 3114, // Sigma(1385)- + -3114, // Sigma(1385)bar+ + 3124, // Lambda(1520)0 + -3124, // Lambda(1520)0bar + 3324, // Xi(1530)0 + -3324, // Xi(1530)0bar + 10323, // K1(1270)+ + -10323, // K1(1270)-bar + 2224, // Delta(1232)+ + -2224, // Delta(1232)bar- + 2114, // Delta(1232)0 + -2114, // Delta(1232)0bar + 123314, // Xi(1820)- + -123314, // Xi(1820)+ + 123324, // Xi(1820)0 + -123324 // Xi(1820)0bar + }; + std::vector> decayDaughters = { + {311, 211}, // K*+- + {-311, -211}, // K*bar+- + {211, -211}, // f_0(980) + {211, -211}, // rho(770)0 + {211, 111}, // rho(770)+ + {-211, 111}, // rho(770)bar- + {3122, 211}, // Sigma(1385)+ + {-3122, -211}, // Sigma(1385)bar- + {3122, -211}, // Sigma(1385)- + {-3122, 211}, // Sigma(1385)bar+ + {2212, -321}, // Lambda(1520)0 + {-2212, 321}, // Lambda(1520)0bar + {3312, 211}, // Xi(1530)0 + {-3312, -211}, // Xi(1530)0bar + {321, 211}, // K1(1270)+ + {-321, -211}, // K1(1270)-bar + {2212, 211}, // Delta(1232)+ + {-2212, -211}, // Delta(1232)bar- + {2212, -211}, // Delta(1232)- + {-2212, 211}, // Delta(1232)bar+ + {3122, -311}, // Xi(1820)- + {3122, 311}, // Xi(1820)+ + {3122, 310}, // Xi(1820)0 + {-3122, 310} // Xi(1820)0bar + }; + + auto nInjection = injectedPDGs.size(); + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) + { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) + { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + std::vector nSignal; + for (int i = 0; i < nInjection; i++) + { + nSignal.push_back(0); + } + std::vector> nDecays; + std::vector nNotDecayed; + for (int i = 0; i < nInjection; i++) + { + std::vector nDecay; + for (int j = 0; j < decayDaughters[i].size(); j++) + { + nDecay.push_back(0); + } + nDecays.push_back(nDecay); + nNotDecayed.push_back(0); + } + auto nEvents = tree->GetEntries(); + bool hasInjection = false; + for (int i = 0; i < nEvents; i++) + { + hasInjection = false; + numberOfEventsProcessed++; + auto check = tree->GetEntry(i); + for (int idxMCTrack = 0; idxMCTrack < tracks->size(); ++idxMCTrack) + { + auto track = tracks->at(idxMCTrack); + auto pdg = track.GetPdgCode(); + auto it = std::find(injectedPDGs.begin(), injectedPDGs.end(), pdg); + int index = std::distance(injectedPDGs.begin(), it); // index of injected PDG + if (it != injectedPDGs.end()) // found + { + // count signal PDG + nSignal[index]++; + if(track.getFirstDaughterTrackId() < 0) + { + nNotDecayed[index]++; + continue; + } + for (int j{track.getFirstDaughterTrackId()}; j <= track.getLastDaughterTrackId(); ++j) + { + auto pdgDau = tracks->at(j).GetPdgCode(); + bool foundDau= false; + // count decay PDGs + for (int idxDaughter = 0; idxDaughter < decayDaughters[index].size(); ++idxDaughter) + { + if (pdgDau == decayDaughters[index][idxDaughter]) + { + nDecays[index][idxDaughter]++; + foundDau= true; + hasInjection = true; + break; + } + } + if (!foundDau) { + std::cerr << "Decay daughter not found: " << pdg << " -> " << pdgDau << "\n"; + } + } + } + } + if (!hasInjection) + { + numberOfEventsProcessedWithoutInjection++; + } + } + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + for (int i = 0; i < nInjection; i++) + { + std::cout << "# Mother \n"; + std::cout << injectedPDGs[i] << " generated: " << nSignal[i] << ", " << nNotDecayed[i] << " did not decay\n"; + if (nSignal[i] == 0){ + std::cerr << "No generated: " << injectedPDGs[i] << "\n"; + // return 1; // At least one of the injected particles should be generated + } + for (int j = 0; j < decayDaughters[i].size(); j++) + { + std::cout << "# Daughter " << decayDaughters[i][j] << ": " << nDecays[i][j] << "\n"; + } + // if (nSignal[i] != nEvents * numberOfInjectedSignalsPerEvent) + // { + // std::cerr << "Number of generated: " << injectedPDGs[i] << ", lower than expected\n"; + // // return 1; // Don't need to return 1, since the number of generated particles is not the same for each event + // } + } + std::cout << "--------------------------------\n"; + std::cout << "Number of events processed: " << numberOfEventsProcessed << "\n"; + std::cout << "Number of input for the gap events: " << numberOfGapEvents << "\n"; + std::cout << "Number of events processed without injection: " << numberOfEventsProcessedWithoutInjection << "\n"; + // injected event + numberOfGapEvents*gap events + injected event + numberOfGapEvents*gap events + ... + // total fraction of the gap event: numberOfEventsProcessedWithoutInjection/numberOfEventsProcessed + float ratioOfNormalEvents = numberOfEventsProcessedWithoutInjection/numberOfEventsProcessed; + if (ratioOfNormalEvents > 0.75) + { + std::cout << "The number of injected event is loo low!!" << std::endl; + return 1; + } + + return 0; +} + +void GeneratorLF_Resonances_pp900() { External(); } diff --git a/MC/config/PWGLF/ini/tests/GeneratorLF_Resonances_pp900_injection.C b/MC/config/PWGLF/ini/tests/GeneratorLF_Resonances_pp900_injection.C new file mode 100644 index 000000000..c4a01777c --- /dev/null +++ b/MC/config/PWGLF/ini/tests/GeneratorLF_Resonances_pp900_injection.C @@ -0,0 +1,146 @@ +int External() +{ + std::string path{"o2sim_Kine.root"}; + int numberOfInjectedSignalsPerEvent{1}; + int numberOfGapEvents{4}; + int numberOfEventsProcessed{0}; + int numberOfEventsProcessedWithoutInjection{0}; + std::vector injectedPDGs = { + 3124, // Lambda(1520)0 + -3124, // Lambda(1520)0bar + 123314, // Xi(1820)- + -123314, // Xi(1820)+ + 123324, // Xi(1820)0 + -123324 // Xi(1820)0bar + }; + std::vector> decayDaughters = { + {2212, -321}, // Lambda(1520)0 + {-2212, 321}, // Lambda(1520)0bar + {3122, -311}, // Xi(1820)- + {3122, 311}, // Xi(1820)+ + {3122, 310}, // Xi(1820)0 + {-3122, 310} // Xi(1820)0bar + }; + + auto nInjection = injectedPDGs.size(); + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) + { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) + { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + std::vector nSignal; + for (int i = 0; i < nInjection; i++) + { + nSignal.push_back(0); + } + std::vector> nDecays; + std::vector nNotDecayed; + for (int i = 0; i < nInjection; i++) + { + std::vector nDecay; + for (int j = 0; j < decayDaughters[i].size(); j++) + { + nDecay.push_back(0); + } + nDecays.push_back(nDecay); + nNotDecayed.push_back(0); + } + auto nEvents = tree->GetEntries(); + bool hasInjection = false; + for (int i = 0; i < nEvents; i++) + { + hasInjection = false; + numberOfEventsProcessed++; + auto check = tree->GetEntry(i); + for (int idxMCTrack = 0; idxMCTrack < tracks->size(); ++idxMCTrack) + { + auto track = tracks->at(idxMCTrack); + auto pdg = track.GetPdgCode(); + auto it = std::find(injectedPDGs.begin(), injectedPDGs.end(), pdg); + int index = std::distance(injectedPDGs.begin(), it); // index of injected PDG + if (it != injectedPDGs.end()) // found + { + // count signal PDG + nSignal[index]++; + if (track.getFirstDaughterTrackId() < 0) + { + nNotDecayed[index]++; + continue; + } + for (int j{track.getFirstDaughterTrackId()}; j <= track.getLastDaughterTrackId(); ++j) + { + auto pdgDau = tracks->at(j).GetPdgCode(); + bool foundDau = false; + // count decay PDGs + for (int idxDaughter = 0; idxDaughter < decayDaughters[index].size(); ++idxDaughter) + { + if (pdgDau == decayDaughters[index][idxDaughter]) + { + nDecays[index][idxDaughter]++; + foundDau = true; + hasInjection = true; + break; + } + } + if (!foundDau) + { + std::cerr << "Decay daughter not found: " << pdg << " -> " << pdgDau << "\n"; + } + } + } + } + if (!hasInjection) + { + numberOfEventsProcessedWithoutInjection++; + } + } + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + for (int i = 0; i < nInjection; i++) + { + std::cout << "# Mother \n"; + std::cout << injectedPDGs[i] << " generated: " << nSignal[i] << ", " << nNotDecayed[i] << " did not decay\n"; + if (nSignal[i] == 0) + { + std::cerr << "No generated: " << injectedPDGs[i] << "\n"; + // return 1; // At least one of the injected particles should be generated + } + for (int j = 0; j < decayDaughters[i].size(); j++) + { + std::cout << "# Daughter " << decayDaughters[i][j] << ": " << nDecays[i][j] << "\n"; + } + // if (nSignal[i] != nEvents * numberOfInjectedSignalsPerEvent) + // { + // std::cerr << "Number of generated: " << injectedPDGs[i] << ", lower than expected\n"; + // // return 1; // Don't need to return 1, since the number of generated particles is not the same for each event + // } + } + std::cout << "--------------------------------\n"; + std::cout << "Number of events processed: " << numberOfEventsProcessed << "\n"; + std::cout << "Number of input for the gap events: " << numberOfGapEvents << "\n"; + std::cout << "Number of events processed without injection: " << numberOfEventsProcessedWithoutInjection << "\n"; + // injected event + numberOfGapEvents*gap events + injected event + numberOfGapEvents*gap events + ... + // total fraction of the gap event: numberOfEventsProcessedWithoutInjection/numberOfEventsProcessed + float ratioOfNormalEvents = numberOfEventsProcessedWithoutInjection / numberOfEventsProcessed; + if (ratioOfNormalEvents > 0.75) + { + std::cout << "The number of injected event is loo low!!" << std::endl; + return 1; + } + + return 0; +} + +void GeneratorLF_Resonances_pp900_injection() { External(); } diff --git a/MC/config/PWGLF/ini/tests/GeneratorLF_Resonances_pp_Rare.C b/MC/config/PWGLF/ini/tests/GeneratorLF_Resonances_pp_Rare.C new file mode 100644 index 000000000..d19d72afc --- /dev/null +++ b/MC/config/PWGLF/ini/tests/GeneratorLF_Resonances_pp_Rare.C @@ -0,0 +1,111 @@ + +int External() +{ + const std::string path{"o2sim_Kine.root"}; + const int numberOfGapEvents{4}; // generateLFRapidity(..., gap=5) + const std::vector injectedPDGs = { + 9010221, // f0(980) + 3324, -3324, // Xi(1530)0 and anti-Xi(1530)0 + 123324, -123324, // Xi(1820)0 and anti-Xi(1820)0 + 123314, -123314, // Xi(1820)- and Xi(1820)+ + 123334, -123334 // Omega(2012)- and Omega(2012)+ + }; + const std::vector> decayDaughters = { + {211, -211}, + {3312, 211}, {-3312, -211}, + {3122, 310}, {-3122, 310}, + {3122, -321}, {-3122, 321}, + {3312, 310}, {-3312, 310} + }; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + auto tree = (TTree*)file.Get("o2sim"); + if (!tree) { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + std::vector nSignal(injectedPDGs.size(), 0); + std::vector nNotDecayed(injectedPDGs.size(), 0); + std::vector> nDecays; + for (const auto& daughters : decayDaughters) { + nDecays.emplace_back(daughters.size(), 0); + } + + int numberOfEventsProcessed{0}; + int numberOfEventsProcessedWithoutInjection{0}; + for (Long64_t i = 0; i < tree->GetEntries(); ++i) { + tree->GetEntry(i); + ++numberOfEventsProcessed; + bool hasInjection{false}; + for (const auto& track : *tracks) { + const auto pdg = track.GetPdgCode(); + const auto it = std::find(injectedPDGs.begin(), injectedPDGs.end(), pdg); + if (it == injectedPDGs.end()) { + continue; + } + const auto index = static_cast(std::distance(injectedPDGs.begin(), it)); + ++nSignal[index]; + if (track.getFirstDaughterTrackId() < 0) { + ++nNotDecayed[index]; + continue; + } + for (int j = track.getFirstDaughterTrackId(); j <= track.getLastDaughterTrackId(); ++j) { + const auto pdgDau = tracks->at(j).GetPdgCode(); + bool foundDau{false}; + for (size_t k = 0; k < decayDaughters[index].size(); ++k) { + if (pdgDau == decayDaughters[index][k]) { + ++nDecays[index][k]; + foundDau = true; + hasInjection = true; + break; + } + } + if (!foundDau) { + std::cerr << "Decay daughter not found: " << pdg << " -> " << pdgDau + << " (mother=" << track.getMotherTrackId() + << ", secondMother=" << track.getSecondMotherTrackId() << ")\n"; + } + } + } + if (!hasInjection) { + ++numberOfEventsProcessedWithoutInjection; + } + } + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << tree->GetEntries() << "\n"; + for (size_t i = 0; i < injectedPDGs.size(); ++i) { + std::cout << "# Mother\n"; + std::cout << injectedPDGs[i] << " generated: " << nSignal[i] + << ", " << nNotDecayed[i] << " did not decay\n"; + for (size_t j = 0; j < decayDaughters[i].size(); ++j) { + std::cout << "# Daughter " << decayDaughters[i][j] << ": " << nDecays[i][j] << "\n"; + } + } + std::cout << "--------------------------------\n"; + std::cout << "Number of events processed: " << numberOfEventsProcessed << "\n"; + std::cout << "Number of input for the gap events: " << numberOfGapEvents << "\n"; + std::cout << "Number of events processed without injection: " + << numberOfEventsProcessedWithoutInjection << "\n"; + const double ratioOfNormalEvents = numberOfEventsProcessed + ? static_cast(numberOfEventsProcessedWithoutInjection) / + numberOfEventsProcessed + : 0.0; + std::cout << "Fraction without injection: " << ratioOfNormalEvents << "\n"; + const double expectedRatio = static_cast(numberOfGapEvents) / (numberOfGapEvents + 1); + std::cout << "Expected fraction for 1+" << numberOfGapEvents << " pattern: " << expectedRatio << "\n"; + + // Same basic gap sanity check as the referenced O2DPG test. + if (ratioOfNormalEvents > 0.90 || ratioOfNormalEvents < 0.70) { + std::cerr << "The number of injected events is too low or too high\n"; + return 1; + } + return 0; +} diff --git a/MC/config/PWGLF/ini/tests/GeneratorLF_Resonances_pp_exotic.C b/MC/config/PWGLF/ini/tests/GeneratorLF_Resonances_pp_exotic.C new file mode 100644 index 000000000..5155fbebc --- /dev/null +++ b/MC/config/PWGLF/ini/tests/GeneratorLF_Resonances_pp_exotic.C @@ -0,0 +1,146 @@ +int External() +{ + std::string path{"o2sim_Kine.root"}; + int numberOfInjectedSignalsPerEvent{1}; + int numberOfGapEvents{4}; + int numberOfEventsProcessed{0}; + int numberOfEventsProcessedWithoutInjection{0}; + std::vector injectedPDGs = { + 225, // f_2(1270) + 115, // a_2(1320) + 10221, // f_0(1370) + 9030221, // f_0(1500) + 335, // f_2(1525) + 10331 // f_0(1710) + }; + std::vector> decayDaughters = { + {310, 310}, // f_2(1270) + {310, 310}, // a_2(1320) + {310, 310}, // f_0(1370) + {310, 310}, // f_0(1500) + {310, 310}, // f_2(1525) + {310, 310} // f_0(1710) + }; + + auto nInjection = injectedPDGs.size(); + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) + { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) + { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + std::vector nSignal; + for (int i = 0; i < nInjection; i++) + { + nSignal.push_back(0); + } + std::vector> nDecays; + std::vector nNotDecayed; + for (int i = 0; i < nInjection; i++) + { + std::vector nDecay; + for (int j = 0; j < decayDaughters[i].size(); j++) + { + nDecay.push_back(0); + } + nDecays.push_back(nDecay); + nNotDecayed.push_back(0); + } + auto nEvents = tree->GetEntries(); + bool hasInjection = false; + for (int i = 0; i < nEvents; i++) + { + hasInjection = false; + numberOfEventsProcessed++; + auto check = tree->GetEntry(i); + for (int idxMCTrack = 0; idxMCTrack < tracks->size(); ++idxMCTrack) + { + auto track = tracks->at(idxMCTrack); + auto pdg = track.GetPdgCode(); + auto it = std::find(injectedPDGs.begin(), injectedPDGs.end(), pdg); + int index = std::distance(injectedPDGs.begin(), it); // index of injected PDG + if (it != injectedPDGs.end()) // found + { + // count signal PDG + nSignal[index]++; + if (track.getFirstDaughterTrackId() < 0) + { + nNotDecayed[index]++; + continue; + } + for (int j{track.getFirstDaughterTrackId()}; j <= track.getLastDaughterTrackId(); ++j) + { + auto pdgDau = tracks->at(j).GetPdgCode(); + bool foundDau = false; + // count decay PDGs + for (int idxDaughter = 0; idxDaughter < decayDaughters[index].size(); ++idxDaughter) + { + if (pdgDau == decayDaughters[index][idxDaughter]) + { + nDecays[index][idxDaughter]++; + foundDau = true; + hasInjection = true; + break; + } + } + if (!foundDau) + { + std::cerr << "Decay daughter not found: " << pdg << " -> " << pdgDau << "\n"; + } + } + } + } + if (!hasInjection) + { + numberOfEventsProcessedWithoutInjection++; + } + } + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + for (int i = 0; i < nInjection; i++) + { + std::cout << "# Mother \n"; + std::cout << injectedPDGs[i] << " generated: " << nSignal[i] << ", " << nNotDecayed[i] << " did not decay\n"; + if (nSignal[i] == 0) + { + std::cerr << "No generated: " << injectedPDGs[i] << "\n"; + // return 1; // At least one of the injected particles should be generated + } + for (int j = 0; j < decayDaughters[i].size(); j++) + { + std::cout << "# Daughter " << decayDaughters[i][j] << ": " << nDecays[i][j] << "\n"; + } + // if (nSignal[i] != nEvents * numberOfInjectedSignalsPerEvent) + // { + // std::cerr << "Number of generated: " << injectedPDGs[i] << ", lower than expected\n"; + // // return 1; // Don't need to return 1, since the number of generated particles is not the same for each event + // } + } + std::cout << "--------------------------------\n"; + std::cout << "Number of events processed: " << numberOfEventsProcessed << "\n"; + std::cout << "Number of input for the gap events: " << numberOfGapEvents << "\n"; + std::cout << "Number of events processed without injection: " << numberOfEventsProcessedWithoutInjection << "\n"; + // injected event + numberOfGapEvents*gap events + injected event + numberOfGapEvents*gap events + ... + // total fraction of the gap event: numberOfEventsProcessedWithoutInjection/numberOfEventsProcessed + float ratioOfNormalEvents = numberOfEventsProcessedWithoutInjection / numberOfEventsProcessed; + if (ratioOfNormalEvents > 0.75) + { + std::cout << "The number of injected event is loo low!!" << std::endl; + return 1; + } + + return 0; +} + +void GeneratorLF_Resonances_pp1360_injection() { External(); } diff --git a/MC/config/PWGLF/ini/tests/GeneratorLF_Resonances_pp_exoticAll.C b/MC/config/PWGLF/ini/tests/GeneratorLF_Resonances_pp_exoticAll.C new file mode 100644 index 000000000..6fe8638d7 --- /dev/null +++ b/MC/config/PWGLF/ini/tests/GeneratorLF_Resonances_pp_exoticAll.C @@ -0,0 +1,167 @@ +int External() +{ + std::string path{"o2sim_Kine.root"}; + int numberOfInjectedSignalsPerEvent{1}; + int numberOfGapEvents{4}; + int numberOfEventsProcessed{0}; + int numberOfEventsProcessedWithoutInjection{0}; + std::vector injectedPDGs = { + 9010221, // f_0(980) + 225, // f_2(1270) + 115, // a_2(1320) + 10221, // f_0(1370) + 9030221, // f_0(1500) + 335, // f_2(1525) + 10331, // f_0(1710) + 20223, // f_1(1285) + 20333, // f_1(1420) + 335, // f_2(1525) + 10323, // K1(1270)+ + -10323, // K1(1270)-bar + 123314, // Xi(1820)- + -123314, // Xi(1820)+ + 123324, // Xi(1820)0 + -123324 // Xi(1820)0bar + }; + std::vector> decayDaughters = { + {211, -211}, // f_0(980) + {310, 310}, // f_2(1270) + {310, 310}, // a_2(1320) + {310, 310}, // f_0(1370) + {310, 310}, // f_0(1500) + {310, 310}, // f_2(1525) + {310, 310}, // f_0(1710) + {310, -321, 211}, // f_1(1285) + {310, -321, 211}, // f_1(1420) + {310, 310}, // f_2(1525) + {321, 211}, // K1(1270)+ + {-321, -211}, // K1(1270)-bar + {2212, 211}, // Delta(1232)+ + {3122, -311}, // Xi(1820)- + {3122, 311}, // Xi(1820)+ + {3122, 310}, // Xi(1820)0 + {-3122, 310} // Xi(1820)0bar + }; + + auto nInjection = injectedPDGs.size(); + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) + { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) + { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + std::vector nSignal; + for (int i = 0; i < nInjection; i++) + { + nSignal.push_back(0); + } + std::vector> nDecays; + std::vector nNotDecayed; + for (int i = 0; i < nInjection; i++) + { + std::vector nDecay; + for (int j = 0; j < decayDaughters[i].size(); j++) + { + nDecay.push_back(0); + } + nDecays.push_back(nDecay); + nNotDecayed.push_back(0); + } + auto nEvents = tree->GetEntries(); + bool hasInjection = false; + for (int i = 0; i < nEvents; i++) + { + hasInjection = false; + numberOfEventsProcessed++; + auto check = tree->GetEntry(i); + for (int idxMCTrack = 0; idxMCTrack < tracks->size(); ++idxMCTrack) + { + auto track = tracks->at(idxMCTrack); + auto pdg = track.GetPdgCode(); + auto it = std::find(injectedPDGs.begin(), injectedPDGs.end(), pdg); + int index = std::distance(injectedPDGs.begin(), it); // index of injected PDG + if (it != injectedPDGs.end()) // found + { + // count signal PDG + nSignal[index]++; + if (track.getFirstDaughterTrackId() < 0) + { + nNotDecayed[index]++; + continue; + } + for (int j{track.getFirstDaughterTrackId()}; j <= track.getLastDaughterTrackId(); ++j) + { + auto pdgDau = tracks->at(j).GetPdgCode(); + bool foundDau = false; + // count decay PDGs + for (int idxDaughter = 0; idxDaughter < decayDaughters[index].size(); ++idxDaughter) + { + if (pdgDau == decayDaughters[index][idxDaughter]) + { + nDecays[index][idxDaughter]++; + foundDau = true; + hasInjection = true; + break; + } + } + if (!foundDau) + { + std::cerr << "Decay daughter not found: " << pdg << " -> " << pdgDau << "\n"; + } + } + } + } + if (!hasInjection) + { + numberOfEventsProcessedWithoutInjection++; + } + } + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + for (int i = 0; i < nInjection; i++) + { + std::cout << "# Mother \n"; + std::cout << injectedPDGs[i] << " generated: " << nSignal[i] << ", " << nNotDecayed[i] << " did not decay\n"; + if (nSignal[i] == 0) + { + std::cerr << "No generated: " << injectedPDGs[i] << "\n"; + // return 1; // At least one of the injected particles should be generated + } + for (int j = 0; j < decayDaughters[i].size(); j++) + { + std::cout << "# Daughter " << decayDaughters[i][j] << ": " << nDecays[i][j] << "\n"; + } + // if (nSignal[i] != nEvents * numberOfInjectedSignalsPerEvent) + // { + // std::cerr << "Number of generated: " << injectedPDGs[i] << ", lower than expected\n"; + // // return 1; // Don't need to return 1, since the number of generated particles is not the same for each event + // } + } + std::cout << "--------------------------------\n"; + std::cout << "Number of events processed: " << numberOfEventsProcessed << "\n"; + std::cout << "Number of input for the gap events: " << numberOfGapEvents << "\n"; + std::cout << "Number of events processed without injection: " << numberOfEventsProcessedWithoutInjection << "\n"; + // injected event + numberOfGapEvents*gap events + injected event + numberOfGapEvents*gap events + ... + // total fraction of the gap event: numberOfEventsProcessedWithoutInjection/numberOfEventsProcessed + float ratioOfNormalEvents = numberOfEventsProcessedWithoutInjection / numberOfEventsProcessed; + if (ratioOfNormalEvents > 0.75) + { + std::cout << "The number of injected event is loo low!!" << std::endl; + return 1; + } + + return 0; +} + +void GeneratorLF_Resonances_pp1360_injection() { External(); } diff --git a/MC/config/PWGLF/ini/tests/GeneratorLF_Resonances_pp_exoticAll_gap2.C b/MC/config/PWGLF/ini/tests/GeneratorLF_Resonances_pp_exoticAll_gap2.C new file mode 100644 index 000000000..74076d41c --- /dev/null +++ b/MC/config/PWGLF/ini/tests/GeneratorLF_Resonances_pp_exoticAll_gap2.C @@ -0,0 +1,154 @@ +int External() +{ + std::string path{"o2sim_Kine.root"}; + int numberOfInjectedSignalsPerEvent{1}; + int numberOfGapEvents{4}; + int numberOfEventsProcessed{0}; + int numberOfEventsProcessedWithoutInjection{0}; + std::vector injectedPDGs = { + 225, // f_2(1270) + 10221, // f_0(1370) + 9030221, // f_0(1500) + 10331, // f_0(1710) + 20223, // f_1(1285) + 20333, // f_1(1420) + 335, // f_2(1525) + 115, // a_2(1320) + 102134, // Lambda(1520)0 + -102134 // Lambda(1520)0bar + }; + std::vector> decayDaughters = { + {310, 310}, // f_2(1270) + {310, 310}, // f_0(1370) + {310, 310}, // f_0(1500) + {310, 310}, // f_0(1710) + {310, -321, 211}, // f_1(1285) + {310, -321, 211}, // f_1(1420) + {310, 310}, // f_2(1525) + {310, 310}, // a_2(1320) + {2212, -321}, // Lambda(1520)0 + {-2212, 321} // Lambda(1520)0bar + }; + + auto nInjection = injectedPDGs.size(); + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) + { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) + { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + std::vector nSignal; + for (int i = 0; i < nInjection; i++) + { + nSignal.push_back(0); + } + std::vector> nDecays; + std::vector nNotDecayed; + for (int i = 0; i < nInjection; i++) + { + std::vector nDecay; + for (int j = 0; j < decayDaughters[i].size(); j++) + { + nDecay.push_back(0); + } + nDecays.push_back(nDecay); + nNotDecayed.push_back(0); + } + auto nEvents = tree->GetEntries(); + bool hasInjection = false; + for (int i = 0; i < nEvents; i++) + { + hasInjection = false; + numberOfEventsProcessed++; + auto check = tree->GetEntry(i); + for (int idxMCTrack = 0; idxMCTrack < tracks->size(); ++idxMCTrack) + { + auto track = tracks->at(idxMCTrack); + auto pdg = track.GetPdgCode(); + auto it = std::find(injectedPDGs.begin(), injectedPDGs.end(), pdg); + int index = std::distance(injectedPDGs.begin(), it); // index of injected PDG + if (it != injectedPDGs.end()) // found + { + // count signal PDG + nSignal[index]++; + if (track.getFirstDaughterTrackId() < 0) + { + nNotDecayed[index]++; + continue; + } + for (int j{track.getFirstDaughterTrackId()}; j <= track.getLastDaughterTrackId(); ++j) + { + auto pdgDau = tracks->at(j).GetPdgCode(); + bool foundDau = false; + // count decay PDGs + for (int idxDaughter = 0; idxDaughter < decayDaughters[index].size(); ++idxDaughter) + { + if (pdgDau == decayDaughters[index][idxDaughter]) + { + nDecays[index][idxDaughter]++; + foundDau = true; + hasInjection = true; + break; + } + } + if (!foundDau) + { + std::cerr << "Decay daughter not found: " << pdg << " -> " << pdgDau << "\n"; + } + } + } + } + if (!hasInjection) + { + numberOfEventsProcessedWithoutInjection++; + } + } + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + for (int i = 0; i < nInjection; i++) + { + std::cout << "# Mother \n"; + std::cout << injectedPDGs[i] << " generated: " << nSignal[i] << ", " << nNotDecayed[i] << " did not decay\n"; + if (nSignal[i] == 0) + { + std::cerr << "No generated: " << injectedPDGs[i] << "\n"; + // return 1; // At least one of the injected particles should be generated + } + for (int j = 0; j < decayDaughters[i].size(); j++) + { + std::cout << "# Daughter " << decayDaughters[i][j] << ": " << nDecays[i][j] << "\n"; + } + // if (nSignal[i] != nEvents * numberOfInjectedSignalsPerEvent) + // { + // std::cerr << "Number of generated: " << injectedPDGs[i] << ", lower than expected\n"; + // // return 1; // Don't need to return 1, since the number of generated particles is not the same for each event + // } + } + std::cout << "--------------------------------\n"; + std::cout << "Number of events processed: " << numberOfEventsProcessed << "\n"; + std::cout << "Number of input for the gap events: " << numberOfGapEvents << "\n"; + std::cout << "Number of events processed without injection: " << numberOfEventsProcessedWithoutInjection << "\n"; + // injected event + numberOfGapEvents*gap events + injected event + numberOfGapEvents*gap events + ... + // total fraction of the gap event: numberOfEventsProcessedWithoutInjection/numberOfEventsProcessed + float ratioOfNormalEvents = numberOfEventsProcessedWithoutInjection / numberOfEventsProcessed; + if (ratioOfNormalEvents > 0.75) + { + std::cout << "The number of injected event is loo low!!" << std::endl; + return 1; + } + + return 0; +} + +void GeneratorLF_Resonances_pp1360_injection() { External(); } diff --git a/MC/config/PWGLF/ini/tests/GeneratorLF_StrangenessInJets_gap4.C b/MC/config/PWGLF/ini/tests/GeneratorLF_StrangenessInJets_gap4.C new file mode 100644 index 000000000..12f069f3c --- /dev/null +++ b/MC/config/PWGLF/ini/tests/GeneratorLF_StrangenessInJets_gap4.C @@ -0,0 +1,27 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + auto nXi = tree->Scan("MCTrack.GetPdgCode()", "TMath::Abs(MCTrack.GetPdgCode()) == 3312"); + auto nOmega = tree->Scan("MCTrack.GetPdgCode()", "TMath::Abs(MCTrack.GetPdgCode()) == 3334"); + auto nStr = nXi+nOmega; + + if (nStr == 0) { + std::cerr << "No event of interest\n"; + return 1; + } + return 0; +} diff --git a/MC/config/PWGLF/ini/tests/GeneratorLF_Strangeness_OO5360_injection.C b/MC/config/PWGLF/ini/tests/GeneratorLF_Strangeness_OO5360_injection.C new file mode 100644 index 000000000..ab7eeb695 --- /dev/null +++ b/MC/config/PWGLF/ini/tests/GeneratorLF_Strangeness_OO5360_injection.C @@ -0,0 +1,28 @@ +int External() +{ + std::string path{"o2sim_Kine.root"}; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) + { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) + { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + auto nEvents = tree->GetEntries(); + if (nEvents < 1) + { + std::cerr << "No events actually generated: not OK!"; + return 1; + } + return 0; +} diff --git a/MC/config/PWGLF/ini/tests/GeneratorLF_Strangeness_OO5360_injection_omega100.C b/MC/config/PWGLF/ini/tests/GeneratorLF_Strangeness_OO5360_injection_omega100.C new file mode 120000 index 000000000..fe732fe72 --- /dev/null +++ b/MC/config/PWGLF/ini/tests/GeneratorLF_Strangeness_OO5360_injection_omega100.C @@ -0,0 +1 @@ +GeneratorLF_SyntheFlow.C \ No newline at end of file diff --git a/MC/config/PWGLF/ini/tests/GeneratorLF_Strangeness_OO5360_injection_omega30.C b/MC/config/PWGLF/ini/tests/GeneratorLF_Strangeness_OO5360_injection_omega30.C new file mode 120000 index 000000000..fe732fe72 --- /dev/null +++ b/MC/config/PWGLF/ini/tests/GeneratorLF_Strangeness_OO5360_injection_omega30.C @@ -0,0 +1 @@ +GeneratorLF_SyntheFlow.C \ No newline at end of file diff --git a/MC/config/PWGLF/ini/tests/GeneratorLF_Strangeness_OO5360_injection_omega300.C b/MC/config/PWGLF/ini/tests/GeneratorLF_Strangeness_OO5360_injection_omega300.C new file mode 120000 index 000000000..fe732fe72 --- /dev/null +++ b/MC/config/PWGLF/ini/tests/GeneratorLF_Strangeness_OO5360_injection_omega300.C @@ -0,0 +1 @@ +GeneratorLF_SyntheFlow.C \ No newline at end of file diff --git a/MC/config/PWGLF/ini/tests/GeneratorLF_Strangeness_OO5360_injection_omega50.C b/MC/config/PWGLF/ini/tests/GeneratorLF_Strangeness_OO5360_injection_omega50.C new file mode 120000 index 000000000..fe732fe72 --- /dev/null +++ b/MC/config/PWGLF/ini/tests/GeneratorLF_Strangeness_OO5360_injection_omega50.C @@ -0,0 +1 @@ +GeneratorLF_SyntheFlow.C \ No newline at end of file diff --git a/MC/config/PWGLF/ini/tests/GeneratorLF_Strangeness_OO5360_injection_omega500.C b/MC/config/PWGLF/ini/tests/GeneratorLF_Strangeness_OO5360_injection_omega500.C new file mode 120000 index 000000000..fe732fe72 --- /dev/null +++ b/MC/config/PWGLF/ini/tests/GeneratorLF_Strangeness_OO5360_injection_omega500.C @@ -0,0 +1 @@ +GeneratorLF_SyntheFlow.C \ No newline at end of file diff --git a/MC/config/PWGLF/ini/tests/GeneratorLF_Strangeness_PbPb5360_injection.C b/MC/config/PWGLF/ini/tests/GeneratorLF_Strangeness_PbPb5360_injection.C new file mode 100644 index 000000000..ab7eeb695 --- /dev/null +++ b/MC/config/PWGLF/ini/tests/GeneratorLF_Strangeness_PbPb5360_injection.C @@ -0,0 +1,28 @@ +int External() +{ + std::string path{"o2sim_Kine.root"}; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) + { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) + { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + auto nEvents = tree->GetEntries(); + if (nEvents < 1) + { + std::cerr << "No events actually generated: not OK!"; + return 1; + } + return 0; +} diff --git a/MC/config/PWGLF/ini/tests/GeneratorLF_Strangeness_ppLStar.C b/MC/config/PWGLF/ini/tests/GeneratorLF_Strangeness_ppLStar.C new file mode 100644 index 000000000..c69a84d5d --- /dev/null +++ b/MC/config/PWGLF/ini/tests/GeneratorLF_Strangeness_ppLStar.C @@ -0,0 +1,95 @@ +int External() +{ + std::string path{"o2sim_Kine.root"}; + const int pdgMother = 3124; // Lambda(1520)0 + const int triggerRatio = 5; // must match generator_Lstar(N) in the .ini file + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + const auto nEvents = tree->GetEntries(); + int nMother = 0; + int nNotDecayed = 0; + int nBadDecay = 0; // mothers not decaying exactly to Lambda + gamma + int nEventsWrongCount = 0; // events with the wrong number of injected mothers + int nGapEvents = 0; // events expected to contain the injected mother + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + + // the generator injects in every triggerRatio-th event (it counts events from 1) + const bool isGapEvent = ((i + 1) % triggerRatio == 0); + if (isGapEvent) { + nGapEvents++; + } + + int nInEvent = 0; + for (auto &track : *tracks) { + const int pdg = track.GetPdgCode(); + if (std::abs(pdg) != pdgMother) { + continue; + } + nInEvent++; + nMother++; + + if (track.getFirstDaughterTrackId() < 0) { + nNotDecayed++; + continue; + } + const int expectedLambda = (pdg > 0) ? 3122 : -3122; + int nLambda = 0, nGamma = 0, nDau = 0; + for (int j = track.getFirstDaughterTrackId(); j <= track.getLastDaughterTrackId(); ++j) { + const int pdgDau = tracks->at(j).GetPdgCode(); + nDau++; + if (pdgDau == expectedLambda) nLambda++; + if (pdgDau == 22) nGamma++; + } + if (nDau != 2 || nLambda != 1 || nGamma != 1) { + nBadDecay++; + std::cerr << "Unexpected decay of " << pdg << " (" << nDau << " daughters)\n"; + } + } + + const int nExpected = isGapEvent ? 1 : 0; + if (nInEvent != nExpected) { + nEventsWrongCount++; + std::cerr << "Event " << i << ": found " << nInEvent + << " Lambda(1520), expected " << nExpected << "\n"; + } + } + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + std::cout << "# Gap-trigger events: " << nGapEvents << " (ratio " << triggerRatio << ")\n"; + std::cout << "# Lambda(1520) + anti: " << nMother << "\n"; + std::cout << "# not decayed: " << nNotDecayed << "\n"; + std::cout << "# unexpected decays: " << nBadDecay << "\n"; + std::cout << "# events wrong count: " << nEventsWrongCount << "\n"; + std::cout << "--------------------------------\n"; + + if (nMother != nGapEvents) { + std::cerr << "Expected one injected Lambda(1520) per gap-trigger event\n"; + return 1; + } + if (nEventsWrongCount > 0) { + std::cerr << "Lambda(1520) must appear in every gap-triggered events\n"; + return 1; + } + if (nNotDecayed > 0 || nBadDecay > 0) { + std::cerr << "Lambda(1520) must always decay to Lambda + gamma\n"; + return 1; + } + return 0; +} + +void GeneratorLF_Strangeness_ppLStar() { External(); } \ No newline at end of file diff --git a/MC/config/PWGLF/ini/tests/GeneratorLF_SyntheFlow.C b/MC/config/PWGLF/ini/tests/GeneratorLF_SyntheFlow.C new file mode 100644 index 000000000..ab7eeb695 --- /dev/null +++ b/MC/config/PWGLF/ini/tests/GeneratorLF_SyntheFlow.C @@ -0,0 +1,28 @@ +int External() +{ + std::string path{"o2sim_Kine.root"}; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) + { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) + { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + auto nEvents = tree->GetEntries(); + if (nEvents < 1) + { + std::cerr << "No events actually generated: not OK!"; + return 1; + } + return 0; +} diff --git a/MC/config/PWGLF/ini/tests/GeneratorLF_SyntheFlowOO.C b/MC/config/PWGLF/ini/tests/GeneratorLF_SyntheFlowOO.C new file mode 100644 index 000000000..ab7eeb695 --- /dev/null +++ b/MC/config/PWGLF/ini/tests/GeneratorLF_SyntheFlowOO.C @@ -0,0 +1,28 @@ +int External() +{ + std::string path{"o2sim_Kine.root"}; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) + { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) + { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + auto nEvents = tree->GetEntries(); + if (nEvents < 1) + { + std::cerr << "No events actually generated: not OK!"; + return 1; + } + return 0; +} diff --git a/MC/config/PWGLF/ini/tests/GeneratorLF_SyntheFlowXi.C b/MC/config/PWGLF/ini/tests/GeneratorLF_SyntheFlowXi.C new file mode 100644 index 000000000..ab7eeb695 --- /dev/null +++ b/MC/config/PWGLF/ini/tests/GeneratorLF_SyntheFlowXi.C @@ -0,0 +1,28 @@ +int External() +{ + std::string path{"o2sim_Kine.root"}; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) + { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) + { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + auto nEvents = tree->GetEntries(); + if (nEvents < 1) + { + std::cerr << "No events actually generated: not OK!"; + return 1; + } + return 0; +} diff --git a/MC/config/PWGLF/ini/tests/GeneratorLF_antid_and_highpt.C b/MC/config/PWGLF/ini/tests/GeneratorLF_antid_and_highpt.C new file mode 100644 index 000000000..ab733edb5 --- /dev/null +++ b/MC/config/PWGLF/ini/tests/GeneratorLF_antid_and_highpt.C @@ -0,0 +1,25 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + auto nEvents = tree->GetEntries(); + auto nSelected = tree->Scan("MCTrack.GetPdgCode()", "TMath::Abs(MCTrack.GetPdgCode()) == 1000010020"); + if (nSelected == 0) { + std::cerr << "No event of interest\n"; + return 1; + } + return 0; +} diff --git a/MC/config/PWGLF/ini/tests/GeneratorLF_deuteron_wigner.C b/MC/config/PWGLF/ini/tests/GeneratorLF_deuteron_wigner.C new file mode 100644 index 000000000..3cf153204 --- /dev/null +++ b/MC/config/PWGLF/ini/tests/GeneratorLF_deuteron_wigner.C @@ -0,0 +1,41 @@ + +int External() { + std::string path{"o2sim_Kine.root"}; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + + std::vector* tracks = nullptr; + tree->SetBranchAddress("MCTrack", &tracks); + + Long64_t nEntries = tree->GetEntries(); + int nSelected = 0; + + for (Long64_t i = 0; i < nEntries; ++i) { + tree->GetEntry(i); + for (const auto& track : *tracks) { + if (std::abs(track.GetPdgCode()) == 1000010020) { // Deuteron + ++nSelected; + break; // Found in this event + } + } + } + + if (nSelected == 0) { + std::cout << "No events with deuterons found.\n"; + } else { + std::cout << "Found " << nSelected << " events with deuterons\n"; + } + return 0; + +} + diff --git a/MC/config/PWGLF/ini/tests/GeneratorLF_doublephi_trigger.C b/MC/config/PWGLF/ini/tests/GeneratorLF_doublephi_trigger.C new file mode 100644 index 000000000..58767f843 --- /dev/null +++ b/MC/config/PWGLF/ini/tests/GeneratorLF_doublephi_trigger.C @@ -0,0 +1,76 @@ +int External() +{ + const std::string path{"o2sim_Kine.root"}; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) + { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) + { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + // Counters + int nMBPhi = 0; + int nKPlusFromMBPhi = 0; + int nKMinusFromMBPhi = 0; + int numberOfEventsProcessed = 0; + + for (Long64_t i = 0; i < tree->GetEntries(); ++i) + { + tree->GetEntry(i); + ++numberOfEventsProcessed; + + for (size_t idx = 0; idx < tracks->size(); ++idx) + { + const auto &track = tracks->at(idx); + const auto pdg = track.GetPdgCode(); + + if (pdg == 333) + { + ++nMBPhi; + + if (track.getFirstDaughterTrackId() >= 0) + { + for (int j = track.getFirstDaughterTrackId(); j <= track.getLastDaughterTrackId(); ++j) + { + auto dauPdg = tracks->at(j).GetPdgCode(); + if (dauPdg == 321) + { + ++nKPlusFromMBPhi; + } + if (dauPdg == -321) + { + ++nKMinusFromMBPhi; + } + } + } + } + } + } + + // --------------------------- Output --------------------------- + std::cout << "=================================================\n"; + std::cout << "Total Events: " << tree->GetEntries() << "\n\n"; + std::cout << "Total events processed: " << numberOfEventsProcessed << "\n"; + std::cout << "Total Minimum Bias Phi (333): " << nMBPhi << "\n"; + std::cout << " -> Decayed to K+: " << nKPlusFromMBPhi << "\n"; + std::cout << " -> Decayed to K-: " << nKMinusFromMBPhi << "\n"; + std::cout << "=================================================\n"; + + return 0; +} + +void GeneratorLF_doublephi_trigger() +{ + External(); +} diff --git a/MC/config/PWGLF/ini/tests/GeneratorLF_doublephi_triggerMasspTcut.C b/MC/config/PWGLF/ini/tests/GeneratorLF_doublephi_triggerMasspTcut.C new file mode 100644 index 000000000..58767f843 --- /dev/null +++ b/MC/config/PWGLF/ini/tests/GeneratorLF_doublephi_triggerMasspTcut.C @@ -0,0 +1,76 @@ +int External() +{ + const std::string path{"o2sim_Kine.root"}; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) + { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) + { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + // Counters + int nMBPhi = 0; + int nKPlusFromMBPhi = 0; + int nKMinusFromMBPhi = 0; + int numberOfEventsProcessed = 0; + + for (Long64_t i = 0; i < tree->GetEntries(); ++i) + { + tree->GetEntry(i); + ++numberOfEventsProcessed; + + for (size_t idx = 0; idx < tracks->size(); ++idx) + { + const auto &track = tracks->at(idx); + const auto pdg = track.GetPdgCode(); + + if (pdg == 333) + { + ++nMBPhi; + + if (track.getFirstDaughterTrackId() >= 0) + { + for (int j = track.getFirstDaughterTrackId(); j <= track.getLastDaughterTrackId(); ++j) + { + auto dauPdg = tracks->at(j).GetPdgCode(); + if (dauPdg == 321) + { + ++nKPlusFromMBPhi; + } + if (dauPdg == -321) + { + ++nKMinusFromMBPhi; + } + } + } + } + } + } + + // --------------------------- Output --------------------------- + std::cout << "=================================================\n"; + std::cout << "Total Events: " << tree->GetEntries() << "\n\n"; + std::cout << "Total events processed: " << numberOfEventsProcessed << "\n"; + std::cout << "Total Minimum Bias Phi (333): " << nMBPhi << "\n"; + std::cout << " -> Decayed to K+: " << nKPlusFromMBPhi << "\n"; + std::cout << " -> Decayed to K-: " << nKMinusFromMBPhi << "\n"; + std::cout << "=================================================\n"; + + return 0; +} + +void GeneratorLF_doublephi_trigger() +{ + External(); +} diff --git a/MC/config/PWGLF/ini/tests/GeneratorLF_highpt_strangeness.C b/MC/config/PWGLF/ini/tests/GeneratorLF_highpt_strangeness.C new file mode 100644 index 000000000..397b32845 --- /dev/null +++ b/MC/config/PWGLF/ini/tests/GeneratorLF_highpt_strangeness.C @@ -0,0 +1,29 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + auto nK0 = tree->Scan("MCTrack.GetPdgCode()", "TMath::Abs(MCTrack.GetPdgCode()) == 310"); + auto nLambda = tree->Scan("MCTrack.GetPdgCode()", "TMath::Abs(MCTrack.GetPdgCode()) == 3122"); + auto nXi = tree->Scan("MCTrack.GetPdgCode()", "TMath::Abs(MCTrack.GetPdgCode()) == 3312"); + auto nOmega = tree->Scan("MCTrack.GetPdgCode()", "TMath::Abs(MCTrack.GetPdgCode()) == 3334"); + auto nStr = nK0+nLambda+nXi+nOmega; + + if (nStr == 0) { + std::cerr << "No event of interest\n"; + return 1; + } + return 0; +} diff --git a/MC/config/PWGLF/ini/tests/GeneratorLF_phiphiInvMass.C b/MC/config/PWGLF/ini/tests/GeneratorLF_phiphiInvMass.C new file mode 100644 index 000000000..f86d9e588 --- /dev/null +++ b/MC/config/PWGLF/ini/tests/GeneratorLF_phiphiInvMass.C @@ -0,0 +1,144 @@ +int External() +{ + const std::string path{"o2sim_Kine.root"}; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) + { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) + { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + // Counters + int nResonance999999 = 0; + int nNotDecayed999999 = 0; + int nPhiFromResonance = 0; + + // Decay counts into K+ K- (PDG 321, -321) + int nKPlusFromResonancePhi = 0; + int nKMinusFromResonancePhi = 0; + + int nMBPhi = 0; + + int nKPlusFromMBPhi = 0; + int nKMinusFromMBPhi = 0; + + int numberOfEventsProcessed = 0; + int numberOfEventsProcessedWithoutInjection = 0; + + for (Long64_t i = 0; i < tree->GetEntries(); ++i) + { + tree->GetEntry(i); + ++numberOfEventsProcessed; + bool hasInjection = false; + + for (size_t idx = 0; idx < tracks->size(); ++idx) + { + const auto &track = tracks->at(idx); + const auto pdg = track.GetPdgCode(); + + // 1. Process Custom Resonance 999999 + if (pdg == 999999) + { + ++nResonance999999; + hasInjection = true; + + if (track.getFirstDaughterTrackId() < 0) + { + ++nNotDecayed999999; + continue; + } + + // Loop through daughters of 999999 (Phi mesons) + for (int j = track.getFirstDaughterTrackId(); j <= track.getLastDaughterTrackId(); ++j) + { + const auto &phiTrack = tracks->at(j); + if (phiTrack.GetPdgCode() == 333) + { + ++nPhiFromResonance; + + // Check daughters of this Phi (granddaughters of 999999) + if (phiTrack.getFirstDaughterTrackId() >= 0) + { + for (int k = phiTrack.getFirstDaughterTrackId(); k <= phiTrack.getLastDaughterTrackId(); ++k) + { + auto grandDauPdg = tracks->at(k).GetPdgCode(); + if (grandDauPdg == 321) + ++nKPlusFromResonancePhi; + if (grandDauPdg == -321) + ++nKMinusFromResonancePhi; + } + } + } + } + } + + // 2. Process Phi (333) Mesons + else if (pdg == 333) + { + int motherId = track.getMotherTrackId(); + int motherPdg = (motherId >= 0 && motherId < (int)tracks->size()) ? tracks->at(motherId).GetPdgCode() : 0; + + // Skip Phi from 999999 here as it was handled above + if (motherPdg == 999999) + { + continue; + } + + // Minimum Bias Phi + ++nMBPhi; + + if (track.getFirstDaughterTrackId() >= 0) + { + for (int j = track.getFirstDaughterTrackId(); j <= track.getLastDaughterTrackId(); ++j) + { + auto dauPdg = tracks->at(j).GetPdgCode(); + if (dauPdg == 321) + ++nKPlusFromMBPhi; + if (dauPdg == -321) + ++nKMinusFromMBPhi; + } + } + } + } + + if (!hasInjection) + { + ++numberOfEventsProcessedWithoutInjection; + } + } + + std::cout << "--------------------------------\n"; + std::cout << "Total Events Processed: " << tree->GetEntries() << "\n\n"; + + std::cout << "--- 1. INJECTED RESONANCE (999999) ---\n"; + std::cout << "Total Injected Resonance 999999: " << nResonance999999 << "\n"; + std::cout << "Resonances not decayed: " << nNotDecayed999999 << "\n"; + std::cout << "Daughter Phi (333) produced from 999999: " << nPhiFromResonance << "\n"; + std::cout << " -> Decayed to K+: " << nKPlusFromResonancePhi << "\n"; + std::cout << " -> Decayed to K-: " << nKMinusFromResonancePhi << "\n\n"; + + std::cout << "--- 2. MINIMUM BIAS PHI (333) ---\n"; + std::cout << "Total Minimum Bias Phi (333): " << nMBPhi << "\n"; + std::cout << " -> Decayed to K+: " << nKPlusFromMBPhi << "\n"; + std::cout << " -> Decayed to K-: " << nKMinusFromMBPhi << "\n"; + std::cout << "--------------------------------\n"; + std::cout << "Events processed without signal injection: " << numberOfEventsProcessedWithoutInjection << "\n"; + + return 0; +} + +void GeneratorLF_phiphiInvMass() +{ + External(); +} diff --git a/MC/config/PWGLF/ini/tests/GeneratorLF_pp1360_wDeCoal.C b/MC/config/PWGLF/ini/tests/GeneratorLF_pp1360_wDeCoal.C new file mode 100644 index 000000000..ab7eeb695 --- /dev/null +++ b/MC/config/PWGLF/ini/tests/GeneratorLF_pp1360_wDeCoal.C @@ -0,0 +1,28 @@ +int External() +{ + std::string path{"o2sim_Kine.root"}; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) + { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) + { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + auto nEvents = tree->GetEntries(); + if (nEvents < 1) + { + std::cerr << "No events actually generated: not OK!"; + return 1; + } + return 0; +} diff --git a/MC/config/PWGLF/ini/tests/GeneratorLF_pp1360_wDeInel.C b/MC/config/PWGLF/ini/tests/GeneratorLF_pp1360_wDeInel.C new file mode 100644 index 000000000..ab7eeb695 --- /dev/null +++ b/MC/config/PWGLF/ini/tests/GeneratorLF_pp1360_wDeInel.C @@ -0,0 +1,28 @@ +int External() +{ + std::string path{"o2sim_Kine.root"}; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) + { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) + { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + auto nEvents = tree->GetEntries(); + if (nEvents < 1) + { + std::cerr << "No events actually generated: not OK!"; + return 1; + } + return 0; +} diff --git a/MC/config/PWGLF/ini/tests/GeneratorLF_pp536_Ropes.C b/MC/config/PWGLF/ini/tests/GeneratorLF_pp536_Ropes.C new file mode 100644 index 000000000..ab7eeb695 --- /dev/null +++ b/MC/config/PWGLF/ini/tests/GeneratorLF_pp536_Ropes.C @@ -0,0 +1,28 @@ +int External() +{ + std::string path{"o2sim_Kine.root"}; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) + { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) + { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + auto nEvents = tree->GetEntries(); + if (nEvents < 1) + { + std::cerr << "No events actually generated: not OK!"; + return 1; + } + return 0; +} diff --git a/MC/config/PWGLF/ini/tests/GeneratorLF_pp536_wDeCoal.C b/MC/config/PWGLF/ini/tests/GeneratorLF_pp536_wDeCoal.C new file mode 100644 index 000000000..ab7eeb695 --- /dev/null +++ b/MC/config/PWGLF/ini/tests/GeneratorLF_pp536_wDeCoal.C @@ -0,0 +1,28 @@ +int External() +{ + std::string path{"o2sim_Kine.root"}; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) + { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) + { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + auto nEvents = tree->GetEntries(); + if (nEvents < 1) + { + std::cerr << "No events actually generated: not OK!"; + return 1; + } + return 0; +} diff --git a/MC/config/PWGLF/ini/tests/GeneratorLF_pp536_wDeInel.C b/MC/config/PWGLF/ini/tests/GeneratorLF_pp536_wDeInel.C new file mode 100644 index 000000000..ab7eeb695 --- /dev/null +++ b/MC/config/PWGLF/ini/tests/GeneratorLF_pp536_wDeInel.C @@ -0,0 +1,28 @@ +int External() +{ + std::string path{"o2sim_Kine.root"}; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) + { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) + { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + auto nEvents = tree->GetEntries(); + if (nEvents < 1) + { + std::cerr << "No events actually generated: not OK!"; + return 1; + } + return 0; +} diff --git a/MC/config/PWGLF/ini/tests/GeneratorSigmaProtonTriggered.C b/MC/config/PWGLF/ini/tests/GeneratorSigmaProtonTriggered.C new file mode 100644 index 000000000..91d47a9a6 --- /dev/null +++ b/MC/config/PWGLF/ini/tests/GeneratorSigmaProtonTriggered.C @@ -0,0 +1,49 @@ +int External() +{ + std::string path{"o2sim_Kine.root"}; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree*)file.Get("o2sim"); + if (!tree) { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + + std::vector* tracks = nullptr; + tree->SetBranchAddress("MCTrack", &tracks); + + const auto nEvents = tree->GetEntries(); + + for (Long64_t iEv = 0; iEv < nEvents; ++iEv) { + tree->GetEntry(iEv); + + bool hasSigma = false; + bool hasProton = false; + + for (const auto& track : *tracks) { + const int pdg = track.GetPdgCode(); + const int absPdg = std::abs(pdg); + + if (absPdg == 3112 || absPdg == 3222) { + hasSigma = true; + } + + if (pdg == 2212) { + hasProton = true; + } + + if (hasSigma && hasProton) { + std::cout << "Found event of interest at entry " << iEv << "\n"; + return 0; + } + } + } + + std::cerr << "No Sigma-proton event of interest\n"; + return 1; +} \ No newline at end of file diff --git a/MC/config/PWGLF/ini/tests/generatorLF_Resonances_flat_mass.C b/MC/config/PWGLF/ini/tests/generatorLF_Resonances_flat_mass.C new file mode 100644 index 000000000..7854e829a --- /dev/null +++ b/MC/config/PWGLF/ini/tests/generatorLF_Resonances_flat_mass.C @@ -0,0 +1,118 @@ +int External() +{ + std::string path{"o2sim_Kine.root"}; + + //Kept only target single resonance (e.g. PDG 10331 -> K0S K0S) + std::vector injectedPDGs = {10331}; + std::vector> decayDaughters = {{310, 310}}; + + int numberOfInjectedSignalsPerEvent = 5; + int numberOfEventsProcessed = 0; + int numberOfEventsProcessedWithoutInjection = 0; + + auto nInjection = injectedPDGs.size(); + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) + { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) + { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + std::vector nSignal(nInjection, 0); + std::vector> nDecays; + std::vector nNotDecayed(nInjection, 0); + + for (size_t i = 0; i < nInjection; i++) + { + std::vector nDecay(decayDaughters[i].size(), 0); + nDecays.push_back(nDecay); + } + + auto nEvents = tree->GetEntries(); + bool hasInjection = false; + + for (int i = 0; i < nEvents; i++) + { + hasInjection = false; + numberOfEventsProcessed++; + tree->GetEntry(i); + + for (size_t idxMCTrack = 0; idxMCTrack < tracks->size(); ++idxMCTrack) + { + auto track = tracks->at(idxMCTrack); + auto pdg = track.GetPdgCode(); + auto it = std::find(injectedPDGs.begin(), injectedPDGs.end(), pdg); + + if (it != injectedPDGs.end()) // Found injected mother particle + { + int index = std::distance(injectedPDGs.begin(), it); + nSignal[index]++; + + if (track.getFirstDaughterTrackId() < 0) + { + nNotDecayed[index]++; + continue; + } + + for (int j{track.getFirstDaughterTrackId()}; j <= track.getLastDaughterTrackId(); ++j) + { + auto pdgDau = tracks->at(j).GetPdgCode(); + bool foundDau = false; + + for (size_t idxDaughter = 0; idxDaughter < decayDaughters[index].size(); ++idxDaughter) + { + if (pdgDau == decayDaughters[index][idxDaughter]) + { + nDecays[index][idxDaughter]++; + foundDau = true; + hasInjection = true; + break; + } + } + if (!foundDau) + { + std::cerr << "Decay daughter not found: " << pdg << " -> " << pdgDau << "\n"; + } + } + } + } + if (!hasInjection) + { + numberOfEventsProcessedWithoutInjection++; + } + } + + std::cout << "--------------------------------\n"; + std::cout << "# Events: " << nEvents << "\n"; + for (size_t i = 0; i < nInjection; i++) + { + std::cout << "# Mother PDG: " << injectedPDGs[i] << "\n"; + std::cout << " Generated: " << nSignal[i] << ", " << nNotDecayed[i] << " did not decay\n"; + + if (nSignal[i] == 0) + { + std::cerr << "No generated signal found for PDG: " << injectedPDGs[i] << "\n"; + } + for (size_t j = 0; j < decayDaughters[i].size(); j++) + { + std::cout << " Daughter PDG " << decayDaughters[i][j] << ": " << nDecays[i][j] << "\n"; + } + } + std::cout << "--------------------------------\n"; + std::cout << "Number of events processed: " << numberOfEventsProcessed << "\n"; + std::cout << "Number of events without injection: " << numberOfEventsProcessedWithoutInjection << "\n"; + + return 0; +} + +void generatorLF_Resonances_flat_mass() { External(); } \ No newline at end of file diff --git a/MC/config/PWGLF/pythia8/decayer/force_h3l_kink_decay.cfg b/MC/config/PWGLF/pythia8/decayer/force_h3l_kink_decay.cfg new file mode 100644 index 000000000..abc615984 --- /dev/null +++ b/MC/config/PWGLF/pythia8/decayer/force_h3l_kink_decay.cfg @@ -0,0 +1,20 @@ +Init:showChangedParticleData = on +1010010030:new = hypertriton anti_hypertriton 1 0 0 0 0 2.991 0.0 0.0 0.0 +1010010030:m0 = 2.991 +1010010030:mWidth = 0.0 +1010010030:tau0 = 78.0 # lifetime in mm (~2.6e-10 s) +1010010030:spinType = 1 +1010010030:chargeType = 3 # +1 e = 3 * (1/3 e) +1010010030:colType = 0 + +1000010030:new = triton anti_triton 1 0 0 0 0 2.80892 0.0 0.0 0.0 +1000010030:m0 = 2.80892 +1000010030:mWidth = 0.0 +1000010030:tau0 = 0.0 # effectively stable +1000010030:spinType = 1 +1000010030:chargeType = 3 # +1 e +1000010030:colType = 0 +1000010030:mayDecay = off + +1010010030:mayDecay = on +1010010030:oneChannel = 1 1.0 0 1000010030 111 diff --git a/MC/config/PWGLF/pythia8/decayer/force_lambda_charged_decay.cfg b/MC/config/PWGLF/pythia8/decayer/force_lambda_charged_decay.cfg new file mode 100644 index 000000000..2446b28c7 --- /dev/null +++ b/MC/config/PWGLF/pythia8/decayer/force_lambda_charged_decay.cfg @@ -0,0 +1,5 @@ +3122:mayDecay = on +3122:oneChannel = 1 1.0 0 2212 -211 + +-3122:mayDecay = on +-3122:oneChannel = 1 1.0 0 -2212 211 \ No newline at end of file diff --git a/MC/config/PWGLF/pythia8/decayer/g4_ext_decayer_h3l.in b/MC/config/PWGLF/pythia8/decayer/g4_ext_decayer_h3l.in new file mode 100644 index 000000000..8bbdf6ca3 --- /dev/null +++ b/MC/config/PWGLF/pythia8/decayer/g4_ext_decayer_h3l.in @@ -0,0 +1,68 @@ + +/control/verbose 2 +/mcVerbose/all 1 +/mcVerbose/geometryManager 1 +/mcVerbose/opGeometryManager 1 +/mcTracking/loopVerbose 1 +/mcVerbose/composedPhysicsList 2 +/mcVerbose/runAction 2 # For looping thresholds control +#/tracking/verbose 1 +#//control/cout/ignoreThreadsExcept 0 + +/mcPhysics/rangeCuts 0.001 mm +/mcRegions/setRangePrecision 5 +/mcTracking/skipNeutrino true +/mcDet/setIsMaxStepInLowDensityMaterials true +/mcDet/setMaxStepInLowDensityMaterials 10 m +/mcMagField/setConstDistance 1 mm +/mcDet/setIsZeroMagField true +/mcControl/useRootRandom true # couple G4 random seed to gRandom + +# optical + +/process/optical/verbose 0 +/process/optical/processActivation Scintillation 0 +/process/optical/processActivation OpWLS 0 +/process/optical/processActivation OpMieHG 0 +/process/optical/cerenkov/setTrackSecondariesFirst false +/mcMagField/stepperType NystromRK4 + +# PAI for TRD +# Geant4 VMC >= v3.2 +/mcPhysics/emModel/setEmModel PAI +/mcPhysics/emModel/setRegions TRD_Gas-mix +/mcPhysics/emModel/setParticles all +/mcPrimaryGenerator/skipUnknownParticles true # don't crash when seeing unknown ion etc. (issue warning) + +# +# Precise Msc for EMCAL +# +# Geant4 VMC >= v3.2 +/mcPhysics/emModel/setEmModel SpecialUrbanMsc +/mcPhysics/emModel/setRegions EMC_Lead$ EMC_Scintillator$ +/mcPhysics/emModel/setParticles e- e+ + +# combined transportation + Msc mode is currently broken for ALICE (Geant 10.2.0) +/process/em/transportationWithMsc Disabled + +# +# Adding extra lines for fixing tracking bias +# +/mcMagField/setDeltaIntersection 1.0e-05 mm +/mcMagField/setMinimumEpsilonStep 0.5e-05 +/mcMagField/setMaximumEpsilonStep 1.0e-05 +/mcMagField/printParameters + +# Change default parameters for killing looping particles +# +/mcPhysics/useHighLooperThresholds +/mcRun/setLooperThresholdImportantEnergy 100. MeV + +# Define media with the INCLXX physics list; here basically in all ITS media +#/mcVerbose/biasingConfigurationManager 3 +/mcPhysics/biasing/setModel inclxx +/mcPhysics/biasing/setRegions ITS_AIR$ ITS_WATER$ ITS_COPPER$ ITS_KAPTON(POLYCH2)$ ITS_GLUE_IBFPC$ ITS_CERAMIC$ ITS_K13D2U2k$ ITS_K13D2U120$ ITS_F6151B05M$ ITS_M60J3K$ ITS_M55J6K$ ITS_FGS003$ ITS_CarbonFleece$ ITS_PEEKCF30$ ITS_GLUE$ ITS_ALUMINUM$ ITS_INOX304$ ALPIDE_METALSTACK$ ALPIDE_SI$ +/mcPhysics/biasing/setParticles proton neutron pi+ pi- + +# external decayer +/mcPhysics/setExtDecayerSelection hypertriton anti_hypertriton \ No newline at end of file diff --git a/MC/config/PWGLF/pythia8/decayer/g4_ext_decayer_lambda.in b/MC/config/PWGLF/pythia8/decayer/g4_ext_decayer_lambda.in new file mode 100644 index 000000000..e843b1d9e --- /dev/null +++ b/MC/config/PWGLF/pythia8/decayer/g4_ext_decayer_lambda.in @@ -0,0 +1,68 @@ + +/control/verbose 2 +/mcVerbose/all 1 +/mcVerbose/geometryManager 1 +/mcVerbose/opGeometryManager 1 +/mcTracking/loopVerbose 1 +/mcVerbose/composedPhysicsList 2 +/mcVerbose/runAction 2 # For looping thresholds control +#/tracking/verbose 1 +#//control/cout/ignoreThreadsExcept 0 + +/mcPhysics/rangeCuts 0.001 mm +/mcRegions/setRangePrecision 5 +/mcTracking/skipNeutrino true +/mcDet/setIsMaxStepInLowDensityMaterials true +/mcDet/setMaxStepInLowDensityMaterials 10 m +/mcMagField/setConstDistance 1 mm +/mcDet/setIsZeroMagField true +/mcControl/useRootRandom true # couple G4 random seed to gRandom + +# optical + +/process/optical/verbose 0 +/process/optical/processActivation Scintillation 0 +/process/optical/processActivation OpWLS 0 +/process/optical/processActivation OpMieHG 0 +/process/optical/cerenkov/setTrackSecondariesFirst false +/mcMagField/stepperType NystromRK4 + +# PAI for TRD +# Geant4 VMC >= v3.2 +/mcPhysics/emModel/setEmModel PAI +/mcPhysics/emModel/setRegions TRD_Gas-mix +/mcPhysics/emModel/setParticles all +/mcPrimaryGenerator/skipUnknownParticles true # don't crash when seeing unknown ion etc. (issue warning) + +# +# Precise Msc for EMCAL +# +# Geant4 VMC >= v3.2 +/mcPhysics/emModel/setEmModel SpecialUrbanMsc +/mcPhysics/emModel/setRegions EMC_Lead$ EMC_Scintillator$ +/mcPhysics/emModel/setParticles e- e+ + +# combined transportation + Msc mode is currently broken for ALICE (Geant 10.2.0) +/process/em/transportationWithMsc Disabled + +# +# Adding extra lines for fixing tracking bias +# +/mcMagField/setDeltaIntersection 1.0e-05 mm +/mcMagField/setMinimumEpsilonStep 0.5e-05 +/mcMagField/setMaximumEpsilonStep 1.0e-05 +/mcMagField/printParameters + +# Change default parameters for killing looping particles +# +/mcPhysics/useHighLooperThresholds +/mcRun/setLooperThresholdImportantEnergy 100. MeV + +# Define media with the INCLXX physics list; here basically in all ITS media +#/mcVerbose/biasingConfigurationManager 3 +/mcPhysics/biasing/setModel inclxx +/mcPhysics/biasing/setRegions ITS_AIR$ ITS_WATER$ ITS_COPPER$ ITS_KAPTON(POLYCH2)$ ITS_GLUE_IBFPC$ ITS_CERAMIC$ ITS_K13D2U2k$ ITS_K13D2U120$ ITS_F6151B05M$ ITS_M60J3K$ ITS_M55J6K$ ITS_FGS003$ ITS_CarbonFleece$ ITS_PEEKCF30$ ITS_GLUE$ ITS_ALUMINUM$ ITS_INOX304$ ALPIDE_METALSTACK$ ALPIDE_SI$ +/mcPhysics/biasing/setParticles proton neutron pi+ pi- + +# external decayer +/mcPhysics/setExtDecayerSelection lambda anti_lambda \ No newline at end of file diff --git a/MC/config/PWGLF/pythia8/generator/antideuteron.gun b/MC/config/PWGLF/pythia8/generator/antideuteron.gun new file mode 100644 index 000000000..a3342f2c2 --- /dev/null +++ b/MC/config/PWGLF/pythia8/generator/antideuteron.gun @@ -0,0 +1,2 @@ +# PDG N ptMin ptMax yMin yMax +-1000010020 1 0.5 10 -1 1 diff --git a/MC/config/PWGLF/pythia8/generator/exotic_nuclei_pbpb.gun b/MC/config/PWGLF/pythia8/generator/exotic_nuclei_pbpb.gun new file mode 100644 index 000000000..4831653c1 --- /dev/null +++ b/MC/config/PWGLF/pythia8/generator/exotic_nuclei_pbpb.gun @@ -0,0 +1,5 @@ +# PDG N ptMin ptMax yMin yMax +1010000030 10 0.2 10 -1 1 +-1010000030 10 0.2 10 -1 1 +1000030040 10 0.2 10 -1 1 +-1000030040 10 0.2 10 -1 1 diff --git a/MC/config/PWGLF/pythia8/generator/exotic_nuclei_pp.gun b/MC/config/PWGLF/pythia8/generator/exotic_nuclei_pp.gun new file mode 100644 index 000000000..6174a13c4 --- /dev/null +++ b/MC/config/PWGLF/pythia8/generator/exotic_nuclei_pp.gun @@ -0,0 +1,5 @@ +# PDG N ptMin ptMax yMin yMax +225 10 0.2 10 -1 1 +115 10 0.2 10 -1 1 +335 10 0.2 10 -1 1 +10331 10 0.2 10 -1 1 diff --git a/MC/config/PWGLF/pythia8/generator/exoticresonancegun.json b/MC/config/PWGLF/pythia8/generator/exoticresonancegun.json new file mode 100644 index 000000000..7043cd6d8 --- /dev/null +++ b/MC/config/PWGLF/pythia8/generator/exoticresonancegun.json @@ -0,0 +1,68 @@ +{ + "f_1(1285)" : { + "pdg": 20223, + "n": 1, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "f_1(1420)" : { + "pdg": 20333, + "n": 1, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "f_0(1500)" : { + "pdg": 9030221, + "n": 1, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "f_0(1710)" : { + "pdg": 10331, + "n": 1, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "f_2(1525)" : { + "pdg": 335, + "n": 1, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "f_0(1370)" : { + "pdg": 10221, + "n": 1, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + } +} diff --git a/MC/config/PWGLF/pythia8/generator/flatMCgun.json b/MC/config/PWGLF/pythia8/generator/flatMCgun.json new file mode 100644 index 000000000..4e0d12d2c --- /dev/null +++ b/MC/config/PWGLF/pythia8/generator/flatMCgun.json @@ -0,0 +1,13 @@ +{ + "f0(1710)" : { + "pdg": 10331, + "n": 100, + "ptMin": 0.0, + "ptMax": 30, + "rapidityMin": -1.0, + "rapidityMax": 1.0, + "massMin": 1.0, + "massMax": 3.0, + "genDecayed": true + } +} diff --git a/MC/config/PWGLF/pythia8/generator/gluelistgun.json b/MC/config/PWGLF/pythia8/generator/gluelistgun.json new file mode 100644 index 000000000..00683e335 --- /dev/null +++ b/MC/config/PWGLF/pythia8/generator/gluelistgun.json @@ -0,0 +1,68 @@ +{ + "f_2(1270)": { + "pdg": 225, + "n": 3, + "ptMin": 0.0, + "ptMax": 30, + "etaMin": -1.0, + "etaMax": 1.0, + "rapidityMin": -1.0, + "rapidityMax": 1.0, + "genDecayed": true + }, + "f_0(1370)": { + "pdg": 10221, + "n": 3, + "ptMin": 0.0, + "ptMax": 30, + "etaMin": -1.0, + "etaMax": 1.0, + "rapidityMin": -1.0, + "rapidityMax": 1.0, + "genDecayed": true + }, + "f_0(1500)": { + "pdg": 9030221, + "n": 3, + "ptMin": 0.0, + "ptMax": 30, + "etaMin": -1.0, + "etaMax": 1.0, + "rapidityMin": -1.0, + "rapidityMax": 1.0, + "genDecayed": true + }, + "f_0(1710)": { + "pdg": 10331, + "n": 3, + "ptMin": 0.0, + "ptMax": 30, + "etaMin": -1.0, + "etaMax": 1.0, + "rapidityMin": -1.0, + "rapidityMax": 1.0, + "genDecayed": true + }, + "f_2(1525)": { + "pdg": 335, + "n": 3, + "ptMin": 0.0, + "ptMax": 30, + "etaMin": -1.0, + "etaMax": 1.0, + "rapidityMin": -1.0, + "rapidityMax": 1.0, + "genDecayed": true + }, + "a_2(1230)": { + "pdg": 115, + "n": 3, + "ptMin": 0.0, + "ptMax": 30, + "etaMin": -1.0, + "etaMax": 1.0, + "rapidityMin": -1.0, + "rapidityMax": 1.0, + "genDecayed": true + } +} \ No newline at end of file diff --git a/MC/config/PWGLF/pythia8/generator/hypernuclei.gun b/MC/config/PWGLF/pythia8/generator/hypernuclei.gun index 55af09ba2..af21f0faf 100644 --- a/MC/config/PWGLF/pythia8/generator/hypernuclei.gun +++ b/MC/config/PWGLF/pythia8/generator/hypernuclei.gun @@ -1,8 +1,8 @@ # PDG N ptMin ptMax yMin yMax -1000010020 1 0.2 6 -1 1 -1000010030 1 0.2 6 -1 1 -1000020030 1 0.2 6 -1 1 -1000020040 1 0.2 6 -1 1 -1010010030 1 0.2 6 -1 1 -1010010040 1 0.2 6 -1 1 -1010020040 1 0.2 6 -1 1 +1000010020 1 0.2 10 -1 1 +1000010030 1 0.2 10 -1 1 +1000020030 1 0.2 10 -1 1 +1000020040 1 0.2 10 -1 1 +1010010030 1 0.2 10 -1 1 +1010010040 1 0.2 10 -1 1 +1010020040 1 0.2 10 -1 1 diff --git a/MC/config/PWGLF/pythia8/generator/hypernuclei_flow.gun b/MC/config/PWGLF/pythia8/generator/hypernuclei_flow.gun new file mode 100644 index 000000000..54455aff0 --- /dev/null +++ b/MC/config/PWGLF/pythia8/generator/hypernuclei_flow.gun @@ -0,0 +1,5 @@ +# PDG N ptMin ptMax yMin yMax +1000010020 20 0.2 10 -1 1 +1000020030 20 0.2 10 -1 1 +1000020040 20 0.2 10 -1 1 +1010010030 20 0.2 10 -1 1 diff --git a/MC/config/PWGLF/pythia8/generator/hypernuclei_oo.gun b/MC/config/PWGLF/pythia8/generator/hypernuclei_oo.gun new file mode 100644 index 000000000..66ba2dc0f --- /dev/null +++ b/MC/config/PWGLF/pythia8/generator/hypernuclei_oo.gun @@ -0,0 +1,10 @@ +# PDG N ptMin ptMax yMin yMax +1000010020 2 0.2 10 -1 1 +1000010030 2 0.2 10 -1 1 +1000020030 2 0.2 10 -1 1 +1000020040 2 0.2 10 -1 1 +1010010030 2 0.2 10 -1 1 +1010010040 2 0.2 10 -1 1 +1010020040 2 0.2 10 -1 1 +1010000030 1 0.2 10 -1 1 +1000030040 1 0.2 10 -1 1 \ No newline at end of file diff --git a/MC/config/PWGLF/pythia8/generator/hypernuclei_pbpb.gun b/MC/config/PWGLF/pythia8/generator/hypernuclei_pbpb.gun new file mode 100644 index 000000000..8bda8396e --- /dev/null +++ b/MC/config/PWGLF/pythia8/generator/hypernuclei_pbpb.gun @@ -0,0 +1,10 @@ +# PDG N ptMin ptMax yMin yMax +1000010020 20 0.2 10 -1 1 +1000010030 20 0.2 10 -1 1 +1000020030 20 0.2 10 -1 1 +1000020040 20 0.2 10 -1 1 +1010010030 20 0.2 10 -1 1 +1010010040 20 0.2 10 -1 1 +1010020040 20 0.2 10 -1 1 +1010000030 10 0.2 10 -1 1 +1000030040 10 0.2 10 -1 1 diff --git a/MC/config/PWGLF/pythia8/generator/lambda1520.cfg b/MC/config/PWGLF/pythia8/generator/lambda1520.cfg new file mode 100644 index 000000000..df201689b --- /dev/null +++ b/MC/config/PWGLF/pythia8/generator/lambda1520.cfg @@ -0,0 +1,10 @@ +### Define resonance +ProcessLevel:all = off # will not look for the 'process' + +# id::all = name antiName spinType chargeType colType m0 mWidth mMin mMax tau0 +102134:all = Lambda1520 Lambda1520bar 4 0 0 1.51950 0.01560 1.47 1.60 0 + +### add Resonance decays absent in PYTHIA8 decay table and set BRs from PDG for other +102134:oneChannel = 1 1.000 0 2212 -321 +102134:onMode = off +102134:onIfMatch = 2212 -321 diff --git a/MC/config/PWGLF/pythia8/generator/lambda1520gun_inj.json b/MC/config/PWGLF/pythia8/generator/lambda1520gun_inj.json new file mode 100644 index 000000000..f7e095729 --- /dev/null +++ b/MC/config/PWGLF/pythia8/generator/lambda1520gun_inj.json @@ -0,0 +1,24 @@ +{ + "Lambda(1520)0" : { + "pdg": 102134, + "n": 5, + "ptMin": 0.0, + "ptMax": 15, + "etaMin": -1.0, + "etaMax": 1.0, + "rapidityMin": -1.0, + "rapidityMax": 1.0, + "genDecayed": true + }, + "anti-Lambda(1520)0" : { + "pdg": -102134, + "n": 5, + "ptMin": 0.0, + "ptMax": 15, + "etaMin": -1.0, + "etaMax": 1.0, + "rapidityMin": -1.0, + "rapidityMax": 1.0, + "genDecayed": true + } +} diff --git a/MC/config/PWGLF/pythia8/generator/pythia8_inel_136tev.cfg b/MC/config/PWGLF/pythia8/generator/pythia8_inel_136tev.cfg index 9f11de75b..1a8453147 100644 --- a/MC/config/PWGLF/pythia8/generator/pythia8_inel_136tev.cfg +++ b/MC/config/PWGLF/pythia8/generator/pythia8_inel_136tev.cfg @@ -7,8 +7,8 @@ Beams:eCM = 13600. # GeV SoftQCD:inelastic = on # all inelastic processes ### decays -ParticleDecays:limitTau0 on -ParticleDecays:tau0Max 10. +ParticleDecays:limitTau0 = on +ParticleDecays:tau0Max = 10. ### phase space cuts PhaseSpace:pTHatMin = 0.000000 diff --git a/MC/config/PWGLF/pythia8/generator/pythia8_inel_136tev_wDeCoal.cfg b/MC/config/PWGLF/pythia8/generator/pythia8_inel_136tev_wDeCoal.cfg new file mode 100644 index 000000000..c35111bfb --- /dev/null +++ b/MC/config/PWGLF/pythia8/generator/pythia8_inel_136tev_wDeCoal.cfg @@ -0,0 +1,23 @@ +### Specify beams +Beams:idA = 2212 +Beams:idB = 2212 +Beams:eCM = 13600. ### energy + +Beams:frameType = 1 +ParticleDecays:limitTau0 = on +ParticleDecays:tau0Max = 10. ### match alice: 1cm/c = 10.0mm/c + +### processes +SoftQCD:inelastic = on # all inelastic processes + +# default: do nothing, Monash 2013 will do its thing +Tune:pp = 14 + +Random:setSeed = on + +# enable deuteron production by coalescence collisions +HadronLevel:DeuteronProduction = on +DeuteronProduction:channels = {2212 2112 > 22} +DeuteronProduction:models = {0} +DeuteronProduction:norm = 1 +DeuteronProduction:parms = {0.195 1} diff --git a/MC/config/PWGLF/pythia8/generator/pythia8_inel_136tev_wDeInel.cfg b/MC/config/PWGLF/pythia8/generator/pythia8_inel_136tev_wDeInel.cfg new file mode 100644 index 000000000..939ba24cd --- /dev/null +++ b/MC/config/PWGLF/pythia8/generator/pythia8_inel_136tev_wDeInel.cfg @@ -0,0 +1,19 @@ +### Specify beams +Beams:idA = 2212 +Beams:idB = 2212 +Beams:eCM = 13600. ### energy + +Beams:frameType = 1 +ParticleDecays:limitTau0 = on +ParticleDecays:tau0Max = 10. ### match alice: 1cm/c = 10.0mm/c + +### processes +SoftQCD:inelastic = on # all inelastic processes + +# default: do nothing, Monash 2013 will do its thing +Tune:pp = 14 + +Random:setSeed = on + +# enable deuteron production by inelastic collisions +HadronLevel:DeuteronProduction = on diff --git a/MC/config/PWGLF/pythia8/generator/pythia8_inel_536tev_wDeCoal.cfg b/MC/config/PWGLF/pythia8/generator/pythia8_inel_536tev_wDeCoal.cfg new file mode 100644 index 000000000..6ad09b191 --- /dev/null +++ b/MC/config/PWGLF/pythia8/generator/pythia8_inel_536tev_wDeCoal.cfg @@ -0,0 +1,23 @@ +### Specify beams +Beams:idA = 2212 +Beams:idB = 2212 +Beams:eCM = 5360. ### energy + +Beams:frameType = 1 +ParticleDecays:limitTau0 = on +ParticleDecays:tau0Max = 10. ### match alice: 1cm/c = 10.0mm/c + +### processes +SoftQCD:inelastic = on # all inelastic processes + +# default: do nothing, Monash 2013 will do its thing +Tune:pp = 14 + +Random:setSeed = on + +# enable deuteron production by coalescence collisions +HadronLevel:DeuteronProduction = on +DeuteronProduction:channels = {2212 2112 > 22} +DeuteronProduction:models = {0} +DeuteronProduction:norm = 1 +DeuteronProduction:parms = {0.195 1} diff --git a/MC/config/PWGLF/pythia8/generator/pythia8_inel_536tev_wDeInel.cfg b/MC/config/PWGLF/pythia8/generator/pythia8_inel_536tev_wDeInel.cfg new file mode 100644 index 000000000..b1154ea4f --- /dev/null +++ b/MC/config/PWGLF/pythia8/generator/pythia8_inel_536tev_wDeInel.cfg @@ -0,0 +1,19 @@ +### Specify beams +Beams:idA = 2212 +Beams:idB = 2212 +Beams:eCM = 5360. ### energy + +Beams:frameType = 1 +ParticleDecays:limitTau0 = on +ParticleDecays:tau0Max = 10. ### match alice: 1cm/c = 10.0mm/c + +### processes +SoftQCD:inelastic = on # all inelastic processes + +# default: do nothing, Monash 2013 will do its thing +Tune:pp = 14 + +Random:setSeed = on + +# enable deuteron production by inelastic collisions +HadronLevel:DeuteronProduction = on diff --git a/MC/config/PWGLF/pythia8/generator/pythia8_inel_forceLStar.cfg b/MC/config/PWGLF/pythia8/generator/pythia8_inel_forceLStar.cfg new file mode 100644 index 000000000..2230a1537 --- /dev/null +++ b/MC/config/PWGLF/pythia8/generator/pythia8_inel_forceLStar.cfg @@ -0,0 +1,20 @@ +### beams +Beams:idA 2212 # proton +Beams:idB 2212 # proton +Beams:eCM 13600. # GeV + +### processes +SoftQCD:inelastic on # all inelastic processes + +### decays +ParticleDecays:limitTau0 on +ParticleDecays:tau0Max 10. + +### Add Lambda* decay 3124 102134 +# id::all = name antiName spinType chargeType colType m0 mWidth mMin mMax tau0 +3124:all = Lambda1520 Lambda1520bar 4 0 0 1.51950 0.01560 1.47 1.60 0 + +### add Resonance decays absent in PYTHIA8 decay table and set BRs from PDG for other +3124:oneChannel = 1 1.000 0 3122 22 +3124:onMode = off +3124:onIfMatch = 3122 22 \ No newline at end of file diff --git a/MC/config/PWGLF/pythia8/generator/pythia8_inel_pp536tev.cfg b/MC/config/PWGLF/pythia8/generator/pythia8_inel_pp536tev.cfg new file mode 100644 index 000000000..6c22eb379 --- /dev/null +++ b/MC/config/PWGLF/pythia8/generator/pythia8_inel_pp536tev.cfg @@ -0,0 +1,18 @@ +### beams +Beams:idA = 2212 # proton +Beams:idB = 2212 # proton +Beams:eCM = 5360. # GeV + +### processes +SoftQCD:inelastic = on # all inelastic processes + +### decays +ParticleDecays:limitTau0 = on +ParticleDecays:tau0Max = 10. + +### phase space cuts +PhaseSpace:pTHatMin = 0.000000 +PhaseSpace:pTHatMax = -1.000000 + +Random:setSeed = on +Random:seed = 0 \ No newline at end of file diff --git a/MC/config/PWGLF/pythia8/generator/pythia8_inel_ropes_136tev.cfg b/MC/config/PWGLF/pythia8/generator/pythia8_inel_ropes_136tev.cfg new file mode 100644 index 000000000..e287ec4ce --- /dev/null +++ b/MC/config/PWGLF/pythia8/generator/pythia8_inel_ropes_136tev.cfg @@ -0,0 +1,26 @@ +### beams +Beams:idA = 2212 # proton +Beams:idB = 2212 # proton +Beams:eCM = 13600. # GeV + +### processes +SoftQCD:inelastic = on # all inelastic processes + +### decays +ParticleDecays:limitTau0 = on +ParticleDecays:tau0Max = 10. + +### phase space cuts +PhaseSpace:pTHatMin = 0.000000 +PhaseSpace:pTHatMax = -1.000000 + +Random:setSeed = on +Random:seed = 0 + +Ropewalk:RopeHadronization = on +Ropewalk:doShoving = off +Ropewalk:doFlavour = on +Ropewalk:r0 = 0.5 +Ropewalk:m0 = 0.2 +Ropewalk:beta = 0.1 +PartonVertex:setVertex = on \ No newline at end of file diff --git a/MC/config/PWGLF/pythia8/generator/pythia8_inel_ropes_536tev.cfg b/MC/config/PWGLF/pythia8/generator/pythia8_inel_ropes_536tev.cfg new file mode 100644 index 000000000..9e4a73bcb --- /dev/null +++ b/MC/config/PWGLF/pythia8/generator/pythia8_inel_ropes_536tev.cfg @@ -0,0 +1,26 @@ +### beams +Beams:idA = 2212 # proton +Beams:idB = 2212 # proton +Beams:eCM = 5360. # GeV + +### processes +SoftQCD:inelastic = on # all inelastic processes + +### decays +ParticleDecays:limitTau0 = on +ParticleDecays:tau0Max = 10. + +### phase space cuts +PhaseSpace:pTHatMin = 0.000000 +PhaseSpace:pTHatMax = -1.000000 + +Random:setSeed = on +Random:seed = 0 + +Ropewalk:RopeHadronization = on +Ropewalk:doShoving = off +Ropewalk:doFlavour = on +Ropewalk:r0 = 0.5 +Ropewalk:m0 = 0.2 +Ropewalk:beta = 0.1 +PartonVertex:setVertex = on \ No newline at end of file diff --git a/MC/config/PWGLF/pythia8/generator/pythia8_jet_ropes_136tev.cfg b/MC/config/PWGLF/pythia8/generator/pythia8_jet_ropes_136tev.cfg new file mode 100644 index 000000000..c198e1db6 --- /dev/null +++ b/MC/config/PWGLF/pythia8/generator/pythia8_jet_ropes_136tev.cfg @@ -0,0 +1,29 @@ +### beams +Beams:idA = 2212 # proton +Beams:idB = 2212 # proton +Beams:eCM = 13600. # GeV + +### processes +SoftQCD:inelastic = off +HardQCD:all = on + +### decays +ParticleDecays:limitTau0 = on +ParticleDecays:tau0Max = 10. + +### phase space cuts +PhaseSpace:pTHatMin = 8 +PhaseSpace:pTHatMax = 600 +PhaseSpace:bias2Selection = on +PhaseSpace:bias2SelectionPow = 4 + +Random:setSeed = on +Random:seed = 0 + +Ropewalk:RopeHadronization = on +Ropewalk:doShoving = off +Ropewalk:doFlavour = on +Ropewalk:r0 = 0.5 +Ropewalk:m0 = 0.2 +Ropewalk:beta = 0.1 +PartonVertex:setVertex = on \ No newline at end of file diff --git a/MC/config/PWGLF/pythia8/generator/pythia8_jet_ropes_536tev.cfg b/MC/config/PWGLF/pythia8/generator/pythia8_jet_ropes_536tev.cfg new file mode 100644 index 000000000..308492494 --- /dev/null +++ b/MC/config/PWGLF/pythia8/generator/pythia8_jet_ropes_536tev.cfg @@ -0,0 +1,29 @@ +### beams +Beams:idA = 2212 # proton +Beams:idB = 2212 # proton +Beams:eCM = 5360. # GeV + +### processes +SoftQCD:inelastic = off +HardQCD:all = on + +### decays +ParticleDecays:limitTau0 = on +ParticleDecays:tau0Max = 10. + +### phase space cuts +PhaseSpace:pTHatMin = 5 +PhaseSpace:pTHatMax = 600 +PhaseSpace:bias2Selection = on +PhaseSpace:bias2SelectionPow = 4 + +Random:setSeed = on +Random:seed = 0 + +Ropewalk:RopeHadronization = on +Ropewalk:doShoving = off +Ropewalk:doFlavour = on +Ropewalk:r0 = 0.5 +Ropewalk:m0 = 0.2 +Ropewalk:beta = 0.1 +PartonVertex:setVertex = on \ No newline at end of file diff --git a/MC/config/PWGLF/pythia8/generator/resonance_ml_k1_kstarpi.cfg b/MC/config/PWGLF/pythia8/generator/resonance_ml_k1_kstarpi.cfg new file mode 100644 index 000000000..4ecb4c80d --- /dev/null +++ b/MC/config/PWGLF/pythia8/generator/resonance_ml_k1_kstarpi.cfg @@ -0,0 +1,8 @@ +# Forced K1(1270) -> K*(892) pi -> K pi pi for single-channel ML signal samples (one decay channel per job). +# Used for both charges: Pythia applies the charge conjugate of the positive-PDG table to K1(1270)-. +# Mass and width are the Pythia defaults of 10323; the width macro samples the line shape (mSel). +# Branching fractions are forced to unity: weight with the natural fractions in the analysis. +### K1(1270)+ -> K*(892)0 pi+ +10323:oneChannel = 1 1.000 0 313 211 +### K*(892)0 -> K+ pi- +313:oneChannel = 1 1.000 0 321 -211 diff --git a/MC/config/PWGLF/pythia8/generator/resonance_ml_k1_rhok.cfg b/MC/config/PWGLF/pythia8/generator/resonance_ml_k1_rhok.cfg new file mode 100644 index 000000000..8dbe76e82 --- /dev/null +++ b/MC/config/PWGLF/pythia8/generator/resonance_ml_k1_rhok.cfg @@ -0,0 +1,8 @@ +# Forced K1(1270) -> K rho0 -> K pi+ pi- for single-channel ML signal samples (one decay channel per job). +# Used for both charges: Pythia applies the charge conjugate of the positive-PDG table to K1(1270)-. +# Mass and width are the Pythia defaults of 10323; the width macro samples the line shape (mSel). +# Branching fractions are forced to unity: weight with the natural fractions in the analysis. +### K1(1270)+ -> K+ rho0 +10323:oneChannel = 1 1.000 0 321 113 +### rho0 -> pi+ pi- +113:oneChannel = 1 1.000 0 211 -211 diff --git a/MC/config/PWGLF/pythia8/generator/resonance_ml_omega2012_xi1530k.cfg b/MC/config/PWGLF/pythia8/generator/resonance_ml_omega2012_xi1530k.cfg new file mode 100644 index 000000000..fb912c3b6 --- /dev/null +++ b/MC/config/PWGLF/pythia8/generator/resonance_ml_omega2012_xi1530k.cfg @@ -0,0 +1,11 @@ +# Forced Omega(2012)- -> Xi(1530)0 K- -> Xi- pi+ K- for single-channel ML signal samples (one decay channel per job). +# Used for both charges: Pythia applies the charge conjugate of the positive-PDG table to Omega(2012)+. +# Particle definition as in resonances_width.cfg (123334 is not a Pythia default particle). +# Xi and Lambda are not decayed by the generator (base.cfg: mayDecay off) but by the transport code with +# their natural branching fractions; forcing them here would have no effect. +# id::all = name antiName spinType chargeType colType m0 mWidth mMin mMax tau0 +123334:all = Omega2012Minus Omega2012Minusbar 4 -3 0 2.0125 0.0064 1.8210 2.0765 0 +### Omega(2012)- -> Xi(1530)0 K- +123334:oneChannel = 1 1.000 0 3324 -321 +### Xi(1530)0 -> Xi- pi+ +3324:oneChannel = 1 1.000 0 3312 211 diff --git a/MC/config/PWGLF/pythia8/generator/resonance_ml_omega2012_xik0s.cfg b/MC/config/PWGLF/pythia8/generator/resonance_ml_omega2012_xik0s.cfg new file mode 100644 index 000000000..a6536ad79 --- /dev/null +++ b/MC/config/PWGLF/pythia8/generator/resonance_ml_omega2012_xik0s.cfg @@ -0,0 +1,9 @@ +# Forced Omega(2012)- -> Xi- K0S for single-channel ML signal samples (one decay channel per job). +# Used for both charges: Pythia applies the charge conjugate of the positive-PDG table to Omega(2012)+. +# Particle definition as in resonances_width.cfg (123334 is not a Pythia default particle). +# Xi, Lambda and K0S are not decayed by the generator (base.cfg: mayDecay off) but by the transport code with +# its natural branching fractions; forcing them here would have no effect. +# id::all = name antiName spinType chargeType colType m0 mWidth mMin mMax tau0 +123334:all = Omega2012Minus Omega2012Minusbar 4 -3 0 2.0125 0.0064 1.8210 2.0765 0 +### Omega(2012)- -> Xi- K0S +123334:oneChannel = 1 1.000 0 3312 310 diff --git a/MC/config/PWGLF/pythia8/generator/resonancegun_ml_k1minus_kstarpi.json b/MC/config/PWGLF/pythia8/generator/resonancegun_ml_k1minus_kstarpi.json new file mode 100644 index 000000000..d9f1a037d --- /dev/null +++ b/MC/config/PWGLF/pythia8/generator/resonancegun_ml_k1minus_kstarpi.json @@ -0,0 +1,13 @@ +{ + "K1(1270)-": { + "pdg": -10323, + "n": 1, + "ptMin": 0.0, + "ptMax": 8.0, + "etaMin": -0.7, + "etaMax": 0.7, + "rapidityMin": -0.7, + "rapidityMax": 0.7, + "genDecayed": true + } +} diff --git a/MC/config/PWGLF/pythia8/generator/resonancegun_ml_k1minus_rhok.json b/MC/config/PWGLF/pythia8/generator/resonancegun_ml_k1minus_rhok.json new file mode 100644 index 000000000..d9f1a037d --- /dev/null +++ b/MC/config/PWGLF/pythia8/generator/resonancegun_ml_k1minus_rhok.json @@ -0,0 +1,13 @@ +{ + "K1(1270)-": { + "pdg": -10323, + "n": 1, + "ptMin": 0.0, + "ptMax": 8.0, + "etaMin": -0.7, + "etaMax": 0.7, + "rapidityMin": -0.7, + "rapidityMax": 0.7, + "genDecayed": true + } +} diff --git a/MC/config/PWGLF/pythia8/generator/resonancegun_ml_k1plus_kstarpi.json b/MC/config/PWGLF/pythia8/generator/resonancegun_ml_k1plus_kstarpi.json new file mode 100644 index 000000000..c1737f238 --- /dev/null +++ b/MC/config/PWGLF/pythia8/generator/resonancegun_ml_k1plus_kstarpi.json @@ -0,0 +1,13 @@ +{ + "K1(1270)+": { + "pdg": 10323, + "n": 1, + "ptMin": 0.0, + "ptMax": 8.0, + "etaMin": -0.7, + "etaMax": 0.7, + "rapidityMin": -0.7, + "rapidityMax": 0.7, + "genDecayed": true + } +} diff --git a/MC/config/PWGLF/pythia8/generator/resonancegun_ml_k1plus_rhok.json b/MC/config/PWGLF/pythia8/generator/resonancegun_ml_k1plus_rhok.json new file mode 100644 index 000000000..c1737f238 --- /dev/null +++ b/MC/config/PWGLF/pythia8/generator/resonancegun_ml_k1plus_rhok.json @@ -0,0 +1,13 @@ +{ + "K1(1270)+": { + "pdg": 10323, + "n": 1, + "ptMin": 0.0, + "ptMax": 8.0, + "etaMin": -0.7, + "etaMax": 0.7, + "rapidityMin": -0.7, + "rapidityMax": 0.7, + "genDecayed": true + } +} diff --git a/MC/config/PWGLF/pythia8/generator/resonancegun_ml_omega2012minus_xi1530k.json b/MC/config/PWGLF/pythia8/generator/resonancegun_ml_omega2012minus_xi1530k.json new file mode 100644 index 000000000..6e13333ff --- /dev/null +++ b/MC/config/PWGLF/pythia8/generator/resonancegun_ml_omega2012minus_xi1530k.json @@ -0,0 +1,13 @@ +{ + "Omega(2012)-": { + "pdg": 123334, + "n": 1, + "ptMin": 0.0, + "ptMax": 15.0, + "etaMin": -0.7, + "etaMax": 0.7, + "rapidityMin": -0.7, + "rapidityMax": 0.7, + "genDecayed": true + } +} diff --git a/MC/config/PWGLF/pythia8/generator/resonancegun_ml_omega2012minus_xik0s.json b/MC/config/PWGLF/pythia8/generator/resonancegun_ml_omega2012minus_xik0s.json new file mode 100644 index 000000000..6e13333ff --- /dev/null +++ b/MC/config/PWGLF/pythia8/generator/resonancegun_ml_omega2012minus_xik0s.json @@ -0,0 +1,13 @@ +{ + "Omega(2012)-": { + "pdg": 123334, + "n": 1, + "ptMin": 0.0, + "ptMax": 15.0, + "etaMin": -0.7, + "etaMax": 0.7, + "rapidityMin": -0.7, + "rapidityMax": 0.7, + "genDecayed": true + } +} diff --git a/MC/config/PWGLF/pythia8/generator/resonancegun_ml_omega2012plus_xi1530k.json b/MC/config/PWGLF/pythia8/generator/resonancegun_ml_omega2012plus_xi1530k.json new file mode 100644 index 000000000..e3a27d8e0 --- /dev/null +++ b/MC/config/PWGLF/pythia8/generator/resonancegun_ml_omega2012plus_xi1530k.json @@ -0,0 +1,13 @@ +{ + "Omega(2012)+": { + "pdg": -123334, + "n": 1, + "ptMin": 0.0, + "ptMax": 15.0, + "etaMin": -0.7, + "etaMax": 0.7, + "rapidityMin": -0.7, + "rapidityMax": 0.7, + "genDecayed": true + } +} diff --git a/MC/config/PWGLF/pythia8/generator/resonancegun_ml_omega2012plus_xik0s.json b/MC/config/PWGLF/pythia8/generator/resonancegun_ml_omega2012plus_xik0s.json new file mode 100644 index 000000000..e3a27d8e0 --- /dev/null +++ b/MC/config/PWGLF/pythia8/generator/resonancegun_ml_omega2012plus_xik0s.json @@ -0,0 +1,13 @@ +{ + "Omega(2012)+": { + "pdg": -123334, + "n": 1, + "ptMin": 0.0, + "ptMax": 15.0, + "etaMin": -0.7, + "etaMax": 0.7, + "rapidityMin": -0.7, + "rapidityMax": 0.7, + "genDecayed": true + } +} diff --git a/MC/config/PWGLF/pythia8/generator/resonancelist.gun b/MC/config/PWGLF/pythia8/generator/resonancelist.gun index daedb88f0..4623a8ad0 100644 --- a/MC/config/PWGLF/pythia8/generator/resonancelist.gun +++ b/MC/config/PWGLF/pythia8/generator/resonancelist.gun @@ -12,9 +12,9 @@ -3114 10 0.2 10 -1.2 1.2 genDecayed 3224 10 0.2 10 -1.2 1.2 genDecayed -3224 10 0.2 10 -1.2 1.2 genDecayed -3124 10 0.2 10 -1.2 1.2 genDecayed --3124 10 0.2 10 -1.2 1.2 genDecayed +102134 10 0.2 10 -1.2 1.2 genDecayed +-102134 10 0.2 10 -1.2 1.2 genDecayed 3324 10 0.2 10 -1.2 1.2 genDecayed -3324 10 0.2 10 -1.2 1.2 genDecayed 10323 10 0.2 10 -1.2 1.2 genDecayed --10323 10 0.2 10 -1.2 1.2 genDecayed \ No newline at end of file +-10323 10 0.2 10 -1.2 1.2 genDecayed diff --git a/MC/config/PWGLF/pythia8/generator/resonancelistgun.json b/MC/config/PWGLF/pythia8/generator/resonancelistgun.json new file mode 100644 index 000000000..8fa6a5b4f --- /dev/null +++ b/MC/config/PWGLF/pythia8/generator/resonancelistgun.json @@ -0,0 +1,266 @@ +{ + "K(892)+" : { + "pdg": 323, + "n": 1, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "K(892)-" : { + "pdg": -323, + "n": 1, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "f_0(980)" : { + "pdg": 9010221, + "n": 1, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "rho(770)0" : { + "pdg": 113, + "n": 1, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "rho(770)+" : { + "pdg": 213, + "n": 1, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "rho(770)-" : { + "pdg": -213, + "n": 1, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "Sigma(1385)-" : { + "pdg": 3114, + "n": 1, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "anti-Sigma(1385)+" : { + "pdg": -3114, + "n": 1, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "Sigma(1385)+" : { + "pdg": 3224, + "n": 1, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "anti-Sigma(1385)-" : { + "pdg": -3224, + "n": 1, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "Lambda(1520)0" : { + "pdg": 102134, + "n": 1, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "anti-Lambda(1520)0" : { + "pdg": -102134, + "n": 1, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "Xi(1530)0" : { + "pdg": 3324, + "n": 1, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "anti-Xi(1530)0" : { + "pdg": -3324, + "n": 1, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "K1(1270)+": { + "pdg": 10323, + "n": 1, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "K1(1270)-": { + "pdg": -10323, + "n": 1, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "Delta++" : { + "pdg": 2224, + "n": 1, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "anti-Delta++" : { + "pdg": -2224, + "n": 1, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "Delta0" : { + "pdg": 2114, + "n": 1, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "anti-Delta0" : { + "pdg": -2114, + "n": 1, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "Xi(1820)0" : { + "pdg": 123314, + "n": 1, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "Anti-Xi(1820)0" : { + "pdg": -123314, + "n": 1, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "Xi(1820)-" : { + "pdg": 123324, + "n": 1, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "Xi(1820)+" : { + "pdg": -123324, + "n": 1, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + } +} diff --git a/MC/config/PWGLF/pythia8/generator/resonancelistgun_baryonic_inj.json b/MC/config/PWGLF/pythia8/generator/resonancelistgun_baryonic_inj.json new file mode 100644 index 000000000..11a339251 --- /dev/null +++ b/MC/config/PWGLF/pythia8/generator/resonancelistgun_baryonic_inj.json @@ -0,0 +1,46 @@ +{ + "Lambda(1520)0" : { + "pdg": 102134, + "n": 3, + "ptMin": 0.0, + "ptMax": 15, + "etaMin": -1.0, + "etaMax": 1.0, + "rapidityMin": -1.0, + "rapidityMax": 1.0, + "genDecayed": true + }, + "anti-Lambda(1520)0" : { + "pdg": -102134, + "n": 3, + "ptMin": 0.0, + "ptMax": 15, + "etaMin": -1.0, + "etaMax": 1.0, + "rapidityMin": -1.0, + "rapidityMax": 1.0, + "genDecayed": true + }, + "Xi(1530)0" : { + "pdg": 3324, + "n": 3, + "ptMin": 0.0, + "ptMax": 15, + "etaMin": -1.0, + "etaMax": 1.0, + "rapidityMin": -1.0, + "rapidityMax": 1.0, + "genDecayed": true + }, + "anti-Xi(1530)0" : { + "pdg": -3324, + "n": 3, + "ptMin": 0.0, + "ptMax": 15, + "etaMin": -1.0, + "etaMax": 1.0, + "rapidityMin": -1.0, + "rapidityMax": 1.0, + "genDecayed": true + } +} diff --git a/MC/config/PWGLF/pythia8/generator/resonancelistgun_exoticAll.json b/MC/config/PWGLF/pythia8/generator/resonancelistgun_exoticAll.json new file mode 100644 index 000000000..800756307 --- /dev/null +++ b/MC/config/PWGLF/pythia8/generator/resonancelistgun_exoticAll.json @@ -0,0 +1,112 @@ +{ + "f_2(1270)": { + "pdg": 225, + "n": 3, + "ptMin": 0.0, + "ptMax": 15, + "etaMin": -1.0, + "etaMax": 1.0, + "rapidityMin": -1.0, + "rapidityMax": 1.0, + "genDecayed": true + }, + "f_0(1370)": { + "pdg": 10221, + "n": 3, + "ptMin": 0.0, + "ptMax": 15, + "etaMin": -1.0, + "etaMax": 1.0, + "rapidityMin": -1.0, + "rapidityMax": 1.0, + "genDecayed": true + }, + "f_0(1500)": { + "pdg": 9030221, + "n": 3, + "ptMin": 0.0, + "ptMax": 15, + "etaMin": -1.0, + "etaMax": 1.0, + "rapidityMin": -1.0, + "rapidityMax": 1.0, + "genDecayed": true + }, + "f_0(1710)": { + "pdg": 10331, + "n": 3, + "ptMin": 0.0, + "ptMax": 15, + "etaMin": -1.0, + "etaMax": 1.0, + "rapidityMin": -1.0, + "rapidityMax": 1.0, + "genDecayed": true + }, + "f_1(1285)": { + "pdg": 20223, + "n": 3, + "ptMin": 0.0, + "ptMax": 15, + "etaMin": -1.0, + "etaMax": 1.0, + "rapidityMin": -1.0, + "rapidityMax": 1.0, + "genDecayed": true + }, + "f_1(1420)": { + "pdg": 20333, + "n": 3, + "ptMin": 0.0, + "ptMax": 15, + "etaMin": -1.0, + "etaMax": 1.0, + "rapidityMin": -1.0, + "rapidityMax": 1.0, + "genDecayed": true + }, + "f_2(1525)": { + "pdg": 335, + "n": 3, + "ptMin": 0.0, + "ptMax": 15, + "etaMin": -1.0, + "etaMax": 1.0, + "rapidityMin": -1.0, + "rapidityMax": 1.0, + "genDecayed": true + }, + "a_2(1320)": { + "pdg": 115, + "n": 3, + "ptMin": 0.0, + "ptMax": 15, + "etaMin": -1.0, + "etaMax": 1.0, + "rapidityMin": -1.0, + "rapidityMax": 1.0, + "genDecayed": true + }, + "Lambda(1520)0" : { + "pdg": 102134, + "n": 3, + "ptMin": 0.0, + "ptMax": 15, + "etaMin": -1.0, + "etaMax": 1.0, + "rapidityMin": -1.0, + "rapidityMax": 1.0, + "genDecayed": true + }, + "anti-Lambda(1520)0" : { + "pdg": -102134, + "n": 3, + "ptMin": 0.0, + "ptMax": 15, + "etaMin": -1.0, + "etaMax": 1.0, + "rapidityMin": -1.0, + "rapidityMax": 1.0, + "genDecayed": true + } +} diff --git a/MC/config/PWGLF/pythia8/generator/resonancelistgun_exotic_pbpb.json b/MC/config/PWGLF/pythia8/generator/resonancelistgun_exotic_pbpb.json new file mode 100644 index 000000000..780b2969a --- /dev/null +++ b/MC/config/PWGLF/pythia8/generator/resonancelistgun_exotic_pbpb.json @@ -0,0 +1,167 @@ +{ + "f_0(980)": { + "pdg": 9010221, + "n": 10, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "f_2(1270)": { + "pdg": 225, + "n": 1, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "f_0(1370)": { + "pdg": 10221, + "n": 10, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "f_0(1500)": { + "pdg": 9030221, + "n": 10, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "f_0(1710)": { + "pdg": 10331, + "n": 10, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "f_1(1285)": { + "pdg": 20223, + "n": 10, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "f_1(1420)": { + "pdg": 20333, + "n": 10, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "f_2(1525)": { + "pdg": 335, + "n": 10, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "a_2(1230)": { + "pdg": 115, + "n": 1, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "K1(1270)+": { + "pdg": 10323, + "n": 10, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "K1(1270)-": { + "pdg": -10323, + "n": 10, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "Xi(1820)0": { + "pdg": 123314, + "n": 10, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "Anti-Xi(1820)0": { + "pdg": -123314, + "n": 10, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "Xi(1820)-": { + "pdg": 123324, + "n": 10, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "Xi(1820)+": { + "pdg": -123324, + "n": 10, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + } +} \ No newline at end of file diff --git a/MC/config/PWGLF/pythia8/generator/resonancelistgun_inj.json b/MC/config/PWGLF/pythia8/generator/resonancelistgun_inj.json new file mode 100644 index 000000000..c15acbf37 --- /dev/null +++ b/MC/config/PWGLF/pythia8/generator/resonancelistgun_inj.json @@ -0,0 +1,79 @@ +{ + "Lambda(1520)0" : { + "pdg": 102134, + "n": 1, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "anti-Lambda(1520)0" : { + "pdg": -102134, + "n": 1, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "f_0(980)" : { + "pdg": 9010221, + "n": 1, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "Xi(1820)0" : { + "pdg": 123314, + "n": 1, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "Anti-Xi(1820)0" : { + "pdg": -123314, + "n": 1, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "Xi(1820)-" : { + "pdg": 123324, + "n": 1, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "Xi(1820)+" : { + "pdg": -123324, + "n": 1, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + } +} diff --git a/MC/config/PWGLF/pythia8/generator/resonancelistgun_inj_pbpb.json b/MC/config/PWGLF/pythia8/generator/resonancelistgun_inj_pbpb.json new file mode 100644 index 000000000..005c8ca76 --- /dev/null +++ b/MC/config/PWGLF/pythia8/generator/resonancelistgun_inj_pbpb.json @@ -0,0 +1,79 @@ +{ + "Lambda(1520)0" : { + "pdg": 102134, + "n": 10, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "anti-Lambda(1520)0" : { + "pdg": -102134, + "n": 10, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "f_0(980)" : { + "pdg": 9010221, + "n": 1, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "Xi(1820)0" : { + "pdg": 123314, + "n": 10, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "Anti-Xi(1820)0" : { + "pdg": -123314, + "n": 10, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "Xi(1820)-" : { + "pdg": 123324, + "n": 10, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "Xi(1820)+" : { + "pdg": -123324, + "n": 10, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + } +} diff --git a/MC/config/PWGLF/pythia8/generator/resonancelistgun_pbpb.json b/MC/config/PWGLF/pythia8/generator/resonancelistgun_pbpb.json new file mode 100644 index 000000000..d2f92f44d --- /dev/null +++ b/MC/config/PWGLF/pythia8/generator/resonancelistgun_pbpb.json @@ -0,0 +1,266 @@ +{ + "K(892)+" : { + "pdg": 323, + "n": 10, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "K(892)-" : { + "pdg": -323, + "n": 10, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "f_0(980)" : { + "pdg": 9010221, + "n": 10, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "rho(770)0" : { + "pdg": 113, + "n": 10, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "rho(770)+" : { + "pdg": 213, + "n": 10, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "rho(770)-" : { + "pdg": -213, + "n": 10, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "Sigma(1385)-" : { + "pdg": 3114, + "n": 10, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "anti-Sigma(1385)+" : { + "pdg": -3114, + "n": 10, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "Sigma(1385)+" : { + "pdg": 3224, + "n": 10, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "anti-Sigma(1385)-" : { + "pdg": -3224, + "n": 10, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "Lambda(1520)0" : { + "pdg": 102134, + "n": 10, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "anti-Lambda(1520)0" : { + "pdg": -102134, + "n": 10, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "Xi(1530)0" : { + "pdg": 3324, + "n": 10, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "anti-Xi(1530)0" : { + "pdg": -3324, + "n": 10, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "K1(1270)+": { + "pdg": 10323, + "n": 10, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "K1(1270)-": { + "pdg": -10323, + "n": 10, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "Delta++" : { + "pdg": 2224, + "n": 10, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "anti-Delta++" : { + "pdg": -2224, + "n": 10, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "Delta0" : { + "pdg": 2114, + "n": 10, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "anti-Delta0" : { + "pdg": -2114, + "n": 10, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "Xi(1820)0" : { + "pdg": 123314, + "n": 10, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "Anti-Xi(1820)0" : { + "pdg": -123314, + "n": 10, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "Xi(1820)-" : { + "pdg": 123324, + "n": 10, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "Xi(1820)+" : { + "pdg": -123324, + "n": 10, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + } +} diff --git a/MC/config/PWGLF/pythia8/generator/resonancelistgun_trig.json b/MC/config/PWGLF/pythia8/generator/resonancelistgun_trig.json new file mode 100644 index 000000000..de1ad01e1 --- /dev/null +++ b/MC/config/PWGLF/pythia8/generator/resonancelistgun_trig.json @@ -0,0 +1,189 @@ +{ + "K(892)+" : { + "pdg": 323, + "n": 1, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "K(892)-" : { + "pdg": -323, + "n": 1, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "rho(770)0" : { + "pdg": 113, + "n": 1, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "rho(770)+" : { + "pdg": 213, + "n": 1, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "rho(770)-" : { + "pdg": -213, + "n": 1, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "Sigma(1385)-" : { + "pdg": 3114, + "n": 1, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "anti-Sigma(1385)+" : { + "pdg": -3114, + "n": 1, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "Sigma(1385)+" : { + "pdg": 3224, + "n": 1, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "anti-Sigma(1385)-" : { + "pdg": -3224, + "n": 1, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "Xi(1530)0" : { + "pdg": 3324, + "n": 1, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "anti-Xi(1530)0" : { + "pdg": -3324, + "n": 1, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "K1(1270)+": { + "pdg": 10323, + "n": 1, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "K1(1270)-": { + "pdg": -10323, + "n": 1, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "Delta++" : { + "pdg": 2224, + "n": 1, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "anti-Delta++" : { + "pdg": -2224, + "n": 1, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "Delta0" : { + "pdg": 2114, + "n": 1, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "anti-Delta0" : { + "pdg": -2114, + "n": 1, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + } +} diff --git a/MC/config/PWGLF/pythia8/generator/resonancelistgun_trig_pbpb.json b/MC/config/PWGLF/pythia8/generator/resonancelistgun_trig_pbpb.json new file mode 100644 index 000000000..f2c692e5a --- /dev/null +++ b/MC/config/PWGLF/pythia8/generator/resonancelistgun_trig_pbpb.json @@ -0,0 +1,189 @@ +{ + "K(892)+" : { + "pdg": 323, + "n": 10, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "K(892)-" : { + "pdg": -323, + "n": 10, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "rho(770)0" : { + "pdg": 113, + "n": 10, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "rho(770)+" : { + "pdg": 213, + "n": 10, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "rho(770)-" : { + "pdg": -213, + "n": 10, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "Sigma(1385)-" : { + "pdg": 3114, + "n": 10, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "anti-Sigma(1385)+" : { + "pdg": -3114, + "n": 10, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "Sigma(1385)+" : { + "pdg": 3224, + "n": 10, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "anti-Sigma(1385)-" : { + "pdg": -3224, + "n": 10, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "Xi(1530)0" : { + "pdg": 3324, + "n": 10, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "anti-Xi(1530)0" : { + "pdg": -3324, + "n": 10, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "K1(1270)+": { + "pdg": 10323, + "n": 10, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "K1(1270)-": { + "pdg": -10323, + "n": 10, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "Delta++" : { + "pdg": 2224, + "n": 10, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "anti-Delta++" : { + "pdg": -2224, + "n": 10, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "Delta0" : { + "pdg": 2114, + "n": 10, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + }, + "anti-Delta0" : { + "pdg": -2114, + "n": 10, + "ptMin": 0.0, + "ptMax": 20, + "etaMin": -1.2, + "etaMax": 1.2, + "rapidityMin": -1.2, + "rapidityMax": 1.2, + "genDecayed": true + } +} diff --git a/MC/config/PWGLF/pythia8/generator/resonancelistgun_width_inj.json b/MC/config/PWGLF/pythia8/generator/resonancelistgun_width_inj.json new file mode 100644 index 000000000..119411991 --- /dev/null +++ b/MC/config/PWGLF/pythia8/generator/resonancelistgun_width_inj.json @@ -0,0 +1,101 @@ +{ + "f_0(980)" : { + "pdg": 9010221, + "n": 1, + "ptMin": 0.0, + "ptMax": 15, + "etaMin": -0.8, + "etaMax": 0.8, + "rapidityMin": -0.8, + "rapidityMax": 0.8, + "genDecayed": true + }, + "Xi(1530)0" : { + "pdg": 3324, + "n": 1, + "ptMin": 0.0, + "ptMax": 12, + "etaMin": -0.8, + "etaMax": 0.8, + "rapidityMin": -0.8, + "rapidityMax": 0.8, + "genDecayed": true + }, + "anti-Xi(1530)0" : { + "pdg": -3324, + "n": 1, + "ptMin": 0.0, + "ptMax": 12, + "etaMin": -0.8, + "etaMax": 0.8, + "rapidityMin": -0.8, + "rapidityMax": 0.8, + "genDecayed": true + }, + "Xi(1820)0" : { + "pdg": 123324, + "n": 1, + "ptMin": 0.0, + "ptMax": 12, + "etaMin": -0.8, + "etaMax": 0.8, + "rapidityMin": -0.8, + "rapidityMax": 0.8, + "genDecayed": true + }, + "Anti-Xi(1820)0" : { + "pdg": -123324, + "n": 1, + "ptMin": 0.0, + "ptMax": 12, + "etaMin": -0.8, + "etaMax": 0.8, + "rapidityMin": -0.8, + "rapidityMax": 0.8, + "genDecayed": true + }, + "Xi(1820)-" : { + "pdg": 123314, + "n": 1, + "ptMin": 0.0, + "ptMax": 12, + "etaMin": -0.8, + "etaMax": 0.8, + "rapidityMin": -0.8, + "rapidityMax": 0.8, + "genDecayed": true + }, + "Xi(1820)+" : { + "pdg": -123314, + "n": 1, + "ptMin": 0.0, + "ptMax": 12, + "etaMin": -0.8, + "etaMax": 0.8, + "rapidityMin": -0.8, + "rapidityMax": 0.8, + "genDecayed": true + }, + "Omega(2012)-" : { + "pdg": 123334, + "n": 1, + "ptMin": 0.0, + "ptMax": 12, + "etaMin": -0.8, + "etaMax": 0.8, + "rapidityMin": -0.8, + "rapidityMax": 0.8, + "genDecayed": true + }, + "Omega(2012)+" : { + "pdg": -123334, + "n": 1, + "ptMin": 0.0, + "ptMax": 12, + "etaMin": -0.8, + "etaMax": 0.8, + "rapidityMin": -0.8, + "rapidityMax": 0.8, + "genDecayed": true + } +} diff --git a/MC/config/PWGLF/pythia8/generator/resonances.cfg b/MC/config/PWGLF/pythia8/generator/resonances.cfg index df226c5fc..d35eaba2e 100644 --- a/MC/config/PWGLF/pythia8/generator/resonances.cfg +++ b/MC/config/PWGLF/pythia8/generator/resonances.cfg @@ -1,34 +1,71 @@ ### Define resonance ProcessLevel:all = off # will not look for the 'process' +102132:all = Lambda1405 Lambda1405bar 4 0 0 1.40500 0.05000 1.30 1.50 0 +102132:oneChannel = 1 0.33333 5 3112 211 +102132:addChannel = 1 0.33333 5 3222 -211 +102132:addChannel = 1 0.33333 5 3212 111 + # id::all = name antiName spinType chargeType colType m0 mWidth mMin mMax tau0 -3124:all = Lambda1520 Lambda1520bar 0 0 1 1.5195 0.01560 0.06240 0.00000E+00 0 1 - -### add Resonance decays absent in PYTHIA8 decay table and set BRs from PDG for other -3124:oneChannel = 1 0.223547 0 2212 -321 -3124:addChannel = 1 0.223547 0 2112 -311 -3124:addChannel = 1 0.139096 0 3222 -211 -3124:addChannel = 1 0.139096 0 3212 111 -3124:addChannel = 1 0.139096 0 3112 211 -3124:addChannel = 1 0.028780 0 3224 -211 -3124:addChannel = 1 0.028780 0 3214 111 -3124:addChannel = 1 0.028780 0 3114 211 -3124:addChannel = 1 0.019373 0 3212 22 -3124:addChannel = 1 0.014638 0 3122 211 -3124:addChannel = 1 0.007948 0 3122 22 -3124:addChannel = 1 0.007319 0 3122 111 - -3124:onMode = off - -3124:onIfMatch = 2212 -321 -3124:onIfMatch = 2112 -311 -3124:onIfMatch = 3222 -211 -3124:onIfMatch = 3212 111 -3124:onIfMatch = 3112 211 -3124:onIfMatch = 3224 -211 -3124:onIfMatch = 3214 111 -3124:onIfMatch = 3114 211 -3124:onIfMatch = 3212 22 -3124:onIfMatch = 3122 211 -3124:onIfMatch = 3122 22 -3124:onIfMatch = 3122 111 \ No newline at end of file +### Lambda1520 +102134:all = Lambda1520 Lambda1520bar 4 0 0 1.51950 0.01560 1.47 1.60 0 +102134:oneChannel = 1 1.000 0 2212 -321 +102134:onMode = off +102134:onIfMatch = 2212 -321 + +### Xi1820 +123324:all = Xi1820 Xi1820bar 0 0 0 1.8234 0.0 0.0 0.0 0 +123324:oneChannel = 1 1.000 0 3122 310 +123324:onMode = off +123324:onIfMatch = 3122 310 + +123314:all = Xi1820Minus Xi1820Plus 0 -1 0 1.8234 0.0 0.0 0.0 0 +123314:oneChannel = 1 1.000 0 3122 -321 +123314:onMode = off +123314:onIfMatch = 3122 -321 + +### f1 study +20223:all = f_1(1285) f_1(1285) 3 0 0 1.28210 0.02420 1.20000 1.50000 0 +20223:oneChannel = 1 0.5 0 310 -321 211 +20223:addChannel = 1 0.5 0 310 321 -211 +20223:onMode = off +20223:onIfMatch = 310 -321 211 +20223:onIfMatch = 310 321 -211 + +20333:all = f_1(1420) f_1(1420) 3 0 0 1.42640 0.05490 1.40000 1.80000 0 +20333:oneChannel = 1 0.5 0 310 -321 211 +20333:addChannel = 1 0.5 0 310 321 -211 +20333:onMode = off +20333:onIfMatch = 310 -321 211 +20333:onIfMatch = 310 321 -211 + +### glueball hunting +9030221:all = f_0(1500) f_0(1500) 0 0 0 1.50600 0.11200 1.40000 1.60000 0 +9030221:oneChannel = 1 1.000 0 310 310 +9030221:onMode = off +9030221:onIfMatch = 310 310 + +10331:all = f_0(1710) f_0(1710) 0 0 0 1.71000 0.15000 1.10000 2.40000 0 +10331:oneChannel = 1 1.000 0 310 310 +10331:onMode = off +10331:onIfMatch = 310 310 + +335:all = f_2(1525) f_2(1525) 4 0 0 1.52500 0.08400 1.10000 2.00000 0 +335:oneChannel = 1 1.000 0 310 310 +335:onMode = off +335:onIfMatch = 310 310 + +10221:all = f_0(1370) f_0(1370) 0 0 0 1.35000 0.20000 0.80000 2.00000 0 +10221:oneChannel = 1 1.000 0 310 310 +10221:onMode = off +10221:onIfMatch = 310 310 + +225:all = f_2(1270) f_2(1270) 4 0 0 1.27500 0.1860 1.10000 1.50000 0 +225:oneChannel = 1 1.000 0 310 310 +225:onMode = off +225:onIfMatch = 310 310 + +115:all = a_2(1320) a_2(1320) 4 0 0 1.31820 0.10780 1.10000 1.50000 0 +115:oneChannel = 1 1.000 0 310 310 +115:onMode = off +115:onIfMatch = 310 310 diff --git a/MC/config/PWGLF/pythia8/generator/resonances_baryonic.cfg b/MC/config/PWGLF/pythia8/generator/resonances_baryonic.cfg new file mode 100644 index 000000000..e05459994 --- /dev/null +++ b/MC/config/PWGLF/pythia8/generator/resonances_baryonic.cfg @@ -0,0 +1,28 @@ +### Define resonance +ProcessLevel:all = off # will not look for the 'process' + +### Lambda1520 +# id::all = name antiName spinType chargeType colType m0 mWidth mMin mMax tau0 +102134:all = Lambda1520 Lambda1520bar 4 0 0 1.51950 0.01560 1.47 1.60 0 +102134:oneChannel = 1 1.000 0 2212 -321 +102134:onMode = off +102134:onIfMatch = 2212 -321 + +### Xi1530 +# id::all = name antiName spinType chargeType colType m0 mWidth mMin mMax tau0 +3324:all = Xi1530 Xi1530bar 4 0 0 1.53180 0.0091 1.50 1.59 0 +3324:oneChannel = 1 1.000 0 3312 211 +3324:onMode = off +3324:onIfMatch = 3312 211 + +### Xi1820 +123324:all = Xi1820 Xi1820bar 4 0 0 1.823 0.024 1.75 1.95 0 +123324:oneChannel = 1 1.000 0 3122 310 +123324:onMode = off +123324:onIfMatch = 3122 310 + +123314:all = Xi1820Minus Xi1820Plus 4 -3 0 1.823 0.024 1.75 1.95 0 +123314:oneChannel = 1 1.000 0 3122 -321 +123314:onMode = off +123314:onIfMatch = 3122 -321 + diff --git a/MC/config/PWGLF/pythia8/generator/resonances_width.cfg b/MC/config/PWGLF/pythia8/generator/resonances_width.cfg new file mode 100644 index 000000000..1c5a80f0a --- /dev/null +++ b/MC/config/PWGLF/pythia8/generator/resonances_width.cfg @@ -0,0 +1,87 @@ +# id::all = name antiName spinType chargeType colType m0 mWidth mMin mMax tau0 +# mass interval: +# mMin ~= sum of daughter masses + 1--2 MeV +# mMax = m0 + 10 * mWidth +# particle properties based on PDG 2025 + +### f0(980) +# 9010221:all = f_0(980) void 1 0 0 0.9900 0.0500 0.2791 1.6900 0 +9010221:onMode = off +# 9010221:oneChannel = 1 1.000 0 211 -211 +9010221:onIfMatch = 211 -211 + +### phi(1020) +# 333:all = phi phi 3 0 0 1.0195 0.0042 1.0091 1.0599 0 +333:onMode = off +333:onIfMatch = 11 -11 + +# ### Delta(1232)++ +# 2224:all = DeltaPlusPlus DeltaPlusPlusbar 4 6 0 1.2320 0.1170 1.0778 2.4020 0 +2224:onMode = off +# 2224:oneChannel = 1 1.000 0 2212 211 +2224:onIfMatch = 2212 211 + +# ### Delta(1232)0 +# 2114:all = DeltaZero DeltaZerobar 4 0 0 1.2320 0.1170 1.0778 2.4020 0 +2114:onMode = off +# 2114:oneChannel = 1 1.000 0 2212 -211 +2114:onIfMatch = 2212 -211 + + +# ### K1(1270)+ / K1(1270)+ bar +# 10323:all = K_1(1270)Plus K_1(1270)Plusbar 3 3 0 1.2530 0.09000 0.7728 2.1530 0 +# ### K1(1270)+ -> K+ rho0 +# 10323:oneChannel = 1 0.500 0 321 113 +# ### K1(1270)+ -> K*(892)0 pi+ +# 10323:addChannel = 1 0.500 0 313 211 +10323:onMode = off +10323:onIfMatch = 321 113 +10323:onIfMatch = 313 211 + + +# ### K1(1270)0 / anti-K1(1270)0 +# 10313:all = K_1(1270)Zero K_1(1270)Zerobar 3 0 0 1.2530 0.0900 0.7682 2.1530 0 +# ### K1(1270)0 -> K0 rho0 +# 10313:oneChannel = 1 0.500 0 311 113 +# ### K1(1270)0 -> K*(892)+ pi- +# 10313:addChannel = 1 0.500 0 323 -211 +10313:onMode = off +10313:onIfMatch = 311 113 +10313:onIfMatch = 323 -211 + +# ### Sigma(1385)+ +# 3224:all = Sigma1385Plus Sigma1385Plusbar 4 3 0 1.3828 0.0362 1.2552 1.6900 0 +# 3224:oneChannel = 1 1 0 3122 211 +3224:onMode = off +3224:onIfMatch = 3122 211 + +# ### Sigma(1385)- +# 3114:all = Sigma1385Minus Sigma1385Minusbar 4 -3 0 1.3872 0.0394 1.2552 1.7812 0 +# 3114:oneChannel = 1 1 0 3122 -211 +3114:onMode = off +3114:onIfMatch = 3122 -211 + +# ### Lambda1520 +# 102134:all = Lambda1520 Lambda1520bar 4 0 0 1.5190 0.01600 1.4319 1.6790 0 +# 102134:oneChannel = 1 1.000 0 2212 -321 +102134:onMode = off +102134:onIfMatch = 2212 -321 + +# ### Xi1530 +# # id::all = name antiName spinType chargeType colType m0 mWidth mMin mMax tau0 +# 3324:all = Xi1530 Xi1530bar 4 0 0 1.5318 0.0091 1.4612 1.6228 0 +# 3324:oneChannel = 1 1.000 0 3312 211 +3324:onMode = off +3324:onIfMatch = 3312 211 + +### Xi1820 +123324:all = Xi1820Zero Xi1820Zerobar 4 0 0 1.8230 0.0240 1.6150 2.0630 0 +123324:oneChannel = 1 1.000 0 3122 310 + +123314:all = Xi1820Minus Xi1820Minusbar 4 -3 0 1.8230 0.0240 1.6110 2.0630 0 +123314:oneChannel = 1 1.000 0 3122 -321 + +### Omega2012 +123334:all = Omega2012Minus Omega2012Minusbar 4 -3 0 2.0125 0.0064 1.8210 2.0765 0 +### Omega(2012)- -> Xi- K0S +123334:oneChannel = 1 1.000 0 3312 310 diff --git a/MC/config/PWGLF/pythia8/generator/sigmapi_resonances_gun.json b/MC/config/PWGLF/pythia8/generator/sigmapi_resonances_gun.json new file mode 100644 index 000000000..6d7e4b2b3 --- /dev/null +++ b/MC/config/PWGLF/pythia8/generator/sigmapi_resonances_gun.json @@ -0,0 +1,29 @@ +{ + "lambda(1405)" : { + "pdg": 102132, + "n": 2, + "ptMin": 0.0, + "ptMax": 10, + "etaMin": -1.2, + "etaMax": 1.2, + "genDecayed": true + }, + "lambda(1520)" : { + "pdg": 102134, + "n": 2, + "ptMin": 0.0, + "ptMax": 10, + "etaMin": -1.2, + "etaMax": 1.2, + "genDecayed": true + }, + "sigma(1385)" : { + "pdg": 3214, + "n": 2, + "ptMin": 0.0, + "ptMax": 10, + "etaMin": -1.2, + "etaMax": 1.2, + "genDecayed": true + } +} diff --git a/MC/config/PWGLF/pythia8/generator/v0particlelist.gun b/MC/config/PWGLF/pythia8/generator/v0particlelist.gun new file mode 100644 index 000000000..1c2ab6979 --- /dev/null +++ b/MC/config/PWGLF/pythia8/generator/v0particlelist.gun @@ -0,0 +1,4 @@ +# PDG N ptMin ptMax etaMin etaMax genDecayed +310 1 0.2 10 -1.2 1.2 +3122 1 0.2 10 -1.2 1.2 +-3122 1 0.2 10 -1.2 1.2 diff --git a/MC/config/PWGLF/pythia8/generator_pythia8_LF.C b/MC/config/PWGLF/pythia8/generator_pythia8_LF.C index b5550fb5e..f2befb6e6 100644 --- a/MC/config/PWGLF/pythia8/generator_pythia8_LF.C +++ b/MC/config/PWGLF/pythia8/generator_pythia8_LF.C @@ -11,7 +11,7 @@ /// `o2-sim -g external --configKeyValues 'GeneratorExternal.fileName=generator_pythia8_LF.C;GeneratorExternal.funcName=generateLF({{1000010020, 10, 0.5, 10}, {1000010030, 10, 0.5, 10}})'` /// Here PDG, Number injected, pT limits are separated are divided per particle /// or: -/// `o2-sim -g external --configKeyValues 'GeneratorExternal.fileName=generator_pythia8_LF.C;GeneratorExternal.funcName=generateLF("${O2DPG_ROOT}/MC/config/PWGLF/pythia8/generator/nuclei.gun")'` +/// `o2-sim -g external --configKeyValues 'GeneratorExternal.fileName=generator_pythia8_LF.C;GeneratorExternal.funcName=generateLF("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/nuclei.gun")'` /// Here PDG, Number injected, pT limits are provided via an intermediate configuration file /// @@ -23,8 +23,8 @@ #include #include "Generators/GeneratorPythia8Param.h" #include "Generators/DecayerPythia8Param.h" -#include #endif +#include #include "generator_pythia8_longlived.C" using namespace Pythia8; @@ -37,8 +37,8 @@ class GeneratorPythia8LF : public o2::eventgen::GeneratorPythia8 GeneratorPythia8LF(bool injOnePerEvent /*= true*/, int gapBetweenInjection /*= 0*/, bool useTrigger /*= false*/, - std::string pythiaCfgMb /*= "${O2DPG_ROOT}/MC/config/PWGLF/pythia8/pythia8_inel_minbias.cfg"*/, - std::string pythiaCfgSignal /*= "${O2DPG_ROOT}/MC/config/PWGLF/pythia8/pythia8_inel_signal.cfg"*/) : GeneratorPythia8{}, + std::string pythiaCfgMb /*= "${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/pythia8_inel_minbias.cfg"*/, + std::string pythiaCfgSignal /*= "${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/pythia8_inel_signal.cfg"*/) : GeneratorPythia8{}, mOneInjectionPerEvent{injOnePerEvent}, mGapBetweenInjection{gapBetweenInjection}, mUseTriggering{useTrigger} @@ -161,14 +161,7 @@ class GeneratorPythia8LF : public o2::eventgen::GeneratorPythia8 LOG(info) << "Using config container "; cfg.print(); if (mUseTriggering) { // Do the triggering - bool doSignal = true; // Do signal or gap - if (mGapBetweenInjection > 0) { - if (mGapBetweenInjection == 1 && mEventCounter % 2 == 0) { - doSignal = false; - } else if (mEventCounter % mGapBetweenInjection != 0) { - doSignal = false; - } - } + bool doSignal{mEventCounter % (mGapBetweenInjection + 1) == 0}; // Do signal or gap if (doSignal) { LOG(info) << "Generating triggered signal event for particle"; @@ -445,8 +438,8 @@ class GeneratorPythia8LF : public o2::eventgen::GeneratorPythia8 std::vector mGunConfigs; // List of gun configurations to use std::vector mGunConfigsGenDecayed; // List of gun configurations to use that will be decayed by the generator - Pythia pythiaObjectSignal; // Signal collision generator - Pythia pythiaObjectMinimumBias; // Minimum bias collision generator + Pythia8::Pythia pythiaObjectSignal; // Signal collision generator + Pythia8::Pythia pythiaObjectMinimumBias; // Minimum bias collision generator }; ///___________________________________________________________ @@ -492,7 +485,7 @@ FairGenerator* generateLF(std::vector cfg, ///___________________________________________________________ /// Create generator via input file -FairGenerator* generateLF(std::string configuration = "${O2DPG_ROOT}/MC/config/PWGLF/pythia8/generator/nuclei.gun", +FairGenerator* generateLF(std::string configuration = "${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/nuclei.gun", bool injectOnePDGPerEvent = true, int gapBetweenInjection = 0, bool useTrigger = false, @@ -546,7 +539,7 @@ FairGenerator* generateLF(std::string configuration = "${O2DPG_ROOT}/MC/config/P ///___________________________________________________________ /// Create generator via input file for the triggered mode -FairGenerator* generateLFTriggered(std::string configuration = "${O2DPG_ROOT}/MC/config/PWGLF/pythia8/generator/nuclei.gun", +FairGenerator* generateLFTriggered(std::string configuration = "${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/nuclei.gun", int gapBetweenInjection = 0, std::string pythiaCfgMb = "", std::string pythiaCfgSignal = "") diff --git a/MC/config/PWGLF/pythia8/generator_pythia8_LF_flat_mass.C b/MC/config/PWGLF/pythia8/generator_pythia8_LF_flat_mass.C new file mode 100644 index 000000000..38f144144 --- /dev/null +++ b/MC/config/PWGLF/pythia8/generator_pythia8_LF_flat_mass.C @@ -0,0 +1,600 @@ +/// \file generator_pythia8_LF_flat_mass.C +/// \author Sawan sawan.sawan@cern.ch +/// \brief Modified LF Gun Generator for Flat Mass PHSP PWA Acceptance/Efficiency MC. +/// Adapted from generator_pythia8_LF_rapidity_width.C + +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include "FairGenerator.h" + +#include "TDatabasePDG.h" +#include "TMath.h" +#include "TParticlePDG.h" +#include "TRandom3.h" + +#include "Generators/GeneratorPythia8.h" +#include "Pythia8/Pythia.h" +#if __has_include("SimulationDataFormat/MCGenStatus.h") +#include "SimulationDataFormat/MCGenStatus.h" +#else +#include "SimulationDataFormat/MCGenProperties.h" +#endif +#if __has_include("SimulationDataFormat/MCUtils.h") +#include "SimulationDataFormat/MCUtils.h" +#endif +#include "TSystem.h" + +#include "Generators/GeneratorPythia8Param.h" +#include "fairlogger/Logger.h" + +#include +#include +#endif +// DecayerPythia8Param needs to be included after the #endif to work with Cling +#include "Generators/DecayerPythia8Param.h" +#if defined(__CLING__) && !defined(__ROOTCLING__) +#if __has_include("SimulationDataFormat/MCGenStatus.h") +#include "SimulationDataFormat/MCGenStatus.h" +#elif __has_include("SimulationDataFormat/MCGenProperties.h") +#include "SimulationDataFormat/MCGenProperties.h" +#endif +#if __has_include("SimulationDataFormat/MCUtils.h") +#include "SimulationDataFormat/MCUtils.h" +#endif +#pragma cling load("libO2Generators") +#endif +#include "generator_pythia8_longlived.C" + +#include + +using namespace Pythia8; +using namespace o2::mcgenstatus; + +class GeneratorPythia8LFRapidity : public o2::eventgen::GeneratorPythia8 +{ +public: + /// Parametric constructor + GeneratorPythia8LFRapidity(bool injOnePerEvent = true, + int gapBetweenInjection = 0, + bool useTrigger = false, + bool useRapidity = false, + std::string pythiaCfgMb = "${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_136tev.cfg", + std::string pythiaCfgSignal = "${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_136tev.cfg") : GeneratorPythia8{}, + mOneInjectionPerEvent{injOnePerEvent}, + mGapBetweenInjection{gapBetweenInjection}, + mUseTriggering{useTrigger}, + mUseRapidity{useRapidity} + { + LOG(info) << "GeneratorPythia8LFRapidity constructor (Flat Mass PHSP mode)"; + LOG(info) << "++ mOneInjectionPerEvent: " << mOneInjectionPerEvent; + LOG(info) << "++ mGapBetweenInjection: " << mGapBetweenInjection; + LOG(info) << "++ mUseTriggering: " << mUseTriggering; + LOG(info) << "++ mUseRapidity: " << mUseRapidity; + LOG(info) << "++ pythiaCfgMb: " << pythiaCfgMb; + LOG(info) << "++ pythiaCfgSignal: " << pythiaCfgSignal; + gRandom->SetSeed(0); + + // Register PDG 10331 f0(1710) in ROOT TDatabasePDG if missing to prevent Geant4 / TDatabase crashes + if (!TDatabasePDG::Instance()->GetParticle(10331)) + { + TDatabasePDG::Instance()->AddParticle("f0_1710", "f0_1710", 1.710, true, 0.150, 0, "meson", 10331); + } + + if (useTrigger) + { + mPythia.readString("ProcessLevel:all off"); + if (pythiaCfgMb == "") + { + auto ¶m = o2::eventgen::GeneratorPythia8Param::Instance(); + pythiaCfgMb = param.config; + } + if (pythiaCfgSignal == "") + { + auto ¶m = o2::eventgen::GeneratorPythia8Param::Instance(); + pythiaCfgSignal = param.config; + } + pythiaCfgMb = gSystem->ExpandPathName(pythiaCfgMb.c_str()); + pythiaCfgSignal = gSystem->ExpandPathName(pythiaCfgSignal.c_str()); + if (!pythiaObjectMinimumBias.readFile(pythiaCfgMb)) + { + LOG(fatal) << "Could not pythiaObjectMinimumBias.readFile(\"" << pythiaCfgMb << "\")"; + } + pythiaObjectMinimumBias.readString("Random:setSeed = on"); + pythiaObjectMinimumBias.readString("Random:seed =" + std::to_string(gRandom->Integer(900000000 - 2) + 1)); + + if (!pythiaObjectSignal.readFile(pythiaCfgSignal)) + { + LOG(fatal) << "Could not pythiaObjectSignal.readFile(\"" << pythiaCfgSignal << "\")"; + } + pythiaObjectSignal.readString("Random:setSeed = on"); + pythiaObjectSignal.readString("Random:seed =" + std::to_string(gRandom->Integer(900000000 - 2) + 1)); + pythiaObjectMinimumBias.particleData.addParticle(1000100200, "20Ne", 6, 30, 0, 19.992440); + mPythiaGun.particleData.addParticle(1000100200, "20Ne", 6, 30, 0, 19.992440); + if (!pythiaObjectMinimumBias.init()) + { + LOG(fatal) << "Could not pythiaObjectMinimumBias.init() from " << pythiaCfgMb; + } + if (!pythiaObjectSignal.init()) + { + LOG(fatal) << "Could not pythiaObjectSignal.init() from " << pythiaCfgSignal; + } + } + else + { + if (pythiaCfgMb == "") + { + auto ¶m = o2::eventgen::GeneratorPythia8Param::Instance(); + pythiaCfgMb = param.config; + } + if (pythiaCfgMb == "") + { + pythiaCfgMb = "${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_136tev.cfg"; + } + pythiaCfgMb = gSystem->ExpandPathName(pythiaCfgMb.c_str()); + if (!pythiaObjectMinimumBias.readFile(pythiaCfgMb)) + { + LOG(fatal) << "Could not pythiaObjectMinimumBias.readFile(\"" << pythiaCfgMb << "\")"; + } + pythiaObjectMinimumBias.readString("Random:setSeed = on"); + pythiaObjectMinimumBias.readString("Random:seed =" + std::to_string(gRandom->Integer(900000000 - 2) + 1)); + + pythiaObjectMinimumBias.particleData.addParticle(1000100200, "20Ne", 6, 30, 0, 19.992440); + mPythiaGun.particleData.addParticle(1000100200, "20Ne", 6, 30, 0, 19.992440); + if (!pythiaObjectMinimumBias.init()) + { + LOG(fatal) << "Could not pythiaObjectMinimumBias.init() from " << pythiaCfgMb; + } + + mPythiaGun.readString("ProcessLevel:all off"); + + auto ¶m = o2::eventgen::DecayerPythia8Param::Instance(); + for (int i = 0; i < 8; ++i) + { + if (param.config[i].empty()) + { + continue; + } + std::string config = gSystem->ExpandPathName(param.config[i].c_str()); + readParticleDataLines(mPythia, config, "import Pythia"); + if (!mPythiaGun.readFile(config, true)) + { + LOG(fatal) << "Failed to init \'DecayerPythia8\': problems with configuration file " << config; + return; + } + } + + if (param.showChanged) + { + mPythiaGun.readString(std::string("Init:showChangedParticleData on")); + } + else + { + mPythiaGun.readString(std::string("Init:showChangedParticleData off")); + } + + if (!mPythiaGun.init()) + { + LOG(fatal) << "Failed to init \'DecayerPythia8\': init returned with error"; + return; + } + } + } + + ~GeneratorPythia8LFRapidity() = default; + + bool Init() override + { + addSubGenerator(kSubGeneratorMinimumBias, "Minimum bias"); + addSubGenerator(kSubGeneratorLFSignal, "LF signal"); + return o2::eventgen::GeneratorPythia8::Init(); + } + + Bool_t generateEvent() override + { + mDoSignalThisEvent = isSignalEvent(); + notifySubGenerator(mDoSignalThisEvent ? kSubGeneratorLFSignal : kSubGeneratorMinimumBias); + + if (!mUseTriggering) + { + bool lGenerationOK = false; + while (!lGenerationOK) + { + lGenerationOK = pythiaObjectMinimumBias.next(); + } + copyMinimumBiasEventForInjection(); + + if (!mDoSignalThisEvent) + { + return true; + } + } + + if (mUseTriggering) + { + mPythia.event.reset(); + } + + mConfigToUse = mOneInjectionPerEvent ? static_cast(gRandom->Uniform(0.f, getNGuns())) : -1; + + int nConfig = mGunConfigs.size(); + for (const ConfigContainer &cfg : mGunConfigsGenDecayed) + { + nConfig++; + if (mConfigToUse >= 0 && (nConfig - 1) != mConfigToUse) + { + continue; + } + + if (mUseTriggering) + { + if (mDoSignalThisEvent) + { + bool satisfiesTrigger = false; + int nTries = 0; + while (!satisfiesTrigger) + { + if (!pythiaObjectSignal.next()) + { + continue; + } + for (int j = 0; j < pythiaObjectSignal.event.size(); j++) + { + const int &pypid = pythiaObjectSignal.event[j].id(); + const float &pyeta = mUseRapidity ? pythiaObjectSignal.event[j].y() : pythiaObjectSignal.event[j].eta(); + const float &pypt = pythiaObjectSignal.event[j].pT(); + if (pypid == cfg.mPdg && cfg.mMin < pyeta && pyeta < cfg.mMax && pypt > cfg.mPtMin && pypt < cfg.mPtMax) + { + satisfiesTrigger = true; + break; + } + } + nTries++; + } + mPythia.event = pythiaObjectSignal.event; + } + else + { + bool lGenerationOK = false; + while (!lGenerationOK) + { + lGenerationOK = pythiaObjectMinimumBias.next(); + } + mPythia.event = pythiaObjectMinimumBias.event; + } + continue; + } + + mPythiaGun.event.reset(); + for (int i{0}; i < cfg.mNInject; ++i) + { + const double pt = gRandom->Uniform(cfg.mPtMin, cfg.mPtMax); + const double eta = gRandom->Uniform(cfg.mMin, cfg.mMax); + const double phi = gRandom->Uniform(0, TMath::TwoPi()); + const double px{pt * std::cos(phi)}; + const double py{pt * std::sin(phi)}; + const double mass = sampleMass(cfg); // Uses Flat Mass Sampling + + double pz = 0; + double et = 0; + + if (mUseRapidity) + { + const double mT = std::sqrt(mass * mass + pt * pt); + pz = mT * std::sinh(eta); + et = mT * std::cosh(eta); + } + else + { + pz = pt * std::sinh(eta); + const double p = pt * std::cosh(eta); + et = std::sqrt(p * p + mass * mass); + } + + Particle particle; + particle.id(cfg.mPdg); + particle.status(11); + particle.m(mass); + particle.px(px); + particle.py(py); + particle.pz(pz); + particle.e(et); + particle.xProd(0.f); + particle.yProd(0.f); + particle.zProd(0.f); + particle.tProd(0.f); + mPythiaGun.particleData.mayDecay(cfg.mPdg, true); + mPythiaGun.event.append(particle); + } + + mPythiaGun.moreDecays(); + mPythiaGun.next(); + + int offset = mPythia.event.size(); + for (int i = 1; i < mPythiaGun.event.size(); ++i) + { + Particle &p = mPythiaGun.event[i]; + int mother1 = p.mother1() > 0 ? p.mother1() + offset - 1 : p.mother1(); + int mother2 = p.mother2() > 0 ? p.mother2() + offset - 1 : p.mother2(); + int daughter1 = p.daughter1() > 0 ? p.daughter1() + offset - 1 : p.daughter1(); + int daughter2 = p.daughter2() > 0 ? p.daughter2() + offset - 1 : p.daughter2(); + + p.mothers(mother1, mother2); + p.daughters(daughter1, daughter2); + + mPythia.event.append(p); + } + } + return true; + } + + Bool_t importParticles() override + { + if (!GeneratorPythia8::importParticles()) + { + return false; + } + + if (!mUseTriggering && mDoSignalThisEvent) + { + int nConfig = 0; + for (const ConfigContainer &cfg : mGunConfigs) + { + nConfig++; + if (mConfigToUse >= 0 && (nConfig - 1) != mConfigToUse) + { + continue; + } + + for (int i{0}; i < cfg.mNInject; ++i) + { + const double pt = gRandom->Uniform(cfg.mPtMin, cfg.mPtMax); + const double eta = gRandom->Uniform(cfg.mMin, cfg.mMax); + const double phi = gRandom->Uniform(0, TMath::TwoPi()); + const double px{pt * std::cos(phi)}; + const double py{pt * std::sin(phi)}; + const double mass = sampleMass(cfg); + double pz = 0; + double et = 0; + + if (mUseRapidity) + { + const double mT = std::sqrt(mass * mass + pt * pt); + pz = mT * std::sinh(eta); + et = mT * std::cosh(eta); + } + else + { + pz = pt * std::sinh(eta); + const double p = pt * std::cosh(eta); + et = std::sqrt(p * p + mass * mass); + } + + mParticles.push_back(TParticle(cfg.mPdg, + MCGenStatusEncoding(1, 1).fullEncoding, + -1, -1, + -1, -1, + px, py, pz, et, + 0., 0., 0., 0.)); + o2::mcutils::MCGenHelper::encodeParticleStatusAndTracking(mParticles.back()); + } + } + } + + mEventCounter++; + return true; + } + + struct ConfigContainer + { + // 1. Set default constructor arguments to 1.0 and 3.0 + ConfigContainer(int input_pdg = 10331, int n = 1, + float ptMin = 0.0, float ptMax = 30.0, + float min = -1.0, float max = 1.0, + double massMin = 1.0, double massMax = 3.0) : mPdg{input_pdg}, + mNInject{n}, + mPtMin{ptMin}, + mPtMax{ptMax}, + mMin{min}, + mMax{max}, + mMassMin{massMin}, + mMassMax{massMax} + { + mMass = GeneratorPythia8LongLivedGun::getMass(mPdg); + if (mMass <= 0) + { + mMass = 1.710; + } + LOGF(info, "ConfigContainer: mPdg = %i, mNInject = %i, mPtMin = %f, mPtMax = %f, mMin = %f, mMax = %f, mMassMin = %f, mMassMax = %f", + mPdg, mNInject, mPtMin, mPtMax, mMin, mMax, mMassMin, mMassMax); + }; + + ConfigContainer(TObjArray *arr) : ConfigContainer( + arr->GetEntries() > 0 ? atoi(arr->At(0)->GetName()) : 10331, + arr->GetEntries() > 1 ? atoi(arr->At(1)->GetName()) : 1, + arr->GetEntries() > 2 ? atof(arr->At(2)->GetName()) : 0.0, + arr->GetEntries() > 3 ? atof(arr->At(3)->GetName()) : 30.0, + arr->GetEntries() > 4 ? atof(arr->At(4)->GetName()) : -1.0, + arr->GetEntries() > 5 ? atof(arr->At(5)->GetName()) : 1.0, + arr->GetEntries() > 6 ? atof(arr->At(6)->GetName()) : 1.0, + arr->GetEntries() > 7 ? atof(arr->At(7)->GetName()) : 3.0) {}; + + ConfigContainer(TString line) : ConfigContainer(line.Tokenize(" ")) {}; + + // 2. Set jsonParams.value fallback defaults to 1.0 and 3.0 + ConfigContainer(const nlohmann::json &jsonParams, bool useRapidity = false) + : ConfigContainer(jsonParams.value("pdg", 10331), + jsonParams.value("n", 1), + jsonParams.value("ptMin", 0.0f), + jsonParams.value("ptMax", 30.0f), + (useRapidity && jsonParams.contains("rapidityMin")) ? jsonParams["rapidityMin"].get() : (jsonParams.contains("min") ? jsonParams["min"].get() : jsonParams.value("etaMin", -1.0f)), + (useRapidity && jsonParams.contains("rapidityMax")) ? jsonParams["rapidityMax"].get() : (jsonParams.contains("max") ? jsonParams["max"].get() : jsonParams.value("etaMax", 1.0f)), + jsonParams.value("massMin", 1.0), + jsonParams.value("massMax", 3.0)) {}; + + const int mPdg = 10331; + const int mNInject = 1; + const float mPtMin = 0.0; + const float mPtMax = 30.0; + const float mMin = -1.f; + const float mMax = 1.f; + double mMass = 0.f; + double mMassMin = 1.0; + double mMassMax = 3.0; + + void print() const + { + LOGF(info, "int mPdg = %i, mNInject = %i, mPtMin = %f, mPtMax = %f, mMin = %f, mMax = %f, mMassMin = %f, mMassMax = %f", + mPdg, mNInject, mPtMin, mPtMax, mMin, mMax, mMassMin, mMassMax); + } + }; + + // FLAT MASS SAMPLING LOGIC + // FLAT MASS SAMPLING LOGIC + double sampleMass(const ConfigContainer &cfg) + { + if (cfg.mMassMax > cfg.mMassMin && cfg.mMassMin > 0.0) + { + return gRandom->Uniform(cfg.mMassMin, cfg.mMassMax); + } + // Fallback if range is invalid or missing: flat uniform in [1.0, 3.0] GeV + return gRandom->Uniform(1.0, 3.0); + } + + ConfigContainer addGun(int input_pdg, int nInject = 1, float ptMin = 0, float ptMax = 30, float min = -1, float max = 1, double massMin = -1.0, double massMax = -1.0) + { + if (mUseTriggering) + { + return addGunGenDecayed(input_pdg, nInject, ptMin, ptMax, min, max, massMin, massMax); + } + ConfigContainer cfg{input_pdg, nInject, ptMin, ptMax, min, max, massMin, massMax}; + mGunConfigs.push_back(cfg); + return cfg; + } + + ConfigContainer addGun(ConfigContainer cfg) { return addGun(cfg.mPdg, cfg.mNInject, cfg.mPtMin, cfg.mPtMax, cfg.mMin, cfg.mMax, cfg.mMassMin, cfg.mMassMax); } + + ConfigContainer addGunGenDecayed(int input_pdg, int nInject = 1, float ptMin = 0, float ptMax = 30, float min = -1, float max = 1, double massMin = -1.0, double massMax = -1.0) + { + ConfigContainer cfg{input_pdg, nInject, ptMin, ptMax, min, max, massMin, massMax}; + mGunConfigsGenDecayed.push_back(cfg); + return cfg; + } + + ConfigContainer addGunGenDecayed(ConfigContainer cfg) { return addGunGenDecayed(cfg.mPdg, cfg.mNInject, cfg.mPtMin, cfg.mPtMax, cfg.mMin, cfg.mMax, cfg.mMassMin, cfg.mMassMax); } + + long int getNGuns() const { return mGunConfigs.size() + mGunConfigsGenDecayed.size(); } + +private: + static constexpr int kSubGeneratorMinimumBias = 0; + static constexpr int kSubGeneratorLFSignal = 1; + + bool isSignalEvent() const { return mGapBetweenInjection <= 1 || mEventCounter % mGapBetweenInjection == 0; } + + void copyMinimumBiasEventForInjection() + { + mPythia.event = pythiaObjectMinimumBias.event; + mPythia.event.init("Minimum-bias event with injected particles", &mPythia.particleData); + mPythia.event.restorePtrs(); + } + + static bool isParticleDataLine(const std::string &line) + { + size_t pos = line.find_first_not_of(" \t"); + if (pos == std::string::npos) + return false; + if (line[pos] == '+' || line[pos] == '-') + ++pos; + if (pos >= line.size() || line[pos] < '0' || line[pos] > '9') + return false; + while (pos < line.size() && line[pos] >= '0' && line[pos] <= '9') + ++pos; + return pos < line.size() && line[pos] == ':'; + } + + static bool readParticleDataLines(Pythia8::Pythia &pythia, const std::string &config, const char *target) + { + std::ifstream input(config.c_str(), ios::in); + if (!input.is_open()) + return false; + std::string line; + while (std::getline(input, line)) + { + TString stripped = line; + stripped.Strip(TString::kBoth, ' '); + if (stripped.IsNull() || stripped.IsWhitespace() || stripped.BeginsWith("#")) + continue; + std::string command = stripped.Data(); + if (!isParticleDataLine(command)) + continue; + if (!pythia.readString(command)) + return false; + } + return true; + } + + const bool mOneInjectionPerEvent = true; + const bool mUseTriggering = false; + const int mGapBetweenInjection = 0; + const bool mUseRapidity = false; + + int mConfigToUse = -1; + int mEventCounter = 0; + bool mDoSignalThisEvent = true; + + std::vector mGunConfigs; + std::vector mGunConfigsGenDecayed; + Pythia8::Pythia pythiaObjectSignal; + Pythia8::Pythia pythiaObjectMinimumBias; + Pythia8::Pythia mPythiaGun; +}; + +FairGenerator *generateLFRapidity(std::string configuration = "", + bool injectOnePDGPerEvent = true, + int gapBetweenInjection = 0, + bool useTrigger = false, + bool useRapidity = false, + std::string pythiaCfgMb = "", + std::string pythiaCfgSignal = "") +{ + std::vector cfgVec; + std::vector cfgVecGenDecayed; + + if (!configuration.empty()) + { + configuration = gSystem->ExpandPathName(configuration.c_str()); + std::ifstream inputFile(configuration.c_str(), ios::in); + if (inputFile.is_open() && TString(configuration.c_str()).EndsWith(".json")) + { + nlohmann::json paramfile = nlohmann::json::parse(inputFile); + for (const auto ¶m : paramfile) + { + if (param.value("genDecayed", true)) + { + cfgVecGenDecayed.push_back(GeneratorPythia8LFRapidity::ConfigContainer{param, useRapidity}); + } + else + { + cfgVec.push_back(GeneratorPythia8LFRapidity::ConfigContainer{param, useRapidity}); + } + } + } + } + + // DEFAULT FALLBACK: If no configuration file passed or empty, default to PDG 10331 Flat Mass PHSP + if (cfgVec.empty() && cfgVecGenDecayed.empty()) + { + LOG(info) << "No particle list provided or empty. Falling back to default PDG 10331 (f0(1710)) Flat Mass setup."; + GeneratorPythia8LFRapidity::ConfigContainer defaultCfg(10331, 1, 0.0, 30.0, -1.0, 1.0, 1.0, 3.0); + cfgVecGenDecayed.push_back(defaultCfg); + } + + GeneratorPythia8LFRapidity *multiGun = new GeneratorPythia8LFRapidity(injectOnePDGPerEvent, gapBetweenInjection, useTrigger, useRapidity, pythiaCfgMb, pythiaCfgSignal); + for (const auto &c : cfgVec) + { + multiGun->addGun(c); + } + for (const auto &c : cfgVecGenDecayed) + { + multiGun->addGunGenDecayed(c); + } + return multiGun; +} \ No newline at end of file diff --git a/MC/config/PWGLF/pythia8/generator_pythia8_LF_rapidity.C b/MC/config/PWGLF/pythia8/generator_pythia8_LF_rapidity.C new file mode 100644 index 000000000..0a10286dd --- /dev/null +++ b/MC/config/PWGLF/pythia8/generator_pythia8_LF_rapidity.C @@ -0,0 +1,721 @@ +/// +/// \file generator_pythia8_LF_rapidity.C +/// \author Hirak Kumar Koley hirak.koley@cern.ch +/// \author Bong-Hwi Lim bong-hwi.lim@cern.ch +/// \author Based on generator_pythia8_LF.C by Nicolò Jacazio +/// \since 2025/08/18 +/// \brief Implementation of a gun generator for multiple particles using rapidity or pseudorapidity (default) instead of eta, built on generator_pythia8_longlived.C +/// Needs PDG, Number of injected, minimum and maximum pT, minimum and maximum y/eta. These can be provided in three ways, bundeling variables, particles or from input file +/// usage: +/// `o2-sim -g external --configKeyValues 'GeneratorExternal.fileName=generator_pythia8_LF_rapidity.C;GeneratorExternal.funcName=generateLFRapidity({1000010020, 1000010030}, {10, 10}, {0.5, 0.5}, {10, 10}, {-1.0, -1.0}, {1.0, 1.0})'` +/// Here PDG, Number injected, pT limits, y/eta limits are separated and matched by index +/// or: +/// `o2-sim -g external --configKeyValues 'GeneratorExternal.fileName=generator_pythia8_LF_rapidity.C;GeneratorExternal.funcName=generateLFRapidity({{1000010020, 10, 0.5, 10, -1.0, 1.0}, {1000010030, 10, 0.5, 10, -1.0, 1.0}})'` +/// Here PDG, Number injected, pT limits, y/eta limits are divided per particle +/// or: +/// `o2-sim -g external --configKeyValues 'GeneratorExternal.fileName=generator_pythia8_LF_rapidity.C;GeneratorExternal.funcName=generateLFRapidity("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/exotic_nuclei_pp.gun")'` +/// Here PDG, Number injected, pT limits, y/eta limits are provided via an intermediate configuration file +/// + +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include "FairGenerator.h" + +#include "TDatabasePDG.h" +#include "TMath.h" +#include "TParticlePDG.h" +#include "TRandom3.h" + +#include "Generators/GeneratorPythia8.h" +#include "Pythia8/Pythia.h" +#if __has_include("SimulationDataFormat/MCGenStatus.h") +#include "SimulationDataFormat/MCGenStatus.h" +#else +#include "SimulationDataFormat/MCGenProperties.h" +#endif +#if __has_include("SimulationDataFormat/MCUtils.h") +#include "SimulationDataFormat/MCUtils.h" +#endif +#include "TSystem.h" + +#include "Generators/GeneratorPythia8Param.h" +#include "fairlogger/Logger.h" + +#include +#include +#endif +// DecayerPythia8Param needs to be included after the #endif to work with Cling +#include "Generators/DecayerPythia8Param.h" +#if defined(__CLING__) && !defined(__ROOTCLING__) +#if __has_include("SimulationDataFormat/MCGenStatus.h") +#include "SimulationDataFormat/MCGenStatus.h" +#elif __has_include("SimulationDataFormat/MCGenProperties.h") +#include "SimulationDataFormat/MCGenProperties.h" +#endif +#if __has_include("SimulationDataFormat/MCUtils.h") +#include "SimulationDataFormat/MCUtils.h" +#endif +#pragma cling load("libO2Generators") +#endif +// #include "Generators/GeneratorPythia8.h" +#include "generator_pythia8_longlived.C" + +#include + +using namespace Pythia8; +using namespace o2::mcgenstatus; + +class GeneratorPythia8LFRapidity : public o2::eventgen::GeneratorPythia8 +{ + public: + /// Parametric constructor + GeneratorPythia8LFRapidity(bool injOnePerEvent = true, + int gapBetweenInjection = 0, + bool useTrigger = false, + bool useRapidity = false, + std::string pythiaCfgMb = "${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_136tev.cfg", + std::string pythiaCfgSignal = "${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_136tev.cfg") : GeneratorPythia8{}, + mOneInjectionPerEvent{injOnePerEvent}, + mGapBetweenInjection{gapBetweenInjection}, + mUseTriggering{useTrigger}, + mUseRapidity{useRapidity} + { + LOG(info) << "GeneratorPythia8LFRapidity constructor"; + LOG(info) << "++ mOneInjectionPerEvent: " << mOneInjectionPerEvent; + LOG(info) << "++ mGapBetweenInjection: " << mGapBetweenInjection; + LOG(info) << "++ mUseTriggering: " << mUseTriggering; + LOG(info) << "++ mUseRapidity: " << mUseRapidity; + LOG(info) << "++ pythiaCfgMb: " << pythiaCfgMb; + LOG(info) << "++ pythiaCfgSignal: " << pythiaCfgSignal; + gRandom->SetSeed(0); + if (useTrigger) { + mPythia.readString("ProcessLevel:all off"); + if (pythiaCfgMb == "") { // If no configuration file is provided, use the one from the Pythia8Param + auto& param = o2::eventgen::GeneratorPythia8Param::Instance(); + LOG(info) << "Instance LFRapidity \'Pythia8\' generator with following parameters for MB event"; + LOG(info) << param; + pythiaCfgMb = param.config; + } + if (pythiaCfgSignal == "") { // If no configuration file is provided, use the one from the Pythia8Param + auto& param = o2::eventgen::GeneratorPythia8Param::Instance(); + LOG(info) << "Instance LFRapidity \'Pythia8\' generator with following parameters for signal event"; + LOG(info) << param; + pythiaCfgSignal = param.config; + } + pythiaCfgMb = gSystem->ExpandPathName(pythiaCfgMb.c_str()); + pythiaCfgSignal = gSystem->ExpandPathName(pythiaCfgSignal.c_str()); + LOG(info) << " ++ Using trigger, initializing Pythia8 for trigger"; + if (!pythiaObjectMinimumBias.readFile(pythiaCfgMb)) { + LOG(fatal) << "Could not pythiaObjectMinimumBias.readFile(\"" << pythiaCfgMb << "\")"; + } + pythiaObjectMinimumBias.readString("Random:setSeed = on"); + pythiaObjectMinimumBias.readString("Random:seed =" + std::to_string(gRandom->Integer(900000000 - 2) + 1)); + + // FIX: Init signal pythia object + if (!pythiaObjectSignal.readFile(pythiaCfgSignal)) { + LOG(fatal) << "Could not pythiaObjectSignal.readFile(\"" << pythiaCfgSignal << "\")"; + } + pythiaObjectSignal.readString("Random:setSeed = on"); + pythiaObjectSignal.readString("Random:seed =" + std::to_string(gRandom->Integer(900000000 - 2) + 1)); + pythiaObjectMinimumBias.particleData.addParticle(1000100200, "20Ne", 6, 30, 0, 19.992440); + mPythiaGun.particleData.addParticle(1000100200, "20Ne", 6, 30, 0, 19.992440); + if (!pythiaObjectMinimumBias.init()) { + LOG(fatal) << "Could not pythiaObjectMinimumBias.init() from " << pythiaCfgMb; + } + if (!pythiaObjectSignal.init()) { + LOG(fatal) << "Could not pythiaObjectSignal.init() from " << pythiaCfgSignal; + } + } else { // Using simple injection with internal decay (if needed). Fetching the parameters from the configuration file of the PythiaDecayer + + if (pythiaCfgMb == "") { // If no configuration file is provided, use the one from the Pythia8Param + auto& param = o2::eventgen::GeneratorPythia8Param::Instance(); + LOG(info) << "Instance LFRapidity \'Pythia8\' generator with following parameters for MB event"; + LOG(info) << param; + pythiaCfgMb = param.config; + } + // FIX: Fallback if still empty to default minbias + if (pythiaCfgMb == "") { + pythiaCfgMb = "${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_136tev.cfg"; + } + pythiaCfgMb = gSystem->ExpandPathName(pythiaCfgMb.c_str()); + if (!pythiaObjectMinimumBias.readFile(pythiaCfgMb)) { + LOG(fatal) << "Could not pythiaObjectMinimumBias.readFile(\"" << pythiaCfgMb << "\")"; + } + pythiaObjectMinimumBias.readString("Random:setSeed = on"); + pythiaObjectMinimumBias.readString("Random:seed =" + std::to_string(gRandom->Integer(900000000 - 2) + 1)); + + pythiaObjectMinimumBias.particleData.addParticle(1000100200, "20Ne", 6, 30, 0, 19.992440); + mPythiaGun.particleData.addParticle(1000100200, "20Ne", 6, 30, 0, 19.992440); + if (!pythiaObjectMinimumBias.init()) { + LOG(fatal) << "Could not pythiaObjectMinimumBias.init() from " << pythiaCfgMb; + } + + /** switch off process level **/ + mPythiaGun.readString("ProcessLevel:all off"); + + auto& param = o2::eventgen::DecayerPythia8Param::Instance(); + LOG(info) << "Init \'GeneratorPythia8LFRapidity\' with following parameters"; + LOG(info) << param; + for (int i = 0; i < 8; ++i) { + if (param.config[i].empty()) { + continue; + } + std::string config = gSystem->ExpandPathName(param.config[i].c_str()); + LOG(info) << "GeneratorPythia8LFRapidity Reading configuration from file: " << config; + if (!mPythiaGun.readFile(config, true)) { + LOG(fatal) << "Failed to init \'DecayerPythia8\': problems with configuration file " + << config; + return; + } + } + + /** show changed particle data **/ + if (param.showChanged) { + mPythiaGun.readString(std::string("Init:showChangedParticleData on")); + } else { + mPythiaGun.readString(std::string("Init:showChangedParticleData off")); + } + + /** initialise **/ + if (!mPythiaGun.init()) { + LOG(fatal) << "Failed to init \'DecayerPythia8\': init returned with error"; + return; + } + if (pythiaCfgSignal != "") { + LOG(fatal) << "Cannot use simple injection and have a configuration file. pythiaCfgSignal= `" << pythiaCfgSignal << "` must be empty"; + } + } + } + + /// Destructor + ~GeneratorPythia8LFRapidity() = default; + + //__________________________________________________________________ + Bool_t generateEvent() override + { + if (!mUseTriggering) { // Injected mode: Embedding into MB + // 1. Generate Background (MB) + // LOG(info) << "Generating background event " << mEventCounter; + + bool lGenerationOK = false; + while (!lGenerationOK) { + lGenerationOK = pythiaObjectMinimumBias.next(); + } + mPythia.event = pythiaObjectMinimumBias.event; + + // 2. Determine if we inject specific particles (Gap logic) + bool doInjection = true; + if (mGapBetweenInjection > 0) { + if (mGapBetweenInjection == 1 && mEventCounter % 2 == 0) { + doInjection = false; + } else if (mEventCounter % mGapBetweenInjection != 0) { + doInjection = false; + } + } + + if (!doInjection) { + LOG(info) << "Skipping injection for event " << mEventCounter; + return true; + } + } + + LOG(info) << "generateEvent (Injection) " << mEventCounter; + + // For Triggered mode, we start clean. For Injected mode, we have MB in mPythia.event + if (mUseTriggering) { + mPythia.event.reset(); + } + + mConfigToUse = mOneInjectionPerEvent ? static_cast(gRandom->Uniform(0.f, getNGuns())) : -1; + LOG(info) << "Using configuration " << mConfigToUse << " out of " << getNGuns() << ", of which " << mGunConfigs.size() << " are transport decayed and " << mGunConfigsGenDecayed.size() << " are generator decayed"; + + bool injectedForThisEvent = false; + int nConfig = mGunConfigs.size(); // We start counting from the configurations of the transport decayed particles + for (const ConfigContainer& cfg : mGunConfigsGenDecayed) { + nConfig++; + if (mConfigToUse >= 0 && (nConfig - 1) != mConfigToUse) { + continue; + } + LOG(info) << "Using config container "; + cfg.print(); + if (mUseTriggering) { // Do the triggering + bool doSignal{mEventCounter % (mGapBetweenInjection + 1) == 0}; // Do signal or gap + + if (doSignal) { + LOG(info) << "Generating triggered signal event for particle"; + cfg.print(); + bool satisfiesTrigger = false; + int nTries = 0; + while (!satisfiesTrigger) { + if (!pythiaObjectSignal.next()) { + continue; + } + // Check if triggered condition satisfied + for (int j = 0; j < pythiaObjectSignal.event.size(); j++) { + const int& pypid = pythiaObjectSignal.event[j].id(); + const float& pyeta = mUseRapidity ? pythiaObjectSignal.event[j].y() : pythiaObjectSignal.event[j].eta(); + const float& pypt = pythiaObjectSignal.event[j].pT(); + if (pypid == cfg.mPdg && cfg.mMin < pyeta && pyeta < cfg.mMax && pypt > cfg.mPtMin && pypt < cfg.mPtMax) { + LOG(info) << "Found particle " << j << " " << pypid << " with " << (mUseRapidity ? "rapidity" : "eta") << " " << pyeta << " and pT " << pypt << " in event " << mEventCounter << " after " << nTries << " tries"; + satisfiesTrigger = true; + break; + } + } + nTries++; + } + mPythia.event = pythiaObjectSignal.event; + } else { + LOG(info) << "Generating background event " << mEventCounter; + // Generate minimum-bias event + bool lGenerationOK = false; + while (!lGenerationOK) { + lGenerationOK = pythiaObjectMinimumBias.next(); + } + mPythia.event = pythiaObjectMinimumBias.event; + } + continue; + } + + // Do the injection + // Use mPythiaGun for separate generation and decay + mPythiaGun.event.reset(); + for (int i{0}; i < cfg.mNInject; ++i) { + const double pt = gRandom->Uniform(cfg.mPtMin, cfg.mPtMax); + const double eta = gRandom->Uniform(cfg.mMin, cfg.mMax); + const double phi = gRandom->Uniform(0, TMath::TwoPi()); + const double px{pt * std::cos(phi)}; + const double py{pt * std::sin(phi)}; + + double pz = 0; + double et = 0; + + if (mUseRapidity) { + // Rapidty Case + const double mT = std::sqrt(cfg.mMass * cfg.mMass + pt * pt); + pz = mT * std::sinh(eta); + et = mT * std::cosh(eta); + } else { + // Eta Case + pz = pt * std::sinh(eta); + const double p = pt * std::cosh(eta); + et = std::sqrt(p * p + cfg.mMass * cfg.mMass); + } + + Particle particle; + particle.id(cfg.mPdg); + particle.status(11); + particle.m(cfg.mMass); + particle.px(px); + particle.py(py); + particle.pz(pz); + particle.e(et); + particle.xProd(0.f); + particle.yProd(0.f); + particle.zProd(0.f); + mPythiaGun.particleData.mayDecay(cfg.mPdg, true); // force decay + mPythiaGun.event.append(particle); + } + // Decay the gun particles + mPythiaGun.moreDecays(); + mPythiaGun.next(); + + // Merge mPythiaGun event into mPythia.event (MB) + int offset = mPythia.event.size(); + LOG(info) << "Merging " << mPythiaGun.event.size() - 1 << " injected particles into MB event of size " << offset; + + for (int i = 1; i < mPythiaGun.event.size(); ++i) { // Skip system particle 0 + Particle& p = mPythiaGun.event[i]; + // Adjust history indices + int mother1 = p.mother1(); + int mother2 = p.mother2(); + int daughter1 = p.daughter1(); + int daughter2 = p.daughter2(); + + if (mother1 > 0) + mother1 += offset - 1; + if (mother2 > 0) + mother2 += offset - 1; + if (daughter1 > 0) + daughter1 += offset - 1; + if (daughter2 > 0) + daughter2 += offset - 1; + + p.mothers(mother1, mother2); + p.daughters(daughter1, daughter2); + + mPythia.event.append(p); + } + + injectedForThisEvent = true; + } + + // For purely trivial injection (no generator decay needed in loop or just transport decay), we still might have injection flag + // But above loop covers generator decayed. + // What if we only have Transport Decay particles? (mGunConfigs) + // We treat them in importParticles usually. + // But if we are in embedding mode, we just need to ensure the MB event is there. + // The mGunConfigs are handled in importParticles. + + if (mVerbose) { + LOG(info) << "Eventlisting"; + mPythia.event.list(1); + mPythia.stat(); + } + return true; + } + + //__________________________________________________________________ + Bool_t importParticles() override + { + if (!mUseTriggering) { // If the triggering is used we handle the the gap when generating the signal + if (mGapBetweenInjection > 0) { + if (mGapBetweenInjection == 1 && mEventCounter % 2 == 0) { + LOG(info) << "Skipping importParticles event " << mEventCounter++; + return true; + } else if (mEventCounter % mGapBetweenInjection != 0) { + LOG(info) << "Skipping importParticles event " << mEventCounter++; + return true; + } + } + } + LOG(info) << "importParticles " << mEventCounter++; + GeneratorPythia8::importParticles(); + int nConfig = 0; + for (const ConfigContainer& cfg : mGunConfigs) { + nConfig++; + if (mConfigToUse >= 0 && (nConfig - 1) != mConfigToUse) { + continue; + } + LOGF(info, "Injecting %i particles with PDG %i, pT in [%f, %f], %s in [%f, %f]", cfg.mNInject, cfg.mPdg, cfg.mPtMin, cfg.mPtMax, (mUseRapidity ? "rapidity" : "eta"), cfg.mMin, cfg.mMax); + + for (int i{0}; i < cfg.mNInject; ++i) { + const double pt = gRandom->Uniform(cfg.mPtMin, cfg.mPtMax); + const double eta = gRandom->Uniform(cfg.mMin, cfg.mMax); + const double phi = gRandom->Uniform(0, TMath::TwoPi()); + const double px{pt * std::cos(phi)}; + const double py{pt * std::sin(phi)}; + double pz = 0; + double et = 0; + + if (mUseRapidity) { + const double mT = std::sqrt(cfg.mMass * cfg.mMass + pt * pt); + pz = mT * std::sinh(eta); + et = mT * std::cosh(eta); + } else { + pz = pt * std::sinh(eta); + const double p = pt * std::cosh(eta); + et = std::sqrt(p * p + cfg.mMass * cfg.mMass); + } + + // TParticle::TParticle(Int_t pdg, + // Int_t status, + // Int_t mother1, Int_t mother2, + // Int_t daughter1, Int_t daughter2, + // Double_t px, Double_t py, Double_t pz, Double_t etot, + // Double_t vx, Double_t vy, Double_t vz, Double_t time) + + mParticles.push_back(TParticle(cfg.mPdg, + MCGenStatusEncoding(1, 1).fullEncoding, + -1, -1, + -1, -1, + px, py, pz, et, + 0., 0., 0., 0.)); + // make sure status code is encoded properly. Transport flag will be set by default and we have nothing + // to do since all pushed particles should be tracked. + o2::mcutils::MCGenHelper::encodeParticleStatusAndTracking(mParticles.back()); + } + nConfig++; + } + if (mVerbose) { + LOG(info) << "Printing particles that are appended"; + int n = 0; + for (const auto& p : mParticles) { + LOG(info) << "Particle " << n++ << " is a " << p.GetPdgCode() << " with status " << p.GetStatusCode() << " and px = " << p.Py() << " py = " << p.Py() << " pz = " << p.Pz(); + } + } + return true; + } + + struct ConfigContainer { + ConfigContainer(int input_pdg = 0, int n = 1, + float ptMin = 1, float ptMax = 10, + float min = -1, float max = 1) : mPdg{input_pdg}, + mNInject{n}, + mPtMin{ptMin}, + mPtMax{ptMax}, + mMin{min}, + mMax{max} + { + // mMass = getMassFromPDG(mPdg); + mMass = GeneratorPythia8LongLivedGun::getMass(mPdg); + if (mMass <= 0) { + LOG(fatal) << "Could not find mass for mPdg " << mPdg; + } + LOGF(info, "ConfigContainer: mPdg = %i, mNInject = %i, mPtMin = %f, mPtMax = %f, mMin = %f, mMax = %f, mMass = %f", + mPdg, mNInject, mPtMin, mPtMax, mMin, mMax, mMass); + }; + + ConfigContainer(TObjArray* arr) : ConfigContainer(atoi(arr->At(0)->GetName()), + atoi(arr->At(1)->GetName()), + atof(arr->At(2)->GetName()), + atof(arr->At(3)->GetName()), + atof(arr->At(4)->GetName()), + atof(arr->At(5)->GetName())) + { + bool hasGenDecayed = false; + for (int i = 0; i < arr->GetEntries(); i++) { + const TString n = arr->At(i)->GetName(); + std::cout << n << std::endl; + if (n == "genDecayed") { + hasGenDecayed = true; + break; + } + } + if (hasGenDecayed) { + if (arr->GetEntries() != 7) { + LOG(fatal) << "Wrong number of entries in the configuration array, should be 7, is " << arr->GetEntries(); + } + } else { + if (arr->GetEntries() != 6) { + LOG(fatal) << "Wrong number of entries in the configuration array, should be 6, is " << arr->GetEntries(); + } + } + }; + ConfigContainer(TString line) : ConfigContainer(line.Tokenize(" ")) {}; + ConfigContainer(const nlohmann::json& jsonParams, bool useRapidity = false) : ConfigContainer(jsonParams["pdg"], + jsonParams["n"], + jsonParams["ptMin"], + jsonParams["ptMax"], + (useRapidity && jsonParams.contains("rapidityMin")) ? jsonParams["rapidityMin"] : (jsonParams.contains("min") ? jsonParams["min"] : jsonParams["etaMin"]), + (useRapidity && jsonParams.contains("rapidityMax")) ? jsonParams["rapidityMax"] : (jsonParams.contains("max") ? jsonParams["max"] : jsonParams["etaMax"])) {}; + + // Data Members + const int mPdg = 0; + const int mNInject = 1; + const float mPtMin = 1; + const float mPtMax = 10; + const float mMin = -1.f; + const float mMax = 1.f; + double mMass = 0.f; + + void print() const + { + LOGF(info, "int mPdg = %i", mPdg); + LOGF(info, "int mNInject = %i", mNInject); + LOGF(info, "float mPtMin = %f", mPtMin); + LOGF(info, "float mPtMax = %f", mPtMax); + LOGF(info, "float mMin = %f", mMin); + LOGF(info, "float mMax = %f", mMax); + LOGF(info, "double mMass = %f", mMass); + } + }; + + //__________________________________________________________________ + ConfigContainer addGun(int input_pdg, int nInject = 1, float ptMin = 1, float ptMax = 10, float min = 1, float max = 10) + { + if (mUseTriggering) { // If in trigger mode, every particle needs to be generated from pythia + return addGunGenDecayed(input_pdg, nInject, ptMin, ptMax, min, max); + } + ConfigContainer cfg{input_pdg, nInject, ptMin, ptMax, min, max}; + mGunConfigs.push_back(cfg); + return cfg; + } + + //__________________________________________________________________ + ConfigContainer addGun(ConfigContainer cfg) { return addGun(cfg.mPdg, cfg.mNInject, cfg.mPtMin, cfg.mPtMax, cfg.mMin, cfg.mMax); } + + //__________________________________________________________________ + ConfigContainer addGunGenDecayed(int input_pdg, int nInject = 1, float ptMin = 1, float ptMax = 10, float min = 1, float max = 10) + { + ConfigContainer cfg{input_pdg, nInject, ptMin, ptMax, min, max}; + mGunConfigsGenDecayed.push_back(cfg); + return cfg; + } + + //__________________________________________________________________ + ConfigContainer addGunGenDecayed(ConfigContainer cfg) { return addGunGenDecayed(cfg.mPdg, cfg.mNInject, cfg.mPtMin, cfg.mPtMax, cfg.mMin, cfg.mMax); } + + //__________________________________________________________________ + long int getNGuns() const { return mGunConfigs.size() + mGunConfigsGenDecayed.size(); } + + //__________________________________________________________________ + void print() + { + LOG(info) << "GeneratorPythia8LFRapidity configuration with " << getNGuns() << " guns:"; + LOG(info) << "Particles decayed by the transport:"; + int n = 0; + for (const auto& cfg : mGunConfigs) { + LOG(info) << n++ << "/" << mGunConfigs.size() << ":"; + cfg.print(); + } + n = 0; + LOG(info) << "Particles decayed by the generator:"; + for (const auto& cfg : mGunConfigsGenDecayed) { + LOG(info) << n++ << "/" << mGunConfigs.size() << ":"; + cfg.print(); + } + } + + void setVerbose(bool verbose = true) { mVerbose = verbose; } + + private: + // Configuration + const bool mOneInjectionPerEvent = true; // if true, only one injection per event is performed, i.e. if multiple PDG (including antiparticles) are requested to be injected only one will be done per event + const bool mUseTriggering = false; // if true, use triggering instead of injection + const int mGapBetweenInjection = 0; // Gap between two signal events. 0 means injection at every event + const bool mUseRapidity = false; // if true, use rapidity instead of eta + + // Running variables + int mConfigToUse = -1; // Index of the configuration to use + int mEventCounter = 0; // Event counter + bool mVerbose = true; // Verbosity flag + + std::vector mGunConfigs; // List of gun configurations to use + std::vector mGunConfigsGenDecayed; // List of gun configurations to use that will be decayed by the generator + Pythia8::Pythia pythiaObjectSignal; // Signal collision generator + Pythia8::Pythia pythiaObjectMinimumBias; // Minimum bias collision generator + Pythia8::Pythia mPythiaGun; // Gun generator with decay support +}; + +///___________________________________________________________ +/// Create generator via arrays of entries. By default injecting in every event and all particles +FairGenerator* generateLFRapidity(std::vector PDGs, std::vector nInject, std::vector ptMin, std::vector ptMax, std::vector min, std::vector max, bool useRapidity = false) +{ + const std::vector entries = {PDGs.size(), nInject.size(), ptMin.size(), ptMax.size(), min.size(), max.size()}; + if (!std::equal(entries.begin() + 1, entries.end(), entries.begin())) { + LOGF(fatal, "Not equal number of entries, check configuration"); + return nullptr; + } + GeneratorPythia8LFRapidity* multiGun = new GeneratorPythia8LFRapidity(false, 0, false, useRapidity, "", ""); + for (unsigned long i = 0; i < entries[0]; i++) { + multiGun->addGun(PDGs[i], nInject[i], ptMin[i], ptMax[i], min[i], max[i]); + } + return multiGun; +} + +///___________________________________________________________ +/// Create generator via an array of configurations +FairGenerator* generateLFRapidity(std::vector cfg, + std::vector cfgGenDecayed, + bool injectOnePDGPerEvent = true, + int gapBetweenInjection = 0, + bool useTrigger = false, + bool useRapidity = false, + std::string pythiaCfgMb = "", + std::string pythiaCfgSignal = "") +{ + GeneratorPythia8LFRapidity* multiGun = new GeneratorPythia8LFRapidity(injectOnePDGPerEvent, gapBetweenInjection, useTrigger, useRapidity, pythiaCfgMb, pythiaCfgSignal); + for (const auto& c : cfg) { + LOGF(info, "Adding gun %i", multiGun->getNGuns()); + c.print(); + multiGun->addGun(c); + } + for (const auto& c : cfgGenDecayed) { + LOGF(info, "Adding gun %i, particle will be decayed by the generator", multiGun->getNGuns()); + c.print(); + multiGun->addGunGenDecayed(c); + } + multiGun->print(); + return multiGun; +} + +///___________________________________________________________ +/// Create generator via input file +FairGenerator* generateLFRapidity(std::string configuration = "${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/exotic_nuclei_pp.gun", + bool injectOnePDGPerEvent = true, + int gapBetweenInjection = 0, + bool useTrigger = false, + bool useRapidity = false, + std::string pythiaCfgMb = "", + std::string pythiaCfgSignal = "") +{ + configuration = gSystem->ExpandPathName(configuration.c_str()); + LOGF(info, "Using configuration file '%s'", configuration.c_str()); + std::ifstream inputFile(configuration.c_str(), ios::in); + std::vector cfgVec; + std::vector cfgVecGenDecayed; + if (!inputFile.is_open()) { + LOGF(fatal, "Can't open '%s' !", configuration.c_str()); + return nullptr; + } + if (TString(configuration.c_str()).EndsWith(".json")) { // read from JSON file + nlohmann::json paramfile = nlohmann::json::parse(inputFile); + std::cout << "paramfile " << paramfile << std::endl; + for (const auto& param : paramfile) { + std::cout << param << std::endl; + // cfgVecGenDecayed.push_back(GeneratorPythia8LFRapidity::ConfigContainer{paramfile[n].template get(), param}); + if (param["genDecayed"]) { + cfgVecGenDecayed.push_back(GeneratorPythia8LFRapidity::ConfigContainer{param, useRapidity}); + } else { + cfgVec.push_back(GeneratorPythia8LFRapidity::ConfigContainer{param, useRapidity}); + } + } + } else { + std::string l; + int n = 0; + while (getline(inputFile, l)) { + TString line = l; + line.Strip(TString::kBoth, ' '); + std::cout << n++ << " '" << line << "'" << endl; + if (line.IsNull() || line.IsWhitespace()) { + continue; + } + if (line.BeginsWith("#")) { + std::cout << "Skipping\n"; + continue; + } + if (line.Contains("genDecayed")) { + cfgVecGenDecayed.push_back(GeneratorPythia8LFRapidity::ConfigContainer{line}); + } else { + cfgVec.push_back(GeneratorPythia8LFRapidity::ConfigContainer{line}); + } + } + } + return generateLFRapidity(cfgVec, cfgVecGenDecayed, injectOnePDGPerEvent, gapBetweenInjection, useTrigger, useRapidity, pythiaCfgMb, pythiaCfgSignal); +} + +///___________________________________________________________ +/// Create generator via input file for the triggered mode +FairGenerator* generateLFRapidityTriggered(std::string configuration = "${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/exotic_nuclei_pp.gun", + int gapBetweenInjection = 0, + bool useRapidity = false, + std::string pythiaCfgMb = "", + std::string pythiaCfgSignal = "") +{ + return generateLFRapidity(configuration, /*injectOnePDGPerEvent=*/true, gapBetweenInjection, /*useTrigger=*/true, useRapidity, pythiaCfgMb, pythiaCfgSignal); +} + +///___________________________________________________________ +void generator_pythia8_LF_rapidity(bool testInj = true, bool testTrg = false, bool useRapidity = false, const char* particleListFile = "cfg_rapidity.json") +{ + LOG(info) << "Compiled correctly!"; + if (!testInj && !testTrg) { + return; + } + // Injected mode + if (testInj) { + LOG(info) << "Testing the injected mode"; + auto* gen = static_cast(generateLFRapidity(particleListFile, true, 0, false, useRapidity)); + gen->setVerbose(); + gen->Print(); + gen->print(); + gen->Init(); + gen->generateEvent(); + gen->importParticles(); + } + + // Triggered mode + if (testTrg) { + LOG(info) << "Testing the triggered mode"; + GeneratorPythia8LFRapidity* gen = static_cast(generateLFRapidityTriggered(particleListFile, + /*gapBetweenInjection=*/0, + useRapidity, + /*pythiaCfgMb=*/"inel136tev.cfg", + /*pythiaCfgSignal=*/"inel136tev.cfg")); + gen->setVerbose(); + gen->Print(); + gen->print(); + gen->Init(); + gen->generateEvent(); + gen->importParticles(); + } +} diff --git a/MC/config/PWGLF/pythia8/generator_pythia8_LF_rapidity_width.C b/MC/config/PWGLF/pythia8/generator_pythia8_LF_rapidity_width.C new file mode 100644 index 000000000..ed0a8c990 --- /dev/null +++ b/MC/config/PWGLF/pythia8/generator_pythia8_LF_rapidity_width.C @@ -0,0 +1,816 @@ +/// +/// \file generator_pythia8_LF_rapidity_width.C +/// \author Hirak Kumar Koley hirak.koley@cern.ch +/// \author Bong-Hwi Lim bong-hwi.lim@cern.ch +/// \author Minjae Kim minjae.kim@cern.ch +/// \author Based on generator_pythia8_LF.C by Nicolò Jacazio +/// \since 2025/08/18 +/// \brief Implementation of a gun generator for multiple particles using rapidity or pseudorapidity (default) instead of eta, built on generator_pythia8_longlived.C +/// Needs PDG, Number of injected, minimum and maximum pT, minimum and maximum y/eta. These can be provided in three ways, bundeling variables, particles or from input file +/// usage: +/// `o2-sim -g external --configKeyValues 'GeneratorExternal.fileName=generator_pythia8_LF_rapidity_width.C;GeneratorExternal.funcName=generateLFRapidity({1000010020, 1000010030}, {10, 10}, {0.5, 0.5}, {10, 10}, {-1.0, -1.0}, {1.0, 1.0})'` +/// Here PDG, Number injected, pT limits, y/eta limits are separated and matched by index +/// or: +/// `o2-sim -g external --configKeyValues 'GeneratorExternal.fileName=generator_pythia8_LF_rapidity_width.C;GeneratorExternal.funcName=generateLFRapidity({{1000010020, 10, 0.5, 10, -1.0, 1.0}, {1000010030, 10, 0.5, 10, -1.0, 1.0}})'` +/// Here PDG, Number injected, pT limits, y/eta limits are divided per particle +/// or: +/// `o2-sim -g external --configKeyValues 'GeneratorExternal.fileName=generator_pythia8_LF_rapidity_width.C;GeneratorExternal.funcName=generateLFRapidity("${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/exotic_nuclei_pp.gun")'` +/// Here PDG, Number injected, pT limits, y/eta limits are provided via an intermediate configuration file +/// + +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include "FairGenerator.h" + +#include "TDatabasePDG.h" +#include "TMath.h" +#include "TParticlePDG.h" +#include "TRandom3.h" + +#include "Generators/GeneratorPythia8.h" +#include "Pythia8/Pythia.h" +#if __has_include("SimulationDataFormat/MCGenStatus.h") +#include "SimulationDataFormat/MCGenStatus.h" +#else +#include "SimulationDataFormat/MCGenProperties.h" +#endif +#if __has_include("SimulationDataFormat/MCUtils.h") +#include "SimulationDataFormat/MCUtils.h" +#endif +#include "TSystem.h" + +#include "Generators/GeneratorPythia8Param.h" +#include "fairlogger/Logger.h" + +#include +#include +#endif +// DecayerPythia8Param needs to be included after the #endif to work with Cling +#include "Generators/DecayerPythia8Param.h" +#if defined(__CLING__) && !defined(__ROOTCLING__) +#if __has_include("SimulationDataFormat/MCGenStatus.h") +#include "SimulationDataFormat/MCGenStatus.h" +#elif __has_include("SimulationDataFormat/MCGenProperties.h") +#include "SimulationDataFormat/MCGenProperties.h" +#endif +#if __has_include("SimulationDataFormat/MCUtils.h") +#include "SimulationDataFormat/MCUtils.h" +#endif +#pragma cling load("libO2Generators") +#endif +// #include "Generators/GeneratorPythia8.h" +#include "generator_pythia8_longlived.C" + +#include + +using namespace Pythia8; +using namespace o2::mcgenstatus; + +class GeneratorPythia8LFRapidity : public o2::eventgen::GeneratorPythia8 +{ + public: + /// Parametric constructor + GeneratorPythia8LFRapidity(bool injOnePerEvent = true, + int gapBetweenInjection = 0, + bool useTrigger = false, + bool useRapidity = false, + std::string pythiaCfgMb = "${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_136tev.cfg", + std::string pythiaCfgSignal = "${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_136tev.cfg") : GeneratorPythia8{}, + mOneInjectionPerEvent{injOnePerEvent}, + mGapBetweenInjection{gapBetweenInjection}, + mUseTriggering{useTrigger}, + mUseRapidity{useRapidity} + { + LOG(info) << "GeneratorPythia8LFRapidity constructor"; + LOG(info) << "++ mOneInjectionPerEvent: " << mOneInjectionPerEvent; + LOG(info) << "++ mGapBetweenInjection: " << mGapBetweenInjection; + LOG(info) << "++ mUseTriggering: " << mUseTriggering; + LOG(info) << "++ mUseRapidity: " << mUseRapidity; + LOG(info) << "++ pythiaCfgMb: " << pythiaCfgMb; + LOG(info) << "++ pythiaCfgSignal: " << pythiaCfgSignal; + gRandom->SetSeed(0); + if (useTrigger) { + mPythia.readString("ProcessLevel:all off"); + if (pythiaCfgMb == "") { // If no configuration file is provided, use the one from the Pythia8Param + auto& param = o2::eventgen::GeneratorPythia8Param::Instance(); + LOG(info) << "Instance LFRapidity \'Pythia8\' generator with following parameters for MB event"; + LOG(info) << param; + pythiaCfgMb = param.config; + } + if (pythiaCfgSignal == "") { // If no configuration file is provided, use the one from the Pythia8Param + auto& param = o2::eventgen::GeneratorPythia8Param::Instance(); + LOG(info) << "Instance LFRapidity \'Pythia8\' generator with following parameters for signal event"; + LOG(info) << param; + pythiaCfgSignal = param.config; + } + pythiaCfgMb = gSystem->ExpandPathName(pythiaCfgMb.c_str()); + pythiaCfgSignal = gSystem->ExpandPathName(pythiaCfgSignal.c_str()); + LOG(info) << " ++ Using trigger, initializing Pythia8 for trigger"; + if (!pythiaObjectMinimumBias.readFile(pythiaCfgMb)) { + LOG(fatal) << "Could not pythiaObjectMinimumBias.readFile(\"" << pythiaCfgMb << "\")"; + } + pythiaObjectMinimumBias.readString("Random:setSeed = on"); + pythiaObjectMinimumBias.readString("Random:seed =" + std::to_string(gRandom->Integer(900000000 - 2) + 1)); + + // FIX: Init signal pythia object + if (!pythiaObjectSignal.readFile(pythiaCfgSignal)) { + LOG(fatal) << "Could not pythiaObjectSignal.readFile(\"" << pythiaCfgSignal << "\")"; + } + pythiaObjectSignal.readString("Random:setSeed = on"); + pythiaObjectSignal.readString("Random:seed =" + std::to_string(gRandom->Integer(900000000 - 2) + 1)); + pythiaObjectMinimumBias.particleData.addParticle(1000100200, "20Ne", 6, 30, 0, 19.992440); + mPythiaGun.particleData.addParticle(1000100200, "20Ne", 6, 30, 0, 19.992440); + if (!pythiaObjectMinimumBias.init()) { + LOG(fatal) << "Could not pythiaObjectMinimumBias.init() from " << pythiaCfgMb; + } + if (!pythiaObjectSignal.init()) { + LOG(fatal) << "Could not pythiaObjectSignal.init() from " << pythiaCfgSignal; + } + } else { // Using simple injection with internal decay (if needed). Fetching the parameters from the configuration file of the PythiaDecayer + + if (pythiaCfgMb == "") { // If no configuration file is provided, use the one from the Pythia8Param + auto& param = o2::eventgen::GeneratorPythia8Param::Instance(); + LOG(info) << "Instance LFRapidity \'Pythia8\' generator with following parameters for MB event"; + LOG(info) << param; + pythiaCfgMb = param.config; + } + // FIX: Fallback if still empty to default minbias + if (pythiaCfgMb == "") { + pythiaCfgMb = "${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_136tev.cfg"; + } + pythiaCfgMb = gSystem->ExpandPathName(pythiaCfgMb.c_str()); + if (!pythiaObjectMinimumBias.readFile(pythiaCfgMb)) { + LOG(fatal) << "Could not pythiaObjectMinimumBias.readFile(\"" << pythiaCfgMb << "\")"; + } + pythiaObjectMinimumBias.readString("Random:setSeed = on"); + pythiaObjectMinimumBias.readString("Random:seed =" + std::to_string(gRandom->Integer(900000000 - 2) + 1)); + + pythiaObjectMinimumBias.particleData.addParticle(1000100200, "20Ne", 6, 30, 0, 19.992440); + mPythiaGun.particleData.addParticle(1000100200, "20Ne", 6, 30, 0, 19.992440); + if (!pythiaObjectMinimumBias.init()) { + LOG(fatal) << "Could not pythiaObjectMinimumBias.init() from " << pythiaCfgMb; + } + + /** switch off process level **/ + mPythiaGun.readString("ProcessLevel:all off"); + + // The gun owns a separate RNG: seeding the inherited mPythia does not + // seed its mass sampler or decays. Derive a valid, nonzero Pythia seed + // from ROOT's RNG; explicit decayer configuration can still override it. + mPythiaGun.readString("Random:setSeed = on"); + mPythiaGun.readString("Random:seed = " + std::to_string(1 + gRandom->Integer(900000000))); + + auto& param = o2::eventgen::DecayerPythia8Param::Instance(); + LOG(info) << "Init \'GeneratorPythia8LFRapidity\' with following parameters"; + LOG(info) << param; + for (int i = 0; i < 8; ++i) { + if (param.config[i].empty()) { + continue; + } + std::string config = gSystem->ExpandPathName(param.config[i].c_str()); + readParticleDataLines(mPythia, config, "import Pythia"); + LOG(info) << "GeneratorPythia8LFRapidity Reading configuration from file: " << config; + if (!mPythiaGun.readFile(config, true)) { + LOG(fatal) << "Failed to init \'DecayerPythia8\': problems with configuration file " + << config; + return; + } + } + + /** show changed particle data **/ + if (param.showChanged) { + mPythiaGun.readString(std::string("Init:showChangedParticleData on")); + } else { + mPythiaGun.readString(std::string("Init:showChangedParticleData off")); + } + + LOG(info) << "LF gun RNG: Random:setSeed=" << mPythiaGun.settings.flag("Random:setSeed") + << ", Random:seed=" << mPythiaGun.settings.mode("Random:seed"); + if (!mPythiaGun.settings.flag("Random:setSeed") || mPythiaGun.settings.mode("Random:seed") <= 0) { + LOG(warn) << "Decayer configuration overrides the ROOT-derived gun seed; " + << "the gun may use default or time-based seeding"; + } + + /** initialise **/ + if (!mPythiaGun.init()) { + LOG(fatal) << "Failed to init \'DecayerPythia8\': init returned with error"; + return; + } + if (pythiaCfgSignal != "") { + LOG(fatal) << "Cannot use simple injection and have a configuration file. pythiaCfgSignal= `" << pythiaCfgSignal << "` must be empty"; + } + } + } + + /// Destructor + ~GeneratorPythia8LFRapidity() = default; + + bool Init() override + { + // Follow the PWGHF gap-trigger convention and persist the event class in + // MCEventHeader::subGeneratorId. The AOD producer exposes this through + // mcCollision.getSubGeneratorId(). + addSubGenerator(kSubGeneratorMinimumBias, "Minimum bias"); + addSubGenerator(kSubGeneratorLFSignal, "LF signal"); + return o2::eventgen::GeneratorPythia8::Init(); + } + + //__________________________________________________________________ + Bool_t generateEvent() override + { + // Follow the gap-trigger convention used by PWGHF/PWGDQ: a ratio N + // produces signal events at 0, N, 2N, ... and minimum-bias events in + // between. Ratios 0 and 1 both mean signal in every event. + mDoSignalThisEvent = isSignalEvent(); + notifySubGenerator(mDoSignalThisEvent ? kSubGeneratorLFSignal : kSubGeneratorMinimumBias); + + if (!mUseTriggering) { // Injected mode: Embedding into MB + // 1. Generate Background (MB) + bool lGenerationOK = false; + while (!lGenerationOK) { + lGenerationOK = pythiaObjectMinimumBias.next(); + } + copyMinimumBiasEventForInjection(); + + // Keep the generated minimum-bias event on gap events. It must still be + // imported in importParticles(), otherwise o2-sim sees an empty event + // and retries until it obtains another injected event. + if (!mDoSignalThisEvent) { + LOG(info) << "Generating minimum-bias gap event " << mEventCounter; + return true; + } + } + + LOG(info) << "generateEvent (Injection) " << mEventCounter; + + // For Triggered mode, we start clean. For Injected mode, we have MB in mPythia.event + if (mUseTriggering) { + mPythia.event.reset(); + } + + mConfigToUse = mOneInjectionPerEvent ? static_cast(gRandom->Uniform(0.f, getNGuns())) : -1; + LOG(info) << "Using configuration " << mConfigToUse << " out of " << getNGuns() << ", of which " << mGunConfigs.size() << " are transport decayed and " << mGunConfigsGenDecayed.size() << " are generator decayed"; + + int nConfig = mGunConfigs.size(); // We start counting from the configurations of the transport decayed particles + for (const ConfigContainer& cfg : mGunConfigsGenDecayed) { + nConfig++; + if (mConfigToUse >= 0 && (nConfig - 1) != mConfigToUse) { + continue; + } + LOG(info) << "Using config container "; + cfg.print(); + if (mUseTriggering) { // Do the triggering + if (mDoSignalThisEvent) { + LOG(info) << "Generating triggered signal event for particle"; + cfg.print(); + bool satisfiesTrigger = false; + int nTries = 0; + while (!satisfiesTrigger) { + if (!pythiaObjectSignal.next()) { + continue; + } + // Check if triggered condition satisfied + for (int j = 0; j < pythiaObjectSignal.event.size(); j++) { + const int& pypid = pythiaObjectSignal.event[j].id(); + const float& pyeta = mUseRapidity ? pythiaObjectSignal.event[j].y() : pythiaObjectSignal.event[j].eta(); + const float& pypt = pythiaObjectSignal.event[j].pT(); + if (pypid == cfg.mPdg && cfg.mMin < pyeta && pyeta < cfg.mMax && pypt > cfg.mPtMin && pypt < cfg.mPtMax) { + LOG(info) << "Found particle " << j << " " << pypid << " with " << (mUseRapidity ? "rapidity" : "eta") << " " << pyeta << " and pT " << pypt << " in event " << mEventCounter << " after " << nTries << " tries"; + satisfiesTrigger = true; + break; + } + } + nTries++; + } + mPythia.event = pythiaObjectSignal.event; + } else { + LOG(info) << "Generating background event " << mEventCounter; + // Generate minimum-bias event + bool lGenerationOK = false; + while (!lGenerationOK) { + lGenerationOK = pythiaObjectMinimumBias.next(); + } + mPythia.event = pythiaObjectMinimumBias.event; + } + continue; + } + + // Do the injection + // Use mPythiaGun for separate generation and decay + mPythiaGun.event.reset(); + for (int i{0}; i < cfg.mNInject; ++i) { + const double pt = gRandom->Uniform(cfg.mPtMin, cfg.mPtMax); + const double eta = gRandom->Uniform(cfg.mMin, cfg.mMax); + const double phi = gRandom->Uniform(0, TMath::TwoPi()); + const double px{pt * std::cos(phi)}; + const double py{pt * std::sin(phi)}; + const double mass = sampleMass(cfg); + + double pz = 0; + double et = 0; + + if (mUseRapidity) { + // Rapidty Case + const double mT = std::sqrt(mass * mass + pt * pt); + pz = mT * std::sinh(eta); + et = mT * std::cosh(eta); + } else { + // Eta Case + pz = pt * std::sinh(eta); + const double p = pt * std::cosh(eta); + et = std::sqrt(p * p + mass * mass); + } + + Particle particle; + particle.id(cfg.mPdg); + particle.status(11); + particle.m(mass); + particle.px(px); + particle.py(py); + particle.pz(pz); + particle.e(et); + particle.xProd(0.f); + particle.yProd(0.f); + particle.zProd(0.f); + mPythiaGun.particleData.mayDecay(cfg.mPdg, true); // force decay + mPythiaGun.event.append(particle); + } + // Decay the gun particles + mPythiaGun.moreDecays(); + mPythiaGun.next(); + + // Merge mPythiaGun event into mPythia.event (MB) + int offset = mPythia.event.size(); + LOG(info) << "Merging " << mPythiaGun.event.size() - 1 << " injected particles into MB event of size " << offset; + + for (int i = 1; i < mPythiaGun.event.size(); ++i) { // Skip system particle 0 + Particle& p = mPythiaGun.event[i]; + // Adjust history indices + int mother1 = p.mother1(); + int mother2 = p.mother2(); + int daughter1 = p.daughter1(); + int daughter2 = p.daughter2(); + + if (mother1 > 0) + mother1 += offset - 1; + if (mother2 > 0) + mother2 += offset - 1; + if (daughter1 > 0) + daughter1 += offset - 1; + if (daughter2 > 0) + daughter2 += offset - 1; + + p.mothers(mother1, mother2); + p.daughters(daughter1, daughter2); + + mPythia.event.append(p); + } + } + + // For purely trivial injection (no generator decay needed in loop or just transport decay), we still might have injection flag + // But above loop covers generator decayed. + // What if we only have Transport Decay particles? (mGunConfigs) + // We treat them in importParticles usually. + // But if we are in embedding mode, we just need to ensure the MB event is there. + // The mGunConfigs are handled in importParticles. + + if (mVerbose) { + LOG(info) << "Eventlisting"; + mPythia.event.list(1); + mPythia.stat(); + } + return true; + } + + //__________________________________________________________________ + Bool_t importParticles() override + { + LOG(info) << "importParticles " << mEventCounter << " (" << (mDoSignalThisEvent ? "signal" : "minimum bias") << ")"; + + // Always import the Pythia event. On gap events this is the minimum-bias + // event prepared in generateEvent(). + if (!GeneratorPythia8::importParticles()) { + return false; + } + + // Generator-decayed particles were already merged into mPythia.event in + // generateEvent(). Append transport-decayed gun particles only on signal + // events in injection mode. + if (!mUseTriggering && mDoSignalThisEvent) { + int nConfig = 0; + for (const ConfigContainer& cfg : mGunConfigs) { + nConfig++; + if (mConfigToUse >= 0 && (nConfig - 1) != mConfigToUse) { + continue; + } + LOGF(info, "Injecting %i particles with PDG %i, pT in [%f, %f], %s in [%f, %f]", cfg.mNInject, cfg.mPdg, cfg.mPtMin, cfg.mPtMax, (mUseRapidity ? "rapidity" : "eta"), cfg.mMin, cfg.mMax); + + for (int i{0}; i < cfg.mNInject; ++i) { + const double pt = gRandom->Uniform(cfg.mPtMin, cfg.mPtMax); + const double eta = gRandom->Uniform(cfg.mMin, cfg.mMax); + const double phi = gRandom->Uniform(0, TMath::TwoPi()); + const double px{pt * std::cos(phi)}; + const double py{pt * std::sin(phi)}; + const double mass = sampleMass(cfg); + double pz = 0; + double et = 0; + + if (mUseRapidity) { + const double mT = std::sqrt(mass * mass + pt * pt); + pz = mT * std::sinh(eta); + et = mT * std::cosh(eta); + } else { + pz = pt * std::sinh(eta); + const double p = pt * std::cosh(eta); + et = std::sqrt(p * p + mass * mass); + } + + // TParticle::TParticle(Int_t pdg, + // Int_t status, + // Int_t mother1, Int_t mother2, + // Int_t daughter1, Int_t daughter2, + // Double_t px, Double_t py, Double_t pz, Double_t etot, + // Double_t vx, Double_t vy, Double_t vz, Double_t time) + + mParticles.push_back(TParticle(cfg.mPdg, + MCGenStatusEncoding(1, 1).fullEncoding, + -1, -1, + -1, -1, + px, py, pz, et, + 0., 0., 0., 0.)); + // make sure status code is encoded properly. Transport flag will be set by default and we have nothing + // to do since all pushed particles should be tracked. + o2::mcutils::MCGenHelper::encodeParticleStatusAndTracking(mParticles.back()); + } + } + } + + mEventCounter++; + if (mVerbose) { + LOG(info) << "Printing particles that are appended"; + int n = 0; + for (const auto& p : mParticles) { + LOG(info) << "Particle " << n++ << " is a " << p.GetPdgCode() << " with status " << p.GetStatusCode() << " and px = " << p.Py() << " py = " << p.Py() << " pz = " << p.Pz(); + } + } + return true; + } + + struct ConfigContainer { + ConfigContainer(int input_pdg = 0, int n = 1, + float ptMin = 1, float ptMax = 10, + float min = -1, float max = 1) : mPdg{input_pdg}, + mNInject{n}, + mPtMin{ptMin}, + mPtMax{ptMax}, + mMin{min}, + mMax{max} + { + // mMass = getMassFromPDG(mPdg); + mMass = GeneratorPythia8LongLivedGun::getMass(mPdg); + if (mMass <= 0) { + LOG(warning) << "Could not find mass for mPdg " << mPdg << " in TDatabasePDG; will try Pythia particle data at injection time"; + } + LOGF(info, "ConfigContainer: mPdg = %i, mNInject = %i, mPtMin = %f, mPtMax = %f, mMin = %f, mMax = %f, mMass = %f", + mPdg, mNInject, mPtMin, mPtMax, mMin, mMax, mMass); + }; + + ConfigContainer(TObjArray* arr) : ConfigContainer(atoi(arr->At(0)->GetName()), + atoi(arr->At(1)->GetName()), + atof(arr->At(2)->GetName()), + atof(arr->At(3)->GetName()), + atof(arr->At(4)->GetName()), + atof(arr->At(5)->GetName())) + { + bool hasGenDecayed = false; + for (int i = 0; i < arr->GetEntries(); i++) { + const TString n = arr->At(i)->GetName(); + std::cout << n << std::endl; + if (n == "genDecayed") { + hasGenDecayed = true; + break; + } + } + if (hasGenDecayed) { + if (arr->GetEntries() != 7) { + LOG(fatal) << "Wrong number of entries in the configuration array, should be 7, is " << arr->GetEntries(); + } + } else { + if (arr->GetEntries() != 6) { + LOG(fatal) << "Wrong number of entries in the configuration array, should be 6, is " << arr->GetEntries(); + } + } + }; + ConfigContainer(TString line) : ConfigContainer(line.Tokenize(" ")) {}; + ConfigContainer(const nlohmann::json& jsonParams, bool useRapidity = false) : ConfigContainer(jsonParams["pdg"], + jsonParams["n"], + jsonParams["ptMin"], + jsonParams["ptMax"], + (useRapidity && jsonParams.contains("rapidityMin")) ? jsonParams["rapidityMin"] : (jsonParams.contains("min") ? jsonParams["min"] : jsonParams["etaMin"]), + (useRapidity && jsonParams.contains("rapidityMax")) ? jsonParams["rapidityMax"] : (jsonParams.contains("max") ? jsonParams["max"] : jsonParams["etaMax"])) {}; + + // Data Members + const int mPdg = 0; + const int mNInject = 1; + const float mPtMin = 1; + const float mPtMax = 10; + const float mMin = -1.f; + const float mMax = 1.f; + double mMass = 0.f; + + void print() const + { + LOGF(info, "int mPdg = %i", mPdg); + LOGF(info, "int mNInject = %i", mNInject); + LOGF(info, "float mPtMin = %f", mPtMin); + LOGF(info, "float mPtMax = %f", mPtMax); + LOGF(info, "float mMin = %f", mMin); + LOGF(info, "float mMax = %f", mMax); + LOGF(info, "double mMass = %f", mMass); + } + }; + + double sampleMass(const ConfigContainer& cfg) + { + const double pythiaMass = mPythiaGun.particleData.m0(cfg.mPdg); + const double mass = mPythiaGun.particleData.mWidth(cfg.mPdg) > 0. + ? mPythiaGun.particleData.mSel(cfg.mPdg) + : pythiaMass > 0. + ? pythiaMass + : cfg.mMass; + if (mass <= 0.) { + LOG(fatal) << "Could not find mass for mPdg " << cfg.mPdg; + } + return mass; + } + + //__________________________________________________________________ + ConfigContainer addGun(int input_pdg, int nInject = 1, float ptMin = 1, float ptMax = 10, float min = 1, float max = 10) + { + if (mUseTriggering) { // If in trigger mode, every particle needs to be generated from pythia + return addGunGenDecayed(input_pdg, nInject, ptMin, ptMax, min, max); + } + ConfigContainer cfg{input_pdg, nInject, ptMin, ptMax, min, max}; + mGunConfigs.push_back(cfg); + return cfg; + } + + //__________________________________________________________________ + ConfigContainer addGun(ConfigContainer cfg) { return addGun(cfg.mPdg, cfg.mNInject, cfg.mPtMin, cfg.mPtMax, cfg.mMin, cfg.mMax); } + + //__________________________________________________________________ + ConfigContainer addGunGenDecayed(int input_pdg, int nInject = 1, float ptMin = 1, float ptMax = 10, float min = 1, float max = 10) + { + ConfigContainer cfg{input_pdg, nInject, ptMin, ptMax, min, max}; + mGunConfigsGenDecayed.push_back(cfg); + return cfg; + } + + //__________________________________________________________________ + ConfigContainer addGunGenDecayed(ConfigContainer cfg) { return addGunGenDecayed(cfg.mPdg, cfg.mNInject, cfg.mPtMin, cfg.mPtMax, cfg.mMin, cfg.mMax); } + + //__________________________________________________________________ + long int getNGuns() const { return mGunConfigs.size() + mGunConfigsGenDecayed.size(); } + + //__________________________________________________________________ + void print() + { + LOG(info) << "GeneratorPythia8LFRapidity configuration with " << getNGuns() << " guns:"; + LOG(info) << "Particles decayed by the transport:"; + int n = 0; + for (const auto& cfg : mGunConfigs) { + LOG(info) << n++ << "/" << mGunConfigs.size() << ":"; + cfg.print(); + } + n = 0; + LOG(info) << "Particles decayed by the generator:"; + for (const auto& cfg : mGunConfigsGenDecayed) { + LOG(info) << n++ << "/" << mGunConfigs.size() << ":"; + cfg.print(); + } + } + + void setVerbose(bool verbose = true) { mVerbose = verbose; } + + private: + static constexpr int kSubGeneratorMinimumBias = 0; + static constexpr int kSubGeneratorLFSignal = 1; + + bool isSignalEvent() const + { + return mGapBetweenInjection <= 1 || mEventCounter % mGapBetweenInjection == 0; + } + + void copyMinimumBiasEventForInjection() + { + mPythia.event = pythiaObjectMinimumBias.event; + // Event assignment adopts the source ParticleData pointer; use the import table for injected custom PDGs. + mPythia.event.init("Minimum-bias event with injected particles", &mPythia.particleData); + mPythia.event.restorePtrs(); + } + + static bool isParticleDataLine(const std::string& line) + { + size_t pos = line.find_first_not_of(" \t"); + if (pos == std::string::npos) { + return false; + } + if (line[pos] == '+' || line[pos] == '-') { + ++pos; + } + if (pos >= line.size() || line[pos] < '0' || line[pos] > '9') { + return false; + } + while (pos < line.size() && line[pos] >= '0' && line[pos] <= '9') { + ++pos; + } + return pos < line.size() && line[pos] == ':'; + } + + static bool readParticleDataLines(Pythia8::Pythia& pythia, const std::string& config, const char* target) + { + std::ifstream input(config.c_str(), ios::in); + if (!input.is_open()) { + LOG(fatal) << "Could not open particle data configuration " << config << " for " << target; + return false; + } + std::string line; + while (std::getline(input, line)) { + TString stripped = line; + stripped.Strip(TString::kBoth, ' '); + if (stripped.IsNull() || stripped.IsWhitespace() || stripped.BeginsWith("#")) { + continue; + } + std::string command = stripped.Data(); + if (!isParticleDataLine(command)) { + continue; + } + LOG(info) << "Reading particle data for " << target << ": " << command; + if (!pythia.readString(command)) { + LOG(fatal) << "Failed to read particle data line for " << target << ": " << command; + return false; + } + } + return true; + } + // Configuration + const bool mOneInjectionPerEvent = true; // if true, only one injection per event is performed, i.e. if multiple PDG (including antiparticles) are requested to be injected only one will be done per event + const bool mUseTriggering = false; // if true, use triggering instead of injection + const int mGapBetweenInjection = 0; // 0/1: signal every event; N>1: signal every Nth event + const bool mUseRapidity = false; // if true, use rapidity instead of eta + + // Running variables + int mConfigToUse = -1; // Index of the configuration to use + int mEventCounter = 0; // Event counter + bool mDoSignalThisEvent = true; + bool mVerbose = true; // Verbosity flag + + std::vector mGunConfigs; // List of gun configurations to use + std::vector mGunConfigsGenDecayed; // List of gun configurations to use that will be decayed by the generator + Pythia8::Pythia pythiaObjectSignal; // Signal collision generator + Pythia8::Pythia pythiaObjectMinimumBias; // Minimum bias collision generator + Pythia8::Pythia mPythiaGun; // Gun generator with decay support +}; + +///___________________________________________________________ +/// Create generator via arrays of entries. By default injecting in every event and all particles +FairGenerator* generateLFRapidity(std::vector PDGs, std::vector nInject, std::vector ptMin, std::vector ptMax, std::vector min, std::vector max, bool useRapidity = false) +{ + const std::vector entries = {PDGs.size(), nInject.size(), ptMin.size(), ptMax.size(), min.size(), max.size()}; + if (!std::equal(entries.begin() + 1, entries.end(), entries.begin())) { + LOGF(fatal, "Not equal number of entries, check configuration"); + return nullptr; + } + GeneratorPythia8LFRapidity* multiGun = new GeneratorPythia8LFRapidity(false, 0, false, useRapidity, "", ""); + for (unsigned long i = 0; i < entries[0]; i++) { + multiGun->addGun(PDGs[i], nInject[i], ptMin[i], ptMax[i], min[i], max[i]); + } + return multiGun; +} + +///___________________________________________________________ +/// Create generator via an array of configurations +FairGenerator* generateLFRapidity(std::vector cfg, + std::vector cfgGenDecayed, + bool injectOnePDGPerEvent = true, + int gapBetweenInjection = 0, + bool useTrigger = false, + bool useRapidity = false, + std::string pythiaCfgMb = "", + std::string pythiaCfgSignal = "") +{ + GeneratorPythia8LFRapidity* multiGun = new GeneratorPythia8LFRapidity(injectOnePDGPerEvent, gapBetweenInjection, useTrigger, useRapidity, pythiaCfgMb, pythiaCfgSignal); + for (const auto& c : cfg) { + LOGF(info, "Adding gun %i", multiGun->getNGuns()); + c.print(); + multiGun->addGun(c); + } + for (const auto& c : cfgGenDecayed) { + LOGF(info, "Adding gun %i, particle will be decayed by the generator", multiGun->getNGuns()); + c.print(); + multiGun->addGunGenDecayed(c); + } + multiGun->print(); + return multiGun; +} + +///___________________________________________________________ +/// Create generator via input file +FairGenerator* generateLFRapidity(std::string configuration = "${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/exotic_nuclei_pp.gun", + bool injectOnePDGPerEvent = true, + int gapBetweenInjection = 0, + bool useTrigger = false, + bool useRapidity = false, + std::string pythiaCfgMb = "", + std::string pythiaCfgSignal = "") +{ + configuration = gSystem->ExpandPathName(configuration.c_str()); + LOGF(info, "Using configuration file '%s'", configuration.c_str()); + std::ifstream inputFile(configuration.c_str(), ios::in); + std::vector cfgVec; + std::vector cfgVecGenDecayed; + if (!inputFile.is_open()) { + LOGF(fatal, "Can't open '%s' !", configuration.c_str()); + return nullptr; + } + if (TString(configuration.c_str()).EndsWith(".json")) { // read from JSON file + nlohmann::json paramfile = nlohmann::json::parse(inputFile); + std::cout << "paramfile " << paramfile << std::endl; + for (const auto& param : paramfile) { + std::cout << param << std::endl; + // cfgVecGenDecayed.push_back(GeneratorPythia8LFRapidity::ConfigContainer{paramfile[n].template get(), param}); + if (param["genDecayed"]) { + cfgVecGenDecayed.push_back(GeneratorPythia8LFRapidity::ConfigContainer{param, useRapidity}); + } else { + cfgVec.push_back(GeneratorPythia8LFRapidity::ConfigContainer{param, useRapidity}); + } + } + } else { + std::string l; + int n = 0; + while (getline(inputFile, l)) { + TString line = l; + line.Strip(TString::kBoth, ' '); + std::cout << n++ << " '" << line << "'" << endl; + if (line.IsNull() || line.IsWhitespace()) { + continue; + } + if (line.BeginsWith("#")) { + std::cout << "Skipping\n"; + continue; + } + if (line.Contains("genDecayed")) { + cfgVecGenDecayed.push_back(GeneratorPythia8LFRapidity::ConfigContainer{line}); + } else { + cfgVec.push_back(GeneratorPythia8LFRapidity::ConfigContainer{line}); + } + } + } + return generateLFRapidity(cfgVec, cfgVecGenDecayed, injectOnePDGPerEvent, gapBetweenInjection, useTrigger, useRapidity, pythiaCfgMb, pythiaCfgSignal); +} + +///___________________________________________________________ +/// Create generator via input file for the triggered mode +FairGenerator* generateLFRapidityTriggered(std::string configuration = "${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/exotic_nuclei_pp.gun", + int gapBetweenInjection = 0, + bool useRapidity = false, + std::string pythiaCfgMb = "", + std::string pythiaCfgSignal = "") +{ + return generateLFRapidity(configuration, /*injectOnePDGPerEvent=*/true, gapBetweenInjection, /*useTrigger=*/true, useRapidity, pythiaCfgMb, pythiaCfgSignal); +} + +///___________________________________________________________ +void generator_pythia8_LF_rapidity(bool testInj = true, bool testTrg = false, bool useRapidity = false, const char* particleListFile = "cfg_rapidity.json") +{ + LOG(info) << "Compiled correctly!"; + if (!testInj && !testTrg) { + return; + } + // Injected mode + if (testInj) { + LOG(info) << "Testing the injected mode"; + auto* gen = static_cast(generateLFRapidity(particleListFile, true, 0, false, useRapidity)); + gen->setVerbose(); + gen->Print(); + gen->print(); + gen->Init(); + gen->generateEvent(); + gen->importParticles(); + } + + // Triggered mode + if (testTrg) { + LOG(info) << "Testing the triggered mode"; + GeneratorPythia8LFRapidity* gen = static_cast(generateLFRapidityTriggered(particleListFile, + /*gapBetweenInjection=*/0, + useRapidity, + /*pythiaCfgMb=*/"inel136tev.cfg", + /*pythiaCfgSignal=*/"inel136tev.cfg")); + gen->setVerbose(); + gen->Print(); + gen->print(); + gen->Init(); + gen->generateEvent(); + gen->importParticles(); + } +} diff --git a/MC/config/PWGLF/pythia8/generator_pythia8_Lstar.C b/MC/config/PWGLF/pythia8/generator_pythia8_Lstar.C new file mode 100644 index 000000000..3a732db28 --- /dev/null +++ b/MC/config/PWGLF/pythia8/generator_pythia8_Lstar.C @@ -0,0 +1,271 @@ +#include "Pythia8/Pythia.h" +#include "Pythia8/HeavyIons.h" +#include "FairGenerator.h" +#include "FairPrimaryGenerator.h" +#include "Generators/GeneratorPythia8.h" +#include "TF1.h" +#include "TRandom3.h" +//#include "TParticlePDG.h" +//#include "TDatabasePDG.h" + +#include +#include + +class GeneratorPythia8ExtraStrangeness : public o2::eventgen::GeneratorPythia8 +{ +public: + /// default constructor + GeneratorPythia8ExtraStrangeness() = default; + + /// Constructor + GeneratorPythia8ExtraStrangeness(int input_pdg, int inputTriggerRatio) + { + + if (inputTriggerRatio <= 0) { + throw std::invalid_argument("inputTriggerRatio must be positive"); + } + + LOG(info) << "Gap trigger ratio N = " << inputTriggerRatio << " events"; + + + mGeneratedEvents = 0; + mTriggerRatio = inputTriggerRatio; + + genMinPt=0.0; + genMaxPt=10.0; + genminY=-1.0; + genmaxY=1.0; + genminEta=-2.5; + genmaxEta=2.5; + + pdg=input_pdg; + m = 0; + E=0; + px=0; + py=0; + pz=0; + p=0; + y=0; + eta=0; + xProd=0.; yProd=0.; zProd=0.; + + fLVHelper = std::make_unique(); + } + + + Double_t y2eta(Double_t pt, Double_t mass, Double_t y){ + Double_t mt = TMath::Sqrt(mass * mass + pt * pt); + return TMath::ASinH(mt / pt * TMath::SinH(y)); + } + + + /// set mass + double sampleMass(int input_pdg) + { + auto& pd = mPythia.particleData; + const double mass = pd.mWidth(input_pdg) > 0. ? pd.mSel(input_pdg) : pd.m0(input_pdg); + return mass; + } + + static double myLevyPt(double* pt, double* par) + { + const double lMass = par[0]; + const double ldNdy = par[1]; + const double lTemp = par[2]; + const double lPower = par[3]; + + const double lBigCoef = ((lPower-1)*(lPower-2)) / (lPower*lTemp*(lPower*lTemp+lMass*(lPower-2))); + const double lInPower = 1 + (TMath::Sqrt(pt[0]*pt[0]+lMass*lMass)-lMass) / (lPower*lTemp); + + return ldNdy * pt[0] * lBigCoef * TMath::Power(lInPower, -lPower); + } + + /// build a Levy-Tsallis pT spectrum for a particle of given mass +TF1* makeLevySpectrum(const char* name, double ptMin, double ptMax, double mass, double T, double n, double norm = 1.) + { + TF1* f = new TF1(name, myLevyPt, ptMin, ptMax, 4); + f->SetParNames("mass", "norm", "T", "n"); + f->FixParameter(0, mass); // mass [GeV/c^2] + f->SetParameter(1, norm); // normalization (irrelevant for GetRandom) + f->SetParameter(2, T); // slope parameter [GeV] + f->SetParameter(3, n); // power-law exponent + f->SetNpx(1000); + return f; + } + + /// set 4-momentum + void set4momentum(double input_px, double input_py, double input_pz){ + px = input_px; + py = input_py; + pz = input_pz; + E = sqrt( m*m+px*px+py*py+pz*pz ); + fourMomentum.px(px); + fourMomentum.py(py); + fourMomentum.pz(pz); + fourMomentum.e(E); + p = sqrt( px*px+py*py+pz*pz ); + y = 0.5*log( (E+pz)/(E-pz) ); + eta = 0.5*log( (p+pz)/(p-pz) ); + } + + + //_________________________________________________________________________________ + /// generate uniform eta and uniform momentum + void genSpectraMomentumEta(double minPt, double maxPt, double minY, double maxY){ + + // generate transverse momentum + //const double gen_pT = ranGenerator->Uniform(minPt, maxPt); + const double gen_pT = fSpectra->GetRandom(minPt, maxPt, &mRandom); + + //Actually could be something else without loss of generality but okay + const double gen_phi = mRandom.Uniform(0,2*TMath::Pi()); + + // sample flat in rapidity, calculate eta + Double_t gen_Y=10, gen_eta=10; + + while( gen_eta>genmaxEta || gen_etaSetPtEtaPhiM(gen_pT, gen_eta, gen_phi, m); + set4momentum(fLVHelper->Px(),fLVHelper->Py(),fLVHelper->Pz()); + } + + //__________________________________________________________________ + Pythia8::Particle createParticle(int id){ + //std::cout << "createParticle() mass " << m << " pdgCode " << pdg << std::endl; + Pythia8::Particle myparticle; + myparticle.id(id); + myparticle.status(11); + myparticle.px(px); + myparticle.py(py); + myparticle.pz(pz); + myparticle.e(E); + myparticle.m(m); + myparticle.xProd(xProd); + myparticle.yProd(yProd); + myparticle.zProd(zProd); + + return myparticle; + } + + + //__________________________________________________________________ + // look for final-state (anti)Lambdas in the event; if there are any, remove one + // (chosen at random) from the event record and return the Lambda id, otherwise return 0 + int removeLambda() + { + int kLambdaPdg = 3122; + auto& event = mPythia.event; + + std::vector candidates; + for (int i = 0; i < event.size(); ++i) { + bool isFromDecay = event[i].status() >= 91 && event[i].status() <= 99; + bool isAcc = std::abs(event[i].y()) < genmaxY; + + if (event[i].idAbs() == kLambdaPdg && event[i].isFinal() && isAcc && !isFromDecay) { + // not considering lambdas that are the first daughter in the event record. + bool isEdge = false; + for (int mom : event[i].motherList()) { + if (i == event[mom].daughter1() || i == event[mom].daughter2()) isEdge = true; + } + + if (!isEdge) { + candidates.push_back(i); + } + } + } + if (candidates.empty()) { + return 0; + } + + const int idx = candidates[static_cast(mRandom.Uniform(0,candidates.size()))]; + const int lambdaId = event[idx].id(); + + // remove the entry and shift the indices of the others + event.remove(idx, idx, true); + + return lambdaId; + } + + //__________________________________________________________________ + Bool_t generateEvent() override { + // Generate PYTHIA event + + if (!fSpectra) { + fSpectra = makeLevySpectrum("fSpectra", genMinPt, genMaxPt, mPythia.particleData.m0(pdg), 0.3942, 9.943); + } + + mPythia.event.reset(); + Bool_t lPythiaOK = kFALSE; + + mGeneratedEvents++; + if (mGeneratedEvents % mTriggerRatio == 0) { + int lambdaId = 0; + + // regenerate until the event contains a Lambda or an anti-Lambda, which is removed + while (lambdaId == 0) + { + lPythiaOK = mPythia.next(); + if (lPythiaOK) { + lambdaId = removeLambda(); + } + } + + m = sampleMass(pdg); + genSpectraMomentumEta(genMinPt, genMaxPt, genminY, genmaxY); + + // Lambda -> Lambda*, anti-Lambda -> anti-Lambda* + const int sign = lambdaId > 0 ? 1 : -1; + + Pythia8::Particle particle = createParticle(sign*pdg); + int idxL = mPythia.event.append(particle); + mPythia.moreDecays(idxL); + } else { + while (!lPythiaOK){ + lPythiaOK = mPythia.next(); + } + } + return true; + } + + +private: + + unsigned long long mGeneratedEvents; + int mTriggerRatio; + + double genMinPt; /// minimum 3-momentum for generated particles + double genMaxPt; /// maximum 3-momentum for generated particles + double genminY; /// minimum pseudorapidity for generated particles + double genmaxY; /// maximum pseudorapidity for generated particles + double genminEta; + double genmaxEta; + + Pythia8::Vec4 fourMomentum; /// four-momentum (px,py,pz,E) + //std::unique_ptr lutGen; + + double E; /// energy: sqrt( m*m+px*px+py*py+pz*pz ) [GeV/c] + double m; /// particle mass [GeV/c^2] + int pdg; /// particle pdg code + double px; /// x-component momentum [GeV/c] + double py; /// y-component momentum [GeV/c] + double pz; /// z-component momentum [GeV/c] + double p; /// momentum + double y; /// rapidity + double eta; /// pseudorapidity + double xProd; /// x-coordinate position production vertex [cm] + double yProd; /// y-coordinate position production vertex [cm] + double zProd; /// z-coordinate position production vertex [cm] + + TF1 *fSpectra = nullptr; /// TF1 to store more realistic shape of spectrum + std::unique_ptr fLVHelper; + TRandom3 mRandom{0}; +}; + + FairGenerator *generator_Lstar(int inputTriggerRatio = 3) + { + auto myGen = new GeneratorPythia8ExtraStrangeness(3124, inputTriggerRatio); + return myGen; + } \ No newline at end of file diff --git a/MC/config/PWGLF/pythia8/generator_pythia8_antid_and_highpt.C b/MC/config/PWGLF/pythia8/generator_pythia8_antid_and_highpt.C new file mode 100644 index 000000000..327b5aa84 --- /dev/null +++ b/MC/config/PWGLF/pythia8/generator_pythia8_antid_and_highpt.C @@ -0,0 +1,167 @@ +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include "FairGenerator.h" +#include "FairPrimaryGenerator.h" +#include "Generators/GeneratorPythia8.h" +#include "Pythia8/Pythia.h" +#include "TDatabasePDG.h" +#include "TMath.h" +#include "TParticlePDG.h" +#include "TRandom3.h" +#include "TSystem.h" +#include "fairlogger/Logger.h" +#include +#include +#include +#include +using namespace Pythia8; +#endif + +/// Pythia8 event generator for pp collisions +/// Selection of events with leading particle (pt>ptThreshold) and containing at +/// least one (anti)deuteron produced by simple coalescence + +class GeneratorPythia8AntidAndHighPt : public o2::eventgen::GeneratorPythia8 { +public: + /// Constructor + GeneratorPythia8AntidAndHighPt(double p0 = 0.3, double pt_leading = 5.0) + : o2::eventgen::GeneratorPythia8() { + mP0 = p0; + mPt_leading = pt_leading; + } + /// Destructor + ~GeneratorPythia8AntidAndHighPt() = default; + + bool Init() override { + addSubGenerator(0, "Pythia8 with (anti)deuterons and high pt particle"); + return o2::eventgen::GeneratorPythia8::Init(); + } + +protected: + bool generateEvent() override { + fmt::printf(">> Generating event %d\n", mGeneratedEvents); + + bool genOk = false; + int localCounter{0}; + while (!genOk) { + if (GeneratorPythia8::generateEvent()) { + genOk = selectEvent(mPythia.event); + } + localCounter++; + } + fmt::printf( + ">> Generation of event of interest successful after %i iterations\n", + localCounter); + std::cout << std::endl << std::endl; + notifySubGenerator(0); + + mGeneratedEvents++; + + return true; + } + + bool selectEvent(Pythia8::Event &event) { + + bool has_particle_of_interest = false; + + // Deuteron Mass [GeV] + double md = 1.87561294257; + + // Protons and Neutrons + vector proton_ID; + vector neutron_ID; + vector proton_status; + vector neutron_status; + + // ptMax + double pt_max{0}; + + for (auto iPart{0}; iPart < event.size(); ++iPart) { + + // Only final-state particles + if (event[iPart].status() <= 0) { + continue; + } + + //(Anti)Proton selection + if (abs(event[iPart].id()) == 2212) { + proton_ID.push_back(iPart); + proton_status.push_back(0); + } + + //(Anti)Neutron selection + if (abs(event[iPart].id()) == 2112) { + neutron_ID.push_back(iPart); + neutron_status.push_back(0); + } + + if (std::abs(event[iPart].eta()) < 0.8 && (!event[iPart].isNeutral()) && + event[iPart].pT() > pt_max) + pt_max = event[iPart].pT(); + } + + // Skip Events with no leading particle + if (pt_max < mPt_leading) + return false; + + if (proton_ID.size() < 1 || neutron_ID.size() < 1) + return false; + + for (uint32_t ip = 0; ip < proton_ID.size(); ++ip) { + + // Skip used protons + if (proton_status[ip] < 0) { + continue; + } + for (uint32_t in = 0; in < neutron_ID.size(); ++in) { + + // Skip used neutrons + if (neutron_status[in] < 0) { + continue; + } + + auto sign_p = + event[proton_ID[ip]].id() / abs(event[proton_ID[ip]].id()); + auto sign_n = + event[neutron_ID[in]].id() / abs(event[neutron_ID[in]].id()); + + auto p1 = event[proton_ID[ip]].p(); + auto p2 = event[neutron_ID[in]].p(); + auto p = p1 + p2; + p1.bstback(p); + p2.bstback(p); + + // Coalescence + if (p1.pAbs() <= mP0 && p2.pAbs() <= mP0 && sign_p == sign_n) { + p.e(std::hypot(p.pAbs(), md)); + event.append(sign_p * 1000010020, 121, 0, 0, 0, 0, 0, 0, p.px(), + p.py(), p.pz(), p.e(), md); + event[proton_ID[ip]].statusNeg(); + event[proton_ID[ip]].daughter1(event.size() - 1); + event[neutron_ID[in]].statusNeg(); + event[neutron_ID[in]].daughter1(event.size() - 1); + proton_status[ip] = -1; + neutron_status[in] = -1; + has_particle_of_interest = true; + } + } + } + + return has_particle_of_interest; + } + +private: + double mP0 = 0.3; + double mPt_leading = 5.0; + uint64_t mGeneratedEvents = 0; +}; + +///___________________________________________________________ +FairGenerator *generateAntidAndHighPt(double p0 = 0.3, + double pt_leading = 5.0) { + + auto myGenerator = new GeneratorPythia8AntidAndHighPt(p0, pt_leading); + auto seed = (gRandom->TRandom::GetSeed() % 900000000); + myGenerator->readString("Random:setSeed on"); + myGenerator->readString("Random:seed " + std::to_string(seed)); + return myGenerator; +} diff --git a/MC/config/PWGLF/pythia8/generator_pythia8_antideuteron_injection.C b/MC/config/PWGLF/pythia8/generator_pythia8_antideuteron_injection.C new file mode 100644 index 000000000..c4f17ea52 --- /dev/null +++ b/MC/config/PWGLF/pythia8/generator_pythia8_antideuteron_injection.C @@ -0,0 +1,114 @@ +#if !defined(__CLING__) || defined(__ROOTCLING__) + +#include "FairGenerator.h" +#include "Generators/GeneratorPythia8.h" +#include "Pythia8/Pythia.h" +#include "TRandom.h" +#include "fairlogger/Logger.h" + +#include +#include +#include + +#endif // !defined(__CLING__) || defined(__ROOTCLING__) + +/// PYTHIA8 event generator with antideuteron injection. +/// Events are generated until a final-state antiproton within the specified etaMax is found. +/// The antiproton is then replaced by an antideuteron with twice its momentum. + +class GeneratorPythia8AntideuteronInjection + : public o2::eventgen::GeneratorPythia8 { + + public: + + GeneratorPythia8AntideuteronInjection(double etaMax = 0.8) + : o2::eventgen::GeneratorPythia8(), + mEtaMax(etaMax) {} + + ~GeneratorPythia8AntideuteronInjection() override = default; + + bool Init() override { + addSubGenerator(0, "Pythia8 events with antideuterons"); + return o2::eventgen::GeneratorPythia8::Init(); + } + + protected: + + + bool generateEvent() override { + constexpr int maxAttempts = 10000; + for (int attempt = 0; attempt < maxAttempts; ++attempt) { + if (!GeneratorPythia8::generateEvent()) { + return false; + } + + if (EventHasAntideuteron(mPythia.event)) { + LOG(debug) << ">> An antideuteron was formed!"; + + notifySubGenerator(0); + ++mGeneratedEvents; + + return true; + } + } + + LOG(error) << "Failed to generate an antideuteron after " << maxAttempts << " attempts"; + return false; + } + + bool EventHasAntideuteron(Pythia8::Event& event) { + + const double md = 1.87561294257; // Deuteron mass in GeV/c^2 + const int pdgAntid = -1000010020; // Antideuteron PDG code + + for (int iPart = 0; iPart < event.size(); ++iPart) { + + if (!event[iPart].isFinal()) { + continue; + } + + if (std::abs(event[iPart].eta()) > mEtaMax) { + continue; + } + + if (event[iPart].id() != -2212) { + continue; + } + + double px = 2.0 * event[iPart].px(); + double py = 2.0 * event[iPart].py(); + double pz = 2.0 * event[iPart].pz(); + double energy = std::sqrt(md * md + px * px + py * py + pz * pz); + + // Remove antiproton from final state + event[iPart].statusNeg(); + + // Add antideuteron + const int iAntid = event.size(); + event.append(pdgAntid, 121, iPart, 0, 0, 0, 0, 0, px, py, pz, energy, md); + // Update daughter indices + event[iPart].daughter1(iAntid); + event[iPart].daughter2(iAntid); + return true; + } + return false; + } + + private: + + double mEtaMax = 0.8; + uint64_t mGeneratedEvents = 0; +}; + +/// Generator factory + +FairGenerator* generateAntideuterons(double etaMax = 0.8) { + + auto* myGenerator = new GeneratorPythia8AntideuteronInjection(etaMax); + auto seed = gRandom->TRandom::GetSeed() % 900000000; + + myGenerator->readString("Random:setSeed = on"); + myGenerator->readString("Random:seed = " + std::to_string(seed)); + + return myGenerator; +} diff --git a/MC/config/PWGLF/pythia8/generator_pythia8_coalescence.C b/MC/config/PWGLF/pythia8/generator_pythia8_coalescence.C new file mode 100644 index 000000000..790710476 --- /dev/null +++ b/MC/config/PWGLF/pythia8/generator_pythia8_coalescence.C @@ -0,0 +1,99 @@ +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include "Pythia8/Pythia.h" +#include "FairGenerator.h" +#include "FairPrimaryGenerator.h" +#include "Generators/GeneratorPythia8.h" +#include "fairlogger/Logger.h" +#include "TRandom3.h" +#include "TParticlePDG.h" +#include "TDatabasePDG.h" +#include "TSystem.h" +#include "TMath.h" +#include +#include +#include +#include +using namespace Pythia8; +#endif + +R__ADD_INCLUDE_PATH($O2DPG_MC_CONFIG_ROOT) +#include "MC/config/common/external/generator/CoalescencePythia8.h" +/// First version of the simple coalescence generator based PYTHIA8 + +class GeneratorPythia8Coalescence : public o2::eventgen::GeneratorPythia8 +{ +public: + /// Constructor + GeneratorPythia8Coalescence(std::vector pdgList, int input_trigger_ratio = 1, double coal_momentum = 0.4) + : o2::eventgen::GeneratorPythia8() + { + fmt::printf(">> Coalescence generator %d\n", input_trigger_ratio); + mInverseTriggerRatio = input_trigger_ratio; + mCoalMomentum = coal_momentum; + mPdgList = pdgList; + } + /// Destructor + ~GeneratorPythia8Coalescence() = default; + + bool Init() override + { + addSubGenerator(0, "Minimum bias"); + addSubGenerator(1, "Coalescence"); + return o2::eventgen::GeneratorPythia8::Init(); + } + +protected: + bool generateEvent() override + { + fmt::printf(">> Generating event %d\n", mGeneratedEvents); + // Simple straightforward check to alternate generators + if (mGeneratedEvents % mInverseTriggerRatio == 0) + { + fmt::printf(">> Generating coalescence event %d\n", mGeneratedEvents); + bool genOk = false; + int localCounter{0}; + while (!genOk) + { + if (GeneratorPythia8::generateEvent()) + { + genOk = CoalescencePythia8(mPythia.event, mPdgList, mCoalMomentum); + } + localCounter++; + } + fmt::printf(">> Coalescence successful after %i generations\n", localCounter); + std::cout << std::endl << std::endl; + notifySubGenerator(1); + } + else + { + fmt::printf(">> Generating minimum-bias event %d\n", mGeneratedEvents); + // Generate minimum-bias event + bool genOk = false; + while (!genOk) + { + genOk = GeneratorPythia8::generateEvent(); + } + notifySubGenerator(0); + } + mGeneratedEvents++; + return true; + } + + +private: + std::vector mPdgList; /// list of pdg codes to be generated + float mCoalMomentum = 0.4; /// coalescence momentum + uint64_t mGeneratedEvents = 0; /// number of events generated so far + int mInverseTriggerRatio = 1; /// injection gap +}; + +///___________________________________________________________ +FairGenerator *generateCoalescence(std::vector pdgList, int input_trigger_ratio, double coal_momentum = 0.4) +{ + auto myGen = new GeneratorPythia8Coalescence(pdgList, input_trigger_ratio, coal_momentum); + auto seed = (gRandom->TRandom::GetSeed() % 900000000); + myGen->readString("Random:setSeed on"); + myGen->readString("Random:seed " + std::to_string(seed)); + return myGen; +} +///___________________________________________________________ \ No newline at end of file diff --git a/MC/config/PWGLF/pythia8/generator_pythia8_deuteron_wigner.C b/MC/config/PWGLF/pythia8/generator_pythia8_deuteron_wigner.C new file mode 100644 index 000000000..0efcdc1a0 --- /dev/null +++ b/MC/config/PWGLF/pythia8/generator_pythia8_deuteron_wigner.C @@ -0,0 +1,157 @@ +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include "FairGenerator.h" +#include "FairPrimaryGenerator.h" +#include "Generators/GeneratorPythia8.h" +#include "Pythia8/Pythia.h" +#include "TDatabasePDG.h" +#include "TMath.h" +#include "TParticlePDG.h" +#include "TRandom3.h" +#include "TSystem.h" +#include "TH2D.h" +#include "TH1D.h" +#include "TFile.h" +#include "fairlogger/Logger.h" +#include "CCDB/BasicCCDBManager.h" +#include +#include +#include +#include +using namespace Pythia8; +#endif + +/// Event generator for proton-proton (pp) collisions using Pythia8. +/// (Anti)deuterons are formed via nucleon coalescence modeled using the Wigner density formalism. + +class GeneratorPythia8DeuteronWigner : public o2::eventgen::GeneratorPythia8 { +public: + GeneratorPythia8DeuteronWigner(double sourceRadius = 1.2) + : o2::eventgen::GeneratorPythia8(), mSourceRadius(sourceRadius) { + + // Connect to CCDB and retrieve coalescence probability two-dimensional table + o2::ccdb::CcdbApi ccdb_api; + ccdb_api.init("https://alice-ccdb.cern.ch"); + + mTwoDimCoalProbability = ccdb_api.retrieveFromTFileAny("Users/a/alcaliva/WignerCoalescence/ArgonneProbability/AddedSDWave"); + + if (!mTwoDimCoalProbability) { + LOG(fatal) << "Could not find coalescence probability table in input file!"; + } + } + + ~GeneratorPythia8DeuteronWigner() override = default; + + bool Init() override { + addSubGenerator(0, "Pythia8 events with (anti)deuterons formed via coalescence using the Wigner density formalism, provided the coalescence condition is fulfilled"); + return o2::eventgen::GeneratorPythia8::Init(); + } + +protected: + bool generateEvent() override { + + if (GeneratorPythia8::generateEvent() && EventHasDeuteron(mPythia.event)) { + LOG(debug) << ">> A Deuteron was formed!"; + } + + notifySubGenerator(0); + ++mGeneratedEvents; + return true; + } + + bool EventHasDeuteron(Pythia8::Event& event) { + + bool deuteronIsFormed = false; + const double md = 1.87561294257; // Deuteron mass [GeV] + + std::vector proton_ID, neutron_ID; + std::vector proton_status, neutron_status; + + for (int iPart = 0; iPart < event.size(); ++iPart) { + if (event[iPart].status() <= 0) { + continue; + } + + int absID = std::abs(event[iPart].id()); + if (absID == 2212) { + proton_ID.push_back(iPart); + proton_status.push_back(0); + } else if (absID == 2112) { + neutron_ID.push_back(iPart); + neutron_status.push_back(0); + } + } + + int radiusBin = mTwoDimCoalProbability->GetXaxis()->FindBin(mSourceRadius); + TH1D* prob_vs_q = mTwoDimCoalProbability->ProjectionY("prob_vs_q", radiusBin, radiusBin, "E"); + prob_vs_q->SetDirectory(nullptr); + + for (size_t ip = 0; ip < proton_ID.size(); ip++) { + if (proton_status[ip] < 0) continue; + + for (size_t in = 0; in < neutron_ID.size(); in++) { + if (neutron_status[in] < 0) continue; + + int protonID = proton_ID[ip]; + int neutronID = neutron_ID[in]; + int sign_p = event[protonID].id() / std::abs(event[protonID].id()); + int sign_n = event[neutronID].id() / std::abs(event[neutronID].id()); + if (sign_p != sign_n) continue; + + auto p1 = event[protonID].p(); + auto p2 = event[neutronID].p(); + auto p = p1 + p2; + p1.bstback(p); + p2.bstback(p); + + Vec4 deltaPVec = p1 - p2; + double deltaP = 0.5 * deltaPVec.pAbs(); + + int binQ = prob_vs_q->FindBin(deltaP); + + // Skip underflow and overflow bins + if (binQ < 1 || binQ > prob_vs_q->GetNbinsX()) { + continue; + } + double coalProb = prob_vs_q->GetBinContent(prob_vs_q->FindBin(deltaP)); + double rndCoalProb = gRandom->Uniform(0.0, 1.0); + double rndSpinIsospin = gRandom->Uniform(0.0, 1.0); + + if (rndCoalProb < coalProb && rndSpinIsospin < 3.0/8.0) { + double energy = std::hypot(p.pAbs(), md); + p.e(energy); + int deuteronPDG = sign_p * 1000010020; + + event.append(deuteronPDG, 121, 0, 0, 0, 0, 0, 0, p.px(), p.py(), p.pz(), p.e(), md); + + event[protonID].statusNeg(); + event[protonID].daughter1(event.size() - 1); + proton_status[ip] = -1; + + event[neutronID].statusNeg(); + event[neutronID].daughter1(event.size() - 1); + neutron_status[in] = -1; + + deuteronIsFormed = true; + } + } + } + + // free allocated memory + delete prob_vs_q; + return deuteronIsFormed; + } + +private: + double mSourceRadius = 1.2; + uint64_t mGeneratedEvents = 0; + TH2D* mTwoDimCoalProbability = nullptr; +}; + +///________________________________________________________________________________________________________ +FairGenerator* generateAntideuteronsWignerCoalescence(double sourceRadius = 1.2) { + auto myGenerator = new GeneratorPythia8DeuteronWigner(sourceRadius); + auto seed = gRandom->TRandom::GetSeed() % 900000000; + myGenerator->readString("Random:setSeed on"); + myGenerator->readString("Random:seed " + std::to_string(seed)); + return myGenerator; +} diff --git a/MC/config/PWGLF/pythia8/generator_pythia8_doubleLambdas.C b/MC/config/PWGLF/pythia8/generator_pythia8_doubleLambdas.C new file mode 100644 index 000000000..b03a57ef0 --- /dev/null +++ b/MC/config/PWGLF/pythia8/generator_pythia8_doubleLambdas.C @@ -0,0 +1,162 @@ +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include "FairGenerator.h" +#include "FairPrimaryGenerator.h" +#include "Generators/GeneratorPythia8.h" +#include "Pythia8/Pythia.h" +#include "TDatabasePDG.h" +#include "TMath.h" +#include "TParticlePDG.h" +#include "TRandom3.h" +#include "TSystem.h" +#include "TVector2.h" +#include "fairlogger/Logger.h" +#include +#include +#include +#include +using namespace Pythia8; +#endif + +/// Event generator using Pythia ropes +/// One event of interest is triggered after n minimum-bias events. +/// Event of interest : an event that contains at least two generated (anti-)Lambdas + +class GeneratorPythia8DoubleLambda : public o2::eventgen::GeneratorPythia8 +{ +public: + /// Constructor + GeneratorPythia8DoubleLambda(int gapSize = 4, double minPt = 0.2, double maxPt = 10, double maxEta = 0.8) + : o2::eventgen::GeneratorPythia8(), + mGapSize(gapSize), + mMinPt(minPt), + mMaxPt(maxPt), + mMaxEta(maxEta) + { + fmt::printf( + ">> Pythia8 generator: two (anti-)Lambdas, gap = %d, minPtLambda = %f, maxPtLambda = %f, |etaLambda| < %f\n", gapSize, minPt, maxPt, maxEta); + } + /// Destructor + ~GeneratorPythia8DoubleLambda() = default; + + bool Init() override + { + addSubGenerator(0, "Pythia8 events with two (anti-)Lambdas"); + return o2::eventgen::GeneratorPythia8::Init(); + } + +protected: + /// Check if particle is physical primary or from HF decay + bool isLambdaPhysicalPrimaryOrFromHF(const Pythia8::Particle &p, const Pythia8::Event &event) + { + // Select only final-state particles + if (!p.isFinal()) + { + return false; + } + + // Walk up ancestry + int motherId = p.mother1(); + + while (motherId > 0) + { + + // Get mother + const auto &mother = event[motherId]; + const int absMotherPdg = std::abs(mother.id()); + + // Check if particle is from HF decay + if ((absMotherPdg / 100 == 4) || (absMotherPdg / 100 == 5) || (absMotherPdg / 1000 == 4) || (absMotherPdg / 1000 == 5)) + { + return true; + } + + // Reject non-physical primary hadrons + if (mother.isHadron() && mother.tau0() > 1.0) + { + return false; + } + motherId = mother.mother1(); + } + return true; + } + + bool generateEvent() override + { + fmt::printf(">> Generating event %d\n", mGeneratedEvents); + + bool genOk = false; + int localCounter{0}; + constexpr int kMaxTries{100000}; + + // Accept mGapSize events unconditionally, then one triggered event + if (mGeneratedEvents % (mGapSize + 1) < mGapSize) + { + genOk = GeneratorPythia8::generateEvent(); + fmt::printf(">> Gap-event (no strangeness check)\n"); + } + else + { + while (!genOk && localCounter < kMaxTries) + { + if (GeneratorPythia8::generateEvent()) + { + genOk = selectEvent(mPythia.event); + } + localCounter++; + } + if (!genOk) + { + fmt::printf("Failed to generate triggered event after %d tries\n", kMaxTries); + return false; + } + fmt::printf(">> Triggered event: event accepted after %d iterations (double (anti-)Lambdas)\n", localCounter); + } + + notifySubGenerator(0); + mGeneratedEvents++; + return true; + } + + bool selectEvent(Pythia8::Event &event) + { + + std::vector particleID; + int nLambda{0}; + + for (int i = 0; i < event.size(); i++) + { + const auto &p = event[i]; + if (std::abs(p.eta()) > mMaxEta || p.pT() < mMinPt || p.pT() > mMaxPt) + continue; + if (!isLambdaPhysicalPrimaryOrFromHF(p, event)) + continue; + + if (std::abs(p.id()) == 3122) + nLambda++; + + particleID.emplace_back(i); + } + if (nLambda < 2) + return false; + + return true; + } + +private: + int mGapSize{4}; + double mMinPt{0.2}; + double mMaxPt{10.0}; + double mMaxEta{0.8}; + uint64_t mGeneratedEvents{0}; +}; + +///___________________________________________________________ +FairGenerator *generateDoubleLambda(int gap = 4, double minPt = 0.2, double maxPt = 10, double maxEta = 0.8) +{ + + auto myGenerator = new GeneratorPythia8DoubleLambda(gap, minPt, maxPt, maxEta); + auto seed = (gRandom->TRandom::GetSeed() % 900000000); + myGenerator->readString("Random:setSeed on"); + myGenerator->readString("Random:seed " + std::to_string(seed)); + return myGenerator; +} diff --git a/MC/config/PWGLF/pythia8/generator_pythia8_extraStrangeness.C b/MC/config/PWGLF/pythia8/generator_pythia8_extraStrangeness.C new file mode 100644 index 000000000..f518c1d7b --- /dev/null +++ b/MC/config/PWGLF/pythia8/generator_pythia8_extraStrangeness.C @@ -0,0 +1,295 @@ + +#include "Pythia8/Pythia.h" +#include "Pythia8/HeavyIons.h" +#include "FairGenerator.h" +#include "FairPrimaryGenerator.h" +#include "Generators/GeneratorPythia8.h" +#include "TRandom3.h" +#include "TF1.h" +#include "TParticlePDG.h" +#include "TDatabasePDG.h" + +#include +#include +#include + +class GeneratorPythia8ExtraStrangeness : public o2::eventgen::GeneratorPythia8 +{ +public: + /// Constructor + GeneratorPythia8ExtraStrangeness(double enhancementXi, double enhancementOmega) { + genMinPt=0.0; + genMaxPt=20.0; + genminY=-1.0; + genmaxY=1.0; + genminEta=-1.0; + genmaxEta=1.0; + + pdg=0; + E=0; + px=0; + py=0; + pz=0; + p=0; + y=0; + eta=0; + xProd=0; + yProd=0; + zProd=0; + + fEnhancementXi = enhancementXi; + fEnhancementOmega = enhancementOmega; + + fLVHelper = std::make_unique(); + + fSpectrumXi = std::make_unique("fSpectrumXi", this, &GeneratorPythia8ExtraStrangeness::boltzPlusPower, 0., genMaxPt, 5, "GeneratorPythia8ExtraStrangeness", "boltzPlusPower"); + + fSpectrumXi->FixParameter(0, 1.32171); + fSpectrumXi->FixParameter(1, 4.84e-1); + fSpectrumXi->FixParameter(2, 111.9); + fSpectrumXi->FixParameter(3, -2.56511e+00); + fSpectrumXi->FixParameter(4, 1.14011e-04); + + fSpectrumOm = std::make_unique("fSpectrumOm", this, &GeneratorPythia8ExtraStrangeness::boltzPlusPower, 0., genMaxPt, 5, "GeneratorPythia8ExtraStrangeness", "boltzPlusPower"); + + fSpectrumOm->FixParameter(0, 1.67245e+00); + fSpectrumOm->FixParameter(1, 5.18174e-01); + fSpectrumOm->FixParameter(2, 1.73747e+01); + fSpectrumOm->FixParameter(3, -2.56681e+00); + fSpectrumOm->FixParameter(4, 1.87513e-04); + } + + Double_t y2eta(Double_t pt, Double_t mass, Double_t y){ + Double_t mt = TMath::Sqrt(mass * mass + pt * pt); + return TMath::ASinH(mt / pt * TMath::SinH(y)); + } + + /// set 4-momentum + void set4momentum(double input_px, double input_py, double input_pz){ + px = input_px; + py = input_py; + pz = input_pz; + E = sqrt( m*m+px*px+py*py+pz*pz ); + fourMomentum.px(px); + fourMomentum.py(py); + fourMomentum.pz(pz); + fourMomentum.e(E); + p = sqrt( px*px+py*py+pz*pz ); + y = 0.5*log( (E+pz)/(E-pz) ); + eta = 0.5*log( (p+pz)/(p-pz) ); + } + + //__________________________________________________________________ + Pythia8::Particle createParticle(){ + //std::cout << "createParticle() mass " << m << " pdgCode " << pdg << std::endl; + Pythia8::Particle myparticle; + myparticle.id(pdg); + myparticle.status(11); + myparticle.px(px); + myparticle.py(py); + myparticle.pz(pz); + myparticle.e(E); + myparticle.m(m); + myparticle.xProd(xProd); + myparticle.yProd(yProd); + myparticle.zProd(zProd); + + return myparticle; + } + + //_________________________________________________________________________________ + /// generate uniform eta and uniform momentum + void genSpectraMomentumEtaXi(double minP, double maxP, double minY, double maxY){ + // random generator + std::unique_ptr ranGenerator { new TRandom3() }; + ranGenerator->SetSeed(0); + + // generate transverse momentum + const double gen_pT = fSpectrumXi->GetRandom(genMinPt,genMaxPt); + + //Actually could be something else without loss of generality but okay + const double gen_phi = ranGenerator->Uniform(0,2*TMath::Pi()); + + // sample flat in rapidity, calculate eta + Double_t gen_Y=10, gen_eta=10; + + while( gen_eta>genmaxEta || gen_etaUniform(minY,maxY); + //(Double_t pt, Double_t mass, Double_t y) + gen_eta = y2eta(gen_pT, m, gen_Y); + } + + fLVHelper->SetPtEtaPhiM(gen_pT, gen_eta, gen_phi, m); + set4momentum(fLVHelper->Px(),fLVHelper->Py(),fLVHelper->Pz()); + } + + //_________________________________________________________________________________ + /// generate uniform eta and uniform momentum + void genSpectraMomentumEtaOm(double minP, double maxP, double minY, double maxY){ + // random generator + std::unique_ptr ranGenerator { new TRandom3() }; + ranGenerator->SetSeed(0); + + // generate transverse momentum + const double gen_pT = fSpectrumOm->GetRandom(genMinPt,genMaxPt); + + //Actually could be something else without loss of generality but okay + const double gen_phi = ranGenerator->Uniform(0,2*TMath::Pi()); + + // sample flat in rapidity, calculate eta + Double_t gen_Y=10, gen_eta=10; + + while( gen_eta>genmaxEta || gen_etaUniform(minY,maxY); + //(Double_t pt, Double_t mass, Double_t y) + gen_eta = y2eta(gen_pT, m, gen_Y); + } + + fLVHelper->SetPtEtaPhiM(gen_pT, gen_eta, gen_phi, m); + set4momentum(fLVHelper->Px(),fLVHelper->Py(),fLVHelper->Pz()); + } + +Double_t boltzPlusPower(const Double_t *x, const Double_t *p) +{ + // a plain parametrization. not meant to be physics worthy. + // adjusted to match preliminary 5 TeV shape. + + Double_t pt = x[0]; + Double_t mass = p[0]; + Double_t mt = TMath::Sqrt(pt * pt + mass * mass); + Double_t T = p[1]; + Double_t norm = p[2]; + + Double_t lowptpart = mt * TMath::Exp(-mt / T); + Double_t highptpart = p[4]*TMath::Power(x[0], p[3]); + + Double_t mixup = 1./(1.+TMath::Exp((x[0]-4.5)/.1)); + + //return pt * norm * (mixup * mt * TMath::Exp(-mt / T) + (1.-mixup)*highptpart) ; + return pt * norm * (mt * TMath::Exp(-mt / T) + (1.-mixup)*highptpart) ; +} + + //__________________________________________________________________ + Bool_t generateEvent() override { + + // Generate PYTHIA event + Bool_t lPythiaOK = kFALSE; + while (!lPythiaOK){ + lPythiaOK = mPythia.next(); + //if(TMath::Abs(mPythia.info.hiInfo->b()-8.5)>0.5) lPythiaOK = false; // select peripheral for flow + } + + // characterise event + Long_t nParticles = mPythia.event.size(); + Long_t nChargedParticlesAtMidRap = 0; + Long_t nPionsAtMidRap = 0; + for ( Long_t j=0; j < nParticles; j++ ) { + Int_t pypid = mPythia.event[j].id(); + Float_t pyrap = mPythia.event[j].y(); + Float_t pyeta = mPythia.event[j].eta(); + + // final only + if (!mPythia.event[j].isFinal()) continue; + + if ( TMath::Abs(pyrap) < 0.5 && TMath::Abs(pypid)==211 ) nPionsAtMidRap++; + if ( TMath::Abs(pyeta) < 0.5 && TMath::Abs(mPythia.event[j].charge())>1e-5 ) nChargedParticlesAtMidRap++; + } + + // now we have the multiplicity + + //+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // XI ABUNDANCE FIX +// FCN=0.35879 FROM MINOS STATUS=SUCCESSFUL 126 CALLS 634 TOTAL +// EDM=3.7456e-09 STRATEGY= 1 ERROR MATRIX ACCURATE +// EXT PARAMETER STEP FIRST +// NO. NAME VALUE ERROR SIZE DERIVATIVE +// 1 p0 4.74929e-03 3.29248e-04 -3.35914e-06 5.38225e+00 +// 2 p1 -4.08255e-03 8.62587e-04 -2.02577e-05 2.45132e+00 +// 3 p2 4.76660e+00 1.93593e+00 1.93593e+00 2.70369e-04 +// Info in : created default TCanvas with name c1 + //Adjust relative abundance of multi-strange particles by injecting some + Double_t lExpectedXiToPion = TMath::Max(4.74929e-03 - 4.08255e-03*TMath::Exp(-nChargedParticlesAtMidRap/4.76660e+00) - 0.00211334,0.); + Double_t lExpectedXi = fEnhancementXi*nPionsAtMidRap*lExpectedXiToPion; // extra rich, factor 5 + Int_t lXiYield = gRandom->Poisson(3*lExpectedXi); //factor 3: fix the rapidity acceptance + m = 1.32171; + pdg = 3312; + cout<<"Adding extra xi: "<Uniform()>0.5?+1:-1; + xProd=0.0; + yProd=0.0; + zProd=0.0; + genSpectraMomentumEtaXi(genMinPt,genMaxPt,genminY,genmaxY); + Pythia8::Particle lAddedParticle = createParticle(); + mPythia.event.append(lAddedParticle); + //lAddedParticles++; + } + //+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + //+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // OMEGA ABUNDANCE FIX + //Adjust relative abundance of multi-strange particles by injecting some + Double_t lExpectedOmegaToPion = TMath::Max(8.55057e-04 - 7.38732e-04*TMath::Exp(-nChargedParticlesAtMidRap/2.40545e+01) - 6.56785e-05,0.); + Double_t lExpectedOmega = fEnhancementOmega*nPionsAtMidRap*lExpectedOmegaToPion; // extra rich, factor 30 (omega -> rarer -> smaller Nch bias -> ok to enrich more) + Int_t lOmegaYield = gRandom->Poisson(3*lExpectedOmega); //factor 3: fix the rapidity acceptance + m = 1.67245; + pdg = 3334; + cout<<"Adding extra omegas: "<Uniform()>0.5?+1:-1; + xProd=0.0; + yProd=0.0; + zProd=0.0; + genSpectraMomentumEtaOm(genMinPt,genMaxPt,genminY,genmaxY); + Pythia8::Particle lAddedParticle = createParticle(); + mPythia.event.append(lAddedParticle); + //lAddedParticles++; + } + //+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + return true; + } + +private: + + double genMinPt; /// minimum 3-momentum for generated particles + double genMaxPt; /// maximum 3-momentum for generated particles + double genminY; /// minimum pseudorapidity for generated particles + double genmaxY; /// maximum pseudorapidity for generated particles + double genminEta; + double genmaxEta; + + Pythia8::Vec4 fourMomentum; /// four-momentum (px,py,pz,E) + + double E; /// energy: sqrt( m*m+px*px+py*py+pz*pz ) [GeV/c] + double m; /// particle mass [GeV/c^2] + int pdg; /// particle pdg code + double px; /// x-component momentum [GeV/c] + double py; /// y-component momentum [GeV/c] + double pz; /// z-component momentum [GeV/c] + double p; /// momentum + double y; /// rapidity + double eta; /// pseudorapidity + double xProd; /// x-coordinate position production vertex [cm] + double yProd; /// y-coordinate position production vertex [cm] + double zProd; /// z-coordinate position production vertex [cm] + + double fEnhancementXi; /// Enhancement factor for Xi + double fEnhancementOmega; /// Enhancement factor for Omega + + std::unique_ptr fLVHelper; + std::unique_ptr fSpectrumXi; + std::unique_ptr fSpectrumOm; +}; + + FairGenerator *generator_extraStrangeness(double enhancementXi = 5.0, double enhancementOmega = 30.0) + { + auto generator = new GeneratorPythia8ExtraStrangeness(enhancementXi,enhancementOmega); + //gRandom->SetSeed(0); + + // Initialize ROOT random number generator with a time-dependent seed to get different sequences each run + gRandom->SetSeed(static_cast(time(nullptr))); + generator->readString("Random:setSeed = on"); + generator->readString("Random:seed =" + std::to_string(gRandom->Integer(900000000 - 2) + 1)); + return generator; + } diff --git a/MC/config/PWGLF/pythia8/generator_pythia8_highpt.C b/MC/config/PWGLF/pythia8/generator_pythia8_highpt.C new file mode 100644 index 000000000..13a2fbe0c --- /dev/null +++ b/MC/config/PWGLF/pythia8/generator_pythia8_highpt.C @@ -0,0 +1,107 @@ +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include "FairGenerator.h" +#include "FairPrimaryGenerator.h" +#include "Generators/GeneratorPythia8.h" +#include "Pythia8/Pythia.h" +#include "TDatabasePDG.h" +#include "TMath.h" +#include "TParticlePDG.h" +#include "TRandom3.h" +#include "TSystem.h" +#include "fairlogger/Logger.h" +#include +#include +#include +#include +using namespace Pythia8; +#endif + +/// Pythia8 event generator for pp collisions +/// Selection of events with leading particle (pt>ptThreshold) and containing at +/// least one particle of interest (default PDG = -2212) + +class GeneratorPythia8HighPt : public o2::eventgen::GeneratorPythia8 { +public: + /// Constructor + GeneratorPythia8HighPt(int pdg_of_interest = -2212, double pt_leading = 5.0) + : o2::eventgen::GeneratorPythia8() + { + fmt::printf(">> Pythia8 generator: PDG of interest = %d, ptLeading > %.1f GeV/c\n", pdg_of_interest, pt_leading); + mPdg_of_interest = pdg_of_interest; + mPt_leading = pt_leading; + } + /// Destructor + ~GeneratorPythia8HighPt() = default; + + bool Init() override { + addSubGenerator(0,"Pythia8 with particle of interest and high pt particle"); + return o2::eventgen::GeneratorPythia8::Init(); + } + +protected: + bool generateEvent() override { + fmt::printf(">> Generating event %d\n", mGeneratedEvents); + + bool genOk = false; + int localCounter{0}; + while (!genOk) { + if (GeneratorPythia8::generateEvent()) { + genOk = selectEvent(mPythia.event); + } + localCounter++; + } + fmt::printf(">> Generation of event of interest successful after %i iterations\n",localCounter); + std::cout << std::endl << std::endl; + notifySubGenerator(0); + + mGeneratedEvents++; + + return true; + } + + bool selectEvent(Pythia8::Event &event) { + + bool contains_particle_of_interest = false; + bool has_leading_particle = false; + + double pt_max{0}; + + for (auto iPart{0}; iPart < event.size(); ++iPart) { + if (std::abs(event[iPart].eta()) > 0.8) { + continue; + } + + if (event[iPart].status() <= 0) { + continue; + } + + if (event[iPart].id() == mPdg_of_interest) + contains_particle_of_interest = true; + + if ((!event[iPart].isNeutral()) && event[iPart].pT() > pt_max) + pt_max = event[iPart].pT(); + } + + if (pt_max > mPt_leading) + has_leading_particle = true; + + if (has_leading_particle && contains_particle_of_interest) + return true; + return false; + } + +private: + int mPdg_of_interest = -2212; + double mPt_leading = 5.0; + uint64_t mGeneratedEvents = 0; +}; + +///___________________________________________________________ +FairGenerator *generateHighPt(int pdg_of_interest = -2212, double pt_leading = 5.0) { + + auto myGenerator = new GeneratorPythia8HighPt(pdg_of_interest, pt_leading); + auto seed = (gRandom->TRandom::GetSeed() % 900000000); + myGenerator->readString("Random:setSeed on"); + myGenerator->readString("Random:seed " + std::to_string(seed)); + return myGenerator; +} diff --git a/MC/config/PWGLF/pythia8/generator_pythia8_highpt_strangeness.C b/MC/config/PWGLF/pythia8/generator_pythia8_highpt_strangeness.C new file mode 100644 index 000000000..0dd1984d9 --- /dev/null +++ b/MC/config/PWGLF/pythia8/generator_pythia8_highpt_strangeness.C @@ -0,0 +1,103 @@ +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include "FairGenerator.h" +#include "FairPrimaryGenerator.h" +#include "Generators/GeneratorPythia8.h" +#include "Pythia8/Pythia.h" +#include "TDatabasePDG.h" +#include "TMath.h" +#include "TParticlePDG.h" +#include "TRandom3.h" +#include "TSystem.h" +#include "fairlogger/Logger.h" +#include +#include +#include +#include +using namespace Pythia8; +#endif + +/// Pythia8 event generator for pp collisions +/// Selection of events with leading particle (pt>ptThreshold) and containing +/// one strange hadron extracted randomly (K0, Lambda, Xi, Omega) + +class GeneratorPythia8HighPtStrangeness : public o2::eventgen::GeneratorPythia8 { +public: + /// Constructor + GeneratorPythia8HighPtStrangeness(double pt_leading = 5.0) + : o2::eventgen::GeneratorPythia8() + { + fmt::printf(">> Pythia8 generator: ptLeading > %.1f GeV/c\n", pt_leading); + mPt_leading = pt_leading; + } + /// Destructor + ~GeneratorPythia8HighPtStrangeness() = default; + + bool Init() override { + addSubGenerator(0,"Pythia8 events containing high pt particle + strange hadrons"); + return o2::eventgen::GeneratorPythia8::Init(); + } + +protected: + bool generateEvent() override { + fmt::printf(">> Generating event %d\n", mGeneratedEvents); + + bool genOk = false; + int localCounter{0}; + while (!genOk) { + if (GeneratorPythia8::generateEvent()) { + genOk = selectEvent(mPythia.event); + } + localCounter++; + } + fmt::printf(">> Generation of event of interest successful after %i iterations\n",localCounter); + std::cout << std::endl << std::endl; + notifySubGenerator(0); + + mGeneratedEvents++; + + return true; + } + + bool selectEvent(Pythia8::Event &event) { + + int pdg_of_interest[7] = {310, 3122, -3122, 3312, -3312, 3334, -3334}; + int partIndex = static_cast(gRandom->Uniform(0,7)); + + bool contains_particle_of_interest = false; + double pt_max{0}; + for (auto iPart{0}; iPart < event.size(); ++iPart) { + if (std::abs(event[iPart].eta()) > 0.8) { + continue; + } + + if (event[iPart].status() <= 0) { + continue; + } + + if (event[iPart].id() == pdg_of_interest[partIndex]) + contains_particle_of_interest = true; + + if ((!event[iPart].isNeutral()) && event[iPart].pT() > pt_max) + pt_max = event[iPart].pT(); + } + + if (pt_max < mPt_leading) + return false; + + return contains_particle_of_interest; + } + +private: + double mPt_leading = 5.0; + uint64_t mGeneratedEvents = 0; +}; + +///___________________________________________________________ +FairGenerator *generateHighPtAndStrangeHadron(double pt_leading = 5.0) { + + auto myGenerator = new GeneratorPythia8HighPtStrangeness(pt_leading); + auto seed = (gRandom->TRandom::GetSeed() % 900000000); + myGenerator->readString("Random:setSeed on"); + myGenerator->readString("Random:seed " + std::to_string(seed)); + return myGenerator; +} diff --git a/MC/config/PWGLF/pythia8/generator_pythia8_longlived.C b/MC/config/PWGLF/pythia8/generator_pythia8_longlived.C index c72a42561..af071914b 100644 --- a/MC/config/PWGLF/pythia8/generator_pythia8_longlived.C +++ b/MC/config/PWGLF/pythia8/generator_pythia8_longlived.C @@ -16,7 +16,7 @@ class GeneratorPythia8LongLivedGun : public o2::eventgen::GeneratorPythia8 { public: /// constructor - GeneratorPythia8LongLivedGun(int input_pdg, int nInject = 1, float ptMin = 1, float ptMax = 10, int input_pdg2 = -1) : pdg{input_pdg}, nParticles{nInject}, genMinPt{ptMin}, genMaxPt{ptMax}, m{getMass(input_pdg)}, pdg2{input_pdg2} + GeneratorPythia8LongLivedGun(int input_pdg, int nInject = 1, float ptMin = 1, float ptMax = 10, float etaMin = -1.0, float etaMax = 1.0, float phiMin = 0.0, float phiMax = TMath::Pi(), int input_pdg2 = -1) : pdg{input_pdg}, nParticles{nInject}, genMinPt{ptMin}, genMaxPt{ptMax}, genMinEta{etaMin}, genMaxEta{etaMax}, genMinPhi{phiMin}, genMaxPhi{phiMax}, m{getMass(input_pdg)}, pdg2{input_pdg2} { } @@ -51,7 +51,7 @@ public: { const double pt = gRandom->Uniform(genMinPt, genMaxPt); const double eta = gRandom->Uniform(genMinEta, genMaxEta); - const double phi = gRandom->Uniform(0, TMath::TwoPi()); + const double phi = gRandom->Uniform(genMinPhi, genMaxPhi); const double px{pt * std::cos(phi)}; const double py{pt * std::sin(phi)}; const double pz{pt * std::sinh(eta)}; @@ -69,7 +69,7 @@ public: { const double pt = gRandom->Uniform(genMinPt, genMaxPt); const double eta = gRandom->Uniform(genMinEta, genMaxEta); - const double phi = gRandom->Uniform(0, TMath::TwoPi()); + const double phi = gRandom->Uniform(genMinPhi, genMaxPhi); const double px{pt * std::cos(phi)}; const double py{pt * std::sin(phi)}; const double pz{pt * std::sinh(eta)}; @@ -88,8 +88,10 @@ public: private: double genMinPt = 0.5; /// minimum 3-momentum for generated particles double genMaxPt = 12.; /// maximum 3-momentum for generated particles - double genMinEta = -1.; /// minimum pseudorapidity for generated particles - double genMaxEta = +1.; /// maximum pseudorapidity for generated particles + double genMinEta = -1.0; /// minimum pseudorapidity for generated particles + double genMaxEta = +1.0; /// maximum pseudorapidity for generated particles + double genMinPhi = 0.0; /// minimum pseudorapidity for generated particles + double genMaxPhi = TMath::Pi(); /// maximum pseudorapidity for generated particles double m = 0; /// particle mass [GeV/c^2] int pdg = 0; /// particle pdg code @@ -101,7 +103,8 @@ private: }; ///___________________________________________________________ -FairGenerator *generateLongLived(int pdg, int nInject, float ptMin = 1, float ptMax = 10, int pdg2 = -1) +FairGenerator *generateLongLived(int pdg, int nInject, float ptMin = 1, float ptMax = 10, float etaMin = -1.0, float etaMax = 1.0, float phiMin = 0.0, float phiMax = TMath::Pi(), int pdg2 = -1) { - return new GeneratorPythia8LongLivedGun(pdg, nInject, ptMin, ptMax, pdg2); + return new GeneratorPythia8LongLivedGun(pdg, nInject, ptMin, ptMax, etaMin, etaMax, phiMin, phiMax, pdg2); } + diff --git a/MC/config/PWGLF/pythia8/generator_pythia8_longlived_gaptriggered.C b/MC/config/PWGLF/pythia8/generator_pythia8_longlived_gaptriggered.C index ff67e41d9..b17a9fb19 100644 --- a/MC/config/PWGLF/pythia8/generator_pythia8_longlived_gaptriggered.C +++ b/MC/config/PWGLF/pythia8/generator_pythia8_longlived_gaptriggered.C @@ -1,10 +1,13 @@ -#if !defined(__CLING__) || defined(__ROOTCLING__) #include "Pythia8/Pythia.h" +#include "Pythia8/HeavyIons.h" // for event plane angle + +#if !defined(__CLING__) || defined(__ROOTCLING__) #include "FairGenerator.h" #include "FairPrimaryGenerator.h" #include "Generators/GeneratorPythia8.h" #include "fairlogger/Logger.h" +#include "CCDB/BasicCCDBManager.h" #include "TRandom3.h" #include "TParticlePDG.h" #include "TDatabasePDG.h" @@ -21,7 +24,7 @@ class GeneratorPythia8LongLivedGapTriggered : public o2::eventgen::GeneratorPyth { public: /// Constructor - GeneratorPythia8LongLivedGapTriggered(std::vector input_pdg, int input_trigger_ratio = 1, int n_injected = 1, float pt_min = 1, float pt_max = 10, float y_min = -1, float y_max = 1) + GeneratorPythia8LongLivedGapTriggered(std::vector input_pdg, int input_trigger_ratio = 1, int n_injected = 1, float pt_min = 1, float pt_max = 10, float y_min = -1, float y_max = 1, bool addSyntheticFlow = false) { mPdg = input_pdg; setNinjected(n_injected); @@ -31,10 +34,38 @@ public: mMass = getMass(input_pdg); mGeneratedEvents = 0; mAlternatingPDGsign = true; + mAddSyntheticFlow = addSyntheticFlow; + + if(mAddSyntheticFlow){ + lutGen = new o2::eventgen::FlowMapper(); + + // -------- CONFIGURE SYNTHETIC FLOW ------------ + // establish connection to ccdb + o2::ccdb::CcdbApi ccdb_api; + ccdb_api.init("https://alice-ccdb.cern.ch"); + + // config was placed at midpoint of run 544122, retrieve that + std::map metadataRCT, headers; + headers = ccdb_api.retrieveHeaders("RCT/Info/RunInformation/544122", metadataRCT, -1); + int64_t tsSOR = atol(headers["SOR"].c_str()); + int64_t tsEOR = atol(headers["EOR"].c_str()); + int64_t midRun = 0.5*tsSOR+0.5*tsEOR; + + map metadata; // can be empty + auto list = ccdb_api.retrieveFromTFileAny("Users/d/ddobrigk/syntheflow", metadata, midRun); + + TH1D *hv2vspT = (TH1D*) list->FindObject("hFlowVsPt_ins1116150_v1_Table_1"); + TH1D *heccvsb = (TH1D*) list->FindObject("hEccentricityVsB"); + + cout<<"Generating LUT for flow test"<CreateLUT(hv2vspT, heccvsb); + cout<<"Finished creating LUT!"<ExpandPathName(file_name.c_str()); std::ifstream config_file(expanded_file_name); @@ -65,6 +96,34 @@ public: mMass = getMass(mPdg); mGeneratedEvents = 0; mAlternatingPDGsign = true; + mAddSyntheticFlow = addSyntheticFlow; + + if(mAddSyntheticFlow){ + lutGen = new o2::eventgen::FlowMapper(); + + // -------- CONFIGURE SYNTHETIC FLOW ------------ + // establish connection to ccdb + o2::ccdb::CcdbApi ccdb_api; + ccdb_api.init("https://alice-ccdb.cern.ch"); + + // config was placed at midpoint of run 544122, retrieve that + std::map metadataRCT, headers; + headers = ccdb_api.retrieveHeaders("RCT/Info/RunInformation/544122", metadataRCT, -1); + int64_t tsSOR = atol(headers["SOR"].c_str()); + int64_t tsEOR = atol(headers["EOR"].c_str()); + int64_t midRun = 0.5*tsSOR+0.5*tsEOR; + + map metadata; // can be empty + auto list = ccdb_api.retrieveFromTFileAny("Users/d/ddobrigk/syntheflow", metadata, midRun); + + TH1D *hv2vspT = (TH1D*) list->FindObject("hFlowVsPt_ins1116150_v1_Table_1"); + TH1D *heccvsb = (TH1D*) list->FindObject("hEccentricityVsB"); + + cout<<"Generating LUT for flow test"<CreateLUT(hv2vspT, heccvsb); + cout<<"Finished creating LUT!"<phi(); + float impactParameter = mPythia.info.hiInfo->b(); + + for ( Long_t j=0; j < mPythia.event.size(); j++ ) { + float pyphi = mPythia.event[j].phi(); + float pypT = mPythia.event[j].pT(); + + // calculate delta with EP + float deltaPhiEP = pyphi - eventPlaneAngle; + float shift = 0.0; + while(deltaPhiEP<0.0){ + deltaPhiEP += 2*TMath::Pi(); + shift += 2*TMath::Pi(); + } + while(deltaPhiEP>2*TMath::Pi()){ + deltaPhiEP -= 2*TMath::Pi(); + shift -= 2*TMath::Pi(); + } + float newDeltaPhiEP = lutGen->MapPhi(deltaPhiEP, impactParameter, pypT); + float pyphiNew = newDeltaPhiEP - shift + eventPlaneAngle; + + if(pyphiNew>TMath::Pi()) + pyphiNew -= 2.0*TMath::Pi(); + if(pyphiNew<-TMath::Pi()) + pyphiNew += 2.0*TMath::Pi(); + mPythia.event[j].rot(0.0, pyphiNew-pyphi); + } + } + //+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + mGeneratedEvents++; return true; } @@ -169,18 +264,21 @@ private: std::vector mYmax; /// maximum rapidity for generated particles bool mAlternatingPDGsign = true; /// bool to randomize the PDG code of the core particle + bool mAddSyntheticFlow = false; /// switch to add synthetic flow (requires EP angle from PYTHIA) std::vector mNinjected; /// Number of injected particles // Control gap-triggering unsigned long long mGeneratedEvents; /// number of events generated so far int mInverseTriggerRatio; /// injection gap + + o2::eventgen::FlowMapper *lutGen; // for mapping phi angles }; ///___________________________________________________________ -FairGenerator *generateLongLivedGapTriggered(std::vector mPdg, int input_trigger_ratio, int n_injected = 1, float pt_min = 1, float pt_max = 10, float y_min = -1, float y_max = 1, bool alternate_sign = true) +FairGenerator *generateLongLivedGapTriggered(std::vector mPdg, int input_trigger_ratio, int n_injected = 1, float pt_min = 1, float pt_max = 10, float y_min = -1, float y_max = 1, bool alternate_sign = true, bool addSyntheticFlow = false) { - auto myGen = new GeneratorPythia8LongLivedGapTriggered(mPdg, input_trigger_ratio, n_injected, pt_min, pt_max, y_min, y_max); + auto myGen = new GeneratorPythia8LongLivedGapTriggered(mPdg, input_trigger_ratio, n_injected, pt_min, pt_max, y_min, y_max, addSyntheticFlow); myGen->setAlternatingPDGsign(alternate_sign); auto seed = (gRandom->TRandom::GetSeed() % 900000000); myGen->readString("Random:setSeed on"); @@ -189,9 +287,9 @@ FairGenerator *generateLongLivedGapTriggered(std::vector mPdg, int input_tr } ///___________________________________________________________ -FairGenerator *generateLongLivedGapTriggered(std::string config_file_name, int input_trigger_ratio, bool alternate_sign = true) +FairGenerator *generateLongLivedGapTriggered(std::string config_file_name, int input_trigger_ratio, bool alternate_sign = true, bool addSyntheticFlow = false) { - auto myGen = new GeneratorPythia8LongLivedGapTriggered(config_file_name, input_trigger_ratio); + auto myGen = new GeneratorPythia8LongLivedGapTriggered(config_file_name, input_trigger_ratio, addSyntheticFlow); myGen->setAlternatingPDGsign(alternate_sign); auto seed = (gRandom->TRandom::GetSeed() % 900000000); myGen->readString("Random:setSeed on"); diff --git a/MC/config/PWGLF/pythia8/generator_pythia8_phiphi.C b/MC/config/PWGLF/pythia8/generator_pythia8_phiphi.C new file mode 100644 index 000000000..46aa5b75d --- /dev/null +++ b/MC/config/PWGLF/pythia8/generator_pythia8_phiphi.C @@ -0,0 +1,210 @@ +/// generator_phi_resonance.C +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include "FairGenerator.h" +#include "Generators/GeneratorPythia8.h" +#include "Generators/GeneratorPythia8Param.h" +#include "Pythia8/Pythia.h" +#include "TRandom3.h" +#include "TMath.h" +#include "TParticle.h" +#include "TSystem.h" +#if __has_include("SimulationDataFormat/MCGenStatus.h") +#include "SimulationDataFormat/MCGenStatus.h" +#else +#include "SimulationDataFormat/MCGenProperties.h" +#endif +#if __has_include("SimulationDataFormat/MCUtils.h") +#include "SimulationDataFormat/MCUtils.h" +#endif +#include +#include +#include +#endif + +class GeneratorPhiResonance : public o2::eventgen::GeneratorPythia8 +{ +public: + GeneratorPhiResonance(int resoPDG = 999999, + float ptMin = 0.0, float ptMax = 50.0, + float yMin = -1.0, float yMax = 1.0, + std::string pythiaCfgMb = "${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_136tev.cfg", + int signalInterval = 0) + : GeneratorPythia8(), mResoPDG(resoPDG), mPtMin(ptMin), mPtMaxPhiPhi(ptMax), mYMin(yMin), mYMax(yMax), mSignalInterval(signalInterval) + { + + // 1. Define Custom Signal Resonance (PDG: 999999) decay into standard Phis (333 333) + std::string createReso = std::to_string(mResoPDG) + ":new = f2_Custom void 5 0 0 2.714 0.012 2.05 3.50 0.0"; + std::string resoMayDecay = std::to_string(mResoPDG) + ":mayDecay = on"; + std::string addResoDecay = std::to_string(mResoPDG) + ":addChannel = 1 1.0 0 333 333"; + + // Helper lambda to load custom particle definitions across ALL Pythia engines + auto applyCustomParticles = [&](Pythia8::Pythia &pythiaInst) + { + pythiaInst.readString(createReso); + pythiaInst.readString(resoMayDecay); + pythiaInst.readString(addResoDecay); + pythiaInst.readString("333:mayDecay = on"); + pythiaInst.readString("333:oneChannel = 1 1.0 0 321 -321"); + }; + + // 1: Apply particle definitions to mPythia, mPythiaGun, and pythiaObjectMinimumBias + applyCustomParticles(mPythia); + applyCustomParticles(mPythiaGun); + + mPythiaGun.readString("ProcessLevel:all off"); + mPythiaGun.readString("Random:setSeed = on"); + mPythiaGun.readString("Random:seed = " + std::to_string(1 + gRandom->Integer(900000000))); + mPythiaGun.init(); + + // 3. Initialize Minimum Bias Pythia Engine + if (pythiaCfgMb.empty()) + { + auto ¶m = o2::eventgen::GeneratorPythia8Param::Instance(); + pythiaCfgMb = param.config; + } + pythiaCfgMb = gSystem->ExpandPathName(pythiaCfgMb.c_str()); + + if (!pythiaObjectMinimumBias.readFile(pythiaCfgMb)) + { + std::cerr << "Fatal: Could not read MB configuration file: " << pythiaCfgMb << std::endl; + } + pythiaObjectMinimumBias.readString("Random:setSeed = on"); + pythiaObjectMinimumBias.readString("Random:seed = " + std::to_string(1 + gRandom->Integer(900000000))); + + applyCustomParticles(pythiaObjectMinimumBias); + pythiaObjectMinimumBias.init(); + } + + Bool_t generateEvent() override + { + mEventCounter++; + + // 1. Generate Minimum Bias Background Event + bool mbOK = false; + while (!mbOK) + { + mbOK = pythiaObjectMinimumBias.next(); + } + + // 2: Copy MB event using exact pointer binding from generator_pythia8_LF_rapidity_width.C + copyMinimumBiasEventForInjection(); + + // 2. Clear Gun event container + mPythiaGun.event.reset(); + + // 3. Inject Signal Gun Particles into mPythiaGun + if (mSignalInterval > 0 && mEventCounter % mSignalInterval == 0) + { + // Resonant signal -> Decays into 333 333 + injectParticle(mResoPDG, 1); + } + else if (mSignalInterval <= 0) + { + // Continuous signal -> Decays into 333 333 + injectParticle(mResoPDG, 1); + } + + // 4. Force Decay of injected particles using Pythia's Decayer + mPythiaGun.moreDecays(); + mPythiaGun.next(); + + // 3: Index Mapping during Event Merging + int offset = mPythia.event.size(); + std::vector indexMap(mPythiaGun.event.size(), 0); + + for (int i = 1; i < mPythiaGun.event.size(); ++i) + { + indexMap[i] = mPythia.event.size(); + Pythia8::Particle p = mPythiaGun.event[i]; + mPythia.event.append(p); + } + + // Re-link mother and daughter index relationships accurately + for (int i = 1; i < mPythiaGun.event.size(); ++i) + { + int newIdx = indexMap[i]; + Pythia8::Particle &p = mPythia.event[newIdx]; + + int m1 = p.mother1(); + int m2 = p.mother2(); + int d1 = p.daughter1(); + int d2 = p.daughter2(); + + p.mothers((m1 > 0 && m1 < (int)indexMap.size()) ? indexMap[m1] : 0, + (m2 > 0 && m2 < (int)indexMap.size()) ? indexMap[m2] : 0); + + p.daughters((d1 > 0 && d1 < (int)indexMap.size()) ? indexMap[d1] : 0, + (d2 > 0 && d2 < (int)indexMap.size()) ? indexMap[d2] : 0); + } + + // 4: Restore Pythia particleData pointers for O2 exporter + mPythia.event.restorePtrs(); + + return true; + } + +private: + void copyMinimumBiasEventForInjection() + { + mPythia.event = pythiaObjectMinimumBias.event; + mPythia.event.init("Minimum-bias event with injected particles", &mPythia.particleData); + mPythia.event.restorePtrs(); + } + + void injectParticle(int pdg, int nParticles) + { + const double phiMass = 1.019461; + + for (int i = 0; i < nParticles; ++i) + { + const double y = gRandom->Uniform(mYMin, mYMax); + const double phi = gRandom->Uniform(0, TMath::TwoPi()); + + double mass = 0.0; + + do + { + mass = gRandom->BreitWigner(2.714, 0.012); + } while (mass <= 2.0 * phiMass || mass < 2.05 || mass > 3.50); + + double pt; + + pt = gRandom->Uniform(mPtMin, mPtMaxPhiPhi); + + const double px = pt * std::cos(phi); + const double py = pt * std::sin(phi); + const double mT = std::sqrt(mass * mass + pt * pt); + const double pz = mT * std::sinh(y); + const double et = mT * std::cosh(y); + + Pythia8::Particle particle; + particle.id(pdg); + particle.status(11); + particle.m(mass); + particle.px(px); + particle.py(py); + particle.pz(pz); + particle.e(et); + particle.xProd(0.); + particle.yProd(0.); + particle.zProd(0.); + + mPythiaGun.particleData.mayDecay(pdg, true); + mPythiaGun.event.append(particle); + } + } + + int mEventCounter = 0; + int mResoPDG; + int mSignalInterval; + float mPtMin, mPtMaxPhiPhi, mYMin, mYMax; + + Pythia8::Pythia mPythiaGun; + Pythia8::Pythia pythiaObjectMinimumBias; +}; + +/// Entry point for o2-sim +FairGenerator *generatePhiResonanceGun(int resoPDG = 999999, float ptMin = 0.0, float ptMax = 50.0, float yMin = -1.0, float yMax = 1.0, std::string pythiaCfgMb = "", int signalInterval = 0) +{ + return new GeneratorPhiResonance(resoPDG, ptMin, ptMax, yMin, yMax, pythiaCfgMb, signalInterval); +} \ No newline at end of file diff --git a/MC/config/PWGLF/pythia8/generator_pythia8_sigma_hadron.C b/MC/config/PWGLF/pythia8/generator_pythia8_sigma_hadron.C new file mode 100644 index 000000000..c006a66d3 --- /dev/null +++ b/MC/config/PWGLF/pythia8/generator_pythia8_sigma_hadron.C @@ -0,0 +1,199 @@ +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include "FairGenerator.h" +#include "FairPrimaryGenerator.h" +#include "Generators/GeneratorPythia8.h" +#include "Pythia8/Pythia.h" +#include "TDatabasePDG.h" +#include "TMath.h" +#include "TParticlePDG.h" +#include "TRandom3.h" +#include "TSystem.h" +#include "TVector2.h" +#include "fairlogger/Logger.h" +#include +#include +#include +#include +using namespace Pythia8; +#endif + +/// Event of interest: +/// an event that contains at least one Sigma- or Sigma+ +/// paired with at least one hadron of a specific PDG code, +/// and the pair has k* < kStarMax + +class GeneratorPythia8SigmaHadron : public o2::eventgen::GeneratorPythia8 +{ +public: + /// Constructor + GeneratorPythia8SigmaHadron(int hadronPdg, int gapSize = 4, double minSigmaPt = 0.2, double minHadronPt = 0.2, + double maxPt = 10, double maxEta = 0.8, double kStarMax = 1.0) + : o2::eventgen::GeneratorPythia8(), + mHadronPdg(hadronPdg), + mGapSize(gapSize), + mMinSigmaPt(minSigmaPt), + mMinHadronPt(minHadronPt), + mMaxPt(maxPt), + mMaxEta(maxEta), + mKStarMax(kStarMax) + { + fmt::printf( + ">> Pythia8 generator: Sigma± + hadron(PDG=%d), gap = %d, minSigmaPt = %f, minHadronPt = %f, maxPt = %f, |eta| < %f, k* < %f\n", + hadronPdg, gapSize, minSigmaPt, minHadronPt, maxPt, maxEta, kStarMax); + } + + ~GeneratorPythia8SigmaHadron() = default; + + bool Init() override + { + addSubGenerator(0, "Pythia8 events with Sigma± and a specific hadron"); + return o2::eventgen::GeneratorPythia8::Init(); + } + +protected: + /// Check whether particle descends from a Sigma+ or Sigma- + bool isFromSigmaDecay(const Pythia8::Particle& p, const Pythia8::Event& event) + { + int motherId = p.mother1(); + + while (motherId > 0) { + const auto& mother = event[motherId]; + const int absMotherPdg = std::abs(mother.id()); + + if (absMotherPdg == 3112 || absMotherPdg == 3222) { + return true; + } + + motherId = mother.mother1(); + } + + return false; + } + + /// k* of a pair from invariant masses and 4-momenta + /// k* = momentum of either particle in the pair rest frame + double computeKStar(const Pythia8::Particle& p1, const Pythia8::Particle& p2) const + { + const double e = p1.e() + p2.e(); + const double px = p1.px() + p2.px(); + const double py = p1.py() + p2.py(); + const double pz = p1.pz() + p2.pz(); + const double s = e * e - px * px - py * py - pz * pz; + if (s <= 0.) { + return 1.e9; + } + const double m1 = p1.m(); + const double m2 = p2.m(); + const double term1 = s - std::pow(m1 + m2, 2); + const double term2 = s - std::pow(m1 - m2, 2); + const double lambda = term1 * term2; + + if (lambda <= 0.) { + return 0.; + } + return 0.5 * std::sqrt(lambda / s); + } + + bool generateEvent() override + { + fmt::printf(">> Generating event %llu\n", mGeneratedEvents); + + bool genOk = false; + int localCounter{0}; + constexpr int kMaxTries{100000}; + + // Accept mGapSize events unconditionally, then one triggered event + if (mGeneratedEvents % (mGapSize + 1) < mGapSize) { + genOk = GeneratorPythia8::generateEvent(); + fmt::printf(">> Gap-event (no trigger check)\n"); + } else { + while (!genOk && localCounter < kMaxTries) { + if (GeneratorPythia8::generateEvent()) { + genOk = selectEvent(mPythia.event); + } + localCounter++; + } + + if (!genOk) { + fmt::printf("Failed to generate triggered event after %d tries\n", kMaxTries); + return false; + } + + fmt::printf(">> Triggered event: accepted after %d iterations (Sigma± + hadron PDG=%d, k* < %f)\n", + localCounter, mHadronPdg, mKStarMax); + } + + notifySubGenerator(0); + mGeneratedEvents++; + return true; + } + + bool selectEvent(Pythia8::Event& event) + { + std::vector sigmaIndices; + std::vector hadronIndices; + + for (int i = 0; i < event.size(); i++) { + const auto& p = event[i]; + + if (std::abs(p.eta()) > mMaxEta || p.pT() > mMaxPt) { + continue; + } + + const int pdg = p.id(); + const int absPdg = std::abs(pdg); + + // Sigma- or Sigma+ + if ((absPdg == 3112 || absPdg == 3222) && p.pT() >= mMinSigmaPt) { + sigmaIndices.push_back(i); + } + + if (std::abs(pdg) == mHadronPdg && p.pT() >= mMinHadronPt && !isFromSigmaDecay(p, event)) { + hadronIndices.push_back(i); + } + } + if (sigmaIndices.empty() || hadronIndices.empty()) { + return false; + } + + for (const auto iSigma : sigmaIndices) { + for (const auto iHadron : hadronIndices) { + + if (iSigma == iHadron) { + continue; + } + + const auto& sigma = event[iSigma]; + const auto& hadron = event[iHadron]; + + const double kStar = computeKStar(sigma, hadron); + if (kStar < mKStarMax) { + return true; + } + } + } + + return false; + } + +private: + int mHadronPdg{211}; + int mGapSize{4}; + double mMinSigmaPt{0.2}; + double mMinHadronPt{0.2}; + double mMaxPt{10.0}; + double mMaxEta{0.8}; + double mKStarMax{1.0}; + uint64_t mGeneratedEvents{0}; +}; + +///___________________________________________________________ +FairGenerator* generateSigmaHadron(int hadronPdg, int gap = 4, double minSigmaPt = 0.2, double minHadronPt = 0.2, + double maxPt = 10, double maxEta = 0.8, double kStarMax = 1.0) +{ + auto myGenerator = new GeneratorPythia8SigmaHadron(hadronPdg, gap, minSigmaPt, minHadronPt, maxPt, maxEta, kStarMax); + auto seed = (gRandom->TRandom::GetSeed() % 900000000); + myGenerator->readString("Random:setSeed on"); + myGenerator->readString("Random:seed " + std::to_string(seed)); + return myGenerator; +} \ No newline at end of file diff --git a/MC/config/PWGLF/pythia8/generator_pythia8_strangeness_in_jets.C b/MC/config/PWGLF/pythia8/generator_pythia8_strangeness_in_jets.C new file mode 100644 index 000000000..8075d27cb --- /dev/null +++ b/MC/config/PWGLF/pythia8/generator_pythia8_strangeness_in_jets.C @@ -0,0 +1,192 @@ +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include "FairGenerator.h" +#include "FairPrimaryGenerator.h" +#include "Generators/GeneratorPythia8.h" +#include "Pythia8/Pythia.h" +#include "TDatabasePDG.h" +#include "TMath.h" +#include "TParticlePDG.h" +#include "TRandom3.h" +#include "TSystem.h" +#include "TVector2.h" +#include "fairlogger/Logger.h" +#include +#include +#include +#include +using namespace Pythia8; +#endif + +/// Event generator using Pythia ropes for Ξ and Ω production in jets. +/// Jets are defined as particles within a cone around the highest-pT particle +/// (approximate jet axis) and include charged final-state particles that are +/// either physical primaries or from heavy-flavor decays. +/// Jets must be fully within the acceptance and have pT > 10 GeV. +/// One event of interest is generated after 4 minimum-bias events. + +class GeneratorPythia8StrangenessInJet : public o2::eventgen::GeneratorPythia8 { +public: + /// Constructor + GeneratorPythia8StrangenessInJet(double ptJetMin = 10.0, double Rjet = 0.4, int gapSize = 4) + : o2::eventgen::GeneratorPythia8(), + mptJetMin(ptJetMin), + mRjet(Rjet), + mGapSize(gapSize) + { + fmt::printf( + ">> Pythia8 generator: Xi/Omega inside jets with pt_{jet} > %.1f GeV, R_{jet} = %.1f, gap = %d\n", + ptJetMin, Rjet, gapSize); + } + /// Destructor + ~GeneratorPythia8StrangenessInJet() = default; + + bool Init() override { + addSubGenerator(0, "Pythia8 events with Xi/Omega inside jets"); + return o2::eventgen::GeneratorPythia8::Init(); + } + +protected: + /// Check if particle is physical primary or from HF decay + bool isPhysicalPrimaryOrFromHF(const Pythia8::Particle& p, const Pythia8::Event& event) + { + // Select only final-state particles + if (!p.isFinal()) { + return false; + } + + // Particle species selection + const int absPdg = std::abs(p.id()); + if (absPdg!=211 && absPdg!=321 && absPdg!= 2212 && absPdg!=1000010020 && absPdg!=11 && absPdg!=13) + return false; + + // Walk up ancestry + int motherId = p.mother1(); + + while (motherId > 0) { + + // Get mother + const auto& mother = event[motherId]; + const int absMotherPdg = std::abs(mother.id()); + + // Check if particle is from HF decay + if ((absMotherPdg / 100 == 4) || (absMotherPdg / 100 == 5) || (absMotherPdg / 1000 == 4) || (absMotherPdg / 1000 == 5)) { + return true; + } + + // Reject non-physical primary hadrons + if (mother.isHadron() && mother.tau0() > 1.0) { + return false; + } + motherId = mother.mother1(); + } + return true; + } + + // Compute delta phi + double getDeltaPhi(double a1, double a2) + { + double deltaPhi{0.0}; + double phi1 = TVector2::Phi_0_2pi(a1); + double phi2 = TVector2::Phi_0_2pi(a2); + double diff = std::fabs(phi1 - phi2); + + if (diff <= M_PI) + deltaPhi = diff; + if (diff > M_PI) + deltaPhi = 2.0 * M_PI - diff; + + return deltaPhi; + } + + bool generateEvent() override { + fmt::printf(">> Generating event %d\n", mGeneratedEvents); + + bool genOk = false; + int localCounter{0}; + constexpr int kMaxTries{100000}; + + // Accept mGapSize events unconditionally, then one triggered event + if (mGeneratedEvents % (mGapSize + 1) < mGapSize) { + genOk = GeneratorPythia8::generateEvent(); + fmt::printf(">> Gap-trigger accepted event (no strangeness check)\n"); + } else { + while (!genOk && localCounter < kMaxTries) { + if (GeneratorPythia8::generateEvent()) { + genOk = selectEvent(mPythia.event); + } + localCounter++; + } + if (!genOk) { + fmt::printf("Failed to generate triggered event after %d tries\n",kMaxTries); + return false; + } + fmt::printf(">> Event accepted after %d iterations (Xi/Omega in jet)\n", localCounter); + } + + notifySubGenerator(0); + mGeneratedEvents++; + return true; + } + + bool selectEvent(Pythia8::Event &event) { + + double etaJet{-999.0}, phiJet{-999.0}, ptMax{0.0}; + std::vector particleID; + bool containsXiOrOmega{false}; + + for (int i = 0; i < event.size(); i++) { + const auto& p = event[i]; + if (std::abs(p.id()) == 3312 || std::abs(p.id()) == 3334) + containsXiOrOmega = true; + + if (std::abs(p.eta()) > 0.8 || p.pT() < 0.1) continue; + if (!isPhysicalPrimaryOrFromHF(p, event)) continue; + particleID.emplace_back(i); + + if (p.pT() > ptMax) { + ptMax = p.pT(); + etaJet = p.eta(); + phiJet = p.phi(); + } + } + if (ptMax == 0.0) + return false; + if (std::abs(etaJet) + mRjet > 0.8) + return false; + if (!containsXiOrOmega) + return false; + + double ptJet{0.0}; + for (int i = 0 ; i < particleID.size() ; i++) { + int id = particleID[i]; + const auto& p = event[id]; + + double deltaEta = std::abs(p.eta() - etaJet); + double deltaPhi = getDeltaPhi(p.phi(),phiJet); + double deltaR = std::sqrt(deltaEta * deltaEta + deltaPhi * deltaPhi); + if (deltaR < mRjet) { + ptJet += p.pT(); + } + } + if (ptJet < mptJetMin) + return false; + + return true; + } + +private: + double mptJetMin{10.0}; + double mRjet{0.4}; + int mGapSize{4}; + uint64_t mGeneratedEvents{0}; +}; + +///___________________________________________________________ +FairGenerator *generateStrangenessInJets(double ptJet = 10.0, double rJet = 0.4, int gap = 4) { + + auto myGenerator = new GeneratorPythia8StrangenessInJet(ptJet,rJet,gap); + auto seed = (gRandom->TRandom::GetSeed() % 900000000); + myGenerator->readString("Random:setSeed on"); + myGenerator->readString("Random:seed " + std::to_string(seed)); + return myGenerator; +} diff --git a/MC/config/PWGLF/pythia8/generator_pythia8_syntheFlow.C b/MC/config/PWGLF/pythia8/generator_pythia8_syntheFlow.C new file mode 100644 index 000000000..5800c0b31 --- /dev/null +++ b/MC/config/PWGLF/pythia8/generator_pythia8_syntheFlow.C @@ -0,0 +1,107 @@ + +#include "Pythia8/Pythia.h" +#include "Pythia8/HeavyIons.h" +#include "FairGenerator.h" +#include "FairPrimaryGenerator.h" +#include "Generators/GeneratorPythia8.h" +#include "TRandom3.h" +#include "TParticlePDG.h" +#include "TDatabasePDG.h" +#include "CCDB/BasicCCDBManager.h" +#include "TH1F.h" +#include "TH1D.h" + +#include +#include + +class GeneratorPythia8SyntheFlow : public o2::eventgen::GeneratorPythia8 +{ +public: + /// Constructor + GeneratorPythia8SyntheFlow() { + lutGen = new o2::eventgen::FlowMapper(); + + // -------- CONFIGURE SYNTHETIC FLOW ------------ + // establish connection to ccdb + o2::ccdb::CcdbApi ccdb_api; + ccdb_api.init("https://alice-ccdb.cern.ch"); + + // config was placed at midpoint of run 544122, retrieve that + std::map metadataRCT, headers; + headers = ccdb_api.retrieveHeaders("RCT/Info/RunInformation/544122", metadataRCT, -1); + int64_t tsSOR = atol(headers["SOR"].c_str()); + int64_t tsEOR = atol(headers["EOR"].c_str()); + int64_t midRun = 0.5*tsSOR+0.5*tsEOR; + + map metadata; // can be empty + auto list = ccdb_api.retrieveFromTFileAny("Users/d/ddobrigk/syntheflow", metadata, midRun); + + TH1D *hv2vspT = (TH1D*) list->FindObject("hFlowVsPt_ins1116150_v1_Table_1"); + TH1D *heccvsb = (TH1D*) list->FindObject("hEccentricityVsB"); + + cout<<"Generating LUT for flow test"<CreateLUT(hv2vspT, heccvsb); + cout<<"Finished creating LUT!"<phi(); + float impactParameter = mPythia.info.hiInfo->b(); + + for ( Long_t j=0; j < mPythia.event.size(); j++ ) { + float pyphi = mPythia.event[j].phi(); + float pypT = mPythia.event[j].pT(); + + // calculate delta with EP + float deltaPhiEP = pyphi - eventPlaneAngle; + float shift = 0.0; + while(deltaPhiEP<0.0){ + deltaPhiEP += 2*TMath::Pi(); + shift += 2*TMath::Pi(); + } + while(deltaPhiEP>2*TMath::Pi()){ + deltaPhiEP -= 2*TMath::Pi(); + shift -= 2*TMath::Pi(); + } + float newDeltaPhiEP = lutGen->MapPhi(deltaPhiEP, impactParameter, pypT); + float pyphiNew = newDeltaPhiEP - shift + eventPlaneAngle; + + if(pyphiNew>TMath::Pi()) + pyphiNew -= 2.0*TMath::Pi(); + if(pyphiNew<-TMath::Pi()) + pyphiNew += 2.0*TMath::Pi(); + mPythia.event[j].rot(0.0, pyphiNew-pyphi); + } + //+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + return true; + } + +private: + o2::eventgen::FlowMapper *lutGen; // for mapping phi angles +}; + + FairGenerator *generator_syntheFlow() + { + auto generator = new GeneratorPythia8SyntheFlow(); + gRandom->SetSeed(0); + generator->readString("Random:setSeed = on"); + generator->readString("Random:seed =" + std::to_string(gRandom->Integer(900000000 - 2) + 1)); + return generator; + } diff --git a/MC/config/PWGLF/pythia8/generator_pythia8_syntheFlowOO.C b/MC/config/PWGLF/pythia8/generator_pythia8_syntheFlowOO.C new file mode 100644 index 000000000..09fac8287 --- /dev/null +++ b/MC/config/PWGLF/pythia8/generator_pythia8_syntheFlowOO.C @@ -0,0 +1,107 @@ + +#include "Pythia8/Pythia.h" +#include "Pythia8/HeavyIons.h" +#include "FairGenerator.h" +#include "FairPrimaryGenerator.h" +#include "Generators/GeneratorPythia8.h" +#include "TRandom3.h" +#include "TParticlePDG.h" +#include "TDatabasePDG.h" +#include "CCDB/BasicCCDBManager.h" +#include "TH1F.h" +#include "TH1D.h" + +#include +#include + +class GeneratorPythia8SyntheFlowOO : public o2::eventgen::GeneratorPythia8 +{ +public: + /// Constructor + GeneratorPythia8SyntheFlowOO() { + lutGen = new o2::eventgen::FlowMapper(); + + // -------- CONFIGURE SYNTHETIC FLOW ------------ + // establish connection to ccdb + o2::ccdb::CcdbApi ccdb_api; + ccdb_api.init("https://alice-ccdb.cern.ch"); + + // config was placed at midpoint of run 544122, retrieve that + std::map metadataRCT, headers; + headers = ccdb_api.retrieveHeaders("RCT/Info/RunInformation/544122", metadataRCT, -1); + int64_t tsSOR = atol(headers["SOR"].c_str()); + int64_t tsEOR = atol(headers["EOR"].c_str()); + int64_t midRun = 0.5*tsSOR+0.5*tsEOR; + + map metadata; // can be empty + auto list = ccdb_api.retrieveFromTFileAny("Users/d/ddobrigk/syntheflow", metadata, midRun); + + TH1D *hv2vspT = (TH1D*) list->FindObject("hFlowVsPt_ins1116150_v1_Table_1"); + TH1D *heccvsb = (TH1D*) list->FindObject("hEccentricityVsB"); + + cout<<"Generating LUT for flow test"<CreateLUT(hv2vspT, heccvsb); + cout<<"Finished creating LUT!"<phi(); + float impactParameter = mPythia.info.hiInfo->b(); + + for ( Long_t j=0; j < mPythia.event.size(); j++ ) { + float pyphi = mPythia.event[j].phi(); + float pypT = mPythia.event[j].pT(); + + // calculate delta with EP + float deltaPhiEP = pyphi - eventPlaneAngle; + float shift = 0.0; + while(deltaPhiEP<0.0){ + deltaPhiEP += 2*TMath::Pi(); + shift += 2*TMath::Pi(); + } + while(deltaPhiEP>2*TMath::Pi()){ + deltaPhiEP -= 2*TMath::Pi(); + shift -= 2*TMath::Pi(); + } + float newDeltaPhiEP = lutGen->MapPhi(deltaPhiEP, impactParameter, pypT); + float pyphiNew = newDeltaPhiEP - shift + eventPlaneAngle; + + if(pyphiNew>TMath::Pi()) + pyphiNew -= 2.0*TMath::Pi(); + if(pyphiNew<-TMath::Pi()) + pyphiNew += 2.0*TMath::Pi(); + mPythia.event[j].rot(0.0, pyphiNew-pyphi); + } + //+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + return true; + } + +private: + o2::eventgen::FlowMapper *lutGen; // for mapping phi angles +}; + + FairGenerator *generator_syntheFlowOO() + { + auto generator = new GeneratorPythia8SyntheFlowOO(); + gRandom->SetSeed(0); + generator->readString("Random:setSeed = on"); + generator->readString("Random:seed =" + std::to_string(gRandom->Integer(900000000 - 2) + 1)); + return generator; + } diff --git a/MC/config/PWGLF/pythia8/generator_pythia8_syntheFlowXi.C b/MC/config/PWGLF/pythia8/generator_pythia8_syntheFlowXi.C new file mode 100644 index 000000000..8042d58ab --- /dev/null +++ b/MC/config/PWGLF/pythia8/generator_pythia8_syntheFlowXi.C @@ -0,0 +1,343 @@ + +#include "Pythia8/Pythia.h" +#include "Pythia8/HeavyIons.h" +#include "FairGenerator.h" +#include "FairPrimaryGenerator.h" +#include "Generators/GeneratorPythia8.h" +#include "TRandom3.h" +#include "TParticlePDG.h" +#include "TDatabasePDG.h" +#include "CCDB/BasicCCDBManager.h" +#include "TH1F.h" +#include "TH1D.h" + +#include +#include + +class GeneratorPythia8SyntheFlowXi : public o2::eventgen::GeneratorPythia8 +{ +public: + /// Constructor + GeneratorPythia8SyntheFlowXi() { + lutGen = new o2::eventgen::FlowMapper(); + + // -------- CONFIGURE SYNTHETIC FLOW ------------ + // establish connection to ccdb + o2::ccdb::CcdbApi ccdb_api; + ccdb_api.init("https://alice-ccdb.cern.ch"); + + // config was placed at midpoint of run 544122, retrieve that + std::map metadataRCT, headers; + headers = ccdb_api.retrieveHeaders("RCT/Info/RunInformation/544122", metadataRCT, -1); + int64_t tsSOR = atol(headers["SOR"].c_str()); + int64_t tsEOR = atol(headers["EOR"].c_str()); + int64_t midRun = 0.5*tsSOR+0.5*tsEOR; + + map metadata; // can be empty + auto list = ccdb_api.retrieveFromTFileAny("Users/d/ddobrigk/syntheflow", metadata, midRun); + + TH1D *hv2vspT = (TH1D*) list->FindObject("hFlowVsPt_ins1116150_v1_Table_1"); + TH1D *heccvsb = (TH1D*) list->FindObject("hEccentricityVsB"); + + cout<<"Generating LUT for flow test"<CreateLUT(hv2vspT, heccvsb); + cout<<"Finished creating LUT!"<(); + + fSpectrumXi = std::make_unique("fSpectrumXi", this, &GeneratorPythia8SyntheFlowXi::boltzPlusPower, 0., genMaxPt, 5, "GeneratorPythia8ExtraStrangeness", "boltzPlusPower"); + + fSpectrumXi->FixParameter(0, 1.32171); + fSpectrumXi->FixParameter(1, 4.84e-1); + fSpectrumXi->FixParameter(2, 111.9); + fSpectrumXi->FixParameter(3, -2.56511e+00); + fSpectrumXi->FixParameter(4, 1.14011e-04); + + fSpectrumOm = std::make_unique("fSpectrumOm", this, &GeneratorPythia8SyntheFlowXi::boltzPlusPower, 0., genMaxPt, 5, "GeneratorPythia8ExtraStrangeness", "boltzPlusPower"); + + fSpectrumOm->FixParameter(0, 1.67245e+00); + fSpectrumOm->FixParameter(1, 5.18174e-01); + fSpectrumOm->FixParameter(2, 1.73747e+01); + fSpectrumOm->FixParameter(3, -2.56681e+00); + fSpectrumOm->FixParameter(4, 1.87513e-04); + } + + /// Destructor + ~GeneratorPythia8SyntheFlowXi() = default; + + Double_t y2eta(Double_t pt, Double_t mass, Double_t y){ + Double_t mt = TMath::Sqrt(mass * mass + pt * pt); + return TMath::ASinH(mt / pt * TMath::SinH(y)); + } + + /// set 4-momentum + void set4momentum(double input_px, double input_py, double input_pz){ + px = input_px; + py = input_py; + pz = input_pz; + E = sqrt( m*m+px*px+py*py+pz*pz ); + fourMomentum.px(px); + fourMomentum.py(py); + fourMomentum.pz(pz); + fourMomentum.e(E); + p = sqrt( px*px+py*py+pz*pz ); + y = 0.5*log( (E+pz)/(E-pz) ); + eta = 0.5*log( (p+pz)/(p-pz) ); + } + + //__________________________________________________________________ + Pythia8::Particle createParticle(){ + //std::cout << "createParticle() mass " << m << " pdgCode " << pdg << std::endl; + Pythia8::Particle myparticle; + myparticle.id(pdg); + myparticle.status(11); + myparticle.px(px); + myparticle.py(py); + myparticle.pz(pz); + myparticle.e(E); + myparticle.m(m); + myparticle.xProd(xProd); + myparticle.yProd(yProd); + myparticle.zProd(zProd); + + return myparticle; + } + + //_________________________________________________________________________________ + /// generate uniform eta and uniform momentum + void genSpectraMomentumEtaXi(double minP, double maxP, double minY, double maxY){ + // random generator + std::unique_ptr ranGenerator { new TRandom3() }; + ranGenerator->SetSeed(0); + + // generate transverse momentum + const double gen_pT = fSpectrumXi->GetRandom(genMinPt,genMaxPt); + + //Actually could be something else without loss of generality but okay + const double gen_phi = ranGenerator->Uniform(0,2*TMath::Pi()); + + // sample flat in rapidity, calculate eta + Double_t gen_Y=10, gen_eta=10; + + while( gen_eta>genmaxEta || gen_etaUniform(minY,maxY); + //(Double_t pt, Double_t mass, Double_t y) + gen_eta = y2eta(gen_pT, m, gen_Y); + } + + fLVHelper->SetPtEtaPhiM(gen_pT, gen_eta, gen_phi, m); + set4momentum(fLVHelper->Px(),fLVHelper->Py(),fLVHelper->Pz()); + } + + //_________________________________________________________________________________ + /// generate uniform eta and uniform momentum + void genSpectraMomentumEtaOm(double minP, double maxP, double minY, double maxY){ + // random generator + std::unique_ptr ranGenerator { new TRandom3() }; + ranGenerator->SetSeed(0); + + // generate transverse momentum + const double gen_pT = fSpectrumOm->GetRandom(genMinPt,genMaxPt); + + //Actually could be something else without loss of generality but okay + const double gen_phi = ranGenerator->Uniform(0,2*TMath::Pi()); + + // sample flat in rapidity, calculate eta + Double_t gen_Y=10, gen_eta=10; + + while( gen_eta>genmaxEta || gen_etaUniform(minY,maxY); + //(Double_t pt, Double_t mass, Double_t y) + gen_eta = y2eta(gen_pT, m, gen_Y); + } + + fLVHelper->SetPtEtaPhiM(gen_pT, gen_eta, gen_phi, m); + set4momentum(fLVHelper->Px(),fLVHelper->Py(),fLVHelper->Pz()); + } + + //_________________________________________________________________________________ + /// shape function + Double_t boltzPlusPower(const Double_t *x, const Double_t *p) + { + // a plain parametrization. not meant to be physics worthy. + // adjusted to match preliminary 5 TeV shape. + + Double_t pt = x[0]; + Double_t mass = p[0]; + Double_t mt = TMath::Sqrt(pt * pt + mass * mass); + Double_t T = p[1]; + Double_t norm = p[2]; + + Double_t lowptpart = mt * TMath::Exp(-mt / T); + Double_t highptpart = p[4]*TMath::Power(x[0], p[3]); + + Double_t mixup = 1./(1.+TMath::Exp((x[0]-4.5)/.1)); + + //return pt * norm * (mixup * mt * TMath::Exp(-mt / T) + (1.-mixup)*highptpart) ; + return pt * norm * (mt * TMath::Exp(-mt / T) + (1.-mixup)*highptpart) ; + } + + //__________________________________________________________________ + Bool_t generateEvent() override { + + // Generate PYTHIA event + Bool_t lPythiaOK = kFALSE; + while (!lPythiaOK){ + lPythiaOK = mPythia.next(); + } + + //+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // add extra xi/omega content + // characterise event + Long_t nParticles = mPythia.event.size(); + Long_t nChargedParticlesAtMidRap = 0; + Long_t nPionsAtMidRap = 0; + for ( Long_t j=0; j < nParticles; j++ ) { + Int_t pypid = mPythia.event[j].id(); + Float_t pyrap = mPythia.event[j].y(); + Float_t pyeta = mPythia.event[j].eta(); + + // final only + if (!mPythia.event[j].isFinal()) continue; + + if ( TMath::Abs(pyrap) < 0.5 && TMath::Abs(pypid)==211 ) nPionsAtMidRap++; + if ( TMath::Abs(pyeta) < 0.5 && TMath::Abs(mPythia.event[j].charge())>1e-5 ) nChargedParticlesAtMidRap++; + } + + // now we have the multiplicity + //+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // XI ABUNDANCE FIX + //Adjust relative abundance of multi-strange particles by injecting some + Double_t lExpectedXiToPion = TMath::Max(4.74929e-03 - 4.08255e-03*TMath::Exp(-nChargedParticlesAtMidRap/4.76660e+00) - 0.00211334,0.); + Double_t lExpectedXi = 5.0*nPionsAtMidRap*lExpectedXiToPion; // extra rich, factor 5 + Int_t lXiYield = gRandom->Poisson(3*lExpectedXi); //factor 3: fix the rapidity acceptance + m = 1.32171; + pdg = 3312; + cout<<"Adding extra xi: "<Uniform()>0.5?+1:-1; + xProd=0.0; + yProd=0.0; + zProd=0.0; + genSpectraMomentumEtaXi(genMinPt,genMaxPt,genminY,genmaxY); + Pythia8::Particle lAddedParticle = createParticle(); + mPythia.event.append(lAddedParticle); + //lAddedParticles++; + } + //+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + //+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // OMEGA ABUNDANCE FIX + //Adjust relative abundance of multi-strange particles by injecting some + Double_t lExpectedOmegaToPion = TMath::Max(8.55057e-04 - 7.38732e-04*TMath::Exp(-nChargedParticlesAtMidRap/2.40545e+01) - 6.56785e-05,0.); + Double_t lExpectedOmega = 5.0*nPionsAtMidRap*lExpectedOmegaToPion; // extra rich, factor 5 + Int_t lOmegaYield = gRandom->Poisson(3*lExpectedOmega); //factor 3: fix the rapidity acceptance + m = 1.67245; + pdg = 3334; + cout<<"Adding extra omegas: "<Uniform()>0.5?+1:-1; + xProd=0.0; + yProd=0.0; + zProd=0.0; + genSpectraMomentumEtaOm(genMinPt,genMaxPt,genminY,genmaxY); + Pythia8::Particle lAddedParticle = createParticle(); + mPythia.event.append(lAddedParticle); + //lAddedParticles++; + } + //+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + //+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // loop over the entire event record and rotate all particles + // synthetic flow exercise + // first: get event plane + float eventPlaneAngle = mPythia.info.hiInfo->phi(); + float impactParameter = mPythia.info.hiInfo->b(); + + for ( Long_t j=0; j < mPythia.event.size(); j++ ) { + float pyphi = mPythia.event[j].phi(); + float pypT = mPythia.event[j].pT(); + + // calculate delta with EP + float deltaPhiEP = pyphi - eventPlaneAngle; + float shift = 0.0; + while(deltaPhiEP<0.0){ + deltaPhiEP += 2*TMath::Pi(); + shift += 2*TMath::Pi(); + } + while(deltaPhiEP>2*TMath::Pi()){ + deltaPhiEP -= 2*TMath::Pi(); + shift -= 2*TMath::Pi(); + } + float newDeltaPhiEP = lutGen->MapPhi(deltaPhiEP, impactParameter, pypT); + float pyphiNew = newDeltaPhiEP - shift + eventPlaneAngle; + + if(pyphiNew>TMath::Pi()) + pyphiNew -= 2.0*TMath::Pi(); + if(pyphiNew<-TMath::Pi()) + pyphiNew += 2.0*TMath::Pi(); + mPythia.event[j].rot(0.0, pyphiNew-pyphi); + } + //+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + return true; + } + +private: + double genMinPt; /// minimum 3-momentum for generated particles + double genMaxPt; /// maximum 3-momentum for generated particles + double genminY; /// minimum pseudorapidity for generated particles + double genmaxY; /// maximum pseudorapidity for generated particles + double genminEta; + double genmaxEta; + + Pythia8::Vec4 fourMomentum; /// four-momentum (px,py,pz,E) + + double E; /// energy: sqrt( m*m+px*px+py*py+pz*pz ) [GeV/c] + double m; /// particle mass [GeV/c^2] + int pdg; /// particle pdg code + double px; /// x-component momentum [GeV/c] + double py; /// y-component momentum [GeV/c] + double pz; /// z-component momentum [GeV/c] + double p; /// momentum + double y; /// rapidity + double eta; /// pseudorapidity + double xProd; /// x-coordinate position production vertex [cm] + double yProd; /// y-coordinate position production vertex [cm] + double zProd; /// z-coordinate position production vertex [cm] + + std::unique_ptr fLVHelper; + std::unique_ptr fSpectrumXi; + std::unique_ptr fSpectrumOm; + o2::eventgen::FlowMapper *lutGen; // for mapping phi angles +}; + + FairGenerator *generator_syntheFlowXi() + { + auto generator = new GeneratorPythia8SyntheFlowXi(); + gRandom->SetSeed(0); + generator->readString("Random:setSeed = on"); + generator->readString("Random:seed =" + std::to_string(gRandom->Integer(900000000 - 2) + 1)); + return generator; + } diff --git a/MC/config/PWGLF/pythia8/generator_pythia8_twophi_trigger.C b/MC/config/PWGLF/pythia8/generator_pythia8_twophi_trigger.C new file mode 100644 index 000000000..af6366971 --- /dev/null +++ b/MC/config/PWGLF/pythia8/generator_pythia8_twophi_trigger.C @@ -0,0 +1,169 @@ +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include "FairGenerator.h" +#include "FairPrimaryGenerator.h" +#include "Generators/GeneratorPythia8.h" +#include "Pythia8/Pythia.h" +#include "TDatabasePDG.h" +#include "TMath.h" +#include "TParticlePDG.h" +#include "TRandom3.h" +#include "TSystem.h" +#include "TVector2.h" +#include "fairlogger/Logger.h" +#include +#include +#include +#include +using namespace Pythia8; +#endif + +/// Event generator using Pythia ropes (Adapted from task generator_pythia8_doubleLambdas.C) +/// Triggers events containing at least two generated phi(1020) mesons. + +class GeneratorPythia8DoublePhi : public o2::eventgen::GeneratorPythia8 +{ +public: + /// Constructor + GeneratorPythia8DoublePhi(int gapSize = 0, double minPt = 0.0, double maxPt = 100.0, double maxEta = 0.8) + : o2::eventgen::GeneratorPythia8(), + mGapSize(gapSize), + mMinPt(minPt), + mMaxPt(maxPt), + mMaxEta(maxEta) + { + fmt::printf(">> Pythia8 generator: two phi(1020) mesons, gap = %d, minPtPhi = %f, maxPtPhi = %f, |etaPhi| < %f\n", gapSize, minPt, maxPt, maxEta); + } + /// Destructor + ~GeneratorPythia8DoublePhi() = default; + + bool Init() override + { + addSubGenerator(0, "Pythia8 events with two phi(1020) mesons"); + return o2::eventgen::GeneratorPythia8::Init(); + } + +protected: + bool isPhiFromHFDecay(const Pythia8::Particle &p, const Pythia8::Event &event) + { + + // Walk up ancestry + int motherId = p.mother1(); + + while (motherId > 0) + { + // Get mother + const auto &mother = event[motherId]; + const int absMotherPdg = std::abs(mother.id()); + + // Check if particle is from HF decay + if (((absMotherPdg / 100) % 10 == 4) || + ((absMotherPdg / 100) % 10 == 5) || + ((absMotherPdg / 1000) % 10 == 4) || + ((absMotherPdg / 1000) % 10 == 5)) + { + return true; + } + + motherId = mother.mother1(); + } + return false; + } + + bool generateEvent() override + { + // fmt::printf(">> Generating event %d\n", mGeneratedEvents); + + bool genOk = false; + int localCounter{0}; + constexpr int kMaxTries{100000}; + + // If mGapSize <= 0, filter ALL events to contain two phis. + // Otherwise, generate mGapSize gap events before 1 triggered event. + if (mGapSize > 0 && (mGeneratedEvents % (mGapSize + 1) < mGapSize)) + { + genOk = GeneratorPythia8::generateEvent(); + // fmt::printf(">> Gap-event (no phi check)\n"); + } + else + { + while (!genOk && localCounter < kMaxTries) + { + if (GeneratorPythia8::generateEvent()) + { + genOk = selectEvent(mPythia.event); + } + localCounter++; + } + if (!genOk) + { + fmt::printf("Failed to generate triggered event after %d tries\n", kMaxTries); + return false; + } + // fmt::printf(">> Triggered event: event accepted after %d iterations (double phi(1020))\n", localCounter); + } + + notifySubGenerator(0); + mGeneratedEvents++; + return true; + } + + bool selectEvent(Pythia8::Event &event) + { + int nPhi{0}; + + for (int i = 0; i < event.size(); i++) + { + const auto &p = event[i]; + + if (std::abs(p.id()) != 333) + continue; + + if (p.pT() < mMinPt || p.pT() > mMaxPt) + continue; + + if (std::abs(p.eta()) > mMaxEta) + continue; + + if (isPhiFromHFDecay(p, event)) + continue; + + // Avoid double-counting copy/history entries: + // Ensure this is the physical produced phi (e.g. check if its daughter is a copy of itself) + int d1 = p.daughter1(); + int d2 = p.daughter2(); + if (d1 > 0 && d1 == d2 && std::abs(event[d1].id()) == 333) + { + // p decayed into another copy of phi, so skip this intermediate entry + continue; + } + + nPhi++; + } + if (nPhi < 2) + return false; + + return true; + } + +private: + int mGapSize{0}; + double mMinPt{0.0}; + double mMaxPt{100.0}; + double mMaxEta{0.8}; + uint64_t mGeneratedEvents{0}; +}; + +///___________________________________________________________ +FairGenerator *generateDoublePhi(int gap = 0, double minPt = 0.0, double maxPt = 100.0, double maxEta = 0.8) +{ + auto myGenerator = new GeneratorPythia8DoublePhi(gap, minPt, maxPt, maxEta); + + myGenerator->readString("333:onMode = off"); + myGenerator->readString("333:onIfMatch = 321 -321"); + + auto seed = (gRandom->TRandom::GetSeed() % 900000000); + myGenerator->readString("Random:setSeed on"); + myGenerator->readString("Random:seed " + std::to_string(seed)); + + return myGenerator; +} \ No newline at end of file diff --git a/MC/config/PWGLF/pythia8/generator_pythia8_twophi_triggerMassCut.C b/MC/config/PWGLF/pythia8/generator_pythia8_twophi_triggerMassCut.C new file mode 100644 index 000000000..4851840c8 --- /dev/null +++ b/MC/config/PWGLF/pythia8/generator_pythia8_twophi_triggerMassCut.C @@ -0,0 +1,193 @@ +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include "FairGenerator.h" +#include "FairPrimaryGenerator.h" +#include "Generators/GeneratorPythia8.h" +#include "Pythia8/Pythia.h" +#include "TDatabasePDG.h" +#include "TMath.h" +#include "TParticlePDG.h" +#include "TRandom3.h" +#include "TSystem.h" +#include "TVector2.h" +#include "fairlogger/Logger.h" +#include +#include +#include +#include "TLorentzVector.h" +#include +using namespace Pythia8; +#endif + +/// Event generator using Pythia ropes +/// Triggers events containing at least two generated phi(1020) mesons. + +class GeneratorPythia8DoublePhi : public o2::eventgen::GeneratorPythia8 +{ +public: + /// Constructor + GeneratorPythia8DoublePhi(int gapSize = 0, double minPt = 0.0, double maxPt = 100.0, double maxEta = 0.8, double minPairMass = 2.4, double minPairPt = 6.0) + : o2::eventgen::GeneratorPythia8(), + mGapSize(gapSize), + mMinPt(minPt), + mMaxPt(maxPt), + mMaxEta(maxEta), + mMinPairMass(minPairMass), + mMinPairPt(minPairPt) + { + fmt::printf(">> Pythia8 generator: two phi(1020) mesons, gap = %d, minPtPhi = %f, maxPtPhi = %f, |etaPhi| < %f, minPairMass = %f, minPairPt = %f\n", + gapSize, minPt, maxPt, maxEta, minPairMass, minPairPt); + } + /// Destructor + ~GeneratorPythia8DoublePhi() = default; + + bool Init() override + { + addSubGenerator(0, "Pythia8 events with two phi(1020) mesons"); + return o2::eventgen::GeneratorPythia8::Init(); + } + +protected: + bool isPhiFromHFDecay(const Pythia8::Particle &p, const Pythia8::Event &event) + { + + // Walk up ancestry + int motherId = p.mother1(); + + while (motherId > 0) + { + // Get mother + const auto &mother = event[motherId]; + const int absMotherPdg = std::abs(mother.id()); + + // Check if particle is from HF decay + if (((absMotherPdg / 100) % 10 == 4) || + ((absMotherPdg / 100) % 10 == 5) || + ((absMotherPdg / 1000) % 10 == 4) || + ((absMotherPdg / 1000) % 10 == 5)) + { + return true; + } + + motherId = mother.mother1(); + } + return false; + } + + bool generateEvent() override + { + // fmt::printf(">> Generating event %d\n", mGeneratedEvents); + + bool genOk = false; + int localCounter{0}; + constexpr int kMaxTries{300000}; + + // If mGapSize <= 0, filter ALL events to contain two phis. + // Otherwise, generate mGapSize gap events before 1 triggered event. + if (mGapSize > 0 && (mGeneratedEvents % (mGapSize + 1) < mGapSize)) + { + genOk = GeneratorPythia8::generateEvent(); + // fmt::printf(">> Gap-event (no phi check)\n"); + } + else + { + while (!genOk && localCounter < kMaxTries) + { + if (GeneratorPythia8::generateEvent()) + { + genOk = selectEvent(mPythia.event); + } + localCounter++; + } + if (!genOk) + { + fmt::printf("Failed to generate triggered event after %d tries\n", kMaxTries); + return false; + } + fmt::printf(">> Triggered event: event accepted after %d iterations (double phi(1020))\n", localCounter); + } + + notifySubGenerator(0); + mGeneratedEvents++; + return true; + } + + bool selectEvent(Pythia8::Event &event) + { + std::vector phiCandidates; + + for (int i = 0; i < event.size(); i++) + { + const auto &p = event[i]; + + if (std::abs(p.id()) != 333) + continue; + + if (p.pT() < mMinPt || p.pT() > mMaxPt) + continue; + + if (std::abs(p.eta()) > mMaxEta) + continue; + + if (isPhiFromHFDecay(p, event)) + continue; + + // Avoid double-counting copy/history entries: + // Ensure this is the physical produced phi (e.g. check if its daughter is a copy of itself) + int d1 = p.daughter1(); + int d2 = p.daughter2(); + if (d1 > 0 && d1 == d2 && std::abs(event[d1].id()) == 333) + { + // p decayed into another copy of phi, so skip this intermediate entry + continue; + } + + TLorentzVector phi; + phi.SetPtEtaPhiM(p.pT(), p.eta(), p.phi(), p.m()); + + phiCandidates.push_back(phi); + } + if (phiCandidates.size() < 2) + return false; + + // Check all possible phi-phi pairs + for (size_t i = 0; i < phiCandidates.size(); i++) + { + for (size_t j = i + 1; j < phiCandidates.size(); j++) + { + TLorentzVector phiPhi = phiCandidates[i] + phiCandidates[j]; + + double mass = phiPhi.M(); + double pt = phiPhi.Pt(); + + if (mass > mMinPairMass && pt > mMinPairPt) + return true; + } + } + + return false; + } + +private: + int mGapSize{0}; + double mMinPt{0.0}; + double mMaxPt{100.0}; + double mMaxEta{0.8}; + double mMinPairMass{2.4}; + double mMinPairPt{6.0}; + uint64_t mGeneratedEvents{0}; +}; + +///___________________________________________________________ +FairGenerator *generateDoublePhi(int gap = 0, double minPt = 0.0, double maxPt = 100.0, double maxEta = 0.8, double minPairMass = 2.4, double minPairPt = 6.0) +{ + auto myGenerator = new GeneratorPythia8DoublePhi(gap, minPt, maxPt, maxEta, minPairMass, minPairPt); + + myGenerator->readString("333:onMode = off"); + myGenerator->readString("333:onIfMatch = 321 -321"); + + auto seed = (gRandom->TRandom::GetSeed() % 900000000); + myGenerator->readString("Random:setSeed on"); + myGenerator->readString("Random:seed " + std::to_string(seed)); + + return myGenerator; +} \ No newline at end of file diff --git a/MC/config/PWGLF/xsection/g4config_had_x2.in b/MC/config/PWGLF/xsection/g4config_had_x2.in new file mode 100644 index 000000000..de09bbd06 --- /dev/null +++ b/MC/config/PWGLF/xsection/g4config_had_x2.in @@ -0,0 +1,73 @@ +# Geant4 standard configuration based on AliDPG commit 7650a5b + +/control/verbose 2 +/mcVerbose/all 1 +/mcVerbose/geometryManager 1 +/mcVerbose/opGeometryManager 1 +/mcTracking/loopVerbose 1 +/mcVerbose/composedPhysicsList 2 +/mcVerbose/runAction 2 # For looping thresholds control +#/tracking/verbose 1 +#//control/cout/ignoreThreadsExcept 0 + +/mcPhysics/rangeCuts 0.001 mm +/mcRegions/setRangePrecision 5 +/mcTracking/skipNeutrino true +/mcDet/setIsMaxStepInLowDensityMaterials true +/mcDet/setMaxStepInLowDensityMaterials 10 m +/mcMagField/setConstDistance 1 mm +/mcDet/setIsZeroMagField true +/mcControl/useRootRandom true # couple G4 random seed to gRandom + +# optical + +/process/optical/verbose 0 +/process/optical/processActivation Scintillation 0 +/process/optical/processActivation OpWLS 0 +/process/optical/processActivation OpMieHG 0 +/process/optical/cerenkov/setTrackSecondariesFirst false +/mcMagField/stepperType NystromRK4 + +# PAI for TRD +# Geant4 VMC >= v3.2 +/mcPhysics/emModel/setEmModel PAI +/mcPhysics/emModel/setRegions TRD_Gas-mix +/mcPhysics/emModel/setParticles all +/mcPrimaryGenerator/skipUnknownParticles true # don't crash when seeing unknown ion etc. (issue warning) + +# +# Precise Msc for EMCAL +# +# Geant4 VMC >= v3.2 +/mcPhysics/emModel/setEmModel SpecialUrbanMsc +/mcPhysics/emModel/setRegions EMC_Lead$ EMC_Scintillator$ +/mcPhysics/emModel/setParticles e- e+ + +# +# Adding extra lines for fixing tracking bias +# +/mcMagField/setDeltaIntersection 1.0e-05 mm +/mcMagField/setMinimumEpsilonStep 0.5e-05 +/mcMagField/setMaximumEpsilonStep 1.0e-05 +/mcMagField/printParameters + +# Change default parameters for killing looping particles +# +/mcPhysics/useHighLooperThresholds +/mcRun/setLooperThresholdImportantEnergy 100. MeV + +# Define media with the INCLXX physics list; here basically in all ITS media +#/mcVerbose/biasingConfigurationManager 3 +/mcPhysics/biasing/setModel inclxx +/mcPhysics/biasing/setRegions ITS_AIR$ ITS_WATER$ ITS_COPPER$ ITS_KAPTON(POLYCH2)$ ITS_GLUE_IBFPC$ ITS_CERAMIC$ ITS_K13D2U2k$ ITS_K13D2U120$ ITS_F6151B05M$ ITS_M60J3K$ ITS_M55J6K$ ITS_FGS003$ ITS_CarbonFleece$ ITS_PEEKCF30$ ITS_GLUE$ ITS_ALUMINUM$ ITS_INOX304$ ALPIDE_METALSTACK$ ALPIDE_SI$ +/mcPhysics/biasing/setParticles proton neutron pi+ pi- + + +# Scale hadronic cross section +/mcPhysics/setCrossSectionFactor deuteron dInelastic 2. +/mcPhysics/setCrossSectionFactor anti_deuteron anti_deuteronInelastic 2. +/mcPhysics/setCrossSectionFactor triton tInelastic 2. +/mcPhysics/setCrossSectionFactor anti_triton anti_tritonInelastic 2. +/mcPhysics/setCrossSectionFactor He3 He3Inelastic 2. +/mcPhysics/setCrossSectionFactor anti_He3 anti_He3Inelastic 2. + diff --git a/MC/config/PWGUD/external/generator/DecayTablesEvtGen/JPSI.4PRONG.DEC b/MC/config/PWGUD/external/generator/DecayTablesEvtGen/JPSI.4PRONG.DEC new file mode 100644 index 000000000..212e77d3e --- /dev/null +++ b/MC/config/PWGUD/external/generator/DecayTablesEvtGen/JPSI.4PRONG.DEC @@ -0,0 +1,7 @@ +Decay J/psi +0.33 p+ anti-p- pi+ pi- PHSP; +0.33 pi+ pi- pi+ pi- PHSP; +0.33 pi+ pi- K+ K- PHSP; +Enddecay +End + diff --git a/MC/config/PWGUD/external/generator/DecayTablesEvtGen/JPSI.6PRONG.DEC b/MC/config/PWGUD/external/generator/DecayTablesEvtGen/JPSI.6PRONG.DEC new file mode 100644 index 000000000..0e49d1906 --- /dev/null +++ b/MC/config/PWGUD/external/generator/DecayTablesEvtGen/JPSI.6PRONG.DEC @@ -0,0 +1,6 @@ +Decay J/psi +0.5 pi+ pi- pi+ pi- K+ K- PHSP; +0.5 pi+ pi- pi+ pi- pi+ pi- PHSP; +Enddecay +End + diff --git a/MC/config/PWGUD/external/generator/DecayTablesEvtGen/JPSI.EE.DEC b/MC/config/PWGUD/external/generator/DecayTablesEvtGen/JPSI.EE.DEC new file mode 100644 index 000000000..3bfe3a10b --- /dev/null +++ b/MC/config/PWGUD/external/generator/DecayTablesEvtGen/JPSI.EE.DEC @@ -0,0 +1,5 @@ +Decay J/psi +1.000 e+ e- PHOTOS VLL; +Enddecay +End + diff --git a/MC/config/PWGUD/external/generator/DecayTablesEvtGen/JPSI.MUMU.DEC b/MC/config/PWGUD/external/generator/DecayTablesEvtGen/JPSI.MUMU.DEC new file mode 100644 index 000000000..7e5ab4de6 --- /dev/null +++ b/MC/config/PWGUD/external/generator/DecayTablesEvtGen/JPSI.MUMU.DEC @@ -0,0 +1,5 @@ +Decay J/psi +1.000 mu+ mu- PHOTOS VLL; +Enddecay +End + diff --git a/MC/config/PWGUD/external/generator/DecayTablesEvtGen/OMEGA.3PI.DEC b/MC/config/PWGUD/external/generator/DecayTablesEvtGen/OMEGA.3PI.DEC new file mode 100644 index 000000000..c6641a920 --- /dev/null +++ b/MC/config/PWGUD/external/generator/DecayTablesEvtGen/OMEGA.3PI.DEC @@ -0,0 +1,11 @@ +Alias mypi0 pi0 + +Decay omega +1.000 pi- pi+ mypi0 OMEGA_DALITZ; +Enddecay + +Decay mypi0 +#1.000 gamma gamma PHSP; +Enddecay + +End diff --git a/MC/config/PWGUD/external/generator/DecayTablesEvtGen/OPENCHARM.DEC b/MC/config/PWGUD/external/generator/DecayTablesEvtGen/OPENCHARM.DEC new file mode 100644 index 000000000..f18b42332 --- /dev/null +++ b/MC/config/PWGUD/external/generator/DecayTablesEvtGen/OPENCHARM.DEC @@ -0,0 +1,58 @@ +Decay D*+ +1.0 D0 pi+ VSS; +Enddecay + +Decay D*- +1.0 anti-D0 pi- VSS; +Enddecay + +Decay D+ +1.0 K- pi+ pi+ D_DALITZ; #[Reconstructed PDG2011] +Enddecay + +Decay D- +1.0 K+ pi- pi- D_DALITZ; #[Reconstructed PDG2011] +Enddecay + +Decay D0 +1.0 K- pi+ PHSP; #[Reconstructed PDG2011] +Enddecay + +Decay anti-D0 +1.0 K+ pi- PHSP; #[Reconstructed PDG2011] +Enddecay + +Decay Lambda_c+ +1.0 p+ K- pi+ PHSP; #[New mode added] #[Reconstructed PDG2011] +Enddecay + +Decay anti-Lambda_c- +1.0 anti-p- K+ pi- PHSP; #[New mode added] #[Reconstructed PDG2011] +Enddecay + +Decay D_s+ +1.0 phi pi+ SVS; #[Reconstructed PDG2011] +Enddecay + +Decay D_s- +1.0 phi pi- SVS; #[Reconstructed PDG2011] +Enddecay + +Decay phi +1.0 K+ K- VSS; #[Reconstructed PDG2011] +Enddecay + +Decay K*0 +1.0 K+ pi- VSS; +Enddecay + +Decay anti-K*0 +1.0 K- pi+ VSS; +Enddecay + +Decay J/psi +0.5 mu+ mu- PHOTOS VLL; +0.5 e+ e- PHOTOS VLL; +Enddecay + +End diff --git a/MC/config/PWGUD/external/generator/DecayTablesEvtGen/PHI.KK.DEC b/MC/config/PWGUD/external/generator/DecayTablesEvtGen/PHI.KK.DEC new file mode 100644 index 000000000..5fa682ee8 --- /dev/null +++ b/MC/config/PWGUD/external/generator/DecayTablesEvtGen/PHI.KK.DEC @@ -0,0 +1,4 @@ +Decay phi +1.000 K+ K- VSS; +Enddecay +End diff --git a/MC/config/PWGUD/external/generator/DecayTablesEvtGen/PSI2S.EEPIPI.DEC b/MC/config/PWGUD/external/generator/DecayTablesEvtGen/PSI2S.EEPIPI.DEC new file mode 100644 index 000000000..25eea861a --- /dev/null +++ b/MC/config/PWGUD/external/generator/DecayTablesEvtGen/PSI2S.EEPIPI.DEC @@ -0,0 +1,11 @@ +Alias myJ/psi J/psi +## +Decay psi(2S) +1.000 myJ/psi pi+ pi- VVPIPI; +Enddecay + +Decay myJ/psi +1.000 e+ e- PHOTOS VLL; +Enddecay +End + diff --git a/MC/config/PWGUD/external/generator/DecayTablesEvtGen/PSI2S.MUMUPIPI.DEC b/MC/config/PWGUD/external/generator/DecayTablesEvtGen/PSI2S.MUMUPIPI.DEC new file mode 100644 index 000000000..d20651ed5 --- /dev/null +++ b/MC/config/PWGUD/external/generator/DecayTablesEvtGen/PSI2S.MUMUPIPI.DEC @@ -0,0 +1,11 @@ +Alias myJ/psi J/psi +## +Decay psi(2S) +1.000 myJ/psi pi+ pi- VVPIPI; +Enddecay + +Decay myJ/psi +1.000 mu+ mu- PHOTOS VLL; +Enddecay +End + diff --git a/MC/config/PWGUD/external/generator/DecayTablesEvtGen/RHOPRIME.RHOPIPI.DEC b/MC/config/PWGUD/external/generator/DecayTablesEvtGen/RHOPRIME.RHOPIPI.DEC new file mode 100644 index 000000000..777ef942a --- /dev/null +++ b/MC/config/PWGUD/external/generator/DecayTablesEvtGen/RHOPRIME.RHOPIPI.DEC @@ -0,0 +1,15 @@ +Alias myrho0 rho0 +## +Decay rho(2S)0 +1.000 myrho0 pi+ pi- VVPIPI; +Enddecay + +Decay rho(3S)0 +1.000 myrho0 pi+ pi- VVPIPI; +Enddecay + +Decay myrho0 +1.000 pi+ pi- VSS; +Enddecay +End + diff --git a/MC/config/PWGUD/external/generator/DecayTablesEvtGen/TAUTAU.6PI.DEC b/MC/config/PWGUD/external/generator/DecayTablesEvtGen/TAUTAU.6PI.DEC new file mode 100644 index 000000000..bbe75c401 --- /dev/null +++ b/MC/config/PWGUD/external/generator/DecayTablesEvtGen/TAUTAU.6PI.DEC @@ -0,0 +1,7 @@ +Decay tau- +1.0 pi- pi- pi+ nu_tau TAUHADNU -0.108 0.775 0.149 1.364 0.400 1.23 0.4; #[Reconstructed PDG2011] +Enddecay +Decay tau+ +1.0 pi+ pi+ pi- anti-nu_tau TAUHADNU -0.108 0.775 0.149 1.364 0.400 1.23 0.4; #[Reconstructed PDG2011] +Enddecay +End diff --git a/MC/config/PWGUD/external/generator/DecayTablesEvtGen/TAUTAU.ELMU.DEC b/MC/config/PWGUD/external/generator/DecayTablesEvtGen/TAUTAU.ELMU.DEC new file mode 100644 index 000000000..cc0976928 --- /dev/null +++ b/MC/config/PWGUD/external/generator/DecayTablesEvtGen/TAUTAU.ELMU.DEC @@ -0,0 +1,9 @@ +Decay tau- +0.5 e- anti-nu_e nu_tau PHOTOS TAULNUNU; #[Reconstructed PDG2011] +0.5 mu- anti-nu_mu nu_tau PHOTOS TAULNUNU; #[Reconstructed PDG2011] +Enddecay +Decay tau+ +0.5 e+ anti-nu_tau nu_e PHOTOS TAULNUNU; #[Reconstructed PDG2011] +0.5 mu+ anti-nu_tau nu_mu PHOTOS TAULNUNU; #[Reconstructed PDG2011] +Enddecay +End diff --git a/MC/config/PWGUD/external/generator/DecayTablesEvtGen/TAUTAU.ELPI.DEC b/MC/config/PWGUD/external/generator/DecayTablesEvtGen/TAUTAU.ELPI.DEC new file mode 100644 index 000000000..02817cef1 --- /dev/null +++ b/MC/config/PWGUD/external/generator/DecayTablesEvtGen/TAUTAU.ELPI.DEC @@ -0,0 +1,10 @@ +Decay tau- +1.0 e- anti-nu_e nu_tau PHOTOS TAULNUNU; #[Reconstructed PDG2011] +Enddecay +Decay tau+ +0.25 pi+ anti-nu_tau TAUSCALARNU; #[Reconstructed PDG2011] +0.25 pi+ pi0 anti-nu_tau TAUHADNU -0.108 0.775 0.149 1.364 0.400; #[Reconstructed PDG2011] +0.25 pi0 pi0 pi+ anti-nu_tau TAUHADNU -0.108 0.775 0.149 1.364 0.400 1.23 0.4; #[Reconstructed PDG2011] +0.25 anti-nu_tau pi+ pi0 pi0 pi0 PHSP; #[Reconstructed PDG2011] +Enddecay +End diff --git a/MC/config/PWGUD/external/generator/DecayTablesEvtGen/TAUTAU.LN3PI.DEC b/MC/config/PWGUD/external/generator/DecayTablesEvtGen/TAUTAU.LN3PI.DEC new file mode 100644 index 000000000..b8c453c67 --- /dev/null +++ b/MC/config/PWGUD/external/generator/DecayTablesEvtGen/TAUTAU.LN3PI.DEC @@ -0,0 +1,9 @@ +Decay tau- +0.5 e- anti-nu_e nu_tau PHOTOS TAULNUNU; #[Reconstructed PDG2011] +0.5 mu- anti-nu_mu nu_tau PHOTOS TAULNUNU; #[Reconstructed PDG2011] +Enddecay +Decay tau+ +0.5 pi+ pi+ pi- anti-nu_tau TAUHADNU -0.108 0.775 0.149 1.364 0.400 1.23 0.4; #[Reconstructed PDG2011] +0.5 anti-nu_tau pi+ pi- pi+ pi0 PHSP; +Enddecay +End diff --git a/MC/config/PWGUD/external/generator/DecayTablesEvtGen/TAUTAU.LP3PI.DEC b/MC/config/PWGUD/external/generator/DecayTablesEvtGen/TAUTAU.LP3PI.DEC new file mode 100644 index 000000000..8b45742ad --- /dev/null +++ b/MC/config/PWGUD/external/generator/DecayTablesEvtGen/TAUTAU.LP3PI.DEC @@ -0,0 +1,9 @@ +Decay tau- +0.5 pi- pi- pi+ nu_tau TAUHADNU -0.108 0.775 0.149 1.364 0.400 1.23 0.4; #[Reconstructed PDG2011] +0.5 nu_tau pi- pi+ pi- pi0 PHSP; +Enddecay +Decay tau+ +0.5 e+ anti-nu_tau nu_e PHOTOS TAULNUNU; #[Reconstructed PDG2011] +0.5 mu+ anti-nu_tau nu_mu PHOTOS TAULNUNU; #[Reconstructed PDG2011] +Enddecay +End diff --git a/MC/config/PWGUD/external/generator/DecayTablesEvtGen/TAUTAU.PN3PI.DEC b/MC/config/PWGUD/external/generator/DecayTablesEvtGen/TAUTAU.PN3PI.DEC new file mode 100644 index 000000000..a23f6bd84 --- /dev/null +++ b/MC/config/PWGUD/external/generator/DecayTablesEvtGen/TAUTAU.PN3PI.DEC @@ -0,0 +1,9 @@ +Decay tau- +0.5 pi- nu_tau TAUSCALARNU; #[Reconstructed PDG2011] +0.5 pi- pi0 nu_tau TAUHADNU -0.108 0.775 0.149 1.364 0.400; #[Reconstructed PDG2011] +Enddecay +Decay tau+ +0.5 pi+ pi+ pi- anti-nu_tau TAUHADNU -0.108 0.775 0.149 1.364 0.400 1.23 0.4; #[Reconstructed PDG2011] +0.5 anti-nu_tau pi+ pi- pi+ pi0 PHSP; +Enddecay +End diff --git a/MC/config/PWGUD/external/generator/DecayTablesEvtGen/TAUTAU.POPI.DEC b/MC/config/PWGUD/external/generator/DecayTablesEvtGen/TAUTAU.POPI.DEC new file mode 100644 index 000000000..9a703958d --- /dev/null +++ b/MC/config/PWGUD/external/generator/DecayTablesEvtGen/TAUTAU.POPI.DEC @@ -0,0 +1,10 @@ +Decay tau+ +1.0 e+ nu_e anti-nu_tau PHOTOS TAULNUNU; #[Reconstructed PDG2011] +Enddecay +Decay tau- +0.25 pi- nu_tau TAUSCALARNU; #[Reconstructed PDG2011] +0.25 pi- pi0 nu_tau TAUHADNU -0.108 0.775 0.149 1.364 0.400; #[Reconstructed PDG2011] +0.25 pi0 pi0 pi- nu_tau TAUHADNU -0.108 0.775 0.149 1.364 0.400 1.23 0.4; #[Reconstructed PDG2011] +0.25 nu_tau pi- pi0 pi0 pi0 PHSP; #[Reconstructed PDG2011] +Enddecay +End diff --git a/MC/config/PWGUD/external/generator/DecayTablesEvtGen/TAUTAU.PP3PI.DEC b/MC/config/PWGUD/external/generator/DecayTablesEvtGen/TAUTAU.PP3PI.DEC new file mode 100644 index 000000000..76bc14f80 --- /dev/null +++ b/MC/config/PWGUD/external/generator/DecayTablesEvtGen/TAUTAU.PP3PI.DEC @@ -0,0 +1,9 @@ +Decay tau- +0.5 pi- pi- pi+ nu_tau TAUHADNU -0.108 0.775 0.149 1.364 0.400 1.23 0.4; #[Reconstructed PDG2011] +0.5 nu_tau pi- pi+ pi- pi0 PHSP; +Enddecay +Decay tau+ +0.5 pi+ anti-nu_tau TAUSCALARNU; #[Reconstructed PDG2011] +0.5 pi+ pi0 anti-nu_tau TAUHADNU -0.108 0.775 0.149 1.364 0.400; #[Reconstructed PDG2011] +Enddecay +End diff --git a/MC/config/PWGUD/external/generator/GeneratorGraniitti.C b/MC/config/PWGUD/external/generator/GeneratorGraniitti.C new file mode 100644 index 000000000..6a8c060f4 --- /dev/null +++ b/MC/config/PWGUD/external/generator/GeneratorGraniitti.C @@ -0,0 +1,125 @@ +namespace o2 +{ +namespace eventgen +{ +class GeneratorGraniitti_class : public Generator +{ + public: + GeneratorGraniitti_class() { }; + ~GeneratorGraniitti_class() = default; + bool setJsonFile(std::string fname) { + jsonFile = fname; + // check if jsonFile exists + if (gSystem->AccessPathName(jsonFile.c_str())) { + return false; + } + + return setHepMCFile(); + } + + bool setHepMCFile() { + // item "OUTPUT" defines the hepmcFile + // find + // "OUTPUT" : hepmcFile + std::string cmd = "grep \"OUTPUT\" "+jsonFile; + auto res = gSystem->GetFromPipe(cmd.c_str()); + auto lines = res.Tokenize("\n"); + if (lines->GetEntries() != 1) { + return false; + } + + auto parts = ((TObjString*)lines->At(0))->GetString().Tokenize(":"); + if (parts->GetEntries() != 2) { + return false; + } + + auto fname = ((TObjString*)parts->At(1))->GetString(); + hepmcFile = std::string(gSystem->Getenv("PWD"))+"/"+(std::string)fname.ReplaceAll("\"", "").ReplaceAll(",", "").ReplaceAll(" ", "")+".hepmc3"; + return true; + } + + bool createHepMCFile() { + // run graniitti with + // jsonFile as input + auto cmd = "$Graniitti_ROOT/bin/gr -i " + jsonFile; + std::cout << "Generating events ..."; + auto res = gSystem->GetFromPipe(cmd.c_str()); + std::cout << "done!\n"; + + // check res to be ok + + return true; + } + + void openHepMCFile() { + reader = new o2::eventgen::GeneratorHepMC(); + reader->setFileNames(hepmcFile); + if (!reader->Init()) + { + std::cout << "GenerateEvent: Graniitti class/object not properly initialized" + << std::endl; + } + }; + + bool generateEvent() override { + if (reader->generateEvent()) { + return true; + } else { + std::cout << "New file needs to be generated."; + delete reader; + createHepMCFile(); + openHepMCFile(); + return reader->generateEvent(); + } + }; + + bool importParticles() override { + mParticles.clear(); + if (!reader->importParticles()) { + return false; + } + for (auto part : reader->getParticles()) { + TParticle particle(part.GetPdgCode(), + 1, + part.GetFirstMother(), + -1, + part.GetFirstDaughter(), + part.GetLastDaughter(), + part.Px(), + part.Py(), + part.Pz(), + part.Energy(), + 0., + 0., + 0., + 0.); + mParticles.push_back(particle); + o2::mcutils::MCGenHelper::encodeParticleStatusAndTracking( + mParticles.back(), true); + } + return true; + }; + + private: + o2::eventgen::GeneratorHepMC *reader = 0x0; + std::string jsonFile; + std::string hepmcFile; + +}; + +} // namespace eventgen +} // namespace o2 + +FairGenerator* GeneratorGraniitti(std::string jsonFile) { + + // create generator + auto gen = new o2::eventgen::GeneratorGraniitti_class(); + if (gen->setJsonFile(jsonFile)) { + if (gen->createHepMCFile()) { + // preparing reader + gen->openHepMCFile(); + } + } + + return gen; +} diff --git a/MC/config/PWGUD/external/generator/GeneratorStarlight.C b/MC/config/PWGUD/external/generator/GeneratorStarlight.C new file mode 100644 index 000000000..e7365efc7 --- /dev/null +++ b/MC/config/PWGUD/external/generator/GeneratorStarlight.C @@ -0,0 +1,397 @@ +R__LOAD_LIBRARY(libDPMJET.so) +R__LOAD_LIBRARY(libDpmJetLib.so) +R__LOAD_LIBRARY(libStarlib.so) +R__ADD_INCLUDE_PATH($STARlight_ROOT/include) + +#include "randomgenerator.h" +#include "upcXevent.h" +#include "upcevent.h" +#include "starlight.h" +#include "inputParameters.h" + +// usage: o2-sim -g external --configKeyValues 'GeneratorExternal.fileName=GeneratorStarlight.C;GeneratorExternal.funcName=GeneratorStarlight("kCohJpsiToMu")' + +unsigned int generateRandomSeed() { + // Use high-resolution clock for time-based seed + auto timeNow = std::chrono::high_resolution_clock::now(); + auto timeSeed = static_cast(timeNow.time_since_epoch().count()); + + // Random device for system entropy + std::random_device rd; + + // Add process ID and thread ID for additional entropy + unsigned int pid = static_cast(getpid()); + unsigned int tid = static_cast(std::hash()(std::this_thread::get_id())); + + // Combine all entropy sources + unsigned int seed = timeSeed ^ (rd() << 1) ^ (pid << 2) ^ (tid << 3); + return seed; +} + +namespace o2 +{ +namespace eventgen +{ +class GeneratorStarlight_class : public Generator +{ + public: + GeneratorStarlight_class(){}; + ~GeneratorStarlight_class() = default; + + void setupDpmjet(std::string dpmjetconf){ + if(dpmjetconf.size() == 0)return; + //Copy necesary files to the working directory + TString pathDPMJET = gSystem->ExpandPathName("$DPMJET_ROOT/dpmdata"); + system(TString::Format("cp -r %s .",pathDPMJET.Data())); + system(TString::Format("cp %s ./my.input",dpmjetconf.c_str())); + + //Reset four seeds of the DPMJET random generator in the config + std::mt19937 gen(generateRandomSeed()); + std::uniform_int_distribution<> dist(1, 168); + + std::string command = "awk -i inplace -v nums=\""; + for (int i = 0; i < 4; ++i)command += TString::Format("%d.0 ", dist(gen)); + command +=" \" \' "; + command += "BEGIN {split(nums, newvals);}"; + command += "{if ($1 == \"RNDMINIT\") {printf \"%-16s%-9s%-9s%-9s%-9s\\n\", $1, newvals[1], newvals[2], newvals[3], newvals[4];}"; + command += " else {print $0;}}\' \"my.input\" "; + system(command.c_str()); + } + + void selectConfiguration(std::string val) { mSelectedConfiguration = val; }; + void setExtraParams(std::string val) { mExtraParams = val; }; + void setCollisionSystem(float energyCM, int beam1Z, int beam1A, int beam2Z, int beam2A) {eCM = energyCM; projZ=beam1Z; projA=beam1A; targZ=beam2Z; targA=beam2A;}; + bool setParameter(std::string line) { + if (not mInputParameters.setParameter(line)){ + std::cout << " --- [ERROR] cannot set parameter: " << line << std::endl; + return false; + } + return true; + } + int getPdgMother(){return mPdgMother;} + double getPhotonEnergy(){ + //std::cout << mEvent.getGamma().gamma.GetE() << std::endl; + return mEvent.getGamma().gamma.GetE(); + } + + bool Init() override + { + Generator::Init(); + + float beam1energy = TMath::Sqrt(Double_t(projZ)/projA*targA/targZ)*eCM/2; + float beam2energy = TMath::Sqrt(Double_t(projA)/projZ*targZ/targA)*eCM/2; + float gamma1 = beam1energy/0.938272; + float gamma2 = beam2energy/0.938272; + float rapMax = 4.1 + 0.5*(TMath::ACosH(gamma2)-TMath::ACosH(gamma1)); + float dy = 0.01; + + const struct SLConfig { + const char* name; + int prod_mode; + int prod_pid; + int nwbins; + float wmin; + float wmax; + int pdg_mother; + bool decay_EvtGen; + } slConfig[] = { + {"kTwoGammaToMuLow", 1, 13, 876, 0.4, 15.0, -1, 0 }, // from 0.4 to 15 GeV + {"kTwoGammaToElLow", 1, 11, 876, 0.4, 15.0, -1, 0 }, // from 0.4 to 15 GeV + {"kTwoGammaToMuMedium", 1, 13, 792, 1.8, 15.0, -1, 0 }, // from 1.8 to 15 GeV + {"kTwoGammaToElMedium", 1, 11, 792, 1.8, 15.0, -1, 0 }, // from 1.8 to 15 GeV + {"kTwoGammaToMuHigh", 1, 13, 660, 4.0, 15.0, -1, 0 }, // from 4.0 to 15 GeV + {"kTwoGammaToElHigh", 1, 11, 660, 4.0, 15.0, -1, 0 }, // from 4.0 to 15 GeV + {"kTwoGammaToRhoRho", 1, 33, 20, -1.0, -1.0, -1, 0 }, // + {"kTwoGammaToF2", 1, 225, 20, -1.0, -1.0, -1, 0 }, // + {"kCohRhoToPi", 3, 113, 1200, -1.0, -1.0, 113, 0 }, // + {"kCohRhoToElEl", 3, 113011, 1200, -1.0, -1.0, 113, 0 }, // + {"kCohRhoToMuMu", 3, 113013, 1200, -1.0, -1.0, 113, 0 }, // + {"kCohRhoToPiWithCont", 3, 913, 1200, -1.0, -1.0, 113, 0 }, // + {"kCohRhoToPiFlat", 3, 113, 1, -1.0, 2.5, 113, 0 }, // + {"kCohPhiToKa", 2, 333, 20, -1.0, -1.0, 333, 0 }, // + {"kCohPhiToEl", 2, 333011, 20, -1.0, -1.0, 333, 0 }, // + {"kDirectPhiToKaKa", 3, 933, 20, -1.0, -1.0, 333, 0 }, // + {"kCohOmegaTo2Pi", 2, 223, 20, -1.0, -1.0, 223, 0 }, // + {"kCohOmegaTo3Pi", 2, 223, 20, -1.0, -1.0, 223, 1 }, // + {"kCohOmegaToPiPiPi", 2, 223211111, 20, -1.0, -1.0, 233, 0 }, // + {"kCohRhoPrimeTo4Pi", 3, 999, 1200, -1.0, 5.0, 30113, 0 }, // + {"kCohJpsiToMu", 2, 443013, 20, -1.0, -1.0, 443, 0 }, // + {"kCohJpsiToEl", 2, 443011, 20, -1.0, -1.0, 443, 0 }, // + {"kCohJpsiToElRad", 2, 443011, 20, -1.0, -1.0, 443, 1 }, // + {"kCohJpsiToProton", 2, 4432212, 20, -1.0, -1.0, 443, 0 }, // + {"kCohJpsiToLLbar", 2, 4433122, 20, -1.0, -1.0, 443, 0 }, // + {"kCohJpsi4Prong", 2, 443013, 20, -1.0, -1.0, 443, 1 }, // + {"kCohJpsi6Prong", 2, 443013, 20, -1.0, -1.0, 443, 1 }, // + {"kCohPsi2sToMu", 2, 444013, 20, -1.0, -1.0, 100443, 0 }, // + {"kCohPsi2sToEl", 2, 444011, 20, -1.0, -1.0, 100443, 0 }, // + {"kCohPsi2sToMuPi", 2, 444013, 20, -1.0, -1.0, 100443, 1 }, // + {"kCohPsi2sToElPi", 2, 444011, 20, -1.0, -1.0, 100443, 1 }, // + {"kCohUpsilon1SToMu", 2, 553013, 20, -1.0, -1.0, 553, 0 }, // + {"kCohUpsilon1SToEl", 2, 553011, 20, -1.0, -1.0, 553, 0 }, // + {"kCohUpsilon2SToMu", 2, 554013, 20, -1.0, -1.0, 554, 0 }, // + {"kCohUpsilon2SToEl", 2, 554011, 20, -1.0, -1.0, 554, 0 }, // + {"kCohUpsilon3SToMu", 2, 555013, 20, -1.0, -1.0, 555, 0 }, // + {"kCohUpsilon3SToEl", 2, 555011, 20, -1.0, -1.0, 555, 0 }, // + {"kIncohRhoToPi", 4, 113, 1200, -1.0, -1.0, 113, 0 }, // + {"kIncohRhoToElEl", 4, 113011, 1200, -1.0, -1.0, 113, 0 }, // + {"kIncohRhoToMuMu", 4, 113013, 1200, -1.0, -1.0, 113, 0 }, // + {"kIncohRhoToPiWithCont",4, 913, 1200, -1.0, -1.0, 113, 0 }, // + {"kIncohRhoToPiFlat", 4, 113, 1, -1.0, 2.5, 113, 0 }, // + {"kIncohPhiToKa", 4, 333, 20, -1.0, -1.0, 333, 0 }, // + {"kIncohOmegaTo2Pi", 4, 223, 20, -1.0, -1.0, 223, 0 }, // + {"kIncohOmegaTo3Pi", 4, 223, 20, -1.0, -1.0, 223, 1 }, // + {"kIncohOmegaToPiPiPi", 4, 223211111, 20, -1.0, -1.0, 223, 0 }, // + {"kIncohRhoPrimeTo4Pi", 4, 999, 1200, -1.0, 5.0, 30113, 0 }, // + {"kIncohJpsiToMu", 4, 443013, 20, -1.0, -1.0, 443, 0 }, // + {"kIncohJpsiToEl", 4, 443011, 20, -1.0, -1.0, 443, 0 }, // + {"kIncohJpsiToElRad", 4, 443011, 20, -1.0, -1.0, 443, 1 }, // + {"kIncohJpsiToProton", 4, 4432212, 20, -1.0, -1.0, 443, 0 }, // + {"kIncohJpsiToLLbar", 4, 4433122, 20, -1.0, -1.0, 443, 0 }, // + {"kIncohPsi2sToMu", 4, 444013, 20, -1.0, -1.0, 100443, 0 }, // + {"kIncohPsi2sToEl", 4, 444011, 20, -1.0, -1.0, 100443, 0 }, // + {"kIncohPsi2sToMuPi", 4, 444013, 20, -1.0, -1.0, 100443, 1 }, // + {"kIncohPsi2sToElPi", 4, 444011, 20, -1.0, -1.0, 100443, 1 }, // + {"kIncohUpsilon1SToMu", 4, 553013, 20, -1.0, -1.0, 553, 0 }, // + {"kIncohUpsilon1SToEl", 4, 553011, 20, -1.0, -1.0, 553, 0 }, // + {"kIncohUpsilon2SToMu", 4, 554013, 20, -1.0, -1.0, 554, 0 }, // + {"kIncohUpsilon2SToEl", 4, 554011, 20, -1.0, -1.0, 554, 0 }, // + {"kIncohUpsilon3SToMu", 4, 555013, 20, -1.0, -1.0, 555, 0 }, // + {"kIncohUpsilon3SToEl", 4, 555011, 20, -1.0, -1.0, 555, 0 }, // + {"kDpmjetSingleA", 5, 113, 20, -1.0, -1.0, -1, 0 }, // + {"kDpmjetSingleC", 5, 113, 20, -1.0, -1.0, -1, 0 }, // + {"kTauLowToL+3Pi", 1, 15, 990, 3.5, 20.0, -1, 1 }, // from 0.4 to 15 GeV + {"kTauLowToL-3Pi", 1, 15, 990, 3.5, 20.0, -1, 1 }, // from 0.4 to 15 GeV + {"kTauLowToPi+3Pi", 1, 15, 990, 3.5, 20.0, -1, 1 }, // from 0.4 to 15 GeV + {"kTauLowToPi-3Pi", 1, 15, 990, 3.5, 20.0, -1, 1 }, // from 0.4 to 15 GeV + {"kTauLowTo6Pi", 1, 15, 990, 3.5, 20.0, -1, 1 }, // from 0.4 to 15 GeV + {"kTauLowToElMu", 1, 15, 990, 3.5, 20.0, -1, 1 }, // from 0.4 to 15 GeV + {"kTauLowToElPiPi0", 1, 15, 990, 3.5, 20.0, -1, 1 }, // from 0.4 to 15 GeV + {"kTauLowToPoPiPi0", 1, 15, 990, 3.5, 20.0, -1, 1 }, // from 0.4 to 15 GeV + }; + + const int nProcess = sizeof(slConfig)/sizeof(SLConfig); + int idx = -1; + for (int i=0; isetInputParameters(&mInputParameters); + mRandomGenerator.SetSeed(mInputParameters.randomSeed()); + mStarLight->setRandomGenerator(&mRandomGenerator); + return mStarLight->init(); + + }; + + bool generateEvent() override { + + if (!mStarLight) { + std::cout <<"GenerateEvent: StarLight class/object not properly constructed"<= 5) { + mUpcEvent = mStarLight->produceUpcEvent(); + mUpcEvent.boost(0.5*(TMath::ACosH(mInputParameters.beam1LorentzGamma()) - TMath::ACosH(mInputParameters.beam2LorentzGamma()))); + } + + mEvent = mStarLight->produceEvent(); + // boost event to the experiment CM frame + mEvent.boost(0.5*(TMath::ACosH(mInputParameters.beam1LorentzGamma()) - TMath::ACosH(mInputParameters.beam2LorentzGamma()))); + + return true; + + }; + + // at importParticles we add particles to the output particle vector + // according to the selected configuration + bool importParticles() override + { + int nVtx(0); + float vtx(0), vty(0), vtz(0), vtt(0); + const std::vector* slVtx; + const std::vector* slPartArr; + int npart = 0; + + if (mInputParameters.interactionType() >= 5) { + slVtx = mUpcEvent.getVertices(); + slPartArr = mUpcEvent.getParticles(); + npart = mUpcEvent.getParticles()->size(); + } + else{ + slVtx = mEvent.getVertices(); + slPartArr = mEvent.getParticles(); + npart = mEvent.getParticles()->size(); + } + + if (slVtx == 0) { // not vertex assume 0,0,0,0; + vtx = vty = vtz = vtt = 0.0; + } + else { // a vertex exits + nVtx = slVtx->size(); + } // end if + + if(mPdgMother != -1){ //Reconstruct mother particle for VM processes + TLorentzVector lmoth; + TLorentzVector ldaug; + for(int ipart=0;ipartat(ipart))); + ldaug.SetPxPyPzE(slPart->GetPx(), slPart->GetPy(), slPart->GetPz(), slPart->GetE()); + lmoth += ldaug; + } + TParticle particle(mPdgMother, + 11, + -1, + -1, + 1, + npart, + lmoth.Px(), + lmoth.Py(), + lmoth.Pz(), + lmoth.E(), + 0,0,0,0); + //particle.Print(); + mParticles.push_back(particle); + o2::mcutils::MCGenHelper::encodeParticleStatusAndTracking(mParticles.back(), false); + } + if(!mDecayEvtGen || mPdgMother == -1){ // Don't import daughters in case of external decayer + for(int ipart=0;ipartat(ipart))); + if (nVtx < 1) { // No verticies + vtx = vty = vtz = vtt = 0.0; + } else { + vtx = (slVtx->at((ipart < nVtx ? ipart : 0))).X(); + vty = (slVtx->at((ipart < nVtx ? ipart : 0))).Y(); + vtz = (slVtx->at((ipart < nVtx ? ipart : 0))).Z(); + vtt = 0.0; // no time given. + } // end if + TParticle particle(slPart->getPdgCode(), + 1, + (mPdgMother != -1 ? 0 :-1), + -1, + -1, + -1, + slPart->GetPx(), + slPart->GetPy(), + (mSelectedConfiguration.compare("kDpmjetSingleC") == 0 ? -1.0*slPart->GetPz() : slPart->GetPz()), + slPart->GetE(), + vtx,vty,vtz,vtt); + //particle.Print(); + mParticles.push_back(particle); + o2::mcutils::MCGenHelper::encodeParticleStatusAndTracking(mParticles.back(), true); + } + } + return true; + } + + //Store couple of event informations to the MC event header + void updateHeader(o2::dataformats::MCEventHeader* eventHeader)override + { + using Key = o2::dataformats::MCInfoKeys; + + eventHeader->putInfo(Key::generator, "STARlight"); + eventHeader->putInfo(Key::processName, mSelectedConfiguration); + eventHeader->putInfo("photonEnergy", getPhotonEnergy()); + } + + protected: + float eCM = 5020; //CMS energy + int projA=208; //Beam + int targA=208; + int projZ=82; + int targZ=82; + + private: + starlight *mStarLight = 0x0; + inputParameters mInputParameters; // simulation input information. + randomGenerator mRandomGenerator; // STARLIGHT's own random generator + upcXEvent mEvent; // object holding STARlight simulated event. + upcEvent mUpcEvent; + std::string mSelectedConfiguration = ""; + std::string mExtraParams = ""; + int mPdgMother = -1; + bool mDecayEvtGen = 0; + + + }; + +} // namespace eventgen +} // namespace o2 + + +FairGenerator* + GeneratorStarlight(std::string configuration = "empty",float energyCM = 5020, int beam1Z = 82, int beam1A = 208, int beam2Z = 82, int beam2A = 208, std::string extrapars = "",std::string dpmjetconf = "") +{ + + auto gen = new o2::eventgen::GeneratorStarlight_class(); + gen->selectConfiguration(configuration); + gen->setCollisionSystem(energyCM, beam1Z, beam1A, beam2Z, beam2A); + gen->setExtraParams(extrapars); + gen->setupDpmjet(dpmjetconf); + return gen; +} + + + + diff --git a/MC/config/PWGUD/external/generator/GeneratorStarlightToEvtGen.C b/MC/config/PWGUD/external/generator/GeneratorStarlightToEvtGen.C new file mode 100644 index 000000000..6a4788302 --- /dev/null +++ b/MC/config/PWGUD/external/generator/GeneratorStarlightToEvtGen.C @@ -0,0 +1,65 @@ +R__ADD_INCLUDE_PATH($O2DPG_MC_CONFIG_ROOT/MC/config/PWGDQ/EvtGen) +R__ADD_INCLUDE_PATH($O2DPG_MC_CONFIG_ROOT/MC/config/PWGUD/external/generator) +#include "GeneratorEvtGen.C" +#include "GeneratorStarlight.C" + +FairGenerator* + GeneratorStarlightToEvtGen(std::string configuration = "empty",float energyCM = 5020, int beam1Z = 82, int beam1A = 208, int beam2Z = 82, int beam2A = 208, std::string extrapars = "", std::string dpmjetconf = "") +{ + auto gen = new o2::eventgen::GeneratorEvtGen(); + gen->selectConfiguration(configuration); + gen->setCollisionSystem(energyCM, beam1Z, beam1A, beam2Z, beam2A); + gen->setExtraParams(extrapars); + gen->setupDpmjet(dpmjetconf); + + if (configuration.find("kTau") != std::string::npos){ + gen->SetSizePdg(2); + gen->AddPdg(15,0); + gen->AddPdg(-15,1); + } + else if(configuration.find("kDpmjet") != std::string::npos){ + gen->SetSizePdg(15); + gen->AddPdg( 411,0); + gen->AddPdg(-411,1); + gen->AddPdg( 421,2); + gen->AddPdg(-421,3); + gen->AddPdg( 413,4); + gen->AddPdg(-413,5); + gen->AddPdg( 431,6); + gen->AddPdg(-431,7); + gen->AddPdg( 4122,8); + gen->AddPdg(-4122,9); + gen->AddPdg( 333,10); + gen->AddPdg(-333,11); + gen->AddPdg( 313,12); + gen->AddPdg(-313,13); + gen->AddPdg( 443,14); + } + else{ + gen->SetPolarization(1); //Transversal + gen->SetSizePdg(3); + gen->AddPdg(443,0); + gen->AddPdg(100443,1); + gen->AddPdg(223,2); + } + + TString pathO2 = gSystem->ExpandPathName("$O2DPG_MC_CONFIG_ROOT/MC/config/PWGUD/external/generator/DecayTablesEvtGen"); + if (configuration.find("Psi2sToMuPi") != std::string::npos) gen->SetDecayTable(Form("%s/PSI2S.MUMUPIPI.DEC",pathO2.Data())); + else if (configuration.find("Psi2sToElPi") != std::string::npos) gen->SetDecayTable(Form("%s/PSI2S.EEPIPI.DEC",pathO2.Data())); + else if (configuration.find("RhoPrime") != std::string::npos) gen->SetDecayTable(Form("%s/RHOPRIME.RHOPIPI.DEC",pathO2.Data())); + else if (configuration.find("OmegaTo3Pi") != std::string::npos) gen->SetDecayTable(Form("%s/OMEGA.3PI.DEC",pathO2.Data())); + else if (configuration.find("JpsiToElRad") != std::string::npos) gen->SetDecayTable(Form("%s/JPSI.EE.DEC",pathO2.Data())); + else if (configuration.find("ToL+3Pi") != std::string::npos) gen->SetDecayTable(Form("%s/TAUTAU.LP3PI.DEC",pathO2.Data())); + else if (configuration.find("ToL-3Pi") != std::string::npos) gen->SetDecayTable(Form("%s/TAUTAU.LN3PI.DEC",pathO2.Data())); + else if (configuration.find("ToPi+3Pi") != std::string::npos) gen->SetDecayTable(Form("%s/TAUTAU.PP3PI.DEC",pathO2.Data())); + else if (configuration.find("ToPi-3Pi") != std::string::npos) gen->SetDecayTable(Form("%s/TAUTAU.PN3PI.DEC",pathO2.Data())); + else if (configuration.find("To6Pi") != std::string::npos) gen->SetDecayTable(Form("%s/TAUTAU.6PI.DEC",pathO2.Data())); + else if (configuration.find("ToElMu") != std::string::npos) gen->SetDecayTable(Form("%s/TAUTAU.ELMU.DEC",pathO2.Data())); + else if (configuration.find("ToElPiPi0") != std::string::npos) gen->SetDecayTable(Form("%s/TAUTAU.ELPI.DEC",pathO2.Data())); + else if (configuration.find("ToPoPiPi0") != std::string::npos) gen->SetDecayTable(Form("%s/TAUTAU.POPI.DEC",pathO2.Data())); + else if (configuration.find("Jpsi4Prong") != std::string::npos) gen->SetDecayTable(Form("%s/JPSI.4PRONG.DEC",pathO2.Data())); + else if (configuration.find("Jpsi6Prong") != std::string::npos) gen->SetDecayTable(Form("%s/JPSI.6PRONG.DEC",pathO2.Data())); + else if (configuration.find("Dpmjet") != std::string::npos) gen->SetDecayTable(Form("%s/OPENCHARM.DEC",pathO2.Data())); + + return gen; +} diff --git a/MC/config/PWGUD/external/generator/GeneratorUpcgen.C b/MC/config/PWGUD/external/generator/GeneratorUpcgen.C new file mode 100644 index 000000000..30cd6e848 --- /dev/null +++ b/MC/config/PWGUD/external/generator/GeneratorUpcgen.C @@ -0,0 +1,177 @@ +R__LOAD_LIBRARY(libUpcgenlib.so) +R__ADD_INCLUDE_PATH($Upcgen_ROOT/include) + +#include "UpcGenerator.h" + +// usage: o2-sim -n 100 -g external --configKeyValues 'GeneratorExternal.fileName=GeneratorUpcgen.C;GeneratorExternal.funcName=GeneratorUpcgen("kDiTau", ".", 5360.)' + +namespace o2 { +namespace eventgen { +class GeneratorUPCgen_class : public Generator { +public: + GeneratorUPCgen_class() { mUPCgen = new UpcGenerator(); }; + ~GeneratorUPCgen_class() = default; + void selectConfiguration(std::string val) { mSelectedConfiguration = val; }; + void setLumiFileDirectory(std::string lumiFileDirectory) { + mUPCgen->setLumiFileDirectory(lumiFileDirectory); + }; + void setCollisionSystem(float energyCM, int beamZ, int beamA) { + eCM = energyCM; + projZ = beamZ; + projA = beamA; + }; + void setSeed(int seed) { mUPCgen->setSeed(seed); } + + // predefined generator configurations + const static int nProcess = 5; + const static int nElements = 9; + const struct UpcgenConfig { + std::string pnames[nElements]{"PROC_ID", "LEP_A", "ALP_MASS", + "ALP_WIDTH", "PT_MIN", "ETA_MIN", + "ETA_MAX", "FLUX_POINT", "BREAKUP_MODE"}; + const struct definitions { + const char *name; + std::string elements[nElements]; + } sets[nProcess] = { + {"kDiElectron", + {"11", "0.0", "0.0", "0.0", "0.0", "-1.0", "1.0", "1", "1"}}, + {"kDiMuon", + {"13", "0.0", "0.0", "0.0", "0.0", "-1.0", "1.0", "1", "1"}}, + {"kDiTau", {"15", "0.0", "0.0", "0.0", "0.0", "-1.0", "1.0", "1", "1"}}, + {"kLightByLight", + {"22", "0.0", "0.0", "0.0", "0.0", "-1.0", "1.0", "1", "1"}}, + {"kAxionLike", + {"51", "0.0", "0.0", "0.0", "0.0", "-1.0", "1.0", "1", "1"}}}; + } upcgenConfig; + + bool Config() { + // select a specific set of parameters + int idx = -1; + for (int i = 0; i < nProcess; ++i) { + if (mSelectedConfiguration.compare(upcgenConfig.sets[i].name) == 0) { + idx = i; + break; + } + } + + if (idx == -1) { + std::cout << "UPCGEN process " << mSelectedConfiguration + << " is not supported" << std::endl; + return false; + } + + // new generator + mUPCgen->setDebugLevel(0); + mUPCgen->setNumThreads(1); + + // update generator parameters - configure + // independent of process + mUPCgen->setParameterValue("DO_PT_CUT", "0"); + mUPCgen->setParameterValue("DO_ETA_CUT", "0"); + mUPCgen->setParameterValue("FLUX_POINT", "0"); + mUPCgen->setParameterValue("USE_ROOT_OUTPUT", "0"); + mUPCgen->setParameterValue("USE_HEPMC_OUTPUT", "0"); + + // process specific + for (int i = 0; i < nElements; ++i) { + mUPCgen->setParameterValue(upcgenConfig.pnames[i], + upcgenConfig.sets[idx].elements[i]); + } + + return true; + } + + bool Init() override { + Generator::Init(); + + // initialize the generator + mUPCgen->init(); + + return true; + }; + + bool generateEvent() override { + if (!mUPCgen) { + std::cout << "GenerateEvent: upcgen class/object not properly constructed" + << std::endl; + return false; + } + + // generate a new event + vector pdgs; + vector statuses; + vector mothers; + vector particles; + // events can be rejected due to applied cuts + bool goon = true; + while (goon) { + auto stat = mUPCgen->generateEvent(pdgs, statuses, mothers, particles); + if (stat == 0) { + nRejected++; + } else { + nAccepted++; + goon = false; + } + } + + return true; + }; + + bool importParticles() override { + std::cout << "\n"; + auto upcgenParticles = mUPCgen->getParticles(); + for (auto part : upcgenParticles) { + TParticle particle(part.GetPdgCode(), 1, part.GetFirstMother(), -1, + part.GetFirstDaughter(), part.GetLastDaughter(), + part.Px(), part.Py(), part.Pz(), part.Energy(), 0., 0., + 0., 0.); + mParticles.push_back(particle); + o2::mcutils::MCGenHelper::encodeParticleStatusAndTracking( + mParticles.back(), true); + } + return true; + }; + + long int acceptedEvents() { return nAccepted; } + long int rejectedEvents() { return nRejected; } + double fiducialXSection() { + return mUPCgen->totNuclX() * nAccepted / (nAccepted + nRejected); + } + +private: + UpcGenerator *mUPCgen = 0x0; + std::string mSelectedConfiguration = ""; + + // keep track of the rejected and accepted events + long int nAccepted{0}; + long int nRejected{0}; + + float eCM = 5020; + int projZ = 82; + int projA = 208; +}; + +} // namespace eventgen +} // namespace o2 + +FairGenerator *GeneratorUpcgen(std::string configuration = "kDiTau", + std::string lumiFileDirectory = ".", + float energyCM = 5020., int beamZ = 82, + int beamA = 208) { + // create generator + auto gen = new o2::eventgen::GeneratorUPCgen_class(); + + // set generator parameters + gen->selectConfiguration(configuration); + gen->setLumiFileDirectory(lumiFileDirectory); + gen->setCollisionSystem(energyCM, beamZ, beamA); + + // configure the generator + cout << "Upcgen is initialized ..."; + gen->Config(); + cout << " config done ..."; + gen->Init(); + cout << " init done!\n"; + + return gen; +} diff --git a/MC/config/PWGUD/external/generator/Generator_nOOn.C b/MC/config/PWGUD/external/generator/Generator_nOOn.C new file mode 100644 index 000000000..798c0d6b2 --- /dev/null +++ b/MC/config/PWGUD/external/generator/Generator_nOOn.C @@ -0,0 +1,66 @@ +R__ADD_INCLUDE_PATH($nOOn_ROOT/include) +R__LOAD_LIBRARY(NeutronGenerator_cxx.so) +#include "GeneratorStarlight.C" +#include "NeutronGenerator.h" + +class Generator_nOOn_class : public o2::eventgen::GeneratorStarlight_class +{ +public: + /// constructor + Generator_nOOn_class(){}; + /// Destructor + ~Generator_nOOn_class() = default; + + bool Init() override + { + GeneratorStarlight_class::Init(); + mNeutronGen = new NeutronGenerator(); + mNeutronGen->SetRapidityCut(-6.0,6.0); + + float beam1energy = TMath::Sqrt(Double_t(projZ)/projA*targA/targZ)*eCM/2; + float gamma1 = beam1energy/0.938272; + mNeutronGen->SetRunMode(NeutronGenerator::kInterface); + mNeutronGen->SetBeamParameters(NeutronGenerator::kPb208,gamma1); + mNeutronGen->SetDataPath(gSystem->ExpandPathName("$nOOn_ROOT/include/Data/")); + mNeutronGen->Initialize(); + mNeutronGen->Setup(); + return true; + } + + bool generateEvent() override + { + GeneratorStarlight_class::generateEvent(); + mNeutronGen->GenerateEvent(getPhotonEnergy()); + return true; + } + + bool importParticles() override + { + GeneratorStarlight_class::importParticles(); + + mNeutrons = mNeutronGen->ImportParticles(); + for(Int_t i = 0; iGetEntriesFast(); i++){ + mParticles.push_back(*(TParticle*)(mNeutrons->At(i))); + o2::mcutils::MCGenHelper::encodeParticleStatusAndTracking(mParticles.back(), true); + } + mNeutronGen->FinishEvent(); + mNeutrons->Clear("C"); + return true; + } + + +private: + NeutronGenerator *mNeutronGen = 0x0; + TClonesArray *mNeutrons = 0x0; + +}; + +///___________________________________________________________ +FairGenerator *Generator_nOOn(std::string configuration = "empty",float energyCM = 5020, int beam1Z = 82, int beam1A = 208, int beam2Z = 82, int beam2A = 208, std::string extrapars = "") +{ + auto gen = new Generator_nOOn_class(); + gen->selectConfiguration(configuration); + gen->setCollisionSystem(energyCM, beam1Z, beam1A, beam2Z, beam2A); + gen->setExtraParams(extrapars); + return gen; +} diff --git a/MC/config/PWGUD/ini/GenStarlight_kCohJpsiToMu_PbPb_5360_Muon.ini b/MC/config/PWGUD/ini/GenStarlight_kCohJpsiToMu_PbPb_5360_Muon.ini new file mode 100644 index 000000000..b62bc7d70 --- /dev/null +++ b/MC/config/PWGUD/ini/GenStarlight_kCohJpsiToMu_PbPb_5360_Muon.ini @@ -0,0 +1,6 @@ +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGUD/external/generator/GeneratorStarlight.C +funcName = GeneratorStarlight("kCohJpsiToMu", 5360.000000, 82, 208, 82, 208) +[TriggerExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGUD/trigger/selectParticlesInAcceptance.C +funcName = selectDaughterPartInAcc(-4.0,-2.5) diff --git a/MC/config/PWGUD/ini/GenStarlight_kCohPsi2sToElPi_PbPb_5360_Cent.ini b/MC/config/PWGUD/ini/GenStarlight_kCohPsi2sToElPi_PbPb_5360_Cent.ini new file mode 100644 index 000000000..cfeb0b9d2 --- /dev/null +++ b/MC/config/PWGUD/ini/GenStarlight_kCohPsi2sToElPi_PbPb_5360_Cent.ini @@ -0,0 +1,6 @@ +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGUD/external/generator/GeneratorStarlightToEvtGen.C +funcName = GeneratorStarlightToEvtGen("kCohPsi2sToElPi", 5360.000000, 82, 208, 82, 208) +[TriggerExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGUD/trigger/selectParticlesInAcceptance.C +funcName = selectMotherPartInAcc(-0.9,0.9) diff --git a/MC/config/PWGUD/ini/dpmjet_PbPb5360.txt b/MC/config/PWGUD/ini/dpmjet_PbPb5360.txt new file mode 100644 index 000000000..a7e25193e --- /dev/null +++ b/MC/config/PWGUD/ini/dpmjet_PbPb5360.txt @@ -0,0 +1,45 @@ +********************************************************************** +* Example for a DTUNUC input file. +* Uncomment the input-cards according to your requirements. +* +* Format: A10,6E10.0,A8 +* (except for the section enclosed by "PHOINPUT" and "ENDINPUT" +* which is format-free) +* lines starting with "*" are comment lines +********************************************************************** +* +* projectile / target / Energy +* ------------------- +* 1 2 3 4 5 6 7 +*23456789012345678901234567890123456789012345678901234567890123456789012345678 +PROJPAR 0.0 PHOTON +TARPAR 208.0 82.0 +ENERGY 1000.0 600000.0 +*ENERGY 100.0 +* Initialize the random number generator +RNDMINIT 55.0 101.0 15.0 73.0 +* +* +* PHOJET-specific input +* --------------------- +* The following lines control the event-generation with PHOJET for +* individual photon/nucleon-nucleon collisions. +* For details see the PHOJET-manual available at +* http://lepton.bartol.udel.edu/~eng/phojet.html +* Any options explained in the PHOJET-manual can be used in between +* the "PHOINPUT" and "ENDINPUT" cards. +PHOINPUT +PROCESS 1 0 1 1 1 1 1 1 +ENDINPUT +* +* Output +* ------ +* some default output (particle multiplicities etc.) +HISTOGRAM 101.0 102.0 +* +* Start of event generation +* ------------------------- +*START 5000.0 0.0 +START 100.0 0.0 +STOP +*...+....1....+....2....+....3....+....4....+....5....+....6....+....7... diff --git a/MC/config/PWGUD/ini/makeGraniittiConfig.py b/MC/config/PWGUD/ini/makeGraniittiConfig.py new file mode 100755 index 000000000..ecf6450ef --- /dev/null +++ b/MC/config/PWGUD/ini/makeGraniittiConfig.py @@ -0,0 +1,131 @@ +#! /usr/bin/env python3 + +### @author: Paul Buehler +### @email: paul.buhler@cern.ch + +import argparse +import os +import subprocess + +def createJson(args): + templateFile = os.getenv("O2DPG_ROOT")+"/MC/config/PWGUD/templates/ALICE_Graniitti.temp" + jsonFile = "ALICE_Graniitti.json" + processes = { + "kCon_pipi" : { + "OUTPUT" : "ALICE_Con_pipi", + "ENERGY" : 13600, + "PROCESS" : "PP[RES+CON] -> pi+ pi-", + "RES" : "" + }, + "kConRes_pipi" : { + "OUTPUT" : "ALICE_Con_pipi", + "ENERGY" : 13600, + "PROCESS" : "PP[RES+CON] -> pi+ pi-", + "RES" : '["f0_500", "rho_770", "f0_980", "phi_1020", "f2_1270", "f0_1500", "f2_1525", "f0_1710", "f2_2150"]' + }, + "kCon_KK" : { + "OUTPUT" : "ALICE_Con_pipi", + "ENERGY" : 13600, + "PROCESS" : "PP[RES+CON] -> pi+ pi-", + "RES" : "" + }, + "kConRes_KK" : { + "OUTPUT" : "ALICE_Con_pipi", + "ENERGY" : 13600, + "PROCESS" : "PP[RES+CON] -> pi+ pi-", + "RES" : '["f0_500", "rho_770", "f0_980", "phi_1020", "f2_1270", "f0_1500", "f2_1525", "f0_1710", "f2_2150"]' + } + } + + # is process defined? + if not args.process in processes.keys(): + print("FATAL ERROR: ") + print(" Process ", args.process) + print(" is not defined!") + exit() + procdefs = processes[args.process] + + # copy templateFile to jsonFile + cmd = "cp "+templateFile+" "+jsonFile + if subprocess.call(cmd, shell=True) > 0: + print("FATAL ERROR: ") + print(" ", templateFile) + print(" can not be copied to") + print(" ", jsonFile) + exit() + + # update jsonFile + stat = 0 + # OUTPUT + nl = ' "OUTPUT" : "' + procdefs["OUTPUT"] + '",' + cmd = "sed -i '/\"OUTPUT\"/c\\" + nl + "' " + jsonFile + stat = stat + subprocess.call(cmd, shell=True) + # NEVENTS + nl = ' "NEVENTS" : ' + args.nEvents + ',' + cmd = "sed -i '/\"NEVENTS\"/c\\" + nl + "' " + jsonFile + stat = stat + subprocess.call(cmd, shell=True) + # ENERGY + beamEne = str(int(args.eCM)/2) + nl = ' "ENERGY" : [' + beamEne + ', ' + beamEne + '],' + cmd = "sed -i '/\"ENERGY\"/c\\" + nl + "' " + jsonFile + stat = stat + subprocess.call(cmd, shell=True) + # PROCESS + nl = ' "PROCESS" : "' + procdefs["PROCESS"] + '",' + cmd = "sed -i '/\"PROCESS\"/c\\" + nl + "' " + jsonFile + stat = stat + subprocess.call(cmd, shell=True) + # RES + if procdefs["RES"] == "": + nl = ' "RES" : [],' + else: + nl = ' "RES" : ' + procdefs["RES"] + ',' + cmd = "sed -i '/\"RES\"/c\\" + nl + "' " + jsonFile + stat = stat + subprocess.call(cmd, shell=True) + + return jsonFile + +# main +parser = argparse.ArgumentParser(description='Make Graniitti configuration', + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + +parser.add_argument('--process',default=None, choices=['kCon_pipi', 'kConRes_pipi', 'kCon_KK', 'kConRes_KK'], + help='Process to switch on') + +parser.add_argument('--nEvents', default='100', + help='Number of events to generate per TF') + +parser.add_argument('--eCM', type=float, default='13600', + help='Centre-of-mass energy') + +parser.add_argument('--rapidity', default='cent', choices=['cent_eta', 'muon_eta'], + help='Rapidity to select') + +parser.add_argument('--output', default='GenGraniitti.ini', + help='Where to write the configuration') + +args = parser.parse_args() + +### prepare the json configuration file for graniitti +jsonFile = createJson(args) + +### open output file +fout = open(args.output, 'w') + +### Generator +fout.write('[GeneratorExternal] \n') +fout.write('fileName = ${O2DPG_ROOT}/MC/config/PWGUD/external/generator/GeneratorGraniitti.C \n') +fout.write('funcName = GeneratorGraniitti("%s") \n' % ("../"+jsonFile)) + +###Trigger +fout.write('[TriggerExternal] \n') +fout.write('fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGUD/trigger/selectParticlesInAcceptance.C \n') +if args.rapidity == 'cent_rap': + fout.write('funcName = selectMotherPartInAcc(-0.9,0.9) \n') +if args.rapidity == 'muon_rap': + fout.write('funcName = selectMotherPartInAcc(-4.0,-2.5) \n') +if args.rapidity == 'cent_eta': + fout.write('funcName = selectDaughterPartInAcc(-0.95,0.95) \n') +if args.rapidity == 'muon_eta': + fout.write('funcName = selectDaughterPartInAcc(-4.05,-2.45) \n') + +### close outout file +fout.close() diff --git a/MC/config/PWGUD/ini/makeStarlightConfig.py b/MC/config/PWGUD/ini/makeStarlightConfig.py new file mode 100755 index 000000000..31f79357c --- /dev/null +++ b/MC/config/PWGUD/ini/makeStarlightConfig.py @@ -0,0 +1,136 @@ +#! /usr/bin/env python3 + +### @author: Michal Broz +### @email: michal.broz@cern.ch + +import argparse + +parser = argparse.ArgumentParser(description='Make Starlight configuration', + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + +parser.add_argument('--collType',default='PbPb', choices=['PbPb', 'pPb', 'Pbp', 'pp', 'OO', 'pO', 'Op', 'NeNe'], + help='Colission system') + +parser.add_argument('--eCM', type=float, default='5360', + help='Centre-of-mass energy') + +parser.add_argument('--rapidity', default='cent', choices=['cent_rap', 'muon_rap', 'cent_eta', 'muon_eta'], + help='Rapidity to select') + +parser.add_argument('--process',default=None, choices=['kTwoGammaToMuLow', 'kTwoGammaToElLow', 'kTwoGammaToMuMedium', 'kTwoGammaToElMedium', 'kTwoGammaToMuHigh', 'kTwoGammaToElHigh', 'kTwoGammaToRhoRho', 'kTwoGammaToF2', 'kCohRhoToPi', 'kCohRhoToElEl', 'kCohRhoToMuMu', 'kCohRhoToPiWithCont', 'kCohRhoToPiFlat', 'kCohPhiToKa', 'kDirectPhiToKaKa', 'kCohPhiToEl', 'kCohOmegaTo2Pi', 'kCohOmegaTo3Pi', 'kCohOmegaToPiPiPi', 'kCohRhoPrimeTo4Pi', 'kCohJpsiToMu', 'kCohJpsiToEl', 'kCohJpsiToElRad', 'kCohJpsiToProton', 'kCohJpsiToLLbar', 'kCohJpsi4Prong', 'kCohJpsi6Prong', 'kCohPsi2sToMu','kCohPsi2sToEl', 'kCohPsi2sToMuPi', 'kCohPsi2sToElPi', 'kCohUpsilon1SToMu', 'kCohUpsilon1SToEl', 'kCohUpsilon2SToMu', 'kCohUpsilon2SToEl', 'kCohUpsilon3SToMu', 'kCohUpsilon3SToEl', 'kIncohRhoToPi', 'kIncohRhoToElEl', 'kIncohRhoToMuMu', 'kIncohRhoToPiWithCont', 'kIncohRhoToPiFlat', 'kIncohPhiToKa', 'kIncohOmegaTo2Pi', 'kIncohOmegaTo3Pi', 'kIncohOmegaToPiPiPi', 'kIncohRhoPrimeTo4Pi', 'kIncohJpsiToMu', 'kIncohJpsiToEl', 'kIncohJpsiToElRad', 'kIncohJpsiToProton', 'kIncohJpsiToLLbar', 'kIncohPsi2sToMu', 'kIncohPsi2sToEl', 'kIncohPsi2sToMuPi', 'kIncohPsi2sToElPi', 'kIncohUpsilon1SToMu', 'kIncohUpsilon1SToEl', 'kIncohUpsilon2SToMu', 'kIncohUpsilon2SToEl', 'kIncohUpsilon3SToMu', 'kIncohUpsilon3SToEl', 'kDpmjetSingleA', 'kDpmjetSingleA_Dzero', 'kDpmjetSingleA_Lc', 'kDpmjetSingleA_Dcharged', 'kDpmjetSingleA_Dstar', 'kDpmjetSingleA_Phi', 'kDpmjetSingleA_Kstar', 'kDpmjetSingleA_Jpsi', 'kDpmjetSingleC', 'kDpmjetSingleC_Dzero', 'kDpmjetSingleC_Lc', 'kDpmjetSingleC_Dcharged', 'kDpmjetSingleC_Dstar', 'kDpmjetSingleC_Phi', 'kDpmjetSingleC_Kstar', 'kDpmjetSingleC_Jpsi', 'kTauLowToL+3Pi', 'kTauLowToL-3Pi', 'kTauLowToPi+3Pi', 'kTauLowToPi-3Pi', 'kTauLowTo6Pi', 'kTauLowToElMu', 'kTauLowToElPiPi0', 'kTauLowToPoPiPi0'], + help='Process to switch on') + + +parser.add_argument('--output', default='GenStarlight.ini', + help='Where to write the configuration') + +parser.add_argument('--extraPars', default='', + help='Extra parameters for SL config') + +parser.add_argument('--dpmjetConf', default='', + help='DPMJET config file') + +parser.add_argument('--nOOn', action='store_true', + help="Enable the neutron production with nOOn") + + +args = parser.parse_args() + +if args.nOOn: + args.extraPars = 'BREAKUP_MODE = 4' + +if 'PbPb' in args.collType: + pZ = 82 + pA = 208 + tZ = 82 + tA = 208 +if 'pPb' in args.collType: + pZ = 1 + pA = 1 + tZ = 82 + tA = 208 +if 'Pbp' in args.collType: + pZ = 82 + pA = 208 + tZ = 1 + tA = 1 +if 'pp' in args.collType: + pZ = 1 + pA = 1 + tZ = 1 + tA = 1 +if 'OO' in args.collType: + pZ = 8 + pA = 16 + tZ = 8 + tA = 16 +if 'pO' in args.collType: + pZ = 1 + pA = 1 + tZ = 8 + tA = 16 +if 'Op' in args.collType: + pZ = 8 + pA = 16 + tZ = 1 + tA = 1 +if 'NeNe' in args.collType: + pZ = 10 + pA = 20 + tZ = 10 + tA = 20 + +### open output file +fout = open(args.output, 'w') + +### Generator +fout.write('[GeneratorExternal] \n') +if args.nOOn: + fout.write('fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGUD/external/generator/Generator_nOOn.C \n') + fout.write('funcName = Generator_nOOn("%s", %f, %d, %d, %d, %d, "%s") \n' % (args.process,args.eCM ,pZ,pA,tZ,tA,args.extraPars)) +else: + if 'Psi2sToMuPi' in args.process or 'Psi2sToElPi' in args.process or 'OmegaTo3Pi' in args.process or 'JpsiToElRad' in args.process or 'Jpsi4Prong' in args.process or 'Jpsi6Prong' in args.process or 'kTau' in args.process or 'Dpmjet' in args.process: + fout.write('fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGUD/external/generator/GeneratorStarlightToEvtGen.C \n') + fout.write('funcName = GeneratorStarlightToEvtGen("%s", %f, %d, %d, %d, %d, "%s", "%s") \n' % (args.process.split('_')[0],args.eCM ,pZ,pA,tZ,tA,args.extraPars,args.dpmjetConf)) + else: + fout.write('fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGUD/external/generator/GeneratorStarlight.C \n') + fout.write('funcName = GeneratorStarlight("%s", %f, %d, %d, %d, %d, "%s", "%s") \n' % (args.process.split('_')[0],args.eCM ,pZ,pA,tZ,tA,args.extraPars,args.dpmjetConf)) + +###Trigger +if not 'kDpmjet' in args.process: + fout.write('[TriggerExternal] \n') + fout.write('fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGUD/trigger/selectParticlesInAcceptance.C \n') + if 'kTwoGamma' in args.process or 'kTau' in args.process: + if args.rapidity == 'cent_eta': + fout.write('funcName = selectDirectPartInAcc(-0.9,0.9) \n') + if args.rapidity == 'muon_eta': + fout.write('funcName = selectDirectPartInAcc(-4.0,-2.5) \n') + else: + if args.rapidity == 'cent_rap': + fout.write('funcName = selectMotherPartInAcc(-0.9,0.9) \n') + if args.rapidity == 'muon_rap': + fout.write('funcName = selectMotherPartInAcc(-4.0,-2.5) \n') + if args.rapidity == 'cent_eta': + fout.write('funcName = selectDaughterPartInAcc(-0.9,0.9) \n') + if args.rapidity == 'muon_eta': + fout.write('funcName = selectDaughterPartInAcc(-4.0,-2.5) \n') +elif '_' in args.process: + fout.write('[TriggerExternal] \n') + fout.write('fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGUD/trigger/triggerDpmjetParticle.C \n') + if 'Dzero' in args.process: + fout.write('funcName = triggerDzero(-0.9,0.9) \n') + if 'Dcharged' in args.process: + fout.write('funcName = triggerDcharged(-0.9,0.9) \n') + if 'Dstar' in args.process: + fout.write('funcName = triggerDstar(-0.9,0.9) \n') + if 'Phi' in args.process: + fout.write('funcName = triggerPhi(-0.9,0.9) \n') + if 'Kstar' in args.process: + fout.write('funcName = triggerKstar(-0.9,0.9) \n') + if 'Lc' in args.process: + fout.write('funcName = triggerLc(-0.9,0.9) \n') + if 'Jpsi' in args.process: + fout.write('funcName = triggerJpsi(-0.9,0.9) \n') + +### close outout file +fout.close() diff --git a/MC/config/PWGUD/ini/makeUpcgenConfig.py b/MC/config/PWGUD/ini/makeUpcgenConfig.py new file mode 100755 index 000000000..9915b2f35 --- /dev/null +++ b/MC/config/PWGUD/ini/makeUpcgenConfig.py @@ -0,0 +1,59 @@ +#! /usr/bin/env python3 + +### @author: Paul Buehler +### @email: paul.buhler@cern.ch + +import argparse + +parser = argparse.ArgumentParser(description='Make Upcgen configuration', + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + +parser.add_argument('--collType',default='PbPb', choices=['PbPb', 'OO'], + help='Colission system') + +parser.add_argument('--eCM', type=float, default='5360', + help='Centre-of-mass energy') + +parser.add_argument('--rapidity', default='cent', choices=['cent_eta', 'muon_eta'], + help='Rapidity to select') + +parser.add_argument('--process',default=None, choices=['kDiElectron', 'kDiMuon', 'kDiTau', 'kLightByLight', 'kAxionLike'], + help='Process to switch on') + + +parser.add_argument('--output', default='GenUpcgen.ini', + help='Where to write the configuration') + + +args = parser.parse_args() + +if 'PbPb' in args.collType: + pZ = 82 + pA = 208 + +if 'OO' in args.collType: + pZ = 8 + pA = 16 + +### open output file +fout = open(args.output, 'w') + +### Generator +fout.write('[GeneratorExternal] \n') +fout.write('fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGUD/external/generator/GeneratorUpcgen.C \n') +fout.write('funcName = GeneratorUpcgen("%s", "%s", %f, %d, %d) \n' % (args.process,"../.",args.eCM,pZ,pA)) + +###Trigger +fout.write('[TriggerExternal] \n') +fout.write('fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGUD/trigger/selectParticlesInAcceptance.C \n') +if args.rapidity == 'cent_rap': + fout.write('funcName = selectMotherPartInAcc(-0.9,0.9) \n') +if args.rapidity == 'muon_rap': + fout.write('funcName = selectMotherPartInAcc(-4.0,-2.5) \n') +if args.rapidity == 'cent_eta': + fout.write('funcName = selectDaughterPartInAcc(-0.95,0.95) \n') +if args.rapidity == 'muon_eta': + fout.write('funcName = selectDaughterPartInAcc(-4.05,-2.45) \n') + +### close outout file +fout.close() diff --git a/MC/config/PWGUD/ini/tests/GenStarlight_kCohJpsiToMu_PbPb_5360_Muon.C b/MC/config/PWGUD/ini/tests/GenStarlight_kCohJpsiToMu_PbPb_5360_Muon.C new file mode 100644 index 000000000..2a9424d87 --- /dev/null +++ b/MC/config/PWGUD/ini/tests/GenStarlight_kCohJpsiToMu_PbPb_5360_Muon.C @@ -0,0 +1,37 @@ +int External() +{ + std::string path{"o2sim_Kine.root"}; + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) + { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) + { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + bool goodEvent = kFALSE; + auto nEvents = tree->GetEntries(); + for (int i = 0; i < nEvents; i++) + { + goodEvent = kFALSE; + auto check = tree->GetEntry(i); + for (int idxMCTrack = 0; idxMCTrack < tracks->size(); ++idxMCTrack) + { + auto track = tracks->at(idxMCTrack); + if (track.GetPdgCode() == 443 && track.getMotherTrackId() == -1){ //Primary J/psi + auto daugh1 = tracks->at(track.getFirstDaughterTrackId()); + auto daugh2 = tracks->at(track.getLastDaughterTrackId()); + if(TMath::Abs(daugh1.GetPdgCode()) == 13 && TMath::Abs(daugh2.GetPdgCode()) == 13) goodEvent = kTRUE; + } + } + if(!goodEvent) return 1; + } + return 0; +} diff --git a/MC/config/PWGUD/ini/tests/GenStarlight_kCohPsi2sToElPi_PbPb_5360_Cent.C b/MC/config/PWGUD/ini/tests/GenStarlight_kCohPsi2sToElPi_PbPb_5360_Cent.C new file mode 100644 index 000000000..74b40771c --- /dev/null +++ b/MC/config/PWGUD/ini/tests/GenStarlight_kCohPsi2sToElPi_PbPb_5360_Cent.C @@ -0,0 +1,51 @@ +int External() +{ + std::string path{"o2sim_Kine.root"}; + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) + { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) + { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + int nPions = 0; + int nElectrons = 0; + int nPhotons = 0; + auto nEvents = tree->GetEntries(); + for (int i = 0; i < nEvents; i++) + { + nPhotons = 0; + nPions = 0; + nElectrons = 0; + auto check = tree->GetEntry(i); + for (int idxMCTrack = 0; idxMCTrack < tracks->size(); ++idxMCTrack) + { + auto track = tracks->at(idxMCTrack); + if (track.GetPdgCode() == 100443 && track.getMotherTrackId() == -1){ //Primary psi2s + for(int iDaugh = track.getFirstDaughterTrackId(); iDaugh<=track.getLastDaughterTrackId(); iDaugh++){ + auto daughPsi2s = tracks->at(iDaugh); + if(daughPsi2s.GetPdgCode() == 22) nPhotons++; + if(TMath::Abs(daughPsi2s.GetPdgCode()) == 211) nPions++; + + if(daughPsi2s.GetPdgCode() == 443 ){ + for(int jDaugh = daughPsi2s.getFirstDaughterTrackId(); jDaugh<=daughPsi2s.getLastDaughterTrackId(); jDaugh++){ + auto daughJPsi = tracks->at(jDaugh); + if(daughJPsi.GetPdgCode() == 22) nPhotons++; + if(TMath::Abs(daughJPsi.GetPdgCode()) == 11) nElectrons++; + } + } + } + } + } + if(nElectrons != 2 || nPions != 2)return 1; + } + return 0; +} diff --git a/MC/config/PWGUD/templates/ALICE_Graniitti.temp b/MC/config/PWGUD/templates/ALICE_Graniitti.temp new file mode 100644 index 000000000..b0614f279 --- /dev/null +++ b/MC/config/PWGUD/templates/ALICE_Graniitti.temp @@ -0,0 +1,123 @@ +// JSON(5) + single line/multiline comments allowed, trailing comma allowed +// +// The format is strictly upper/lower case sensitive. +// +// mikael.mieskolainen@cern.ch, 2021 +{ + + // ---------------------------------------------------------------------- + // GENERAL setup + + "GENERALPARAM" : { + + "OUTPUT" : + "FORMAT" : "hepmc3", // hepmc3, hepmc2, hepevt + "CORES" : 0, // Number of CPU threads (0 for automatic) + "NEVENTS" : + "INTEGRATOR" : "VEGAS", // Integrator (VEGAS, FLAT) + "WEIGHTED" : false, // Weighted events (default false) + "MODELPARAM" : "TUNE0" // General model tune + }, + + // ---------------------------------------------------------------------- + // Process setup + + "PROCESSPARAM" : { + + "BEAM" : ["p+","p+"], // Beam PDG-ID / Name + "ENERGY" : + "PROCESS" : + "RES" : + "POMLOOP" : false, // Eikonal Pomeron loop screening + "NSTARS" : 0, // N* excitation (0 = elastic, 1 = single, 2 = double) + "LHAPDF" : "CT10nlo", // LHAPDF parton distribution set + "HIST" : 0, // On-the-flight histograms (0 = off,1,2) + "RNDSEED" : 0, // Random seed (uint) + }, + + // ---------------------------------------------------------------------- + // Monte Carlo integrator setup + + "INTEGRALPARAM" : { + + // Loop integration + "POMLOOP" : { + "ND" : 0, // Integral discretization [larger -> more discrete] (0 gives default discretization) (int) + }, + + // VEGAS integrator + "VEGAS" : { + + "BINS" : 512, // Maximum number of bins per dimension (NOTE: EVEN NUMBER) + "LAMBDA" : 1.25, // Regularization parameter + "NCALL" : 40000, // Number of function calls per iteration (5000 .. 50000 or more) + "ITER" : 50, // Number of initialization iteration (10 or more) + "CHI2MAX" : 10.0, // Maximum Chi^2 in initialization + "PRECISION" : 0.01, // Integral relative precision target + + "DEBUG" : -1 // Debug output (default -1) + }, + + // FLAT integrator + "FLAT" : { + + "PRECISION" : 0.01, // Integral relative precision + "MIN_EVENTS" : 1000000 // Minimum number of events to be sampled + } + }, + + + // ---------------------------------------------------------------------- + // Generation cuts + + "GENCUTS" : { + + "" : { + "Rap" : [-1.0, 1.0] // Rapidity boundaries of the final states ( class) + }, + "" : { + "Rap" : [-2.0, 2.0], // Rapidity boundaries of the system ( class) + "M" : [ 0.0, 5.0] + }, + "" : { + "Xi" : [0.0, 0.05] // Invariant scale M^2/s (forward excitation) + } + }, + + + // ---------------------------------------------------------------------- + // Central system fiducial cuts + // All central system particles need to fullfill the conditions + + "FIDCUTS" : { + + "active" : true, + + // Central system final states + "PARTICLE" : { + + "Eta" : [-0.95, 0.95], // Pseudorapidity + "Rap" : [-10.0, 10.0], // Rapidity + "Pt" : [0.08, 100000.0], // Transverse momentum + "Et" : [0.0, 100000.0] // Transverse energy + }, + + // Central system + "SYSTEM" : { + + "M" : [0.0, 100000.0], // Mass + "Rap" : [-10.0, 10.0], // Rapidity + "Pt" : [0.0, 100000.0] // Transverse momentum + }, + + // Forward system + "FORWARD" : { + "M" : [0.0, 100000.0], // Mass + "t" : [0.0, 100000.0], // Forward protons/N* Mandelstam t (absolute value) (GeV^2) + }, + + // Custom user cuts (default false, otherwise identifier ID) + "USERCUTS" : false + } + +} diff --git a/MC/config/PWGUD/trigger/selectParticlesInAcceptance.C b/MC/config/PWGUD/trigger/selectParticlesInAcceptance.C new file mode 100644 index 000000000..8e74b0811 --- /dev/null +++ b/MC/config/PWGUD/trigger/selectParticlesInAcceptance.C @@ -0,0 +1,57 @@ +R__ADD_INCLUDE_PATH($O2DPG_MC_CONFIG_ROOT) +#include +#include "Generators/Trigger.h" + +/// ================================================================================================================================= +/// Select event with VM or track in a given rapidity or eta window +/// ================================================================================================================================= + +o2::eventgen::Trigger selectMotherPartInAcc(double rapidityMin = -1., double rapidityMax = -1.) +{ + return [rapidityMin, rapidityMax](const std::vector& particles) -> bool { + for (const auto& particle : particles) { + if (TMath::Abs(particle.GetPdgCode()) == 2112)continue; + if (particle.GetFirstMother() == -1) + if ((particle.Y() > rapidityMin) && (particle.Y() < rapidityMax)) + return kTRUE; + } + return kFALSE; + }; +} + +o2::eventgen::Trigger selectDaughterPartInAcc(double etaMin = -1., double etaMax = -1.) +{ + return [etaMin, etaMax](const std::vector& particles) -> bool { + for (const auto& particle : particles) { + if (TMath::Abs(particle.GetPdgCode()) == 2112)continue; + if (particle.GetFirstMother() == -1) + if ((particle.Y() < etaMin) || (particle.Y() > etaMax)) return kFALSE; + if (particle.GetFirstMother() != -1 && particle.GetFirstDaughter() == -1 && particle.GetPdgCode() != 22 && TMath::Abs(particle.GetPdgCode()) != 12 && TMath::Abs(particle.GetPdgCode()) != 14 && TMath::Abs(particle.GetPdgCode()) != 16) + if ((particle.Eta() < etaMin) || (particle.Eta() > etaMax)) return kFALSE; + } + return kTRUE; + }; +} + +o2::eventgen::Trigger selectDileptonsInAcc(double etaMin = -1., double etaMax = -1.) +{ + return [etaMin, etaMax](const std::vector& particles) -> bool { + for (const auto& particle : particles) { + if (particle.GetFirstMother() != -1 && particle.GetFirstDaughter() == -1 && (TMath::Abs(particle.GetPdgCode()) == 11 || TMath::Abs(particle.GetPdgCode() == 13))) + if ((particle.Eta() < etaMin) || (particle.Eta() > etaMax)) return kFALSE; + } + return kTRUE; + }; +} + +o2::eventgen::Trigger selectDirectPartInAcc(double etaMin = -1., double etaMax = -1.) +{ + return [etaMin, etaMax](const std::vector& particles) -> bool { + for (const auto& particle : particles) { + if (particle.GetFirstMother() == -1) + if ((particle.Eta() < etaMin) || (particle.Eta() > etaMax)) + return kFALSE; + } + return kTRUE; + }; +} diff --git a/MC/config/PWGUD/trigger/triggerDpmjetParticle.C b/MC/config/PWGUD/trigger/triggerDpmjetParticle.C new file mode 100644 index 000000000..7f53aa904 --- /dev/null +++ b/MC/config/PWGUD/trigger/triggerDpmjetParticle.C @@ -0,0 +1,94 @@ +R__ADD_INCLUDE_PATH($O2DPG_MC_CONFIG_ROOT) +#include +#include "Generators/Trigger.h" + +/// ================================================================================================================================= +/// Select events with at least one particle in a given rapidity or eta window +/// ================================================================================================================================= + +o2::eventgen::Trigger triggerDzero(double rapidityMin = -1., double rapidityMax = -1.) +{ + return [rapidityMin, rapidityMax](const std::vector& particles) -> bool { + for (const auto& particle : particles) { + if (TMath::Abs(particle.GetPdgCode()) == 421) + if ((particle.Y() > rapidityMin) && (particle.Y() < rapidityMax)) + return kTRUE; + } + return kFALSE; + }; +} + +o2::eventgen::Trigger triggerDcharged(double rapidityMin = -1., double rapidityMax = -1.) +{ + return [rapidityMin, rapidityMax](const std::vector& particles) -> bool { + for (const auto& particle : particles) { + if (TMath::Abs(particle.GetPdgCode()) == 411) + if ((particle.Y() > rapidityMin) && (particle.Y() < rapidityMax)) + return kTRUE; + } + return kFALSE; + }; +} + +o2::eventgen::Trigger triggerDstar(double rapidityMin = -1., double rapidityMax = -1.) +{ + return [rapidityMin, rapidityMax](const std::vector& particles) -> bool { + for (const auto& particle : particles) { + if (TMath::Abs(particle.GetPdgCode()) == 413) + if ((particle.Y() > rapidityMin) && (particle.Y() < rapidityMax)) + return kTRUE; + } + return kFALSE; + }; +} + +o2::eventgen::Trigger triggerPhi(double rapidityMin = -1., double rapidityMax = -1.) +{ + return [rapidityMin, rapidityMax](const std::vector& particles) -> bool { + for (const auto& particle : particles) { + if (TMath::Abs(particle.GetPdgCode()) == 333) + if ((particle.Y() > rapidityMin) && (particle.Y() < rapidityMax)) + return kTRUE; + } + return kFALSE; + }; +} + +o2::eventgen::Trigger triggerKstar(double rapidityMin = -1., double rapidityMax = -1.) +{ + return [rapidityMin, rapidityMax](const std::vector& particles) -> bool { + for (const auto& particle : particles) { + if (TMath::Abs(particle.GetPdgCode()) == 313) + if ((particle.Y() > rapidityMin) && (particle.Y() < rapidityMax)) + return kTRUE; + } + return kFALSE; + }; +} + +o2::eventgen::Trigger triggerLc(double rapidityMin = -1., double rapidityMax = -1.) +{ + return [rapidityMin, rapidityMax](const std::vector& particles) -> bool { + for (const auto& particle : particles) { + if (TMath::Abs(particle.GetPdgCode()) == 4122) + if ((particle.Y() > rapidityMin) && (particle.Y() < rapidityMax)) + return kTRUE; + } + return kFALSE; + }; +} + +o2::eventgen::Trigger triggerJpsi(double rapidityMin = -1., double rapidityMax = -1.) +{ + return [rapidityMin, rapidityMax](const std::vector& particles) -> bool { + for (const auto& particle : particles) { + if (TMath::Abs(particle.GetPdgCode()) == 443) + if ((particle.Y() > rapidityMin) && (particle.Y() < rapidityMax)) + return kTRUE; + } + return kFALSE; + }; +} + + + diff --git a/MC/config/QC/json/ITSTPCmatchedTracks_direct_MC.json b/MC/config/QC/json/ITSTPCmatchedTracks_direct_MC.json index 9f6f9eae6..8b80ff34f 100644 --- a/MC/config/QC/json/ITSTPCmatchedTracks_direct_MC.json +++ b/MC/config/QC/json/ITSTPCmatchedTracks_direct_MC.json @@ -1,74 +1,75 @@ { - "qc" : { - "config" : { - "database" : { - "implementation" : "CCDB", - "host" : "ccdb-test.cern.ch:8080", - "username" : "not_applicable", - "password" : "not_applicable", - "name" : "not_applicable" + "qc": { + "config": { + "database": { + "implementation": "CCDB", + "host": "ccdb-test.cern.ch:8080", + "username": "not_applicable", + "password": "not_applicable", + "name": "not_applicable" }, - "Activity" : { - "number" : "42", - "type" : "2", + "Activity": { + "number": "42", + "type": "2", "provenance": "qc_mc", "passName": "passMC", "periodName": "SimChallenge" }, - "monitoring" : { - "url" : "no-op://" + "monitoring": { + "url": "no-op://" }, - "consul" : { - "url" : "" + "consul": { + "url": "" }, - "conditionDB" : { - "url" : "ccdb-test.cern.ch:8080" - }, - "infologger" : { "" : "Configuration of the Infologger (optional).", - "filterDiscardDebug" : "false", - "" : "Set to true to discard debug and trace messages (default: false)", - "filterDiscardLevel" : "21", - "" : "Message at this level or above are discarded (default: 21 - Trace)" } + "conditionDB": { + "url": "alice-ccdb.cern.ch" + } }, - "tasks" : { - "MatchTrITSTPC" : { - "active" : "true", - "className" : "o2::quality_control_modules::glo::ITSTPCMatchingTask", - "moduleName" : "QcGLO", - "detectorName" : "GLO", - "cycleDurationSeconds" : "3600", - "maxNumberCycles" : "-1", - "dataSource" : { - "type" : "direct", - "query_comment" : "checking every matched track", - "query" : "trackITSTPC:GLO/TPCITS/0;trackITSTPCABREFS:GLO/TPCITSAB_REFS/0;trackITSTPCABCLID:GLO/TPCITSAB_CLID/0;trackTPC:TPC/TRACKS;trackTPCClRefs:TPC/CLUSREFS;trackITS:ITS/TRACKS/0;trackITSROF:ITS/ITSTrackROF/0;trackITSClIdx:ITS/TRACKCLSID/0;alpparITS:ITS/ALPIDEPARAM/0?lifetime=condition&ccdb-path=ITS/Config/AlpideParam;trackTPCMCTR:TPC/TRACKSMCLBL;trackITSTPCMCTR:GLO/TPCITS_MC;trackITSTPCABMCTR:GLO/TPCITSAB_MC;trackITSMCTR:ITS/TRACKSMCTR" + "tasks": { + "MatchTrITSTPC": { + "active": "true", + "className": "o2::quality_control_modules::glo::ITSTPCMatchingTask", + "moduleName": "QcGLO", + "detectorName": "GLO", + "cycleDurationSeconds": "3600", + "maxNumberCycles": "-1", + "dataSource": { + "type": "direct", + "query_comment": "checking every matched track", + "query": "trackITSTPC:GLO/TPCITS/0;trackITSTPCABREFS:GLO/TPCITSAB_REFS/0;trackITSTPCABCLID:GLO/TPCITSAB_CLID/0;trackTPC:TPC/TRACKS;trackTPCClRefs:TPC/CLUSREFS;trackITS:ITS/TRACKS/0;trackITSROF:ITS/ITSTrackROF/0;trackITSClIdx:ITS/TRACKCLSID/0;alpparITS:ITS/ALPIDEPARAM/0?lifetime=condition&ccdb-path=ITS/Config/AlpideParam;trackTPCMCTR:TPC/TRACKSMCLBL;trackITSTPCMCTR:GLO/TPCITS_MC;trackITSTPCABMCTR:GLO/TPCITSAB_MC;trackITSMCTR:ITS/TRACKSMCTR" }, - "taskParameters" : { - "GID" : "ITS-TPC,ITS", - "verbose" : "false", - "minPtCut" : "0.1f", - "etaCut" : "1.4f", - "minNTPCClustersCut" : "60", - "minDCACut" : "100.f", - "minDCACutY" : "10.f", - "isMC" : "true" + "taskParameters": { + "GID": "ITS-TPC,ITS", + "verbose": "false", + "minPtITSCut": "0.1f", + "etaITSCut": "1e10f", + "minNITSClustersCut": "0", + "maxChi2PerClusterITS": "100000", + "minPtTPCCut": "0.1f", + "etaTPCCut": "0.9f", + "minNTPCClustersCut": "60", + "minDCACut": "100.f", + "minDCACutY": "10.f", + "minPtCut": "0.1f", + "maxPtCut": "20.f", + "etaCut": "1e10f", + "isMC": "true" }, - "grpGeomRequest" : { - "geomRequest": "Aligned", + "grpGeomRequest": { + "geomRequest": "None", "askGRPECS": "false", "askGRPLHCIF": "false", "askGRPMagField": "true", "askMatLUT": "false", "askTime": "false", "askOnceAllButField": "true", - "needPropagatorD": "false" + "needPropagatorD": "false" }, - "location" : "remote", - "saveObjectsToFile" : "ITSTPCmatched.root", - "" : "For debugging, path to the file where to save. If empty or missing it won't save." + "location": "remote", + "saveObjectsToFile": "ITSTPCmatched.root", + "": "For debugging, path to the file where to save. If empty or missing it won't save." } } }, - "dataSamplingPolicies" : [ - ] + "dataSamplingPolicies": [] } diff --git a/MC/config/QC/json/cpv-clusters-task.json b/MC/config/QC/json/cpv-clusters-task.json index 6292682ac..5d03bc94e 100644 --- a/MC/config/QC/json/cpv-clusters-task.json +++ b/MC/config/QC/json/cpv-clusters-task.json @@ -16,13 +16,13 @@ "periodName": "SimChallenge" }, "monitoring": { - "url": "infologger:///debug?qc" + "url": "no-op://" }, "consul": { "url": "" }, "conditionDB": { - "url": "ccdb-test.cern.ch:8080" + "url": "alice-ccdb.cern.ch" } }, "tasks": { diff --git a/MC/config/QC/json/cpv-digits-task.json b/MC/config/QC/json/cpv-digits-task.json index bf6d788ef..2fd5f3c30 100644 --- a/MC/config/QC/json/cpv-digits-task.json +++ b/MC/config/QC/json/cpv-digits-task.json @@ -16,13 +16,13 @@ "periodName": "SimChallenge" }, "monitoring": { - "url": "infologger:///debug?qc" + "url": "no-op://" }, "consul": { "url": "" }, "conditionDB": { - "url": "ccdb-test.cern.ch:8080" + "url": "alice-ccdb.cern.ch" } }, "tasks": { diff --git a/MC/config/QC/json/emc-cell-task.json b/MC/config/QC/json/emc-bc-task.json similarity index 66% rename from MC/config/QC/json/emc-cell-task.json rename to MC/config/QC/json/emc-bc-task.json index b9fef5db6..38e5c26eb 100644 --- a/MC/config/QC/json/emc-cell-task.json +++ b/MC/config/QC/json/emc-bc-task.json @@ -22,24 +22,28 @@ "url": "" }, "conditionDB": { - "url": "ccdb-test.cern.ch:8080" + "url": "alice-ccdb.cern.ch" } }, "tasks": { - "Cells": { + "BCTaskEMCAL": { "active": "true", - "className": "o2::quality_control_modules::emcal::CellTask", + "taskName": "BCs", + "className": "o2::quality_control_modules::emcal::BCTask", "moduleName": "QcEMCAL", "detectorName": "EMC", "cycleDurationSeconds": "60", "maxNumberCycles": "-1", "dataSource": { "type": "direct", - "query": "emcal-cells:EMC/CELLS;emcal-triggerecords:EMC/CELLSTRGR" + "query": "emcal-triggers:EMC/CELLSTRGR/0;ctp-digits:CTP/DIGITS;ctp-config:CTP/CONFIG/0?lifetime=condition&ccdb-path=CTP/Config/Config&ccdb-run-dependent=1" + }, + "taskParameters": { + "AliasMB": "MINBIAS_TVX_L0,C0TVX,CMTVXTSC,C0TVXTSC,C0TVXTCE" } } }, "checks": {} }, "dataSamplingPolicies": [] -} +} \ No newline at end of file diff --git a/MC/config/QC/json/emc-reco-tasks.json b/MC/config/QC/json/emc-reco-tasks.json new file mode 100644 index 000000000..1c99fb4b1 --- /dev/null +++ b/MC/config/QC/json/emc-reco-tasks.json @@ -0,0 +1,82 @@ +{ + "qc": { + "config": { + "database": { + "implementation": "CCDB", + "host": "ccdb-test.cern.ch:8080", + "username": "not_applicable", + "password": "not_applicable", + "name": "not_applicable" + }, + "Activity": { + "number": "42", + "type": "2", + "provenance": "qc_mc", + "passName": "passMC", + "periodName": "SimChallenge" + }, + "monitoring": { + "url": "no-op://" + }, + "consul": { + "url": "" + }, + "conditionDB": { + "url": "alice-ccdb.cern.ch" + } + }, + "tasks": { + "Cells": { + "active": "true", + "className": "o2::quality_control_modules::emcal::CellTask", + "moduleName": "QcEMCAL", + "detectorName": "EMC", + "cycleDurationSeconds": "60", + "maxNumberCycles": "-1", + "dataSource": { + "type": "direct", + "query": "emcal-cells:EMC/CELLS/0;emcal-triggerecords:EMC/CELLSTRGR/0" + } + }, + "Clusters": { + "active": "true", + "taskName": "Clusters", + "className": "o2::quality_control_modules::emcal::ClusterTask", + "moduleName": "QcEMCAL", + "detectorName": "EMC", + "cycleDurationSeconds": "60", + "maxNumberCycles": "-1", + "dataSource": { + "type": "direct", + "query": "emcal-cells:EMC/CELLS/0;emcal-triggerecords:EMC/CELLSTRGR/0" + }, + "taskParameters": { + "useInternalClusterizer": "true", + "bindingCellTriggerRecords": "emcal-triggerecords", + "calibrateCells": "false", + "clusterizerSeedThreshold": "0.5", + "clusterizerCellTreshold": "0.1", + "clusterizerDoGradientCut": "true", + "clusterizerGradientCut": "0.03", + "clusterizerMinTime": "-300.", + "clusterizerMaxTime": "300.", + "clusterizerMaxTimeDelta": "1000.", + "hasInvMassMesons": "true", + "mesonClustersRejectExotics": 1 + }, + "grpGeomRequest": { + "geomRequest": "Ideal", + "askGRPECS": "false", + "askGRPLHCIF": "false", + "askGRPMagField": "true", + "askMatLUT": "false", + "askTime": "false", + "askOnceAllButField": "true", + "needPropagatorD": "false" + } + } + }, + "checks": {} + }, + "dataSamplingPolicies": [] +} \ No newline at end of file diff --git a/MC/config/QC/json/event-selection-qa.json b/MC/config/QC/json/event-selection-qa.json index 5a662309a..aafa2f51a 100644 --- a/MC/config/QC/json/event-selection-qa.json +++ b/MC/config/QC/json/event-selection-qa.json @@ -1,40 +1,40 @@ { - "internal-dpl-aod-index-builder": "", - "internal-dpl-aod-reader": { - "time-limit": "0", - "orbit-offset-enumeration": "0", - "orbit-multiplier-enumeration": "0", - "start-value-enumeration": "0", - "end-value-enumeration": "-1", - "step-value-enumeration": "1", - "aod-file": "../AO2D.root" - }, - "internal-dpl-aod-spawner": "", - "timestamp-task": { - "verbose": "false", - "rct-path": "RCT\/RunInformation\/", - "start-orbit-path": "GRP\/StartOrbit", - "ccdb-url": "http:\/\/alice-ccdb.cern.ch", - "isRun2MC": "false" - }, - "bc-selection-task": { - "processRun2": "false", - "processRun3": "true" - }, - "event-selection-task": { - "syst": "pp", - "muonSelection": "0", - "customDeltaBC": "300", - "isMC": "true", - "processRun2": "false", - "processRun3": "true" - }, - "event-selection-qa-task": { - "isMC": "true", - "processRun2": "false", - "processRun3": "true", - "processMCRun3": "true" - }, - "internal-dpl-injected-dummy-sink": "", - "internal-dpl-aod-global-analysis-file-sink": "" -} + "internal-dpl-aod-index-builder": "", + "internal-dpl-aod-reader": { + "time-limit": "0", + "orbit-offset-enumeration": "0", + "orbit-multiplier-enumeration": "0", + "start-value-enumeration": "0", + "end-value-enumeration": "-1", + "step-value-enumeration": "1", + "aod-file": "../AO2D.root" + }, + "internal-dpl-aod-spawner": "", + "timestamp-task": { + "verbose": "false", + "rct-path": "RCT/RunInformation/", + "start-orbit-path": "GRP/StartOrbit", + "ccdb-url": "http://alice-ccdb.cern.ch", + "isRun2MC": "false" + }, + "bc-selection-task": { + "processRun2": "false", + "processRun3": "true" + }, + "event-selection-task": { + "syst": "pp", + "muonSelection": "0", + "customDeltaBC": "300", + "isMC": "true", + "processRun2": "false", + "processRun3": "true" + }, + "event-selection-qa-task": { + "isMC": "true", + "processRun2": "false", + "processRun3": "true", + "processMCRun3": "true" + }, + "internal-dpl-injected-dummy-sink": "", + "internal-dpl-aod-global-analysis-file-sink": "" +} \ No newline at end of file diff --git a/MC/config/QC/json/event-track-qa.json b/MC/config/QC/json/event-track-qa.json index 2a28f345a..303772188 100644 --- a/MC/config/QC/json/event-track-qa.json +++ b/MC/config/QC/json/event-track-qa.json @@ -1,142 +1,142 @@ { - "bc-selection-task": { - "processRun2": "false", - "processRun3": "true" - }, - "qa-tracking-efficiency-data": { - "eta-min": "-0.8", - "eta-max": "0.8", - "pt-min": "0.1", - "pt-max": "10", - "track-sel": "true", - "pt-bins": "500", - "log-pt": "1", - "eta-bins": "500", - "phi-bins": "500", - "make-eff": "true" - }, - "qa-tracking-efficiency-muon": { - "eta-min": "-0.8", - "eta-max": "0.8", - "pt-min": "0.1", - "pt-max": "10", - "track-sel": "true", - "pt-bins": "500", - "log-pt": "1", - "eta-bins": "500", - "phi-bins": "500", - "make-eff": "true", - "sel-prim": "true" - }, - "qa-tracking-efficiency-pion": { - "eta-min": "-0.8", - "eta-max": "0.8", - "pt-min": "0.1", - "pt-max": "10", - "track-sel": "true", - "pt-bins": "500", - "log-pt": "1", - "eta-bins": "500", - "phi-bins": "500", - "make-eff": "true", - "sel-prim": "true" - }, - "qa-tracking-efficiency-electron": { - "eta-min": "-0.8", - "eta-max": "0.8", - "pt-min": "0.1", - "pt-max": "10", - "track-sel": "true", - "pt-bins": "500", - "log-pt": "1", - "eta-bins": "500", - "phi-bins": "500", - "make-eff": "true", - "sel-prim": "true" - }, - "qa-tracking-efficiency-kaon": { - "eta-min": "-0.8", - "eta-max": "0.8", - "pt-min": "0.1", - "pt-max": "10", - "track-sel": "true", - "pt-bins": "500", - "log-pt": "1", - "eta-bins": "500", - "phi-bins": "500", - "make-eff": "true", - "sel-prim": "true" - }, - "qa-tracking-efficiency-proton": { - "eta-min": "-0.8", - "eta-max": "0.8", - "pt-min": "0.1", - "pt-max": "10", - "track-sel": "true", - "pt-bins": "500", - "log-pt": "1", - "eta-bins": "500", - "phi-bins": "500", - "make-eff": "true", - "sel-prim": "true" - }, - "qa-tracking-efficiency-deuteron": { - "eta-min": "-0.8", - "eta-max": "0.8", - "pt-min": "0.1", - "pt-max": "10", - "track-sel": "true", - "pt-bins": "500", - "log-pt": "1", - "eta-bins": "500", - "phi-bins": "500", - "make-eff": "true", - "sel-prim": "true" - }, - "qa-tracking-efficiency-triton": { - "eta-min": "-0.8", - "eta-max": "0.8", - "pt-min": "0.1", - "pt-max": "10", - "track-sel": "true", - "pt-bins": "500", - "log-pt": "1", - "eta-bins": "500", - "phi-bins": "500", - "make-eff": "true", - "sel-prim": "true" - }, - "qa-tracking-efficiency-helium3": { - "eta-min": "-0.8", - "eta-max": "0.8", - "pt-min": "0.1", - "pt-max": "10", - "track-sel": "true", - "pt-bins": "500", - "log-pt": "1", - "eta-bins": "500", - "phi-bins": "500", - "make-eff": "true", - "sel-prim": "true" - }, - "event-selection-task": { - "syst": "pp", - "muonSelection": "0", - "isMC": "true", - "processRun2": "false", - "processRun3": "true" - }, - "track-extension": { - "processRun2": "false", - "processRun3": "true" - }, - "track-selection": { - "isRun3": "true" - }, - "qa-event-track": { - "isMC": "true", - "isRun3": "true", - "processData": "false", - "processMC": "true" - } -} + "bc-selection-task": { + "processRun2": "false", + "processRun3": "true" + }, + "qa-tracking-efficiency-data": { + "eta-min": "-0.8", + "eta-max": "0.8", + "pt-min": "0.1", + "pt-max": "10", + "track-sel": "true", + "pt-bins": "500", + "log-pt": "1", + "eta-bins": "500", + "phi-bins": "500", + "make-eff": "true" + }, + "qa-tracking-efficiency-muon": { + "eta-min": "-0.8", + "eta-max": "0.8", + "pt-min": "0.1", + "pt-max": "10", + "track-sel": "true", + "pt-bins": "500", + "log-pt": "1", + "eta-bins": "500", + "phi-bins": "500", + "make-eff": "true", + "sel-prim": "true" + }, + "qa-tracking-efficiency-pion": { + "eta-min": "-0.8", + "eta-max": "0.8", + "pt-min": "0.1", + "pt-max": "10", + "track-sel": "true", + "pt-bins": "500", + "log-pt": "1", + "eta-bins": "500", + "phi-bins": "500", + "make-eff": "true", + "sel-prim": "true" + }, + "qa-tracking-efficiency-electron": { + "eta-min": "-0.8", + "eta-max": "0.8", + "pt-min": "0.1", + "pt-max": "10", + "track-sel": "true", + "pt-bins": "500", + "log-pt": "1", + "eta-bins": "500", + "phi-bins": "500", + "make-eff": "true", + "sel-prim": "true" + }, + "qa-tracking-efficiency-kaon": { + "eta-min": "-0.8", + "eta-max": "0.8", + "pt-min": "0.1", + "pt-max": "10", + "track-sel": "true", + "pt-bins": "500", + "log-pt": "1", + "eta-bins": "500", + "phi-bins": "500", + "make-eff": "true", + "sel-prim": "true" + }, + "qa-tracking-efficiency-proton": { + "eta-min": "-0.8", + "eta-max": "0.8", + "pt-min": "0.1", + "pt-max": "10", + "track-sel": "true", + "pt-bins": "500", + "log-pt": "1", + "eta-bins": "500", + "phi-bins": "500", + "make-eff": "true", + "sel-prim": "true" + }, + "qa-tracking-efficiency-deuteron": { + "eta-min": "-0.8", + "eta-max": "0.8", + "pt-min": "0.1", + "pt-max": "10", + "track-sel": "true", + "pt-bins": "500", + "log-pt": "1", + "eta-bins": "500", + "phi-bins": "500", + "make-eff": "true", + "sel-prim": "true" + }, + "qa-tracking-efficiency-triton": { + "eta-min": "-0.8", + "eta-max": "0.8", + "pt-min": "0.1", + "pt-max": "10", + "track-sel": "true", + "pt-bins": "500", + "log-pt": "1", + "eta-bins": "500", + "phi-bins": "500", + "make-eff": "true", + "sel-prim": "true" + }, + "qa-tracking-efficiency-helium3": { + "eta-min": "-0.8", + "eta-max": "0.8", + "pt-min": "0.1", + "pt-max": "10", + "track-sel": "true", + "pt-bins": "500", + "log-pt": "1", + "eta-bins": "500", + "phi-bins": "500", + "make-eff": "true", + "sel-prim": "true" + }, + "event-selection-task": { + "syst": "pp", + "muonSelection": "0", + "isMC": "true", + "processRun2": "false", + "processRun3": "true" + }, + "track-extension": { + "processRun2": "false", + "processRun3": "true" + }, + "track-selection": { + "isRun3": "true" + }, + "qa-event-track": { + "isMC": "true", + "isRun3": "true", + "processData": "false", + "processMC": "true" + } +} \ No newline at end of file diff --git a/MC/config/QC/json/fdd-recpoints.json b/MC/config/QC/json/fdd-recpoints.json new file mode 100644 index 000000000..8f61b6893 --- /dev/null +++ b/MC/config/QC/json/fdd-recpoints.json @@ -0,0 +1,38 @@ +{ + "qc": { + "config": { + "database": { + "implementation": "CCDB", + "host": "ccdb-test.cern.ch:8080", + "username": "not_applicable", + "password": "not_applicable", + "name": "not_applicable" + }, + "Activity": { + "provenance": "qc_mc", + "passName": "passMC", + "periodName": "SimChallenge" + }, + "monitoring": { + "url": "no-op://" + }, + "conditionDB": { + "url": "alice-ccdb.cern.ch" + } + }, + "tasks": { + "RecPoints": { + "active": "true", + "className": "o2::quality_control_modules::fdd::RecPointsQcTask", + "moduleName": "QcFDD", + "detectorName": "FDD", + "cycleDurationSeconds": "600", + "maxNumberCycles": "-1", + "dataSource": { + "type": "direct", + "query": "recpoints:FDD/RECPOINTS/0;channels:FDD/RECCHDATA/0" + } + } + } + } +} \ No newline at end of file diff --git a/MC/config/QC/json/ft0-reconstruction-config.json b/MC/config/QC/json/ft0-reconstruction-config.json index 0f8f770cf..95b93f6f6 100644 --- a/MC/config/QC/json/ft0-reconstruction-config.json +++ b/MC/config/QC/json/ft0-reconstruction-config.json @@ -1,44 +1,53 @@ { - "qc": { - "config": { - "database": { - "implementation": "CCDB", - "host": "ccdb-test.cern.ch:8080", - "username": "not_applicable", - "password": "not_applicable", - "name": "not_applicable" - }, - "Activity": { - "number": "42", - "type": "2", - "provenance": "qc_mc", - "passName": "passMC", - "periodName": "SimChallenge" - }, - "monitoring": { - "url": "no-op://" - }, - "consul": { - "url": "" - }, - "conditionDB": { - "url": "ccdb-test.cern.ch:8080" - } + "qc": { + "config": { + "database": { + "implementation": "CCDB", + "host": "ccdb-test.cern.ch:8080", + "username": "not_applicable", + "password": "not_applicable", + "name": "not_applicable" + }, + "Activity": { + "number": "42", + "type": "2", + "provenance": "qc_mc", + "passName": "passMC", + "periodName": "SimChallenge" }, - "tasks": { - "RecPoints": { - "active": "true", - "className": "o2::quality_control_modules::ft0::RecPointsQcTask", - "moduleName": "QcFT0", - "detectorName": "FT0", - "cycleDurationSeconds": "600", - "maxNumberCycles": "-1", - "dataSource": { - "type": "direct", - "query": "recpoints:FT0/RECPOINTS/0;channels:FT0/RECCHDATA/0" - } + "monitoring": { + "url": "no-op://" + }, + "consul": { + "url": "" + }, + "conditionDB": { + "url": "alice-ccdb.cern.ch" + } + }, + "tasks": { + "RecPoints": { + "active": "true", + "className": "o2::quality_control_modules::ft0::RecPointsQcTask", + "moduleName": "QcFT0", + "detectorName": "FT0", + "cycleDurationSeconds": "600", + "maxNumberCycles": "-1", + "dataSource": { + "type": "direct", + "query": "recpoints:FT0/RECPOINTS/0;channels:FT0/RECCHDATA/0" + }, + "grpGeomRequest" : { + "geomRequest": "None", + "askGRPECS": "false", + "askGRPLHCIF": "true", + "askGRPMagField": "false", + "askMatLUT": "false", + "askTime": "false", + "askOnceAllButField": "false", + "needPropagatorD": "false" } - } } -} + } +} \ No newline at end of file diff --git a/MC/config/QC/json/fv0-digits.json b/MC/config/QC/json/fv0-digits.json new file mode 100644 index 000000000..dbb559621 --- /dev/null +++ b/MC/config/QC/json/fv0-digits.json @@ -0,0 +1,51 @@ +{ + "qc": { + "config": { + "database": { + "implementation": "CCDB", + "host": "ccdb-test.cern.ch:8080", + "username": "not_applicable", + "password": "not_applicable", + "name": "not_applicable" + }, + "Activity": { + "provenance": "qc_mc", + "passName": "passMC", + "periodName": "SimChallenge" + }, + "monitoring": { + "url": "no-op://" + }, + "conditionDB": { + "url": "alice-ccdb.cern.ch" + } + }, + "tasks": { + "Digits": { + "active": "true", + "className": "o2::quality_control_modules::fv0::DigitQcTask", + "moduleName": "QcFV0", + "detectorName": "FV0", + "cycleDurationSeconds": "600", + "maxNumberCycles": "-1", + "dataSource": { + "type": "direct", + "query": "digits:FV0/DIGITSBC/0;channels:FV0/DIGITSCH/0" + }, + "taskParameters": { + "#ChannelIDs": "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", + "#ChannelIDsAmpVsTime": "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", + "trgModeInnerOuterThresholdVar": "Ampl", + "trgThresholdNChannels": "2", + "trgThresholdCharge": "8", + "trgThresholdChargeInner": "4", + "trgThresholdChargeOuter": "4", + "trgChargeLevelLow": "6", + "trgChargeLevelHigh": "4095", + "minGateTimeForRatioHistogram": "-192", + "maxGateTimeForRatioHistogram": "192" + } + } + } + } +} \ No newline at end of file diff --git a/MC/config/QC/json/hmp.json b/MC/config/QC/json/hmp.json new file mode 100644 index 000000000..2995fef64 --- /dev/null +++ b/MC/config/QC/json/hmp.json @@ -0,0 +1,57 @@ +{ + "qc":{ + "config":{ + "database":{ + "implementation":"CCDB", + "host":"ccdb-test.cern.ch:8080", + "username":"not_applicable", + "password":"not_applicable", + "name":"not_applicable" + }, + "Activity":{ + "number": "42", + "type": "2", + "provenance": "qc_mc", + "passName": "passMC", + "periodName": "SimChallenge" + }, + "monitoring":{ + "url":"infologger:///debug?qc" + }, + "consul":{ + "url":"http://consul-test.cern.ch:8500" + }, + "conditionDB":{ + "url":"ccdb-test.cern.ch:8080" + } + }, + "tasks": { + "HMPClusters":{ + "active":"true", + "taskName" : "Clusters", + "className":"o2::quality_control_modules::hmpid::HmpidTaskClusters", + "moduleName":"QcHMPID", + "detectorName":"HMP", + "cycleDurationSeconds":"60", + "maxNumberCycles":"-1", + "dataSource": { + "type":"direct", + "query":"intrecord:HMP/INTRECORDS1/0;clusters:HMP/CLUSTERS/0" + } + }, + "HMPMatches":{ + "active":"true", + "taskName" : "Matches", + "className":"o2::quality_control_modules::hmpid::HmpidTaskMatches", + "moduleName":"QcHMPID", + "detectorName":"HMP", + "cycleDurationSeconds":"60", + "maxNumberCycles":"-1", + "dataSource": { + "type":"direct", + "query":"matchHMP:HMP/MATCHES/0;hmpidcluster:HMP/CLUSTERS/0;hmpidtriggers:HMP/INTRECORDS1/0" + } + } + } + } +} diff --git a/MC/config/QC/json/its-clusters-tracks-qc.json b/MC/config/QC/json/its-clusters-tracks-qc.json index 5c0cef231..d29046c08 100644 --- a/MC/config/QC/json/its-clusters-tracks-qc.json +++ b/MC/config/QC/json/its-clusters-tracks-qc.json @@ -22,7 +22,7 @@ "url": "" }, "conditionDB": { - "url": "ccdb-test.cern.ch:8080" + "url": "alice-ccdb.cern.ch" } }, "tasks": { @@ -41,20 +41,10 @@ "taskParameters": { "layer": "1111111", "nThreads": "1", - "nBCbins" : "103", - "dicttimestamp" : "0", - "publishSummary1D": "0", - "publishDetailedSummary": "1" - }, - "grpGeomRequest" : { - "geomRequest": "Aligned", - "askGRPECS": "false", - "askGRPLHCIF": "false", - "askGRPMagField": "false", - "askMatLUT": "false", - "askTime": "false", - "askOnceAllButField": "true", - "needPropagatorD": "false" + "nBCbins": "103", + "dicttimestamp": "0", + "publishSummary1D": "0", + "publishDetailedSummary": "1" } }, "Tracks": { @@ -74,11 +64,12 @@ "vertexXYsize": "0.5", "vertexZsize": "15", "vertexRsize": "0.8", - "NtracksMAX" : "5000", + "NtracksMAX": "5000", "doTTree": "0", - "nBCbins" : "103", - "dicttimestamp" : "0", - "doNorm": "1" + "nBCbins": "103", + "dicttimestamp": "0", + "doNorm": "1", + "publishMore": "0" } } }, @@ -90,15 +81,15 @@ "policy": "OnEachSeparately", "detectorName": "ITS", "checkParameters": { - "maxcluoccL0" : "5", - "maxcluoccL1" : "4", - "maxcluoccL2" : "3", - "maxcluoccL3" : "2", - "maxcluoccL4" : "1", - "maxcluoccL5" : "1", - "maxcluoccL6" : "1", - "skipxbinsoccupancy" : "", - "skipybinsoccupancy" : "" + "maxcluoccL0": "5", + "maxcluoccL1": "4", + "maxcluoccL2": "3", + "maxcluoccL3": "2", + "maxcluoccL4": "1", + "maxcluoccL5": "1", + "maxcluoccL6": "1", + "skipxbinsoccupancy": "", + "skipybinsoccupancy": "" }, "dataSource": [ { @@ -141,11 +132,11 @@ "VertexCoordinates", "VertexRvsZ", "VertexZ", - "BunchCrossingIDvsClusterRatio" + "BunchCrossingIDvsClusterRatio" ] } ] } } - } + } } diff --git a/MC/config/QC/json/its-mc-tracks-qc.json b/MC/config/QC/json/its-mc-tracks-qc.json index 187c2110d..97cb8846c 100644 --- a/MC/config/QC/json/its-mc-tracks-qc.json +++ b/MC/config/QC/json/its-mc-tracks-qc.json @@ -1,49 +1,48 @@ { - "qc" : { - "config" : { - "database" : { - "implementation" : "CCDB", - "host" : "ccdb-test.cern.ch:8080", - "username" : "not_applicable", - "password" : "not_applicable", - "name" : "not_applicable" + "qc": { + "config": { + "database": { + "implementation": "CCDB", + "host": "ccdb-test.cern.ch:8080", + "username": "not_applicable", + "password": "not_applicable", + "name": "not_applicable" }, - "Activity" : { - "number" : "42", - "type" : "2", + "Activity": { + "number": "42", + "type": "2", "provenance": "qc_mc", "passName": "passMC", "periodName": "SimChallenge" }, - "monitoring" : { - "url" : "no-op://" + "monitoring": { + "url": "no-op://" }, - "consul" : { - "url" : "" + "consul": { + "url": "" }, - "conditionDB" : { - "url" : "ccdb-test.cern.ch:8080" + "conditionDB": { + "url": "alice-ccdb.cern.ch" } }, - "tasks" : { - "TracksMc" : { - "active" : "true", - "className" : "o2::quality_control_modules::its::ITSTrackSimTask", - "moduleName" : "QcITS", - "detectorName" : "ITS", - "cycleDurationSeconds" : "30", - "maxNumberCycles" : "-1", - "dataSource_comment" : "The other type of dataSource is \"direct\", see basic-no-sampling.json.", - "dataSource" : { - "type" : "direct", - "query" : "tracks:ITS/TRACKS/0;mstruth:ITS/TRACKSMCTR/0;compclus:ITS/COMPCLUSTERS/0;mcclustruth:ITS/CLUSTERSMCTR/0" + "tasks": { + "TracksMc": { + "active": "true", + "className": "o2::quality_control_modules::its::ITSTrackSimTask", + "moduleName": "QcITS", + "detectorName": "ITS", + "cycleDurationSeconds": "30", + "maxNumberCycles": "-1", + "dataSource_comment": "The other type of dataSource is \"direct\", see basic-no-sampling.json.", + "dataSource": { + "type": "direct", + "query": "tracks:ITS/TRACKS/0;mstruth:ITS/TRACKSMCTR/0;compclus:ITS/COMPCLUSTERS/0;mcclustruth:ITS/CLUSTERSMCTR/0" }, - "taskParameters" : { - "O2GrpPath" : "./o2sim_grp.root", - "collisionsContextPath": "./collisioncontext.root" - } - + "taskParameters": { + "O2GrpPath": "./o2sim_grp.root", + "collisionsContextPath": "./collisioncontext.root" + } } } - } -} + } +} \ No newline at end of file diff --git a/MC/config/QC/json/mch-digits-task.json b/MC/config/QC/json/mch-digits-task.json new file mode 100644 index 000000000..aad04c70a --- /dev/null +++ b/MC/config/QC/json/mch-digits-task.json @@ -0,0 +1,58 @@ +{ + "qc": { + "config": { + "database": { + "implementation": "CCDB", + "host": "localhost:6464", + "username": "not_applicable", + "password": "not_applicable", + "name": "not_applicable", + "maxObjectSize": "12582912" + }, + "Activity": { + "number": "42", + "type": "2", + "provenance": "qc_mc", + "passName": "passMC", + "periodName": "SimChallenge" + }, + "monitoring": { + "url": "infologger:///debug?qc" + }, + "consul": { + "url": "" + }, + "conditionDB": { + "url": "" + } + }, + "tasks": { + "MCHDigits": { + "active": "true", + "taskName": "Digits", + "className": "o2::quality_control_modules::muonchambers::DigitsTask", + "moduleName": "QcMuonChambers", + "detectorName": "MCH", + "cycleDurationSeconds": "300", + "maxNumberCycles": "-1", + "dataSource": { + "type": "direct", + "query": "digits:MCH/DIGITS" + }, + "taskParameters": { + "Diagnostic": "false" + }, + "grpGeomRequest": { + "geomRequest": "None", + "askGRPECS": "true", + "askGRPLHCIF": "false", + "askGRPMagField": "false", + "askMatLUT": "false", + "askTime": "false", + "askOnceAllButField": "false", + "needPropagatorD": "false" + } + } + } + } +} diff --git a/MC/config/QC/json/mch-errors-task.json b/MC/config/QC/json/mch-errors-task.json new file mode 100644 index 000000000..154cb3c75 --- /dev/null +++ b/MC/config/QC/json/mch-errors-task.json @@ -0,0 +1,44 @@ +{ + "qc": { + "config": { + "database": { + "implementation": "CCDB", + "host": "http://ccdb-test.cern.ch:8080", + "username": "not_applicable", + "password": "not_applicable", + "name": "not_applicable" + }, + "Activity": { + "number": "42", + "type": "2", + "provenance": "qc_mc", + "passName": "passMC", + "periodName": "SimChallenge" + }, + "monitoring": { + "url": "infologger:///debug?qc" + }, + "consul": { + "url": "" + }, + "conditionDB": { + "url": "" + } + }, + "tasks": { + "MCHErrors": { + "active": "true", + "taskName": "Errors", + "className": "o2::quality_control_modules::muonchambers::ErrorTask", + "moduleName": "QcMuonChambers", + "detectorName": "MCH", + "cycleDurationSeconds": "600", + "maxNumberCycles": "-1", + "dataSource": { + "type": "direct", + "query": "errors:MCH/PROCERRORS/0" + } + } + } + } +} diff --git a/MC/config/QC/json/mch-reco-task.json b/MC/config/QC/json/mch-reco-task.json new file mode 100644 index 000000000..e6f2b719b --- /dev/null +++ b/MC/config/QC/json/mch-reco-task.json @@ -0,0 +1,74 @@ +{ + "qc": { + "config": { + "database": { + "implementation": "CCDB", + "host": "localhost:6464", + "username": "not_applicable", + "password": "not_applicable", + "name": "not_applicable" + }, + "Activity": { + "number": "42", + "type": "2", + "provenance": "qc_mc", + "passName": "passMC", + "periodName": "SimChallenge" + }, + "monitoring": { + "url": "infologger:///debug?qc" + }, + "consul": { + "url": "" + }, + "conditionDB": { + "url": "" + } + }, + "tasks": { + "Rofs": { + "active": "true", + "className": "o2::quality_control_modules::muonchambers::RofsTask", + "moduleName": "QcMuonChambers", + "detectorName": "MCH", + "cycleDurationSeconds": "300", + "maxNumberCycles": "-1", + "dataSource": { + "type": "direct", + "query": "digits:MCH/DIGITS;rofs:MCH/DIGITROFS" + }, + "taskParameters": { + "Diagnostic": "false" + } + }, + "MCHPreclusters": { + "active": "true", + "taskName": "Preclusters", + "className": "o2::quality_control_modules::muonchambers::PreclustersTask", + "moduleName": "QcMuonChambers", + "detectorName": "MCH", + "cycleDurationSeconds": "300", + "maxNumberCycles": "-1", + "dataSource": { + "type": "direct", + "query": "preclusters:MCH/PRECLUSTERS/0;preclusterdigits:MCH/PRECLUSTERDIGITS/0" + } + }, + "FRofs": { + "active": "true", + "className": "o2::quality_control_modules::muonchambers::RofsTask", + "moduleName": "QcMuonChambers", + "detectorName": "MCH", + "cycleDurationSeconds": "300", + "maxNumberCycles": "-1", + "dataSource": { + "type": "direct", + "query": "digits:MCH/F-DIGITS;rofs:MCH/TC-F-DIGITROFS" + }, + "taskParameters": { + "Diagnostic": "false" + } + } + } + } +} diff --git a/MC/config/QC/json/mch-tracks-task.json b/MC/config/QC/json/mch-tracks-task.json new file mode 100644 index 000000000..6b4d9fc64 --- /dev/null +++ b/MC/config/QC/json/mch-tracks-task.json @@ -0,0 +1,58 @@ +{ + "qc": { + "config": { + "database": { + "implementation": "CCDB", + "host": "localhost:6464", + "username": "not_applicable", + "password": "not_applicable", + "name": "not_applicable" + }, + "Activity": { + "number": "42", + "type": "2", + "provenance": "qc_mc", + "passName": "passMC", + "periodName": "SimChallenge" + }, + "monitoring": { + "url": "infologger:///debug?qc" + }, + "consul": { + "url": "" + }, + "conditionDB": { + "url": "https://alice-ccdb.cern.ch" + } + }, + "tasks": { + "MCHTracks": { + "active": "true", + "taskName": "Tracks", + "className": "o2::quality_control_modules::muon::TracksTask", + "moduleName": "QcMUONCommon", + "detectorName": "MCH", + "cycleDurationSeconds": "180", + "maxNumberCycles": "-1", + "dataSource": { + "type": "direct", + "query": "trackMCH:MCH/TRACKS;trackMCHROF:MCH/TRACKROFS;trackMCHTRACKCLUSTERS:MCH/TRACKCLUSTERS;mchtrackdigits:MCH/CLUSTERDIGITS" + }, + "taskParameters": { + "maxTracksPerTF": "600", + "GID": "MCH" + }, + "grpGeomRequest": { + "geomRequest": "Aligned", + "askGRPECS": "true", + "askGRPLHCIF": "false", + "askGRPMagField": "true", + "askMatLUT": "false", + "askTime": "false", + "askOnceAllButField": "true", + "needPropagatorD": "false" + } + } + } + } +} diff --git a/MC/config/QC/json/mchmid-tracks-task.json b/MC/config/QC/json/mchmid-tracks-task.json new file mode 100644 index 000000000..67c6e22f9 --- /dev/null +++ b/MC/config/QC/json/mchmid-tracks-task.json @@ -0,0 +1,59 @@ +{ + "qc": { + "config": { + "database": { + "implementation": "CCDB", + "host": "localhost:6464", + "username": "not_applicable", + "password": "not_applicable", + "name": "not_applicable" + }, + "Activity": { + "number": "42", + "type": "2", + "provenance": "qc_mc", + "passName": "passMC", + "periodName": "SimChallenge" + }, + "monitoring": { + "url": "infologger:///debug?qc" + }, + "consul": { + "url": "" + }, + "conditionDB": { + "url": "https://alice-ccdb.cern.ch" + } + }, + "tasks": { + "TaskMUONTracks": { + "active": "true", + "className": "o2::quality_control_modules::muon::TracksTask", + "moduleName": "QcMUONCommon", + "detectorName": "GLO", + "taskName": "MUONTracks", + "cycleDurationSeconds": "300", + "maxNumberCycles": "-1", + "dataSource": { + "type": "direct", + "query": "trackMCH:MCH/TRACKS;trackMCHROF:MCH/TRACKROFS;trackMCHTRACKCLUSTERS:MCH/TRACKCLUSTERS;mchtrackdigits:MCH/CLUSTERDIGITS;trackMID:MID/TRACKS/0;trackMIDROF:MID/TRACKROFS/0;trackMIDTRACKCLUSTERS:MID/TRACKCLUSTERS/0;trackClMIDROF:MID/TRCLUSROFS/0;matchMCHMID:GLO/MTC_MCHMID" + }, + "taskParameters": { + "maxTracksPerTF": "600", + "GID" : "MCH,MID,MCH-MID" + }, + "grpGeomRequest": { + "geomRequest": "Aligned", + "askGRPECS": "true", + "askGRPLHCIF": "false", + "askGRPMagField": "true", + "askMatLUT": "false", + "askTime": "false", + "askOnceAllButField": "false", + "needPropagatorD": "false" + }, + "location": "remote" + } + } + } +} diff --git a/MC/config/QC/json/mft-clusters.json b/MC/config/QC/json/mft-clusters.json index 896ad635a..69cbfe20e 100644 --- a/MC/config/QC/json/mft-clusters.json +++ b/MC/config/QC/json/mft-clusters.json @@ -22,7 +22,7 @@ "url": "" }, "conditionDB": { - "url": "ccdb-test.cern.ch:8080" + "url": "alice-ccdb.cern.ch" } }, "tasks": { @@ -38,36 +38,45 @@ "type": "direct", "query": "randomcluster:MFT/COMPCLUSTERS/0;clustersrof:MFT/CLUSTERSROF/0;patterns:MFT/PATTERNS/0;cldict:MFT/CLUSDICT/0?lifetime=condition&ccdb-path=MFT/Calib/ClusterDictionary" }, - "taskParameters" : { - "maxClusterROFSize" : "50000", - "maxDuration" : "60000", - "timeBinSize" : "0.1", - "ROFLengthInBC" : "594" + "taskParameters": { + "onlineQC" : "0", + "maxClusterROFSize": "50000", + "maxDuration": "60000", + "timeBinSize": "0.1", + "ROFLengthInBC": "594" }, - "grpGeomRequest" : { - "geomRequest": "Aligned", - "askGRPECS": "false", + "grpGeomRequest" : { + "geomRequest": "None", + "askGRPECS": "true", "askGRPLHCIF": "false", - "askGRPMagField": "true", + "askGRPMagField": "false", "askMatLUT": "false", "askTime": "false", - "askOnceAllButField": "true", - "needPropagatorD": "false" - }, + "askOnceAllButField": "false", + "needPropagatorD": "false" + }, "location": "remote" } }, "checks": { "Clusters": { "active": "true", - "dataSource": [{ - "type": "Task", - "name": "Clusters", - "MOs" : ["mClusterOccupancy","mClusterPatternIndex","mClusterSizeSummary","mGroupedClusterSizeSummary","mClusterOccupancySummary"] - }], - "checkParameters" : { - "ZoneThresholdMedium" : "2", - "ZoneThresholdBad" : "5" + "dataSource": [ + { + "type": "Task", + "name": "Clusters", + "MOs": [ + "mClusterOccupancy", + "mClusterPatternIndex", + "mClusterSizeSummary", + "mGroupedClusterSizeSummary", + "mClusterOccupancySummary" + ] + } + ], + "checkParameters": { + "LadderThresholdMedium": "1", + "LadderThresholdBad": "2" }, "className": "o2::quality_control_modules::mft::QcMFTClusterCheck", "moduleName": "QcMFT", @@ -76,6 +85,5 @@ } } }, - "dataSamplingPolicies": [ - ] + "dataSamplingPolicies": [] } diff --git a/MC/config/QC/json/mft-digits-0.json b/MC/config/QC/json/mft-digits-0.json index ce6ab23dc..c1248e74c 100644 --- a/MC/config/QC/json/mft-digits-0.json +++ b/MC/config/QC/json/mft-digits-0.json @@ -22,7 +22,7 @@ "url": "" }, "conditionDB": { - "url": "ccdb-test.cern.ch:8080" + "url": "alice-ccdb.cern.ch" } }, "tasks": { @@ -40,11 +40,21 @@ }, "taskParameters": { "FLP": "0", - "NoiseScan" : "0", - "maxDigitROFSize" : "5000", - "maxDuration" : "60000", - "timeBinSize" : "0.1", - "ROFLengthInBC" : "594" + "NoiseScan": "0", + "maxDigitROFSize": "5000", + "maxDuration": "60000", + "timeBinSize": "0.1", + "ROFLengthInBC": "594" + }, + "grpGeomRequest" : { + "geomRequest": "None", + "askGRPECS": "true", + "askGRPLHCIF": "false", + "askGRPMagField": "false", + "askMatLUT": "false", + "askTime": "false", + "askOnceAllButField": "false", + "needPropagatorD": "false" }, "location": "remote" } @@ -56,18 +66,23 @@ "moduleName": "QcMFT", "detectorName": "MFT", "policy": "OnEachSeparately", - "checkParameters" : { - "ZoneThresholdMedium" : "2", - "ZoneThresholdBad" : "5" + "checkParameters": { + "LadderThresholdMedium": "1", + "LadderThresholdBad": "2" }, - "dataSource": [{ - "type": "Task", - "name": "Digits", - "MOs" : ["mDigitChipOccupancy","mDigitOccupancySummary","mDigitChipStdDev"] - }] + "dataSource": [ + { + "type": "Task", + "name": "Digits", + "MOs": [ + "mDigitChipOccupancy", + "mDigitOccupancySummary", + "mDigitChipStdDev" + ] + } + ] } } }, - "dataSamplingPolicies": [ - ] + "dataSamplingPolicies": [] } diff --git a/MC/config/QC/json/mft-digits-1.json b/MC/config/QC/json/mft-digits-1.json index d4219645c..90021bd57 100644 --- a/MC/config/QC/json/mft-digits-1.json +++ b/MC/config/QC/json/mft-digits-1.json @@ -22,7 +22,7 @@ "url": "" }, "conditionDB": { - "url": "ccdb-test.cern.ch:8080" + "url": "alice-ccdb.cern.ch" } }, "tasks": { @@ -40,11 +40,21 @@ }, "taskParameters": { "FLP": "1", - "NoiseScan" : "0", - "maxDigitROFSize" : "5000", - "maxDuration" : "60000", - "timeBinSize" : "0.1", - "ROFLengthInBC" : "594" + "NoiseScan": "0", + "maxDigitROFSize": "5000", + "maxDuration": "60000", + "timeBinSize": "0.1", + "ROFLengthInBC": "594" + }, + "grpGeomRequest" : { + "geomRequest": "None", + "askGRPECS": "true", + "askGRPLHCIF": "false", + "askGRPMagField": "false", + "askMatLUT": "false", + "askTime": "false", + "askOnceAllButField": "false", + "needPropagatorD": "false" }, "location": "remote" } @@ -56,18 +66,23 @@ "moduleName": "QcMFT", "detectorName": "MFT", "policy": "OnEachSeparately", - "checkParameters" : { - "ZoneThresholdMedium" : "2", - "ZoneThresholdBad" : "5" + "checkParameters": { + "LadderThresholdMedium": "1", + "LadderThresholdBad": "2" }, - "dataSource": [{ - "type": "Task", - "name": "Digits", - "MOs" : ["mDigitChipOccupancy","mDigitOccupancySummary","mDigitChipStdDev"] - }] + "dataSource": [ + { + "type": "Task", + "name": "Digits", + "MOs": [ + "mDigitChipOccupancy", + "mDigitOccupancySummary", + "mDigitChipStdDev" + ] + } + ] } } }, - "dataSamplingPolicies": [ - ] + "dataSamplingPolicies": [] } diff --git a/MC/config/QC/json/mft-digits-2.json b/MC/config/QC/json/mft-digits-2.json index e8f3e6c23..c4391c4f9 100644 --- a/MC/config/QC/json/mft-digits-2.json +++ b/MC/config/QC/json/mft-digits-2.json @@ -22,7 +22,7 @@ "url": "" }, "conditionDB": { - "url": "ccdb-test.cern.ch:8080" + "url": "alice-ccdb.cern.ch" } }, "tasks": { @@ -40,11 +40,21 @@ }, "taskParameters": { "FLP": "2", - "NoiseScan" : "0", - "maxDigitROFSize" : "5000", - "maxDuration" : "60000", - "timeBinSize" : "0.1", - "ROFLengthInBC" : "594" + "NoiseScan": "0", + "maxDigitROFSize": "5000", + "maxDuration": "60000", + "timeBinSize": "0.1", + "ROFLengthInBC": "594" + }, + "grpGeomRequest" : { + "geomRequest": "None", + "askGRPECS": "true", + "askGRPLHCIF": "false", + "askGRPMagField": "false", + "askMatLUT": "false", + "askTime": "false", + "askOnceAllButField": "false", + "needPropagatorD": "false" }, "location": "remote" } @@ -56,18 +66,23 @@ "moduleName": "QcMFT", "detectorName": "MFT", "policy": "OnEachSeparately", - "checkParameters" : { - "ZoneThresholdMedium" : "2", - "ZoneThresholdBad" : "5" + "checkParameters": { + "LadderThresholdMedium": "1", + "LadderThresholdBad": "2" }, - "dataSource": [{ - "type": "Task", - "name": "Digits", - "MOs" : ["mDigitChipOccupancy","mDigitOccupancySummary","mDigitChipStdDev"] - }] + "dataSource": [ + { + "type": "Task", + "name": "Digits", + "MOs": [ + "mDigitChipOccupancy", + "mDigitOccupancySummary", + "mDigitChipStdDev" + ] + } + ] } } }, - "dataSamplingPolicies": [ - ] + "dataSamplingPolicies": [] } diff --git a/MC/config/QC/json/mft-digits-3.json b/MC/config/QC/json/mft-digits-3.json index e581d42a1..1179fb19d 100644 --- a/MC/config/QC/json/mft-digits-3.json +++ b/MC/config/QC/json/mft-digits-3.json @@ -22,7 +22,7 @@ "url": "" }, "conditionDB": { - "url": "ccdb-test.cern.ch:8080" + "url": "alice-ccdb.cern.ch" } }, "tasks": { @@ -40,11 +40,21 @@ }, "taskParameters": { "FLP": "3", - "NoiseScan" : "0", - "maxDigitROFSize" : "5000", - "maxDuration" : "60000", - "timeBinSize" : "0.1", - "ROFLengthInBC" : "594" + "NoiseScan": "0", + "maxDigitROFSize": "5000", + "maxDuration": "60000", + "timeBinSize": "0.1", + "ROFLengthInBC": "594" + }, + "grpGeomRequest" : { + "geomRequest": "None", + "askGRPECS": "true", + "askGRPLHCIF": "false", + "askGRPMagField": "false", + "askMatLUT": "false", + "askTime": "false", + "askOnceAllButField": "false", + "needPropagatorD": "false" }, "location": "remote" } @@ -56,18 +66,23 @@ "moduleName": "QcMFT", "detectorName": "MFT", "policy": "OnEachSeparately", - "checkParameters" : { - "ZoneThresholdMedium" : "2", - "ZoneThresholdBad" : "5" + "checkParameters": { + "LadderThresholdMedium": "1", + "LadderThresholdBad": "2" }, - "dataSource": [{ - "type": "Task", - "name": "Digits", - "MOs" : ["mDigitChipOccupancy","mDigitOccupancySummary","mDigitChipStdDev"] - }] + "dataSource": [ + { + "type": "Task", + "name": "Digits", + "MOs": [ + "mDigitChipOccupancy", + "mDigitOccupancySummary", + "mDigitChipStdDev" + ] + } + ] } } }, - "dataSamplingPolicies": [ - ] + "dataSamplingPolicies": [] } diff --git a/MC/config/QC/json/mft-digits-4.json b/MC/config/QC/json/mft-digits-4.json index 62068af25..313c4f754 100644 --- a/MC/config/QC/json/mft-digits-4.json +++ b/MC/config/QC/json/mft-digits-4.json @@ -22,7 +22,7 @@ "url": "" }, "conditionDB": { - "url": "ccdb-test.cern.ch:8080" + "url": "alice-ccdb.cern.ch" } }, "tasks": { @@ -40,11 +40,21 @@ }, "taskParameters": { "FLP": "4", - "NoiseScan" : "0", - "maxDigitROFSize" : "5000", - "maxDuration" : "60000", - "timeBinSize" : "0.1", - "ROFLengthInBC" : "594" + "NoiseScan": "0", + "maxDigitROFSize": "5000", + "maxDuration": "60000", + "timeBinSize": "0.1", + "ROFLengthInBC": "594" + }, + "grpGeomRequest" : { + "geomRequest": "None", + "askGRPECS": "true", + "askGRPLHCIF": "false", + "askGRPMagField": "false", + "askMatLUT": "false", + "askTime": "false", + "askOnceAllButField": "false", + "needPropagatorD": "false" }, "location": "remote" } @@ -56,18 +66,23 @@ "moduleName": "QcMFT", "detectorName": "MFT", "policy": "OnEachSeparately", - "checkParameters" : { - "ZoneThresholdMedium" : "2", - "ZoneThresholdBad" : "5" + "checkParameters": { + "LadderThresholdMedium": "1", + "LadderThresholdBad": "2" }, - "dataSource": [{ - "type": "Task", - "name": "Digits", - "MOs" : ["mDigitChipOccupancy","mDigitOccupancySummary","mDigitChipStdDev"] - }] + "dataSource": [ + { + "type": "Task", + "name": "Digits", + "MOs": [ + "mDigitChipOccupancy", + "mDigitOccupancySummary", + "mDigitChipStdDev" + ] + } + ] } } }, - "dataSamplingPolicies": [ - ] + "dataSamplingPolicies": [] } diff --git a/MC/config/QC/json/mft-tracks-mc.json b/MC/config/QC/json/mft-tracks-mc.json new file mode 100644 index 000000000..1dcd7774d --- /dev/null +++ b/MC/config/QC/json/mft-tracks-mc.json @@ -0,0 +1,49 @@ +{ + "qc" : { + "config" : { + "database" : { + "implementation" : "CCDB", + "host" : "ccdb-test.cern.ch:8080", + "username" : "not_applicable", + "password" : "not_applicable", + "name" : "not_applicable" + }, + "Activity" : { + "number" : "42", + "type" : "2", + "provenance": "qc_mc", + "passName": "passMC", + "periodName": "SimChallenge" + }, + "monitoring" : { + "url" : "no-op://" + }, + "consul" : { + "url" : "" + }, + "conditionDB" : { + "url" : "alice-ccdb.cern.ch" + } + }, + "tasks" : { + "TracksMC" : { + "active" : "true", + "className" : "o2::quality_control_modules::mft::QcMFTTrackMCTask", + "moduleName" : "QcMFT", + "detectorName" : "MFT", + "cycleDurationSeconds" : "30", + "maxNumberCycles" : "-1", + "dataSource_comment" : "The other type of dataSource is \"direct\", see basic-no-sampling.json.", + "dataSource" : { + "type" : "direct", + "query" : "tracks:MFT/TRACKS/0;mctruth:MFT/TRACKSMCTR/0" + }, + "location" : "remote", + "taskParameters" : { + "collisionsContextPath": "./collisioncontext.root" + } + } + } + } + } + \ No newline at end of file diff --git a/MC/config/QC/json/mft-tracks.json b/MC/config/QC/json/mft-tracks.json index 548dc8243..1f6cb91aa 100644 --- a/MC/config/QC/json/mft-tracks.json +++ b/MC/config/QC/json/mft-tracks.json @@ -22,7 +22,7 @@ "url": "" }, "conditionDB": { - "url": "ccdb-test.cern.ch:8080" + "url": "alice-ccdb.cern.ch" } }, "tasks": { @@ -37,18 +37,27 @@ "type": "direct", "query": "tracks:MFT/TRACKS/0;tracksrofs:MFT/MFTTrackROF/0;clustersrofs:MFT/CLUSTERSROF/0" }, - "taskParameters" : { + "taskParameters": { "ROFLengthInBC": "594", "MaxTrackROFSize": "10000", "MaxClusterROFSize": "50000", "MaxDuration": "60000", "TimeBinSize": "0.1" }, + "grpGeomRequest" : { + "geomRequest": "None", + "askGRPECS": "true", + "askGRPLHCIF": "false", + "askGRPMagField": "false", + "askMatLUT": "false", + "askTime": "false", + "askOnceAllButField": "false", + "needPropagatorD": "false" + }, "location": "remote" } }, "checks": {} }, - "dataSamplingPolicies": [ - ] + "dataSamplingPolicies": [] } diff --git a/MC/config/QC/json/mftmch-tracks-task.json b/MC/config/QC/json/mftmch-tracks-task.json new file mode 100644 index 000000000..d90420ea1 --- /dev/null +++ b/MC/config/QC/json/mftmch-tracks-task.json @@ -0,0 +1,59 @@ +{ + "qc": { + "config": { + "database": { + "implementation": "CCDB", + "host": "localhost:6464", + "username": "not_applicable", + "password": "not_applicable", + "name": "not_applicable" + }, + "Activity": { + "number": "42", + "type": "2", + "provenance": "qc_mc", + "passName": "passMC", + "periodName": "SimChallenge" + }, + "monitoring": { + "url": "infologger:///debug?qc" + }, + "consul": { + "url": "" + }, + "conditionDB": { + "url": "https://alice-ccdb.cern.ch" + } + }, + "tasks": { + "TaskMUONTracks": { + "active": "true", + "className": "o2::quality_control_modules::muon::TracksTask", + "moduleName": "QcMUONCommon", + "detectorName": "GLO", + "taskName": "MUONTracks", + "cycleDurationSeconds": "300", + "maxNumberCycles": "-1", + "dataSource": { + "type": "direct", + "query": "trackMCH:MCH/TRACKS;trackMCHROF:MCH/TRACKROFS;trackMCHTRACKCLUSTERS:MCH/TRACKCLUSTERS;mchtrackdigits:MCH/CLUSTERDIGITS;trackMFT:MFT/TRACKS;trackMFTROF:MFT/MFTTrackROF;trackMFTClIdx:MFT/TRACKCLSID;alpparMFT:MFT/ALPIDEPARAM/0?lifetime=condition&ccdb-path=MFT/Config/AlpideParam;fwdtracks:GLO/GLFWD" + }, + "taskParameters": { + "maxTracksPerTF": "600", + "GID" : "MCH,MFT,MFT-MCH" + }, + "grpGeomRequest": { + "geomRequest": "Aligned", + "askGRPECS": "true", + "askGRPLHCIF": "false", + "askGRPMagField": "true", + "askMatLUT": "false", + "askTime": "false", + "askOnceAllButField": "false", + "needPropagatorD": "false" + }, + "location": "remote" + } + } + } +} diff --git a/MC/config/QC/json/mftmchmid-tracks-task.json b/MC/config/QC/json/mftmchmid-tracks-task.json new file mode 100644 index 000000000..6f82b5038 --- /dev/null +++ b/MC/config/QC/json/mftmchmid-tracks-task.json @@ -0,0 +1,106 @@ +{ + "qc": { + "config": { + "database": { + "implementation": "CCDB", + "host": "localhost:6464", + "username": "not_applicable", + "password": "not_applicable", + "name": "not_applicable" + }, + "Activity": { + "number": "42", + "type": "2", + "provenance": "qc_mc", + "passName": "passMC", + "periodName": "SimChallenge" + }, + "monitoring": { + "url": "infologger:///debug?qc" + }, + "consul": { + "url": "" + }, + "conditionDB": { + "url": "https://alice-ccdb.cern.ch" + } + }, + "tasks": { + "TaskMUONTracks": { + "active": "true", + "className": "o2::quality_control_modules::muon::TracksTask", + "moduleName": "QcMUONCommon", + "detectorName": "GLO", + "taskName": "MUONTracks", + "cycleDurationSeconds": "300", + "maxNumberCycles": "-1", + "dataSource": { + "type": "direct", + "query": "trackMCH:MCH/TRACKS;trackMCHROF:MCH/TRACKROFS;trackMCHTRACKCLUSTERS:MCH/TRACKCLUSTERS;mchtrackdigits:MCH/CLUSTERDIGITS;trackMFT:MFT/TRACKS;trackMFTROF:MFT/MFTTrackROF;trackMFTClIdx:MFT/TRACKCLSID;alpparMFT:MFT/ALPIDEPARAM/0?lifetime=condition&ccdb-path=MFT/Config/AlpideParam;fwdtracks:GLO/GLFWD;trackMID:MID/TRACKS/0;trackMIDROF:MID/TRACKROFS/0;trackMIDTRACKCLUSTERS:MID/TRACKCLUSTERS/0;trackClMIDROF:MID/TRCLUSROFS/0;matchMCHMID:GLO/MTC_MCHMID" + }, + "taskParameters": { + "maxTracksPerTF": "600", + "cutRAbsMin": "17.6", + "cutRAbsMax": "89.5", + "cutEtaMin": "-4.0", + "cutEtaMax": "-2.5", + "cutPtMin": "0.5", + "nSigmaPDCA": "6", + "cutChi2Max": "1000", + "diMuonTimeCut": "100", + "fullHistos": "0", + "GID" : "MCH,MCH-MID" + }, + "grpGeomRequest": { + "geomRequest": "Aligned", + "askGRPECS": "true", + "askGRPLHCIF": "false", + "askGRPMagField": "true", + "askMatLUT": "false", + "askTime": "false", + "askOnceAllButField": "false", + "needPropagatorD": "false" + }, + "location": "remote" + }, + "TaskMUONTracksMFT": { + "active": "true", + "className": "o2::quality_control_modules::muon::TracksTask", + "moduleName": "QcMUONCommon", + "detectorName": "GLO", + "taskName": "MUONTracksMFT", + "cycleDurationSeconds": "300", + "maxNumberCycles": "-1", + "dataSource": { + "type": "direct", + "query": "trackMCH:MCH/TRACKS;trackMCHROF:MCH/TRACKROFS;trackMCHTRACKCLUSTERS:MCH/TRACKCLUSTERS;mchtrackdigits:MCH/CLUSTERDIGITS;trackMFT:MFT/TRACKS;trackMFTROF:MFT/MFTTrackROF;trackMFTClIdx:MFT/TRACKCLSID;alpparMFT:MFT/ALPIDEPARAM/0?lifetime=condition&ccdb-path=MFT/Config/AlpideParam;fwdtracks:GLO/GLFWD;trackMID:MID/TRACKS/0;trackMIDROF:MID/TRACKROFS/0;trackMIDTRACKCLUSTERS:MID/TRACKCLUSTERS/0;trackClMIDROF:MID/TRCLUSROFS/0;matchMCHMID:GLO/MTC_MCHMID" + }, + "taskParameters": { + "maxTracksPerTF": "600", + "cutRAbsMin": "26.5", + "cutRAbsMax": "89.5", + "cutEtaMin": "-3.3", + "cutEtaMax": "-2.5", + "cutPtMin": "0.5", + "nSigmaPDCA": "6", + "cutChi2Max": "1000", + "matchChi2MaxMFT": "45", + "diMuonTimeCut": "100", + "fullHistos": "0", + "GID" : "MCH,MFT-MCH,MCH-MID,MFT-MCH-MID" + }, + "grpGeomRequest": { + "geomRequest": "Aligned", + "askGRPECS": "true", + "askGRPLHCIF": "false", + "askGRPMagField": "true", + "askMatLUT": "false", + "askTime": "false", + "askOnceAllButField": "false", + "needPropagatorD": "false" + }, + "location": "remote" + } + } + } +} diff --git a/MC/config/QC/json/mid-task.json b/MC/config/QC/json/mid-task.json new file mode 100644 index 000000000..6d5671bce --- /dev/null +++ b/MC/config/QC/json/mid-task.json @@ -0,0 +1,124 @@ +{ + "qc": { + "config": { + "database": { + "implementation": "CCDB", + "host": "ccdb-test.cern.ch:8080", + "username": "not_applicable", + "password": "not_applicable", + "name": "not_applicable" + }, + "Activity": { + "number": "42", + "type": "2", + "provenance": "qc_mc", + "passName": "passMC", + "periodName": "SimChallenge" + }, + "monitoring": { + "url": "no-op://" + }, + "consul": { + "url": "" + }, + "conditionDB": { + "url": "alice-ccdb.cern.ch" + } + }, + "tasks": { + "MIDDigits": { + "active": "true", + "taskName": "Digits", + "className": "o2::quality_control_modules::mid::DigitsQcTask", + "moduleName": "QcMID", + "detectorName": "MID", + "cycleDurationSeconds": "60", + "dataSource": { + "type": "direct", + "query": "digits:MID/DATA/0;digits_rof:MID/DATAROF/0" + } + }, + "MIDClusters": { + "active": "true", + "taskName": "Clusters", + "className": "o2::quality_control_modules::mid::ClustQcTask", + "moduleName": "QcMID", + "detectorName": "MID", + "cycleDurationSeconds": "60", + "dataSource": { + "type": "direct", + "query": "clusters:MID/TRACKCLUSTERS/0;clusterrofs:MID/TRCLUSROFS/0" + } + }, + "MIDTracks": { + "active": "true", + "taskName": "Tracks", + "className": "o2::quality_control_modules::mid::TracksQcTask", + "moduleName": "QcMID", + "detectorName": "MID", + "cycleDurationSeconds": "60", + "dataSource": { + "type": "direct", + "query": "tracks:MID/TRACKS/0;trackrofs:MID/TRACKROFS/0" + } + } + }, + "checks": { + "MIDDigits": { + "active": "true", + "checkName": "Digits", + "className": "o2::quality_control_modules::mid::DigitsQcCheck", + "moduleName": "QcMID", + "detectorName": "MID", + "policy": "OnAny", + "checkParameters": { + "MeanMultThreshold": "100.", + "MinMultThreshold": "0.0", + "NbOrbitPerTF": "32.", + "LocalBoardScale": "200.0", + "LocalBoardThreshold": "800.0", + "NbBadLocalBoard": "10.", + "NbEmptyLocalBoard": "117." + }, + "dataSource": [ + { + "type": "Task", + "name": "MIDDigits" + } + ] + }, + "MIDClusters": { + "active": "true", + "checkName": "Clusters", + "className": "o2::quality_control_modules::mid::ClustQcCheck", + "moduleName": "QcMID", + "detectorName": "MID", + "policy": "OnAny", + "dataSource": [ + { + "type": "Task", + "name": "MIDClusters" + } + ] + }, + "MIDTracks": { + "active": "true", + "checkName": "Tracks", + "className": "o2::quality_control_modules::mid::TracksQcCheck", + "moduleName": "QcMID", + "detectorName": "MID", + "policy": "OnAny", + "checkParameters": { + "Ratio44Threshold": "0.1" + }, + "dataSource": [ + { + "type": "Task", + "name": "MIDTracks" + } + ] + } + } + }, + "dataSamplingPolicies": [] +} diff --git a/MC/config/QC/json/phs-cells-clusters-task.json b/MC/config/QC/json/phs-cells-clusters-task.json index bb56a398e..acff6b067 100644 --- a/MC/config/QC/json/phs-cells-clusters-task.json +++ b/MC/config/QC/json/phs-cells-clusters-task.json @@ -9,21 +9,18 @@ "name": "not_applicable" }, "Activity": { - "number": "43", + "number": "42", "type": "2", "provenance": "qc_mc", "passName": "passMC", "periodName": "SimChallenge" }, "monitoring": { - "url": "infologger:///debug?qc" + "url": "no-op://" }, "consul": { "url": "" }, - "#conditionDB": { - "url": "ccdb-test.cern.ch:8080" - }, "conditionDB": { "url": "alice-ccdb.cern.ch" } diff --git a/MC/config/QC/json/pidft0tof.json b/MC/config/QC/json/pidft0tof.json index 8b292a7ef..13f115001 100644 --- a/MC/config/QC/json/pidft0tof.json +++ b/MC/config/QC/json/pidft0tof.json @@ -1,73 +1,66 @@ { - "qc": { - "config": { - "database": { - "implementation": "CCDB", - "host": "ccdb-test.cern.ch:8080", - "username": "not_applicable", - "password": "not_applicable", - "name": "not_applicable" - }, - "Activity": { - "number": "42", - "type": "2", - "provenance": "qc_mc", - "passName": "passMC", - "periodName": "SimChallenge" - }, - "monitoring": { - "url": "no-op://" - }, - "consul": { - "url": "" - }, - "conditionDB": { - "url": "ccdb-test.cern.ch:8080" - }, - "infologger": { - "": "Configuration of the Infologger (optional).", - "filterDiscardDebug": "false", - "": "Set to true to discard debug and trace messages (default: false)", - "filterDiscardLevel": "21", - "": "Message at this level or above are discarded (default: 21 - Trace)" - } - }, - "tasks": { - "PID": { - "active": "true", - "className": "o2::quality_control_modules::pid::TaskFT0TOF", - "moduleName": "QcTOF", - "detectorName": "TOF", - "cycleDurationSeconds": "10", - "maxNumberCycles": "-1", - "dataSource": { - "type": "direct", - "query_comment": "checking every matched track", - "query" : "matchITSTPCTOF:TOF/MTC_ITSTPC/0;matchTPCTOF:TOF/MTC_TPC/0;trackTPCTOF:TOF/TOFTRACKS_TPC/0;trackITSTPC:GLO/TPCITS/0;trackITSTPCABREFS:GLO/TPCITSAB_REFS/0;trackITSTPCABCLID:GLO/TPCITSAB_CLID/0;trackTPC:TPC/TRACKS/0;trackTPCClRefs:TPC/CLUSREFS/0;tofcluster:TOF/CLUSTERS/0;trackTPCMCTR:TPC/TRACKSMCLBL;trackITSTPCMCTR:GLO/TPCITS_MC;trackITSTPCABMCTR:GLO/TPCITSAB_MC;clsTOF_TPC_MCTR:TOF/MCMTC_TPC;clsTOF_GLO_MCTR:TOF/MCMTC_ITSTPC;trackITSTPCTRD:TRD/MATCH_ITSTPC/0;trackITSTPCTRDMCTR:TRD/MCLB_ITSTPC/0;trackITSTPCTRDSAMCTR:TRD/MCLB_ITSTPC_TRD/0;trackTPCTRD:TRD/MATCH_TPC/0;trackTPCTRDMCTR:TRD/MCLB_TPC/0;trackTPCTRDSAMCTR:TRD/MCLB_TPC_TRD/0;trigITSTPCTRD:TRD/TRGREC_ITSTPC/0;trigTPCTRD:TRD/TRGREC_TPC/0;matchITSTPCTRDTOF:TOF/MTC_ITSTPCTRD/0;clsTOF_GLO3_MCTR:TOF/MCMTC_ITSTPCTRD/0;matchTPCTRDTOF:TOF/MTC_TPCTRD/0;clsTOF_GLO2_MCTR:TOF/MCMTC_TPCTRD/0;recpoints:FT0/RECPOINTS/0" - }, - "taskParameters": { - "GID" : "ITS-TPC,TPC,ITS-TPC-TOF,TPC-TOF,TPC-TRD,ITS-TPC-TRD,ITS-TPC-TRD-TOF,TPC-TRD-TOF", - "verbose": "false", - "minPtCut": "0.3f", - "etaCut": "0.8f", - "useFT0": "true", - "minNTPCClustersCut": "60", - "minDCACut": "100.f", - "minDCACutY": "10.f" - }, - "grpGeomRequest" : { - "geomRequest": "Aligned", - "askGRPECS": "false", - "askGRPLHCIF": "false", - "askGRPMagField": "true", - "askMatLUT": "false", - "askTime": "false", - "askOnceAllButField": "true", - "needPropagatorD": "false" - }, - "": "For debugging, path to the file where to save. If empty or missing it won't save." - } - } + "qc": { + "config": { + "database": { + "implementation": "CCDB", + "host": "ccdb-test.cern.ch:8080", + "username": "not_applicable", + "password": "not_applicable", + "name": "not_applicable" + }, + "Activity": { + "number": "42", + "type": "2", + "provenance": "qc_mc", + "passName": "passMC", + "periodName": "SimChallenge" + }, + "monitoring": { + "url": "no-op://" + }, + "consul": { + "url": "" + }, + "conditionDB": { + "url": "alice-ccdb.cern.ch" + } }, - "dataSamplingPolicies": [] -} + "tasks": { + "PID": { + "active": "true", + "className": "o2::quality_control_modules::pid::TaskFT0TOF", + "moduleName": "QcTOF", + "detectorName": "TOF", + "cycleDurationSeconds": "10", + "maxNumberCycles": "-1", + "dataSource": { + "type": "direct", + "query_comment": "checking every matched track", + "query": "matchITSTPCTOF:TOF/MTC_ITSTPC/0;matchTPCTOF:TOF/MTC_TPC/0;trackTPCTOF:TOF/TOFTRACKS_TPC/0;trackITSTPC:GLO/TPCITS/0;trackITSTPCABREFS:GLO/TPCITSAB_REFS/0;trackITSTPCABCLID:GLO/TPCITSAB_CLID/0;trackTPC:TPC/TRACKS/0;trackTPCClRefs:TPC/CLUSREFS/0;tofcluster:TOF/CLUSTERS/0;trackTPCMCTR:TPC/TRACKSMCLBL;trackITSTPCMCTR:GLO/TPCITS_MC;trackITSTPCABMCTR:GLO/TPCITSAB_MC;clsTOF_TPC_MCTR:TOF/MCMTC_TPC;clsTOF_GLO_MCTR:TOF/MCMTC_ITSTPC;trackITSTPCTRD:TRD/MATCH_ITSTPC/0;trackITSTPCTRDMCTR:TRD/MCLB_ITSTPC/0;trackITSTPCTRDSAMCTR:TRD/MCLB_ITSTPC_TRD/0;trackTPCTRD:TRD/MATCH_TPC/0;trackTPCTRDMCTR:TRD/MCLB_TPC/0;trackTPCTRDSAMCTR:TRD/MCLB_TPC_TRD/0;trigITSTPCTRD:TRD/TRGREC_ITSTPC/0;trigTPCTRD:TRD/TRGREC_TPC/0;matchITSTPCTRDTOF:TOF/MTC_ITSTPCTRD/0;clsTOF_GLO3_MCTR:TOF/MCMTC_ITSTPCTRD/0;matchTPCTRDTOF:TOF/MTC_TPCTRD/0;clsTOF_GLO2_MCTR:TOF/MCMTC_TPCTRD/0;recpoints:FT0/RECPOINTS/0" + }, + "taskParameters": { + "GID": "ITS-TPC,TPC,ITS-TPC-TOF,TPC-TOF,TPC-TRD,ITS-TPC-TRD,ITS-TPC-TRD-TOF,TPC-TRD-TOF", + "verbose": "false", + "minPtCut": "0.3f", + "etaCut": "0.8f", + "useFT0": "true", + "minNTPCClustersCut": "60", + "minDCACut": "100.f", + "minDCACutY": "10.f" + }, + "grpGeomRequest": { + "geomRequest": "Aligned", + "askGRPECS": "false", + "askGRPLHCIF": "false", + "askGRPMagField": "true", + "askMatLUT": "false", + "askTime": "false", + "askOnceAllButField": "true", + "needPropagatorD": "false" + }, + "": "For debugging, path to the file where to save. If empty or missing it won't save." + } + } + }, + "dataSamplingPolicies": [] +} \ No newline at end of file diff --git a/MC/config/QC/json/pidft0tofNoTRD.json b/MC/config/QC/json/pidft0tofNoTRD.json index da568ad46..c72a2e5c5 100644 --- a/MC/config/QC/json/pidft0tofNoTRD.json +++ b/MC/config/QC/json/pidft0tofNoTRD.json @@ -1,73 +1,66 @@ { - "qc": { - "config": { - "database": { - "implementation": "CCDB", - "host": "ccdb-test.cern.ch:8080", - "username": "not_applicable", - "password": "not_applicable", - "name": "not_applicable" - }, - "Activity": { - "number": "42", - "type": "2", - "provenance": "qc_mc", - "passName": "passMC", - "periodName": "SimChallenge" - }, - "monitoring": { - "url": "no-op://" - }, - "consul": { - "url": "" - }, - "conditionDB": { - "url": "ccdb-test.cern.ch:8080" - }, - "infologger": { - "": "Configuration of the Infologger (optional).", - "filterDiscardDebug": "false", - "": "Set to true to discard debug and trace messages (default: false)", - "filterDiscardLevel": "21", - "": "Message at this level or above are discarded (default: 21 - Trace)" - } - }, - "tasks": { - "PID": { - "active": "true", - "className": "o2::quality_control_modules::pid::TaskFT0TOF", - "moduleName": "QcTOF", - "detectorName": "TOF", - "cycleDurationSeconds": "10", - "maxNumberCycles": "-1", - "dataSource": { - "type": "direct", - "query_comment": "checking every matched track", - "query" : "matchITSTPCTOF:TOF/MTC_ITSTPC/0;matchTPCTOF:TOF/MTC_TPC/0;trackTPCTOF:TOF/TOFTRACKS_TPC/0;trackITSTPC:GLO/TPCITS/0;trackITSTPCABREFS:GLO/TPCITSAB_REFS/0;trackITSTPCABCLID:GLO/TPCITSAB_CLID/0;trackTPC:TPC/TRACKS/0;trackTPCClRefs:TPC/CLUSREFS/0;tofcluster:TOF/CLUSTERS/0;trackTPCMCTR:TPC/TRACKSMCLBL;trackITSTPCMCTR:GLO/TPCITS_MC;trackITSTPCABMCTR:GLO/TPCITSAB_MC;clsTOF_TPC_MCTR:TOF/MCMTC_TPC;clsTOF_GLO_MCTR:TOF/MCMTC_ITSTPC;recpoints:FT0/RECPOINTS/0" - }, - "taskParameters": { - "GID" : "ITS-TPC,TPC,ITS-TPC-TOF,TPC-TOF", - "verbose": "false", - "minPtCut": "0.3f", - "etaCut": "0.8f", - "useFT0": "true", - "minNTPCClustersCut": "60", - "minDCACut": "100.f", - "minDCACutY": "10.f" - }, - "grpGeomRequest" : { - "geomRequest": "Aligned", - "askGRPECS": "false", - "askGRPLHCIF": "false", - "askGRPMagField": "true", - "askMatLUT": "false", - "askTime": "false", - "askOnceAllButField": "true", - "needPropagatorD": "false" - }, - "": "For debugging, path to the file where to save. If empty or missing it won't save." - } - } + "qc": { + "config": { + "database": { + "implementation": "CCDB", + "host": "ccdb-test.cern.ch:8080", + "username": "not_applicable", + "password": "not_applicable", + "name": "not_applicable" + }, + "Activity": { + "number": "42", + "type": "2", + "provenance": "qc_mc", + "passName": "passMC", + "periodName": "SimChallenge" + }, + "monitoring": { + "url": "no-op://" + }, + "consul": { + "url": "" + }, + "conditionDB": { + "url": "alice-ccdb.cern.ch" + } }, - "dataSamplingPolicies": [] -} + "tasks": { + "PID": { + "active": "true", + "className": "o2::quality_control_modules::pid::TaskFT0TOF", + "moduleName": "QcTOF", + "detectorName": "TOF", + "cycleDurationSeconds": "10", + "maxNumberCycles": "-1", + "dataSource": { + "type": "direct", + "query_comment": "checking every matched track", + "query": "matchITSTPCTOF:TOF/MTC_ITSTPC/0;matchTPCTOF:TOF/MTC_TPC/0;trackTPCTOF:TOF/TOFTRACKS_TPC/0;trackITSTPC:GLO/TPCITS/0;trackITSTPCABREFS:GLO/TPCITSAB_REFS/0;trackITSTPCABCLID:GLO/TPCITSAB_CLID/0;trackTPC:TPC/TRACKS/0;trackTPCClRefs:TPC/CLUSREFS/0;tofcluster:TOF/CLUSTERS/0;trackTPCMCTR:TPC/TRACKSMCLBL;trackITSTPCMCTR:GLO/TPCITS_MC;trackITSTPCABMCTR:GLO/TPCITSAB_MC;clsTOF_TPC_MCTR:TOF/MCMTC_TPC;clsTOF_GLO_MCTR:TOF/MCMTC_ITSTPC;recpoints:FT0/RECPOINTS/0" + }, + "taskParameters": { + "GID": "ITS-TPC,TPC,ITS-TPC-TOF,TPC-TOF", + "verbose": "false", + "minPtCut": "0.3f", + "etaCut": "0.8f", + "useFT0": "true", + "minNTPCClustersCut": "60", + "minDCACut": "100.f", + "minDCACutY": "10.f" + }, + "grpGeomRequest": { + "geomRequest": "Aligned", + "askGRPECS": "false", + "askGRPLHCIF": "false", + "askGRPMagField": "true", + "askMatLUT": "false", + "askTime": "false", + "askOnceAllButField": "true", + "needPropagatorD": "false" + }, + "": "For debugging, path to the file where to save. If empty or missing it won't save." + } + } + }, + "dataSamplingPolicies": [] +} \ No newline at end of file diff --git a/MC/config/QC/json/pidtof.json b/MC/config/QC/json/pidtof.json index 22b75c9d5..ac23d75cc 100644 --- a/MC/config/QC/json/pidtof.json +++ b/MC/config/QC/json/pidtof.json @@ -1,72 +1,65 @@ { - "qc": { - "config": { - "database": { - "implementation": "CCDB", - "host": "ccdb-test.cern.ch:8080", - "username": "not_applicable", - "password": "not_applicable", - "name": "not_applicable" - }, - "Activity": { - "number": "42", - "type": "2", - "provenance": "qc_mc", - "passName": "passMC", - "periodName": "SimChallenge" - }, - "monitoring": { - "url": "no-op://" - }, - "consul": { - "url": "" - }, - "conditionDB": { - "url": "ccdb-test.cern.ch:8080" - }, - "infologger": { - "": "Configuration of the Infologger (optional).", - "filterDiscardDebug": "false", - "": "Set to true to discard debug and trace messages (default: false)", - "filterDiscardLevel": "21", - "": "Message at this level or above are discarded (default: 21 - Trace)" - } - }, - "tasks": { - "PID": { - "active": "true", - "className": "o2::quality_control_modules::pid::TaskFT0TOF", - "moduleName": "QcTOF", - "detectorName": "TOF", - "cycleDurationSeconds": "10", - "maxNumberCycles": "-1", - "dataSource": { - "type": "direct", - "query_comment": "checking every matched track", - "query" : "matchITSTPCTOF:TOF/MTC_ITSTPC/0;matchTPCTOF:TOF/MTC_TPC/0;trackTPCTOF:TOF/TOFTRACKS_TPC/0;trackITSTPC:GLO/TPCITS/0;trackITSTPCABREFS:GLO/TPCITSAB_REFS/0;trackITSTPCABCLID:GLO/TPCITSAB_CLID/0;trackTPC:TPC/TRACKS/0;trackTPCClRefs:TPC/CLUSREFS/0;tofcluster:TOF/CLUSTERS/0;trackTPCMCTR:TPC/TRACKSMCLBL;trackITSTPCMCTR:GLO/TPCITS_MC;trackITSTPCABMCTR:GLO/TPCITSAB_MC;clsTOF_TPC_MCTR:TOF/MCMTC_TPC;clsTOF_GLO_MCTR:TOF/MCMTC_ITSTPC;trackITSTPCTRD:TRD/MATCH_ITSTPC/0;trackITSTPCTRDMCTR:TRD/MCLB_ITSTPC/0;trackITSTPCTRDSAMCTR:TRD/MCLB_ITSTPC_TRD/0;trackTPCTRD:TRD/MATCH_TPC/0;trackTPCTRDMCTR:TRD/MCLB_TPC/0;trackTPCTRDSAMCTR:TRD/MCLB_TPC_TRD/0;trigITSTPCTRD:TRD/TRGREC_ITSTPC/0;trigTPCTRD:TRD/TRGREC_TPC/0;matchITSTPCTRDTOF:TOF/MTC_ITSTPCTRD/0;clsTOF_GLO3_MCTR:TOF/MCMTC_ITSTPCTRD/0;matchTPCTRDTOF:TOF/MTC_TPCTRD/0;clsTOF_GLO2_MCTR:TOF/MCMTC_TPCTRD/0" - }, - "taskParameters": { - "GID" : "ITS-TPC,TPC,ITS-TPC-TOF,TPC-TOF,TPC-TRD,ITS-TPC-TRD,ITS-TPC-TRD-TOF,TPC-TRD-TOF", - "verbose": "false", - "minPtCut": "0.3f", - "etaCut": "0.8f", - "minNTPCClustersCut": "60", - "minDCACut": "100.f", - "minDCACutY": "10.f" - }, - "grpGeomRequest" : { - "geomRequest": "Aligned", - "askGRPECS": "false", - "askGRPLHCIF": "false", - "askGRPMagField": "true", - "askMatLUT": "false", - "askTime": "false", - "askOnceAllButField": "true", - "needPropagatorD": "false" - }, - "": "For debugging, path to the file where to save. If empty or missing it won't save." - } - } + "qc": { + "config": { + "database": { + "implementation": "CCDB", + "host": "ccdb-test.cern.ch:8080", + "username": "not_applicable", + "password": "not_applicable", + "name": "not_applicable" + }, + "Activity": { + "number": "42", + "type": "2", + "provenance": "qc_mc", + "passName": "passMC", + "periodName": "SimChallenge" + }, + "monitoring": { + "url": "no-op://" + }, + "consul": { + "url": "" + }, + "conditionDB": { + "url": "alice-ccdb.cern.ch" + } }, - "dataSamplingPolicies": [] -} + "tasks": { + "PID": { + "active": "true", + "className": "o2::quality_control_modules::pid::TaskFT0TOF", + "moduleName": "QcTOF", + "detectorName": "TOF", + "cycleDurationSeconds": "10", + "maxNumberCycles": "-1", + "dataSource": { + "type": "direct", + "query_comment": "checking every matched track", + "query": "matchITSTPCTOF:TOF/MTC_ITSTPC/0;matchTPCTOF:TOF/MTC_TPC/0;trackTPCTOF:TOF/TOFTRACKS_TPC/0;trackITSTPC:GLO/TPCITS/0;trackITSTPCABREFS:GLO/TPCITSAB_REFS/0;trackITSTPCABCLID:GLO/TPCITSAB_CLID/0;trackTPC:TPC/TRACKS/0;trackTPCClRefs:TPC/CLUSREFS/0;tofcluster:TOF/CLUSTERS/0;trackTPCMCTR:TPC/TRACKSMCLBL;trackITSTPCMCTR:GLO/TPCITS_MC;trackITSTPCABMCTR:GLO/TPCITSAB_MC;clsTOF_TPC_MCTR:TOF/MCMTC_TPC;clsTOF_GLO_MCTR:TOF/MCMTC_ITSTPC;trackITSTPCTRD:TRD/MATCH_ITSTPC/0;trackITSTPCTRDMCTR:TRD/MCLB_ITSTPC/0;trackITSTPCTRDSAMCTR:TRD/MCLB_ITSTPC_TRD/0;trackTPCTRD:TRD/MATCH_TPC/0;trackTPCTRDMCTR:TRD/MCLB_TPC/0;trackTPCTRDSAMCTR:TRD/MCLB_TPC_TRD/0;trigITSTPCTRD:TRD/TRGREC_ITSTPC/0;trigTPCTRD:TRD/TRGREC_TPC/0;matchITSTPCTRDTOF:TOF/MTC_ITSTPCTRD/0;clsTOF_GLO3_MCTR:TOF/MCMTC_ITSTPCTRD/0;matchTPCTRDTOF:TOF/MTC_TPCTRD/0;clsTOF_GLO2_MCTR:TOF/MCMTC_TPCTRD/0" + }, + "taskParameters": { + "GID": "ITS-TPC,TPC,ITS-TPC-TOF,TPC-TOF,TPC-TRD,ITS-TPC-TRD,ITS-TPC-TRD-TOF,TPC-TRD-TOF", + "verbose": "false", + "minPtCut": "0.3f", + "etaCut": "0.8f", + "minNTPCClustersCut": "60", + "minDCACut": "100.f", + "minDCACutY": "10.f" + }, + "grpGeomRequest": { + "geomRequest": "Aligned", + "askGRPECS": "false", + "askGRPLHCIF": "false", + "askGRPMagField": "true", + "askMatLUT": "false", + "askTime": "false", + "askOnceAllButField": "true", + "needPropagatorD": "false" + }, + "": "For debugging, path to the file where to save. If empty or missing it won't save." + } + } + }, + "dataSamplingPolicies": [] +} \ No newline at end of file diff --git a/MC/config/QC/json/pidtofNoTRD.json b/MC/config/QC/json/pidtofNoTRD.json index 6b5781a6e..f249889c1 100644 --- a/MC/config/QC/json/pidtofNoTRD.json +++ b/MC/config/QC/json/pidtofNoTRD.json @@ -1,72 +1,65 @@ { - "qc": { - "config": { - "database": { - "implementation": "CCDB", - "host": "ccdb-test.cern.ch:8080", - "username": "not_applicable", - "password": "not_applicable", - "name": "not_applicable" - }, - "Activity": { - "number": "42", - "type": "2", - "provenance": "qc_mc", - "passName": "passMC", - "periodName": "SimChallenge" - }, - "monitoring": { - "url": "no-op://" - }, - "consul": { - "url": "" - }, - "conditionDB": { - "url": "ccdb-test.cern.ch:8080" - }, - "infologger": { - "": "Configuration of the Infologger (optional).", - "filterDiscardDebug": "false", - "": "Set to true to discard debug and trace messages (default: false)", - "filterDiscardLevel": "21", - "": "Message at this level or above are discarded (default: 21 - Trace)" - } - }, - "tasks": { - "PID": { - "active": "true", - "className": "o2::quality_control_modules::pid::TaskFT0TOF", - "moduleName": "QcTOF", - "detectorName": "TOF", - "cycleDurationSeconds": "10", - "maxNumberCycles": "-1", - "dataSource": { - "type": "direct", - "query_comment": "checking every matched track", - "query" : "matchITSTPCTOF:TOF/MTC_ITSTPC/0;matchTPCTOF:TOF/MTC_TPC/0;trackTPCTOF:TOF/TOFTRACKS_TPC/0;trackITSTPC:GLO/TPCITS/0;trackITSTPCABREFS:GLO/TPCITSAB_REFS/0;trackITSTPCABCLID:GLO/TPCITSAB_CLID/0;trackTPC:TPC/TRACKS/0;trackTPCClRefs:TPC/CLUSREFS/0;tofcluster:TOF/CLUSTERS/0;trackTPCMCTR:TPC/TRACKSMCLBL;trackITSTPCMCTR:GLO/TPCITS_MC;trackITSTPCABMCTR:GLO/TPCITSAB_MC;clsTOF_TPC_MCTR:TOF/MCMTC_TPC;clsTOF_GLO_MCTR:TOF/MCMTC_ITSTPC" - }, - "taskParameters": { - "GID" : "ITS-TPC,TPC,ITS-TPC-TOF,TPC-TOF", - "verbose": "false", - "minPtCut": "0.3f", - "etaCut": "0.8f", - "minNTPCClustersCut": "60", - "minDCACut": "100.f", - "minDCACutY": "10.f" - }, - "grpGeomRequest" : { - "geomRequest": "Aligned", - "askGRPECS": "false", - "askGRPLHCIF": "false", - "askGRPMagField": "true", - "askMatLUT": "false", - "askTime": "false", - "askOnceAllButField": "true", - "needPropagatorD": "false" - }, - "": "For debugging, path to the file where to save. If empty or missing it won't save." - } - } + "qc": { + "config": { + "database": { + "implementation": "CCDB", + "host": "ccdb-test.cern.ch:8080", + "username": "not_applicable", + "password": "not_applicable", + "name": "not_applicable" + }, + "Activity": { + "number": "42", + "type": "2", + "provenance": "qc_mc", + "passName": "passMC", + "periodName": "SimChallenge" + }, + "monitoring": { + "url": "no-op://" + }, + "consul": { + "url": "" + }, + "conditionDB": { + "url": "alice-ccdb.cern.ch" + } }, - "dataSamplingPolicies": [] -} + "tasks": { + "PID": { + "active": "true", + "className": "o2::quality_control_modules::pid::TaskFT0TOF", + "moduleName": "QcTOF", + "detectorName": "TOF", + "cycleDurationSeconds": "10", + "maxNumberCycles": "-1", + "dataSource": { + "type": "direct", + "query_comment": "checking every matched track", + "query": "matchITSTPCTOF:TOF/MTC_ITSTPC/0;matchTPCTOF:TOF/MTC_TPC/0;trackTPCTOF:TOF/TOFTRACKS_TPC/0;trackITSTPC:GLO/TPCITS/0;trackITSTPCABREFS:GLO/TPCITSAB_REFS/0;trackITSTPCABCLID:GLO/TPCITSAB_CLID/0;trackTPC:TPC/TRACKS/0;trackTPCClRefs:TPC/CLUSREFS/0;tofcluster:TOF/CLUSTERS/0;trackTPCMCTR:TPC/TRACKSMCLBL;trackITSTPCMCTR:GLO/TPCITS_MC;trackITSTPCABMCTR:GLO/TPCITSAB_MC;clsTOF_TPC_MCTR:TOF/MCMTC_TPC;clsTOF_GLO_MCTR:TOF/MCMTC_ITSTPC" + }, + "taskParameters": { + "GID": "ITS-TPC,TPC,ITS-TPC-TOF,TPC-TOF", + "verbose": "false", + "minPtCut": "0.3f", + "etaCut": "0.8f", + "minNTPCClustersCut": "60", + "minDCACut": "100.f", + "minDCACutY": "10.f" + }, + "grpGeomRequest": { + "geomRequest": "Aligned", + "askGRPECS": "false", + "askGRPLHCIF": "false", + "askGRPMagField": "true", + "askMatLUT": "false", + "askTime": "false", + "askOnceAllButField": "true", + "needPropagatorD": "false" + }, + "": "For debugging, path to the file where to save. If empty or missing it won't save." + } + } + }, + "dataSamplingPolicies": [] +} \ No newline at end of file diff --git a/MC/config/QC/json/tof-trending-hits.json b/MC/config/QC/json/tof-trending-hits.json index 81126e8f1..7b8ccbc34 100644 --- a/MC/config/QC/json/tof-trending-hits.json +++ b/MC/config/QC/json/tof-trending-hits.json @@ -9,10 +9,11 @@ "name": "not_applicable" }, "Activity": { - "number": "", + "number": "42", + "type": "2", + "provenance": "qc_mc", "passName": "passMC", - "periodName": "", - "provenance" : "qc_mc" + "periodName": "SimChallenge" }, "monitoring": { "url": "no-op://" @@ -21,7 +22,7 @@ "url": "" }, "conditionDB": { - "url": "ccdb-test.cern.ch:8080" + "url": "alice-ccdb.cern.ch" } }, "postprocessing": { @@ -62,5 +63,4 @@ } } } -} - +} \ No newline at end of file diff --git a/MC/config/QC/json/tofMatchedTracks_AllTypes_direct_MC.json b/MC/config/QC/json/tofMatchedTracks_AllTypes_direct_MC.json index 8df5f9b23..7ae8f16e6 100644 --- a/MC/config/QC/json/tofMatchedTracks_AllTypes_direct_MC.json +++ b/MC/config/QC/json/tofMatchedTracks_AllTypes_direct_MC.json @@ -1,73 +1,68 @@ { - "qc" : { - "config" : { - "database" : { - "implementation" : "CCDB", - "host" : "ccdb-test.cern.ch:8080", - "username" : "not_applicable", - "password" : "not_applicable", - "name" : "not_applicable" + "qc": { + "config": { + "database": { + "implementation": "CCDB", + "host": "ccdb-test.cern.ch:8080", + "username": "not_applicable", + "password": "not_applicable", + "name": "not_applicable" }, - "Activity" : { - "number" : "42", - "type" : "2", - "provenance" : "qc_mc", - "passName" : "passMC", - "periodName" : "SimChallenge" + "Activity": { + "number": "42", + "type": "2", + "provenance": "qc_mc", + "passName": "passMC", + "periodName": "SimChallenge" }, - "monitoring" : { - "url" : "no-op://" + "monitoring": { + "url": "no-op://" }, - "consul" : { - "url" : "" + "consul": { + "url": "" }, - "conditionDB" : { - "url" : "ccdb-test.cern.ch:8080" - }, - "infologger" : { "" : "Configuration of the Infologger (optional).", - "filterDiscardDebug" : "false", - "" : "Set to true to discard debug and trace messages (default: false)", - "filterDiscardLevel" : "21", - "" : "Message at this level or above are discarded (default: 21 - Trace)" } + "conditionDB": { + "url": "alice-ccdb.cern.ch" + } }, - "tasks" : { - "MatchTrAll" : { - "active" : "true", - "className" : "o2::quality_control_modules::tof::TOFMatchedTracks", - "moduleName" : "QcTOF", - "detectorName" : "TOF", - "cycleDurationSeconds" : "10", - "maxNumberCycles" : "-1", - "dataSource" : { - "type" : "direct", - "query_comment" : "checking every matched track", - "query" : "matchITSTPCTOF:TOF/MTC_ITSTPC/0;matchTPCTOF:TOF/MTC_TPC/0;trackTPCTOF:TOF/TOFTRACKS_TPC/0;trackITSTPC:GLO/TPCITS/0;trackITSTPCABREFS:GLO/TPCITSAB_REFS/0;trackITSTPCABCLID:GLO/TPCITSAB_CLID/0;trackTPC:TPC/TRACKS/0;trackTPCClRefs:TPC/CLUSREFS/0;tofcluster:TOF/CLUSTERS/0;trackTPCMCTR:TPC/TRACKSMCLBL;trackITSTPCMCTR:GLO/TPCITS_MC;trackITSTPCABMCTR:GLO/TPCITSAB_MC;clsTOF_TPC_MCTR:TOF/MCMTC_TPC;clsTOF_GLO_MCTR:TOF/MCMTC_ITSTPC;trackITSTPCTRD:TRD/MATCH_ITSTPC/0;trackITSTPCTRDMCTR:TRD/MCLB_ITSTPC/0;trackITSTPCTRDSAMCTR:TRD/MCLB_ITSTPC_TRD/0;trackTPCTRD:TRD/MATCH_TPC/0;trackTPCTRDMCTR:TRD/MCLB_TPC/0;trackTPCTRDSAMCTR:TRD/MCLB_TPC_TRD/0;trigITSTPCTRD:TRD/TRGREC_ITSTPC/0;trigTPCTRD:TRD/TRGREC_TPC/0;matchITSTPCTRDTOF:TOF/MTC_ITSTPCTRD/0;clsTOF_GLO3_MCTR:TOF/MCMTC_ITSTPCTRD/0;matchTPCTRDTOF:TOF/MTC_TPCTRD/0;clsTOF_GLO2_MCTR:TOF/MCMTC_TPCTRD/0" + "tasks": { + "MatchTrAll": { + "active": "true", + "className": "o2::quality_control_modules::tof::TOFMatchedTracks", + "moduleName": "QcTOF", + "detectorName": "TOF", + "cycleDurationSeconds": "10", + "maxNumberCycles": "-1", + "dataSource": { + "type": "direct", + "query_comment": "checking every matched track", + "query": "matchITSTPCTOF:TOF/MTC_ITSTPC/0;matchTPCTOF:TOF/MTC_TPC/0;trackTPCTOF:TOF/TOFTRACKS_TPC/0;trackITSTPC:GLO/TPCITS/0;trackITSTPCABREFS:GLO/TPCITSAB_REFS/0;trackITSTPCABCLID:GLO/TPCITSAB_CLID/0;trackTPC:TPC/TRACKS/0;trackTPCClRefs:TPC/CLUSREFS/0;tofcluster:TOF/CLUSTERS/0;trackTPCMCTR:TPC/TRACKSMCLBL;trackITSTPCMCTR:GLO/TPCITS_MC;trackITSTPCABMCTR:GLO/TPCITSAB_MC;clsTOF_TPC_MCTR:TOF/MCMTC_TPC;clsTOF_GLO_MCTR:TOF/MCMTC_ITSTPC;trackITSTPCTRD:TRD/MATCH_ITSTPC/0;trackITSTPCTRDMCTR:TRD/MCLB_ITSTPC/0;trackITSTPCTRDSAMCTR:TRD/MCLB_ITSTPC_TRD/0;trackTPCTRD:TRD/MATCH_TPC/0;trackTPCTRDMCTR:TRD/MCLB_TPC/0;trackTPCTRDSAMCTR:TRD/MCLB_TPC_TRD/0;trigITSTPCTRD:TRD/TRGREC_ITSTPC/0;trigTPCTRD:TRD/TRGREC_TPC/0;matchITSTPCTRDTOF:TOF/MTC_ITSTPCTRD/0;clsTOF_GLO3_MCTR:TOF/MCMTC_ITSTPCTRD/0;matchTPCTRDTOF:TOF/MTC_TPCTRD/0;clsTOF_GLO2_MCTR:TOF/MCMTC_TPCTRD/0" }, - "taskParameters" : { - "GID" : "ITS-TPC,TPC,ITS-TPC-TOF,TPC-TOF,TPC-TRD,ITS-TPC-TRD,ITS-TPC-TRD-TOF,TPC-TRD-TOF", - "verbose" : "false", - "isMC" : "true", - "minPtCut" : "0.3f", - "etaCut" : "0.8f", - "minNTPCClustersCut" : "60", - "minDCACut" : "100.f", - "minDCACutY" : "10.f" + "taskParameters": { + "GID": "ITS-TPC,TPC,ITS-TPC-TOF,TPC-TOF,TPC-TRD,ITS-TPC-TRD,ITS-TPC-TRD-TOF,TPC-TRD-TOF", + "verbose": "false", + "isMC": "true", + "minPtCut": "0.1f", + "etaCut": "0.8f", + "minNTPCClustersCut": "60", + "minDCACut": "100.f", + "minDCACutY": "10.f" }, - "grpGeomRequest" : { - "geomRequest": "Aligned", + "grpGeomRequest": { + "geomRequest": "None", "askGRPECS": "false", "askGRPLHCIF": "false", "askGRPMagField": "true", "askMatLUT": "false", "askTime": "false", "askOnceAllButField": "true", - "needPropagatorD": "false" + "needPropagatorD": "false" }, - "location" : "remote", - "saveObjectsToFile" : "TOFmatchedTracks_AllTypes_MC.root", - "" : "For debugging, path to the file where to save. If empty or missing it won't save." + "location": "remote", + "saveObjectsToFile": "TOFmatchedTracks_AllTypes_MC.root", + "": "For debugging, path to the file where to save. If empty or missing it won't save." } } }, - "dataSamplingPolicies" : [] + "dataSamplingPolicies": [] } diff --git a/MC/config/QC/json/tofMatchedTracks_ITSTPCTOF_TPCTOF_direct_MC.json b/MC/config/QC/json/tofMatchedTracks_ITSTPCTOF_TPCTOF_direct_MC.json index 3a64ca2ca..95f6f9008 100644 --- a/MC/config/QC/json/tofMatchedTracks_ITSTPCTOF_TPCTOF_direct_MC.json +++ b/MC/config/QC/json/tofMatchedTracks_ITSTPCTOF_TPCTOF_direct_MC.json @@ -1,74 +1,69 @@ { - "qc" : { - "config" : { - "database" : { - "implementation" : "CCDB", - "host" : "ccdb-test.cern.ch:8080", - "username" : "not_applicable", - "password" : "not_applicable", - "name" : "not_applicable" + "qc": { + "config": { + "database": { + "implementation": "CCDB", + "host": "ccdb-test.cern.ch:8080", + "username": "not_applicable", + "password": "not_applicable", + "name": "not_applicable" }, - "Activity" : { - "number" : "42", - "type" : "2", - "provenance" : "qc_mc", - "passName" : "passMC", - "periodName" : "SimChallenge" + "Activity": { + "number": "42", + "type": "2", + "provenance": "qc_mc", + "passName": "passMC", + "periodName": "SimChallenge" }, - "monitoring" : { - "url" : "no-op://" + "monitoring": { + "url": "no-op://" }, - "consul" : { - "url" : "" + "consul": { + "url": "" }, - "conditionDB" : { - "url" : "ccdb-test.cern.ch:8080" - }, - "infologger" : { "" : "Configuration of the Infologger (optional).", - "filterDiscardDebug" : "false", - "" : "Set to true to discard debug and trace messages (default: false)", - "filterDiscardLevel" : "21", - "" : "Message at this level or above are discarded (default: 21 - Trace)" } + "conditionDB": { + "url": "alice-ccdb.cern.ch" + } }, - "tasks" : { - "MatchTrNoTRD" : { - "taskName" : "MatchTrNoTRD", - "active" : "true", - "className" : "o2::quality_control_modules::tof::TOFMatchedTracks", - "moduleName" : "QcTOF", - "detectorName" : "TOF", - "cycleDurationSeconds" : "10", - "maxNumberCycles" : "-1", - "dataSource" : { - "type" : "direct", - "query_comment" : "checking every matched track", - "query" : "matchITSTPCTOF:TOF/MTC_ITSTPC/0;matchTPCTOF:TOF/MTC_TPC/0;trackTPCTOF:TOF/TOFTRACKS_TPC/0;trackITSTPC:GLO/TPCITS/0;trackITSTPCABREFS:GLO/TPCITSAB_REFS/0;trackITSTPCABCLID:GLO/TPCITSAB_CLID/0;trackTPC:TPC/TRACKS/0;trackTPCClRefs:TPC/CLUSREFS/0;tofcluster:TOF/CLUSTERS/0;trackTPCMCTR:TPC/TRACKSMCLBL;trackITSTPCMCTR:GLO/TPCITS_MC;trackITSTPCABMCTR:GLO/TPCITSAB_MC;clsTOF_TPC_MCTR:TOF/MCMTC_TPC;clsTOF_GLO_MCTR:TOF/MCMTC_ITSTPC" + "tasks": { + "MatchTrNoTRD": { + "taskName": "MatchTrNoTRD", + "active": "true", + "className": "o2::quality_control_modules::tof::TOFMatchedTracks", + "moduleName": "QcTOF", + "detectorName": "TOF", + "cycleDurationSeconds": "10", + "maxNumberCycles": "-1", + "dataSource": { + "type": "direct", + "query_comment": "checking every matched track", + "query": "matchITSTPCTOF:TOF/MTC_ITSTPC/0;matchTPCTOF:TOF/MTC_TPC/0;trackTPCTOF:TOF/TOFTRACKS_TPC/0;trackITSTPC:GLO/TPCITS/0;trackITSTPCABREFS:GLO/TPCITSAB_REFS/0;trackITSTPCABCLID:GLO/TPCITSAB_CLID/0;trackTPC:TPC/TRACKS/0;trackTPCClRefs:TPC/CLUSREFS/0;tofcluster:TOF/CLUSTERS/0;trackTPCMCTR:TPC/TRACKSMCLBL;trackITSTPCMCTR:GLO/TPCITS_MC;trackITSTPCABMCTR:GLO/TPCITSAB_MC;clsTOF_TPC_MCTR:TOF/MCMTC_TPC;clsTOF_GLO_MCTR:TOF/MCMTC_ITSTPC" }, - "taskParameters" : { - "GID" : "ITS-TPC,TPC,ITS-TPC-TOF,TPC-TOF", - "verbose" : "false", - "isMC" : "true", - "minPtCut" : "0.3f", - "etaCut" : "0.8f", - "minNTPCClustersCut" : "60", - "minDCACut" : "100.f", - "minDCACutY" : "10.f" + "taskParameters": { + "GID": "ITS-TPC,TPC,ITS-TPC-TOF,TPC-TOF", + "verbose": "false", + "isMC": "true", + "minPtCut": "0.1f", + "etaCut": "0.8f", + "minNTPCClustersCut": "60", + "minDCACut": "100.f", + "minDCACutY": "10.f" }, - "grpGeomRequest" : { - "geomRequest": "Aligned", + "grpGeomRequest": { + "geomRequest": "None", "askGRPECS": "false", "askGRPLHCIF": "false", "askGRPMagField": "true", "askMatLUT": "false", "askTime": "false", "askOnceAllButField": "true", - "needPropagatorD": "false" + "needPropagatorD": "false" }, - "location" : "remote", - "saveObjectsToFile" : "TOFmatchedITSTPCTOF_TPCTOF_MC.root", - "" : "For debugging, path to the file where to save. If empty or missing it won't save." + "location": "remote", + "saveObjectsToFile": "TOFmatchedITSTPCTOF_TPCTOF_MC.root", + "": "For debugging, path to the file where to save. If empty or missing it won't save." } } }, - "dataSamplingPolicies" : [] + "dataSamplingPolicies": [] } diff --git a/MC/config/QC/json/tofdigits.json b/MC/config/QC/json/tofdigits.json index 0dc23d3ef..8a80101da 100644 --- a/MC/config/QC/json/tofdigits.json +++ b/MC/config/QC/json/tofdigits.json @@ -11,7 +11,9 @@ "Activity": { "number": "42", "type": "2", - "provenance" : "qc_mc" + "provenance": "qc_mc", + "passName": "passMC", + "periodName": "SimChallenge" }, "monitoring": { "url": "no-op://" @@ -20,7 +22,7 @@ "url": "" }, "conditionDB": { - "url": "ccdb-test.cern.ch:8080" + "url": "alice-ccdb.cern.ch" } }, "tasks": { @@ -41,9 +43,9 @@ "PerChannel": "True", "RangeMaxMultiplicity": "15000", "NbinsMultiplicity": "15000", - "applyCalib": "True" + "applyCalib": "False" }, - "grpGeomRequest" : { + "grpGeomRequest": { "geomRequest": "Aligned", "askGRPECS": "false", "askGRPLHCIF": "false", @@ -51,7 +53,7 @@ "askMatLUT": "false", "askTime": "false", "askOnceAllButField": "true", - "needPropagatorD": "false" + "needPropagatorD": "false" }, "location": "remote" } @@ -128,7 +130,7 @@ "id": "tof-digits", "active": "true", "machines": [], - "query": "tofdigits:TOF/DIGITS/0;readoutwin:TOF/READOUTWINDOW/0;patterns:TOF/PATTERNS;diafreq:TOF/DIAFREQ/0", + "query": "tofdigits:TOF/DIGITS/0;readoutwin:TOF/READOUTWINDOW/0;patterns:TOF/PATTERNS;diafreq:TOF/DIAFREQ/0;channelCalib:TOF/CHCALIB/0?lifetime=condition&ccdb-path=TOF/Calib/ChannelCalib;lhcPhase:TOF/LHCP/0?lifetime=condition&ccdb-path=TOF/Calib/LHCphase", "samplingConditions": [ { "condition": "random", @@ -139,4 +141,4 @@ "blocking": "false" } ] -} +} \ No newline at end of file diff --git a/MC/config/QC/json/tpc-qc-standard-direct.json b/MC/config/QC/json/tpc-qc-standard-direct.json index 6f560a5ce..533bc4ea5 100644 --- a/MC/config/QC/json/tpc-qc-standard-direct.json +++ b/MC/config/QC/json/tpc-qc-standard-direct.json @@ -6,8 +6,7 @@ "host": "ccdb-test.cern.ch:8080", "username": "not_applicable", "password": "not_applicable", - "name": "not_applicable", - "maxObjectSize": "20971520" + "name": "not_applicable" }, "Activity": { "number": "42", @@ -16,10 +15,6 @@ "passName": "passMC", "periodName": "SimChallenge" }, - "infologger": { - "filterDiscardDebug": "true", - "filterDiscardLevel": "1" - }, "monitoring": { "url": "no-op://" }, @@ -27,7 +22,7 @@ "url": "" }, "conditionDB": { - "url": "ccdb-test.cern.ch:8080" + "url": "alice-ccdb.cern.ch" } }, "tasks": { @@ -37,19 +32,48 @@ "moduleName": "QcTPC", "detectorName": "TPC", "cycleDurationSeconds": "60", - "maxNumberCycles": "-1", "dataSource": { "type": "direct", "query": "inputClus:TPC/CLUSTERNATIVE" }, "taskParameters": { "mergeableOutput": "true", - "NClustersNBins": "100", "NClustersXMin": "0", "NClustersXMax": "100", - "QmaxNBins": "200", "QmaxXMin": "0", "QmaxXMax": "200", - "QtotNBins": "600", "QtotXMin": "10", "QtotXMax": "600", - "SigmaPadNBins": "200", "SigmaPadXMin": "0", "SigmaPadXMax": "2", - "SigmaTimeNBins": "200", "SigmaTimeXMin": "0", "SigmaTimeXMax": "2", - "TimeBinNBins": "1000", "TimeBinXMin": "0", "TimeBinXMax": "100000" + "NClustersNBins": "100", + "NClustersXMin": "0", + "NClustersXMax": "100", + "QmaxNBins": "200", + "QmaxXMin": "0", + "QmaxXMax": "200", + "QtotNBins": "600", + "QtotXMin": "10", + "QtotXMax": "600", + "SigmaPadNBins": "200", + "SigmaPadXMin": "0", + "SigmaPadXMax": "2", + "SigmaTimeNBins": "200", + "SigmaTimeXMin": "0", + "SigmaTimeXMax": "2", + "TimeBinNBins": "1000", + "TimeBinXMin": "0", + "TimeBinXMax": "100000" + } + }, + "TrackClusters": { + "active": "true", + "className": "o2::quality_control_modules::tpc::TrackClusters", + "moduleName": "QcTPC", + "detectorName": "TPC", + "cycleDurationSeconds": "60", + "dataSource": { + "type": "direct", + "query": "inputTracks:TPC/TRACKS/0;inputClusters:TPC/CLUSTERNATIVE;inputClusRefs:TPC/CLUSREFS/0" + }, + "taskParameters": { + "cutAbsEta": "1.", + "cutMinNCluster": "60", + "cutMindEdxTot": "20.", + "seed": "0", + "samplingFraction": "0.1" } }, "PID": { @@ -58,10 +82,9 @@ "moduleName": "QcTPC", "detectorName": "TPC", "cycleDurationSeconds": "60", - "maxNumberCycles": "-1", "dataSource": { "type": "direct", - "query" : "inputTracks:TPC/TRACKS/0" + "query": "inputTracks:TPC/TRACKS/0" }, "taskParameters": { "cutMinNCluster": "60", @@ -72,7 +95,7 @@ "cutMaxpTPC": "20.", "cutMinpTPCMIPs": "0.45", "cutMaxpTPCMIPs": "0.55" - } + } }, "Tracks": { "active": "true", @@ -83,16 +106,27 @@ "maxNumberCycles": "-1", "dataSource": { "type": "direct", - "query" : "inputTracks:TPC/TRACKS/0" + "query": "inputTracks:TPC/TRACKS/0" }, "taskParameters": { "cutAbsEta": "1.", "cutMinNCluster": "60", - "cutMindEdxTot": "20." + "cutMindEdxTot": "20.", + "samplingFractionDCAr": "0.1", + "cutPtForDCAr": "1.5" + }, + "grpGeomRequest": { + "geomRequest": "None", + "askGRPECS": "false", + "askGRPLHCIF": "false", + "askGRPMagField": "true", + "askMatLUT": "true", + "askTime": "false", + "askOnceAllButField": "true", + "needPropagatorD": "false" } } } }, - "dataSamplingPolicies": [ - ] -} + "dataSamplingPolicies": [] +} \ No newline at end of file diff --git a/MC/config/QC/json/tpc-qc-tracking-direct.json b/MC/config/QC/json/tpc-qc-tracking-direct.json index ae98df0cf..bb22d4d60 100644 --- a/MC/config/QC/json/tpc-qc-tracking-direct.json +++ b/MC/config/QC/json/tpc-qc-tracking-direct.json @@ -22,7 +22,7 @@ "url": "" }, "conditionDB": { - "url": "ccdb-test.cern.ch:8080" + "url": "alice-ccdb.cern.ch" } }, "tasks": { @@ -34,8 +34,8 @@ "cycleDurationSeconds": "10", "maxNumberCycles": "-1", "dataSource": { - "type": "direct", - "query": "inputTracks:TPC/TRACKS/0;inputTrackLabels:TPC/TRACKSMCLBL/0;inputClusRefs:TPC/CLUSREFS/0;inputClusters:TPC/CLUSTERNATIVE/0;inputClusterLabels:TPC/CLNATIVEMCLBL/0" + "type": "direct", + "query": "inputTracks:TPC/TRACKS/0;inputTrackLabels:TPC/TRACKSMCLBL/0;inputClusRefs:TPC/CLUSREFS/0;inputClusters:TPC/CLUSTERNATIVE/0;inputClusterLabels:TPC/CLNATIVEMCLBL/0" }, "taskParameters": { "myOwnKey": "myOwnValue" @@ -43,9 +43,7 @@ "location": "remote" } }, - "checks": { - } + "checks": {} }, - "dataSamplingPolicies": [ - ] -} + "dataSamplingPolicies": [] +} \ No newline at end of file diff --git a/MC/config/QC/json/trd-digits-task.json b/MC/config/QC/json/trd-digits-task.json deleted file mode 100644 index 301e78427..000000000 --- a/MC/config/QC/json/trd-digits-task.json +++ /dev/null @@ -1,55 +0,0 @@ -{ - "qc": { - "config": { - "database": { - "implementation": "CCDB", - "host": "ccdb-test.cern.ch:8080", - "username": "not_applicable", - "password": "not_applicable", - "name": "not_applicable" - }, - "Activity": { - "number": "42", - "type": "2", - "provenance": "qc_mc", - "passName": "passMC", - "periodName": "SimChallenge" - }, - "monitoring": { - "url": "no-op://" - }, - "consul": { - "url": "" - }, - "conditionDB": { - "url": "ccdb-test.cern.ch:8080" - }, - "infologger": { - "filterDiscardDebug": "false", - "filterDiscardLevel": "2" - } - }, - "tasks": { - "Digits": { - "active": "true", - "className": "o2::quality_control_modules::trd::DigitsTask", - "moduleName": "QcTRD", - "detectorName": "TRD", - "cycleDurationSeconds": "60", - "maxNumberCycles": "-1", - "dataSource": { - "type": "direct", - "query": "digits:TRD/DIGITS;tracklets:TRD/TRACKLETS;triggers:TRD/TRKTRGRD" - }, - "taskParameters": { - "peakregionstart": "7.0", - "peakregionend": "20.0", - "pulseheightpeaklower": "1.0", - "pulseheightpeakupper": "5.0" - } - } - }, - "dataSamplingPolicies": [ - ] -} -} diff --git a/MC/config/QC/json/trd-standalone-task.json b/MC/config/QC/json/trd-standalone-task.json new file mode 100644 index 000000000..8ad3f7523 --- /dev/null +++ b/MC/config/QC/json/trd-standalone-task.json @@ -0,0 +1,57 @@ +{ + "qc": { + "config": { + "database": { + "implementation": "CCDB", + "host": "ccdb-test.cern.ch:8080" + }, + "Activity": { + "number": "42", + "type": "2", + "provenance": "qc_mc", + "passName": "passMC", + "periodName": "SimChallenge" + }, + "monitoring": { + "url": "no-op://" + }, + "consul": { + "url": "" + }, + "conditionDB": { + "url": "alice-ccdb.cern.ch" + } + }, + "tasks": { + "Digits": { + "active": "true", + "className": "o2::quality_control_modules::trd::DigitsTask", + "moduleName": "QcTRD", + "detectorName": "TRD", + "cycleDurationSeconds": "60", + "dataSource": { + "type": "direct", + "query": "digits:TRD/DIGITS;tracklets:TRD/TRACKLETS;triggers:TRD/TRKTRGRD;noiseMap:TRD/NOISEMAP/0?lifetime=condition&ccdb-path=TRD/Calib/NoiseMapMCM;chamberStatus:TRD/CHSTATUS/0?lifetime=condition&ccdb-path=TRD/Calib/HalfChamberStatusQC;fedChamberStatus:TRD/FCHSTATUS/0?lifetime=condition&ccdb-path=TRD/Calib/DCSDPsFedChamberStatus" + }, + "taskParameters": { + "peakregionstart": "7.0", + "peakregionend": "20.0", + "pulseheightpeaklower": "1.0", + "pulseheightpeakupper": "5.0" + } + }, + "Tracklets": { + "active": "true", + "className": "o2::quality_control_modules::trd::TrackletsTask", + "moduleName": "QcTRD", + "detectorName": "TRD", + "cycleDurationSeconds": "60", + "dataSource": { + "type": "direct", + "query": "tracklets:TRD/TRACKLETS;triggers:TRD/TRKTRGRD;noiseMap:TRD/NOISEMAP/0?lifetime=condition&ccdb-path=TRD/Calib/NoiseMapMCM;chamberStatus:TRD/CHSTATUS/0?lifetime=condition&ccdb-path=TRD/Calib/HalfChamberStatusQC;fedChamberStatus:TRD/FCHSTATUS/0?lifetime=condition&ccdb-path=TRD/Calib/DCSDPsFedChamberStatus" + } + } + }, + "dataSamplingPolicies": [] + } +} diff --git a/MC/config/QC/json/trd-tracking-task.json b/MC/config/QC/json/trd-tracking-task.json new file mode 100644 index 000000000..f8093814f --- /dev/null +++ b/MC/config/QC/json/trd-tracking-task.json @@ -0,0 +1,44 @@ +{ + "qc": { + "config": { + "database": { + "implementation": "CCDB", + "host": "ccdb-test.cern.ch:8080" + }, + "Activity": { + "number": "42", + "type": "2", + "provenance": "qc_mc", + "passName": "passMC", + "periodName": "SimChallenge" + }, + "monitoring": { + "url": "no-op://" + }, + "consul": { + "url": "" + }, + "conditionDB": { + "url": "alice-ccdb.cern.ch" + } + }, + "tasks": { + "Tracking": { + "active": "true", + "className": "o2::quality_control_modules::trd::TrackingTask", + "moduleName": "QcTRD", + "detectorName": "TRD", + "cycleDurationSeconds": "60", + "dataSource": { + "type": "direct", + "query": "trackITSTPCTRD:TRD/MATCH_ITSTPC;trigITSTPCTRD:TRD/TRGREC_ITSTPC;trackTPCTRD:TRD/MATCH_TPC;trigTPCTRD:TRD/TRGREC_TPC" + }, + "taskParameters": { + "detailedQC": "false", + "trackSources": "ITS-TPC-TRD,TPC-TRD" + } + } + }, + "dataSamplingPolicies": [] + } +} diff --git a/MC/config/QC/json/vertexing-qc-direct-mc.json b/MC/config/QC/json/vertexing-qc-direct-mc.json index 2447b0995..5e83bd3d2 100644 --- a/MC/config/QC/json/vertexing-qc-direct-mc.json +++ b/MC/config/QC/json/vertexing-qc-direct-mc.json @@ -1,72 +1,69 @@ { - "qc" : { - "config" : { - "database" : { - "implementation" : "CCDB", - "host" : "ccdb-test.cern.ch:8080", - "username" : "not_applicable", - "password" : "not_applicable", - "name" : "not_applicable" + "qc": { + "config": { + "database": { + "implementation": "CCDB", + "host": "ccdb-test.cern.ch:8080", + "username": "not_applicable", + "password": "not_applicable", + "name": "not_applicable" }, - "Activity" : { - "number" : "42", - "type" : "2", + "Activity": { + "number": "42", + "type": "2", "provenance": "qc_mc", "passName": "passMC", "periodName": "SimChallenge" }, - "monitoring" : { - "url" : "no-op://" + "monitoring": { + "url": "no-op://" }, - "consul" : { - "url" : "" + "consul": { + "url": "" }, - "conditionDB" : { - "url" : "ccdb-test.cern.ch:8080" - }, - "infologger" : { "" : "Configuration of the Infologger (optional).", - "filterDiscardDebug" : "1", - "" : "Set to true to discard debug and trace messages (default: false)", - "filterDiscardLevel" : "21", - "" : "Message at this level or above are discarded (default: 21 - Trace)" } + "conditionDB": { + "url": "alice-ccdb.cern.ch" + } }, - "tasks" : { - "Vertexing" : { - "active" : "true", - "className" : "o2::quality_control_modules::glo::VertexingQcTask", - "moduleName" : "QcGLO", - "detectorName" : "GLO", - "cycleDurationSeconds" : "10", - "maxNumberCycles" : "-1", - "" : "The other type of dataSource is \"direct\", see basic-no-sampling.json.", - "dataSource" : { - "type" : "direct", - "query_comment" : "checking every vertex, in MC", - "query" : "pvtx:GLO/PVTX/0;pvtxLbl:GLO/PVTX_MCTR/0" + "tasks": { + "Vertexing": { + "active": "true", + "className": "o2::quality_control_modules::glo::VertexingQcTask", + "moduleName": "QcGLO", + "detectorName": "GLO", + "cycleDurationSeconds": "10", + "maxNumberCycles": "-1", + "": "For debugging, path to the file where to save. If empty or missing it won't save.", + "dataSource": { + "type": "direct", + "query_comment": "checking every vertex, in MC", + "query": "pvtx:GLO/PVTX/0;pvtxLbl:GLO/PVTX_MCTR/0" }, - "taskParameters" : { - "isMC" : "true" + "taskParameters": { + "isMC": "true" }, - "location" : "remote", - "saveObjectsToFile" : "testVertexingQC_MC.root", - "" : "For debugging, path to the file where to save. If empty or missing it won't save." + "location": "remote", + "saveObjectsToFile": "testVertexingQC_MC.root" } }, - "checks" : { - "QcCheck" : { - "active" : "false", - "className" : "o2::quality_control_modules::skeleton::SkeletonCheck", - "moduleName" : "QcSkeleton", - "policy" : "OnAny", - "detectorName" : "GLO", - "dataSource" : [ { - "type" : "Task", - "name" : "Vertexing", - "MOs" : ["example"] - } ] + "checks": { + "QcCheck": { + "active": "false", + "className": "o2::quality_control_modules::skeleton::SkeletonCheck", + "moduleName": "QcSkeleton", + "policy": "OnAny", + "detectorName": "GLO", + "dataSource": [ + { + "type": "Task", + "name": "Vertexing", + "MOs": [ + "example" + ] + } + ] } } }, - "dataSamplingPolicies" : [ - ] -} + "dataSamplingPolicies": [] +} \ No newline at end of file diff --git a/MC/config/analysis_testing/json/EventSelectionQA/pbpb/analysis-testing-data.json b/MC/config/analysis_testing/json/EventSelectionQA/pbpb/analysis-testing-data.json deleted file mode 100644 index 4823f8664..000000000 --- a/MC/config/analysis_testing/json/EventSelectionQA/pbpb/analysis-testing-data.json +++ /dev/null @@ -1,568 +0,0 @@ -{ - "EMCClusterMonitorTask": { - "clusterDefinition": "10", - "clustertime-binning": { - "values": [ - 1500, - -600, - 900 - ] - }, - "doEventSel": "0", - "numclusters-binning": { - "values": [ - 201, - -0.5, - 200.5 - ] - }, - "processAmbiguous": "0", - "processCollisions": "1", - "selectBCID": "all", - "vertexCut": "-1", - "vetoBCID": "" - }, - "EMCClusterMonitorTaskAmbiguous": { - "clusterDefinition": "10", - "clustertime-binning": { - "values": [ - 1500, - -600, - 900 - ] - }, - "doEventSel": "0", - "numclusters-binning": { - "values": [ - 201, - -0.5, - 200.5 - ] - }, - "processAmbiguous": "1", - "processCollisions": "0", - "selectBCID": "all", - "vertexCut": "-1", - "vetoBCID": "" - }, - "ambiguous-track-propagation": { - "ccdb-url": "http://alice-ccdb.cern.ch", - "geoPath": "GLO/Config/GeometryAligned", - "grpmagPath": "GLO/Config/GRPMagField", - "mVtxPath": "GLO/Calib/MeanVertex", - "processCentral": "false", - "processMFT": "true" - }, - "bc-selection-task": { - "processRun2": "false", - "processRun3": "true" - }, - "cell-monitor": { - "maxCellTimeMain": "100", - "minCellAmplitude": "0", - "minCellAmplitudeTimeHists": "0.3", - "minCellTimeMain": "-50", - "selectBCID": "all", - "vetoBCID": "" - }, - "emc-tmmonitor": { - "clusterDefinition": "10", - "clustertime-binning": { - "values": [ - 1500, - -600, - 900 - ] - }, - "doEventSel": "0", - "hasPropagatedTracks": "1", - "maxTime": "20", - "minM02": "0.1", - "minTime": "-25", - "processCollisions": "1", - "selectBCID": "all", - "tpcNsigmaBack": { - "values": [ - -10, - -4 - ] - }, - "tpcNsigmaElectron": { - "values": [ - -1, - 3 - ] - }, - "tpcNsigmaPion": { - "values": [ - -3, - 3 - ] - }, - "usePionRejection": "0", - "vertexCut": "-1", - "vetoBCID": "" - }, - "emcal-correction-task": { - "clusterDefinition": "kV3Default", - "disableNonLin": "0", - "exoticCellDiffTime": "1e+06", - "exoticCellFraction": "0.97", - "exoticCellInCrossMinAmplitude": "0.1", - "exoticCellMinAmplitude": "4", - "hasPropagatedTracks": "0", - "hasShaperCorrection": "1", - "isMC": "0", - "logWeight": "4.5", - "maxMatchingDistance": "0.4", - "nonlinearityFunction": "DATA_TestbeamFinal_NoScale", - "processFull": "1", - "processMCFull": "0", - "processStandalone": "0", - "selectedCellType": "1", - "useWeightExotic": "0" - }, - "event-selection-qa-task": { - "isMC": "false", - "isLowFlux" : "false", - "processRun2": "false", - "processRun3": "true" - }, - "event-selection-task": { - "isMC": "false", - "muonSelection": "0", - "processRun2": "false", - "processRun3": "true", - "syst": "PbPb" - }, - "lambdakzero-builder": { - "ccdb-url": "http://alice-ccdb.cern.ch", - "createV0CovMats": "-1", - "dQAK0ShortMassWindow": "0.005", - "dQALambdaMassWindow": "0.005", - "dQAMaxPt": "5", - "dQANBinsMass": "400", - "dQANBinsPtCoarse": "10", - "dQANBinsRadius": "500", - "d_UseAbsDCA": "1", - "d_UseAutodetectMode": "0", - "d_UseWeightedPCA": "0", - "d_bz": "-999", - "d_doQA": "0", - "d_doTrackQA": "0", - "dcanegtopv": "0.1", - "dcapostopv": "0.1", - "dcav0dau": "1", - "geoPath": "GLO/Config/GeometryAligned", - "grpPath": "GLO/GRP/GRP", - "grpmagPath": "GLO/Config/GRPMagField", - "lutPath": "GLO/Param/MatLUT", - "processRun2": "0", - "processRun3": "1", - "rejDiffCollTracks": "0", - "tpcrefit": "0", - "useMatCorrType": "2", - "v0cospa": "0.995", - "v0radius": "1" - }, - "multiplicity-table": { - "processRun2": "false", - "processRun3": "true" - }, - "perf-k0s-resolution": { - "etaBins": { - "values": [ - "20", - "-1", - "1" - ] - }, - "eventSelection": "true", - "mBins": { - "values": [ - "300", - "0.35", - "0.65" - ] - }, - "nSigTPC": "10", - "pTBins": { - "values": [ - "200", - "0", - "10" - ] - }, - "phiBins": { - "values": [ - "54", - "0", - "6.2831853" - ] - }, - "rapidity": "0.5", - "requireTRDneg": "0", - "requireTRDpos": "0", - "v0lifetime": "3", - "v0setting_cospa": "0.995", - "v0setting_dcanegtopv": "0.1", - "v0setting_dcapostopv": "0.1", - "v0setting_dcav0dau": "1", - "v0setting_radius": "0.9" - }, - "pid-multiplicity": { - "processIU": "1", - "processStandard": "0" - }, - "qa-efficiency": { - "applyEvSel": "2", - "do-al": "false", - "do-de": "false", - "do-el": "false", - "do-he": "false", - "do-ka": "false", - "do-mu": "false", - "do-pi": "false", - "do-pr": "false", - "do-tr": "false", - "doNegativePDG": "true", - "doPositivePDG": "true", - "doPtEta": "false", - "etaBins": { - "values": [ - "200", - "-0.8", - "0.8" - ] - }, - "globalTrackSelection": "4", - "log-pt": "0", - "make-eff": "true", - "maxProdRadius": "9999", - "nMinNumberOfContributors": "2", - "noFakesHits": "false", - "phiBins": { - "values": [ - "200", - "0", - "6.2839999198913574" - ] - }, - "processData": "true", - "processMC": "false", - "processMCWithoutCollisions": "false", - "ptBins": { - "values": [ - "200", - "0", - "5" - ] - }, - "trackSelection": "true", - "vertex-z-max": "10", - "vertex-z-min": "-10", - "yBins": { - "values": [ - "200", - "-0.5", - "0.5" - ] - } - }, - "qa-event-track": { - "binsPt": { - "values": [ - "0", - "0", - "0.10000000000000001", - "0.20000000000000001", - "0.29999999999999999", - "0.40000000000000002", - "0.5", - "0.59999999999999998", - "0.69999999999999996", - "0.80000000000000004", - "0.90000000000000002", - "1", - "1.1000000000000001", - "1.2", - "1.3", - "1.3999999999999999", - "1.5", - "2", - "5", - "10", - "20", - "50" - ] - }, - "binsTrackMultiplcity": { - "values": [ - "200", - "0", - "200" - ] - }, - "binsVertexPosXY": { - "values": [ - "500", - "-1", - "1" - ] - }, - "binsVertexPosZ": { - "values": [ - "100", - "-20", - "20" - ] - }, - "fractionOfSampledEvents": "1", - "isRun3": "true", - "maxEta": "2", - "maxPhi": "10", - "maxPt": "1e+10", - "minEta": "-2", - "minPhi": "-1", - "minPt": "-10", - "processData": "true", - "processDataIU": "false", - "processDataIUFiltered": "false", - "processMC": "false", - "processTableData": "false", - "processTableMC": "false", - "selectCharge": "0", - "selectGoodEvents": "true", - "selectMaxVtxZ": "100", - "selectPID": "0", - "selectPrim": "false", - "selectSec": "false", - "targetNumberOfEvents": "10000000", - "trackSelection": "1" - }, - "qa-k0s-tracking-efficiency": { - "eventSelection": "true", - "nSigTPC": "10", - "nSigmaBins": { - "values": [ - "1000", - "-100", - "100" - ] - }, - "processIU": "true", - "rapidity": "0.5", - "v0cospa": "0.995" - }, - "tof-signal": "", - "tpc-pid-qa": { - "etaBins": { - "values": [ - 50, - -1, - 1 - ] - }, - "dEdxBins": { - "values": [ - 3000, - 0, - 1000 - ] - }, - "applyRapidityCut": "0", - "processFullWithTOFDeuteron": "0", - "phiBins": { - "values": [ - 50, - 0, - 6.283185307179586 - ] - }, - "processProton": "0", - "processHelium3": "0", - "processFullElectron": "1", - "processKaon": "0", - "processAlpha": "0", - "processFullWithTOFPion": "0", - "processFullHelium3": "1", - "minP": "0.01", - "enableDeDxPlot": "1", - "processFullWithTOFElectron": "0", - "logAxis": "1", - "processFullWithTOFAlpha": "0", - "processFullKaon": "1", - "processMuon": "0", - "processFullDeuteron": "1", - "processFullWithTOFMuon": "0", - "processFullWithTOFProton": "0", - "maxP": "5", - "processFullAlpha": "0", - "processElectron": "0", - "processDeuteron": "0", - "trackLengthBins": { - "values": [ - 50, - 0, - 1000 - ] - }, - "processFullProton": "1", - "processFullWithTOFKaon": "0", - "minTPCNcls": "0", - "processFullMuon": "1", - "processFullWithTOFTriton": "0", - "nSigmaBins": { - "values": [ - 400, - -10, - 10 - ] - }, - "trackSelection": "1", - "processFullWithTOFHelium3": "0", - "processFullTriton": "1", - "processTriton": "0", - "processPion": "0", - "splitSignalPerCharge": "1", - "expSigmaBins": { - "values": [ - 100, - 0, - 200 - ] - }, - "applyEvSel": "2", - "nBinsP": "400", - "deltaBins": { - "values": [ - 100, - -1000, - 1000 - ] - }, - "processFullPion": "1" - }, - "tof-pid-qa": { - "etaBins": { - "values": [ - 50, - -1, - 1 - ] - }, - "applyRapidityCut": "1", - "phiBins": { - "values": [ - 25, - 0, - 6.283185307179586 - ] - }, - "processProton": "0", - "processHelium3": "0", - "processFullElectron": "0", - "processKaon": "0", - "processAlpha": "0", - "processFullHelium3": "1", - "minP": "0.1", - "logAxis": "0", - "processFullKaon": "1", - "produceDeltaTEtaPhiMap": "0", - "processMuon": "0", - "processFullDeuteron": "1", - "maxP": "5", - "processFullAlpha": "0", - "ptDeltaTEtaPhiMap": "3", - "processElectron": "0", - "processDeuteron": "0", - "trackLengthBins": { - "values": [ - 50, - 0, - 1000 - ] - }, - "processFullProton": "1", - "enableEvTimeSplitting": "0", - "enableVsMomentumHistograms": "0", - "processFullMuon": "0", - "nSigmaBins": { - "values": [ - 400, - -10, - 10 - ] - }, - "trackSelection": "1", - "processFullTriton": "1", - "processTriton": "0", - "processPion": "0", - "splitSignalPerCharge": "1", - "expSigmaBins": { - "values": [ - 200, - 0, - 200 - ] - }, - "applyEvSel": "2", - "nBinsP": "400", - "deltaBins": { - "values": [ - 100, - -1000, - 1000 - ] - }, - "processFullPion": "1" - }, - "tpc-pid-full": { - "autofetchNetworks": "true", - "ccdb-timestamp": "0", - "ccdb-url": "http://alice-ccdb.cern.ch", - "ccdbPath": "Analysis/PID/TPC/Response", - "enableNetworkOptimizations": "true", - "networkPathCCDB": "Analysis/PID/TPC/ML", - "networkPathLocally": "network.onnx", - "networkSetNumThreads": "0", - "param-file": "", - "pid-al": "-1", - "pid-de": "-1", - "pid-el": "-1", - "pid-he": "-1", - "pid-ka": "-1", - "pid-mu": "-1", - "pid-pi": "-1", - "pid-pr": "-1", - "pid-tr": "-1", - "recoPass": "", - "skipTPCOnly": "false", - "useNetworkCorrection": "false" - }, - "track-extension": { - "processRun2": "false", - "processRun3": "true" - }, - "track-propagation": { - "ccdb-url": "http://alice-ccdb.cern.ch", - "geoPath": "GLO/Config/GeometryAligned", - "grpmagPath": "GLO/Config/GRPMagField", - "lutPath": "GLO/Param/MatLUT", - "mVtxPath": "GLO/Calib/MeanVertex", - "processCovariance": "true", - "processStandard": "false" - }, - "track-selection": { - "compatibilityIU": "false", - "dcaSetup": "0", - "etaMax": "0.8", - "etaMin": "-0.8", - "isRun3": "true", - "itsMatching": "1", - "produceFBextendedTable": "-1", - "produceTable": "-1", - "ptMax": "1e+10", - "ptMin": "0.1" - } -} diff --git a/MC/config/analysis_testing/json/EventTrackQA/pbpb/analysis-testing-data.json b/MC/config/analysis_testing/json/EventTrackQA/pbpb/analysis-testing-data.json deleted file mode 100644 index 52c800a8b..000000000 --- a/MC/config/analysis_testing/json/EventTrackQA/pbpb/analysis-testing-data.json +++ /dev/null @@ -1,106 +0,0 @@ -{ - "bc-selection-task": { - "processRun2": "false", - "processRun3": "true" - }, - "event-selection-task": { - "isMC": "false", - "muonSelection": "0", - "processRun2": "false", - "processRun3": "true", - "syst": "pp" - }, - "qa-event-track": { - "binsPt": { - "values": [ - "0", - "0", - "0.10000000000000001", - "0.20000000000000001", - "0.29999999999999999", - "0.40000000000000002", - "0.5", - "0.59999999999999998", - "0.69999999999999996", - "0.80000000000000004", - "0.90000000000000002", - "1", - "1.1000000000000001", - "1.2", - "1.3", - "1.3999999999999999", - "1.5", - "2", - "5", - "10", - "20", - "50" - ] - }, - "binsTrackMultiplcity": { - "values": [ - "200", - "0", - "200" - ] - }, - "binsVertexPosXY": { - "values": [ - "500", - "-1", - "1" - ] - }, - "binsVertexPosZ": { - "values": [ - "100", - "-20", - "20" - ] - }, - "fractionOfSampledEvents": "1", - "isRun3": "true", - "maxEta": "2", - "maxPhi": "10", - "maxPt": "1e+10", - "minEta": "-2", - "minPhi": "-1", - "minPt": "-10", - "processData": "true", - "processDataIU": "true", - "processDataIUFiltered": "true", - "processMC": "false", - "processTableData": "false", - "processTableMC": "false", - "selectCharge": "0", - "selectGoodEvents": "true", - "selectMaxVtxZ": "100", - "selectPID": "0", - "selectPrim": "false", - "selectSec": "false", - "targetNumberOfEvents": "10000000", - "trackSelection": "1" - }, - "track-propagation": { - "ccdb-url": "http://alice-ccdb.cern.ch", - "geoPath": "GLO/Config/GeometryAligned", - "grpmagPath": "GLO/Config/GRPMagField", - "lutPath": "GLO/Param/MatLUT", - "mVtxPath": "GLO/Calib/MeanVertex", - "minPropagationDistance": "5", - "processCovariance": "true", - "processStandard": "false" - }, - "track-selection": { - "compatibilityIU": "0", - "dcaSetup": "0", - "etaMax": "0.8", - "etaMin": "-0.8", - "isRun3": "1", - "itsMatching": "1", - "produceFBextendedTable": "-1", - "produceTable": "1", - "ptMax": "1e+10", - "ptMin": "0.1" - } -} diff --git a/MC/config/analysis_testing/json/EventTrackQA/pp/analysis-testing-data.json b/MC/config/analysis_testing/json/EventTrackQA/pp/analysis-testing-data.json deleted file mode 100644 index 52c800a8b..000000000 --- a/MC/config/analysis_testing/json/EventTrackQA/pp/analysis-testing-data.json +++ /dev/null @@ -1,106 +0,0 @@ -{ - "bc-selection-task": { - "processRun2": "false", - "processRun3": "true" - }, - "event-selection-task": { - "isMC": "false", - "muonSelection": "0", - "processRun2": "false", - "processRun3": "true", - "syst": "pp" - }, - "qa-event-track": { - "binsPt": { - "values": [ - "0", - "0", - "0.10000000000000001", - "0.20000000000000001", - "0.29999999999999999", - "0.40000000000000002", - "0.5", - "0.59999999999999998", - "0.69999999999999996", - "0.80000000000000004", - "0.90000000000000002", - "1", - "1.1000000000000001", - "1.2", - "1.3", - "1.3999999999999999", - "1.5", - "2", - "5", - "10", - "20", - "50" - ] - }, - "binsTrackMultiplcity": { - "values": [ - "200", - "0", - "200" - ] - }, - "binsVertexPosXY": { - "values": [ - "500", - "-1", - "1" - ] - }, - "binsVertexPosZ": { - "values": [ - "100", - "-20", - "20" - ] - }, - "fractionOfSampledEvents": "1", - "isRun3": "true", - "maxEta": "2", - "maxPhi": "10", - "maxPt": "1e+10", - "minEta": "-2", - "minPhi": "-1", - "minPt": "-10", - "processData": "true", - "processDataIU": "true", - "processDataIUFiltered": "true", - "processMC": "false", - "processTableData": "false", - "processTableMC": "false", - "selectCharge": "0", - "selectGoodEvents": "true", - "selectMaxVtxZ": "100", - "selectPID": "0", - "selectPrim": "false", - "selectSec": "false", - "targetNumberOfEvents": "10000000", - "trackSelection": "1" - }, - "track-propagation": { - "ccdb-url": "http://alice-ccdb.cern.ch", - "geoPath": "GLO/Config/GeometryAligned", - "grpmagPath": "GLO/Config/GRPMagField", - "lutPath": "GLO/Param/MatLUT", - "mVtxPath": "GLO/Calib/MeanVertex", - "minPropagationDistance": "5", - "processCovariance": "true", - "processStandard": "false" - }, - "track-selection": { - "compatibilityIU": "0", - "dcaSetup": "0", - "etaMax": "0.8", - "etaMin": "-0.8", - "isRun3": "1", - "itsMatching": "1", - "produceFBextendedTable": "-1", - "produceTable": "1", - "ptMax": "1e+10", - "ptMin": "0.1" - } -} diff --git a/MC/config/analysis_testing/json/TPCSkimming/pbpb/analysis-testing-data.json b/MC/config/analysis_testing/json/TPCSkimming/pbpb/analysis-testing-data.json deleted file mode 100644 index 5a73aeb23..000000000 --- a/MC/config/analysis_testing/json/TPCSkimming/pbpb/analysis-testing-data.json +++ /dev/null @@ -1,315 +0,0 @@ -{ - "bc-selection-task": { - "processRun2": "false", - "processRun3": "true", - "triggerBcShift": "999" - }, - "event-selection-task": { - "customDeltaBC": "300", - "isMC": "false", - "muonSelection": "0", - "processRun2": "false", - "processRun3": "true", - "syst": "pp" - }, - "lambdakzero-builder": { - "ccdb-url": "http://alice-ccdb.cern.ch", - "createV0CovMats": "0", - "dQAK0ShortMassWindow": "0.00499999989", - "dQALambdaMassWindow": "0.00499999989", - "dQAMaxPt": "5", - "dQANBinsMass": "400", - "dQANBinsPtCoarse": "10", - "dQANBinsRadius": "500", - "d_UseAbsDCA": "true", - "d_UseAutodetectMode": "false", - "d_UseWeightedPCA": "false", - "d_bz": "-999", - "d_doQA": "false", - "d_doTrackQA": "false", - "dcanegtopv": "0.100000001", - "dcapostopv": "0.100000001", - "dcav0dau": "1", - "geoPath": "GLO/Config/GeometryAligned", - "grpPath": "GLO/GRP/GRP", - "grpmagPath": "GLO/Config/GRPMagField", - "lutPath": "GLO/Param/MatLUT", - "processRun2": "false", - "processRun3": "true", - "rejDiffCollTracks": "0", - "tpcrefit": "0", - "useMatCorrType": "0", - "v0cospa": "0.995", - "v0radius": "0.899999976" - }, - "lambdakzero-initializer": "", - "lambdakzero-preselector": { - "dIfMCgenerateAntiHypertriton": "false", - "dIfMCgenerateAntiLambda": "false", - "dIfMCgenerateGamma": "false", - "dIfMCgenerateHypertriton": "false", - "dIfMCgenerateK0Short": "false", - "dIfMCgenerateLambda": "false", - "dPreselectOnlyBaryons": "false", - "dTPCNCrossedRows": "50", - "ddEdxPreSelectAntiHypertriton": "false", - "ddEdxPreSelectAntiLambda": "true", - "ddEdxPreSelectGamma": "true", - "ddEdxPreSelectHypertriton": "false", - "ddEdxPreSelectK0Short": "true", - "ddEdxPreSelectLambda": "true", - "ddEdxPreSelectionWindow": "7", - "processBuildAll": "true", - "processBuildMCAssociated": "false", - "processBuildValiddEdx": "false", - "processBuildValiddEdxMCAssociated": "false" - }, - "lambdakzero-v0-data-link-builder": "", - "multiplicity-table": { - "doVertexZeq": "1", - "processRun2": "false", - "processRun3": "true" - }, - "pid-multiplicity": { - "processIU": "false", - "processStandard": "true" - }, - "timestamp-task": { - "ccdb-url": "http://alice-ccdb.cern.ch", - "isRun2MC": "false", - "orbit-reset-path": "CTP/Calib/OrbitReset", - "rct-path": "RCT/Info/RunInformation", - "verbose": "false" - }, - "tof-event-time": { - "ccdb-timestamp": "-1", - "ccdb-url": "http://alice-ccdb.cern.ch", - "ccdbPath": "Analysis/PID/TOF", - "maxEvTimeTOF": "100000", - "maxMomentum": "2", - "minMomentum": "0.5", - "param-file": "", - "param-sigma": "TOFReso", - "processFT0": "false", - "processNoFT0": "true", - "processOnlyFT0": "false", - "processRun2": "false" - }, - "tof-pid-beta": { - "tof-expreso": "80" - }, - "tof-pid-full": { - "ccdb-timestamp": "-1", - "ccdb-url": "http://alice-ccdb.cern.ch", - "ccdbPath": "Analysis/PID/TOF", - "enableTimeDependentResponse": "false", - "fatalOnPassNotAvailable": "true", - "param-file": "", - "param-sigma": "TOFResoParams", - "passName": "", - "pid-al": "-1", - "pid-de": "-1", - "pid-el": "-1", - "pid-he": "-1", - "pid-ka": "-1", - "pid-mu": "-1", - "pid-pi": "-1", - "pid-pr": "-1", - "pid-tr": "-1", - "processWSlice": "true", - "processWoSlice": "false", - "processWoSliceDev": "false", - "useParamCollection": "false" - }, - "tof-signal": { - "processRun2": "false", - "processRun3": "true" - }, - "tpc-pid-full": { - "autofetchNetworks": "true", - "ccdb-timestamp": "0", - "ccdb-url": "http://ccdb-test.cern.ch:8080", - "ccdbPath": "Analysis/PID/TPC/Response", - "enableNetworkOptimizations": "true", - "networkPathCCDB": "Analysis/PID/TPC/ML", - "networkPathLocally": "network.onnx", - "networkSetNumThreads": "0", - "param-file": "", - "pid-al": "-1", - "pid-de": "-1", - "pid-el": "-1", - "pid-he": "-1", - "pid-ka": "-1", - "pid-mu": "-1", - "pid-pi": "-1", - "pid-pr": "-1", - "pid-tr": "-1", - "useNetworkCorrection": "false" - }, - "tpc-pid-qa": { - "applyEvSel": "2", - "applyRapidityCut": "false", - "dEdxBins": { - "values": [ - "5000", - "0", - "5000" - ] - }, - "deltaBins": { - "values": [ - "200", - "-1000", - "1000" - ] - }, - "enableDeDxPlot": "true", - "etaBins": { - "values": [ - "100", - "-1", - "1" - ] - }, - "expSigmaBins": { - "values": [ - "200", - "0", - "200" - ] - }, - "logAxis": "1", - "maxP": "20", - "minP": "0.100000001", - "minTPCNcls": "0", - "nBinsP": "200", - "nSigmaBins": { - "values": [ - "401", - "-10.024999618530273", - "10.024999618530273" - ] - }, - "phiBins": { - "values": [ - "100", - "0", - "6.283185307179586" - ] - }, - "processAlpha": "false", - "processDeuteron": "false", - "processElectron": "false", - "processFullAlpha": "true", - "processFullDeuteron": "true", - "processFullElectron": "true", - "processFullHelium3": "true", - "processFullKaon": "true", - "processFullMuon": "true", - "processFullPion": "true", - "processFullProton": "true", - "processFullTriton": "true", - "processFullWithTOFAlpha": "false", - "processFullWithTOFDeuteron": "false", - "processFullWithTOFElectron": "false", - "processFullWithTOFHelium3": "false", - "processFullWithTOFKaon": "false", - "processFullWithTOFMuon": "false", - "processFullWithTOFPion": "false", - "processFullWithTOFProton": "false", - "processFullWithTOFTriton": "false", - "processHelium3": "false", - "processKaon": "false", - "processMuon": "false", - "processPion": "false", - "processProton": "false", - "processTriton": "false", - "splitSignalPerCharge": "true", - "trackLengthBins": { - "values": [ - "100", - "0", - "1000" - ] - }, - "trackSelection": "1" - }, - "track-pid-qa": { - "dcamax": "1e+10", - "dcamin": "0", - "maxchi2tpc": "4", - "mincrossedrows": "70", - "processDummy": "true", - "processQA": "true" - }, - "track-propagation": { - "ccdb-url": "http://alice-ccdb.cern.ch", - "geoPath": "GLO/Config/GeometryAligned", - "grpmagPath": "GLO/Config/GRPMagField", - "lutPath": "GLO/Param/MatLUT", - "mVtxPath": "GLO/Calib/MeanVertex", - "minPropagationDistance": "2.5", - "processCovariance": "true", - "processStandard": "false" - }, - "track-selection": { - "compatibilityIU": "false", - "etaMax": "0.8", - "etaMin": "-0.8", - "isRun3": "true", - "itsMatching": "1", - "produceFBextendedTable": "0", - "ptMax": "1e+10", - "ptMin": "0.1" - }, - "tree-writer-t-p-c-t-o-f": { - "applyEvSel": "2", - "applyTrkSel": "1", - "downsamplingTsalisKaons": "-1", - "downsamplingTsalisPions": "-1", - "downsamplingTsalisProtons": "-1", - "dwnSmplFactor_Ka": "4.0000000000000003e-05", - "dwnSmplFactor_Pi": "4.0000000000000003e-05", - "dwnSmplFactor_Pr": "0.00014999999999999999", - "maxMomTPCOnlyKa": "0.5", - "maxMomTPCOnlyPi": "0.5", - "maxMomTPCOnlyPr": "0.600000024", - "nClNorm": "152", - "nSigmaTOF_TPCTOF_Ka": "4", - "nSigmaTOF_TPCTOF_Pi": "4", - "nSigmaTOF_TPCTOF_Pr": "4", - "nSigmaTPCOnlyKa": "4", - "nSigmaTPCOnlyPi": "4", - "nSigmaTPCOnlyPr": "4", - "nSigmaTPC_TPCTOF_Ka": "5", - "nSigmaTPC_TPCTOF_Pi": "5", - "nSigmaTPC_TPCTOF_Pr": "5", - "sqrt_s_NN": "0", - "trackSelection": "1" - }, - "tree-writer-tpc-v0": { - "applyEvSel": "2", - "downsamplingTsalisElectrons": "-1", - "downsamplingTsalisPions": "-1", - "downsamplingTsalisProtons": "-1", - "dwnSmplFactor_El": "0.01", - "dwnSmplFactor_Pi": "0.001", - "dwnSmplFactor_Pr": "0.02", - "nClNorm": "152", - "nSigmaTOFdautrack": "5", - "sqrt_s_NN": "0", - "trackSelection": "1" - }, - "v0-selector": { - "dcamax": "1e+10", - "dcamin": "0", - "dcav0dau": "0.300000012", - "maxchi2tpc": "4", - "maxpsipair": "1.60000002", - "mincrossedrows": "70", - "v0Rmax": "90", - "v0Rmin": "0", - "v0cospa": "0.998000026", - "v0max_mee": "0.100000001" - } -} diff --git a/MC/config/analysis_testing/json/TPCSkimming/pbpb/analysis-testing-mc.json b/MC/config/analysis_testing/json/TPCSkimming/pbpb/analysis-testing-mc.json deleted file mode 100644 index 5a73aeb23..000000000 --- a/MC/config/analysis_testing/json/TPCSkimming/pbpb/analysis-testing-mc.json +++ /dev/null @@ -1,315 +0,0 @@ -{ - "bc-selection-task": { - "processRun2": "false", - "processRun3": "true", - "triggerBcShift": "999" - }, - "event-selection-task": { - "customDeltaBC": "300", - "isMC": "false", - "muonSelection": "0", - "processRun2": "false", - "processRun3": "true", - "syst": "pp" - }, - "lambdakzero-builder": { - "ccdb-url": "http://alice-ccdb.cern.ch", - "createV0CovMats": "0", - "dQAK0ShortMassWindow": "0.00499999989", - "dQALambdaMassWindow": "0.00499999989", - "dQAMaxPt": "5", - "dQANBinsMass": "400", - "dQANBinsPtCoarse": "10", - "dQANBinsRadius": "500", - "d_UseAbsDCA": "true", - "d_UseAutodetectMode": "false", - "d_UseWeightedPCA": "false", - "d_bz": "-999", - "d_doQA": "false", - "d_doTrackQA": "false", - "dcanegtopv": "0.100000001", - "dcapostopv": "0.100000001", - "dcav0dau": "1", - "geoPath": "GLO/Config/GeometryAligned", - "grpPath": "GLO/GRP/GRP", - "grpmagPath": "GLO/Config/GRPMagField", - "lutPath": "GLO/Param/MatLUT", - "processRun2": "false", - "processRun3": "true", - "rejDiffCollTracks": "0", - "tpcrefit": "0", - "useMatCorrType": "0", - "v0cospa": "0.995", - "v0radius": "0.899999976" - }, - "lambdakzero-initializer": "", - "lambdakzero-preselector": { - "dIfMCgenerateAntiHypertriton": "false", - "dIfMCgenerateAntiLambda": "false", - "dIfMCgenerateGamma": "false", - "dIfMCgenerateHypertriton": "false", - "dIfMCgenerateK0Short": "false", - "dIfMCgenerateLambda": "false", - "dPreselectOnlyBaryons": "false", - "dTPCNCrossedRows": "50", - "ddEdxPreSelectAntiHypertriton": "false", - "ddEdxPreSelectAntiLambda": "true", - "ddEdxPreSelectGamma": "true", - "ddEdxPreSelectHypertriton": "false", - "ddEdxPreSelectK0Short": "true", - "ddEdxPreSelectLambda": "true", - "ddEdxPreSelectionWindow": "7", - "processBuildAll": "true", - "processBuildMCAssociated": "false", - "processBuildValiddEdx": "false", - "processBuildValiddEdxMCAssociated": "false" - }, - "lambdakzero-v0-data-link-builder": "", - "multiplicity-table": { - "doVertexZeq": "1", - "processRun2": "false", - "processRun3": "true" - }, - "pid-multiplicity": { - "processIU": "false", - "processStandard": "true" - }, - "timestamp-task": { - "ccdb-url": "http://alice-ccdb.cern.ch", - "isRun2MC": "false", - "orbit-reset-path": "CTP/Calib/OrbitReset", - "rct-path": "RCT/Info/RunInformation", - "verbose": "false" - }, - "tof-event-time": { - "ccdb-timestamp": "-1", - "ccdb-url": "http://alice-ccdb.cern.ch", - "ccdbPath": "Analysis/PID/TOF", - "maxEvTimeTOF": "100000", - "maxMomentum": "2", - "minMomentum": "0.5", - "param-file": "", - "param-sigma": "TOFReso", - "processFT0": "false", - "processNoFT0": "true", - "processOnlyFT0": "false", - "processRun2": "false" - }, - "tof-pid-beta": { - "tof-expreso": "80" - }, - "tof-pid-full": { - "ccdb-timestamp": "-1", - "ccdb-url": "http://alice-ccdb.cern.ch", - "ccdbPath": "Analysis/PID/TOF", - "enableTimeDependentResponse": "false", - "fatalOnPassNotAvailable": "true", - "param-file": "", - "param-sigma": "TOFResoParams", - "passName": "", - "pid-al": "-1", - "pid-de": "-1", - "pid-el": "-1", - "pid-he": "-1", - "pid-ka": "-1", - "pid-mu": "-1", - "pid-pi": "-1", - "pid-pr": "-1", - "pid-tr": "-1", - "processWSlice": "true", - "processWoSlice": "false", - "processWoSliceDev": "false", - "useParamCollection": "false" - }, - "tof-signal": { - "processRun2": "false", - "processRun3": "true" - }, - "tpc-pid-full": { - "autofetchNetworks": "true", - "ccdb-timestamp": "0", - "ccdb-url": "http://ccdb-test.cern.ch:8080", - "ccdbPath": "Analysis/PID/TPC/Response", - "enableNetworkOptimizations": "true", - "networkPathCCDB": "Analysis/PID/TPC/ML", - "networkPathLocally": "network.onnx", - "networkSetNumThreads": "0", - "param-file": "", - "pid-al": "-1", - "pid-de": "-1", - "pid-el": "-1", - "pid-he": "-1", - "pid-ka": "-1", - "pid-mu": "-1", - "pid-pi": "-1", - "pid-pr": "-1", - "pid-tr": "-1", - "useNetworkCorrection": "false" - }, - "tpc-pid-qa": { - "applyEvSel": "2", - "applyRapidityCut": "false", - "dEdxBins": { - "values": [ - "5000", - "0", - "5000" - ] - }, - "deltaBins": { - "values": [ - "200", - "-1000", - "1000" - ] - }, - "enableDeDxPlot": "true", - "etaBins": { - "values": [ - "100", - "-1", - "1" - ] - }, - "expSigmaBins": { - "values": [ - "200", - "0", - "200" - ] - }, - "logAxis": "1", - "maxP": "20", - "minP": "0.100000001", - "minTPCNcls": "0", - "nBinsP": "200", - "nSigmaBins": { - "values": [ - "401", - "-10.024999618530273", - "10.024999618530273" - ] - }, - "phiBins": { - "values": [ - "100", - "0", - "6.283185307179586" - ] - }, - "processAlpha": "false", - "processDeuteron": "false", - "processElectron": "false", - "processFullAlpha": "true", - "processFullDeuteron": "true", - "processFullElectron": "true", - "processFullHelium3": "true", - "processFullKaon": "true", - "processFullMuon": "true", - "processFullPion": "true", - "processFullProton": "true", - "processFullTriton": "true", - "processFullWithTOFAlpha": "false", - "processFullWithTOFDeuteron": "false", - "processFullWithTOFElectron": "false", - "processFullWithTOFHelium3": "false", - "processFullWithTOFKaon": "false", - "processFullWithTOFMuon": "false", - "processFullWithTOFPion": "false", - "processFullWithTOFProton": "false", - "processFullWithTOFTriton": "false", - "processHelium3": "false", - "processKaon": "false", - "processMuon": "false", - "processPion": "false", - "processProton": "false", - "processTriton": "false", - "splitSignalPerCharge": "true", - "trackLengthBins": { - "values": [ - "100", - "0", - "1000" - ] - }, - "trackSelection": "1" - }, - "track-pid-qa": { - "dcamax": "1e+10", - "dcamin": "0", - "maxchi2tpc": "4", - "mincrossedrows": "70", - "processDummy": "true", - "processQA": "true" - }, - "track-propagation": { - "ccdb-url": "http://alice-ccdb.cern.ch", - "geoPath": "GLO/Config/GeometryAligned", - "grpmagPath": "GLO/Config/GRPMagField", - "lutPath": "GLO/Param/MatLUT", - "mVtxPath": "GLO/Calib/MeanVertex", - "minPropagationDistance": "2.5", - "processCovariance": "true", - "processStandard": "false" - }, - "track-selection": { - "compatibilityIU": "false", - "etaMax": "0.8", - "etaMin": "-0.8", - "isRun3": "true", - "itsMatching": "1", - "produceFBextendedTable": "0", - "ptMax": "1e+10", - "ptMin": "0.1" - }, - "tree-writer-t-p-c-t-o-f": { - "applyEvSel": "2", - "applyTrkSel": "1", - "downsamplingTsalisKaons": "-1", - "downsamplingTsalisPions": "-1", - "downsamplingTsalisProtons": "-1", - "dwnSmplFactor_Ka": "4.0000000000000003e-05", - "dwnSmplFactor_Pi": "4.0000000000000003e-05", - "dwnSmplFactor_Pr": "0.00014999999999999999", - "maxMomTPCOnlyKa": "0.5", - "maxMomTPCOnlyPi": "0.5", - "maxMomTPCOnlyPr": "0.600000024", - "nClNorm": "152", - "nSigmaTOF_TPCTOF_Ka": "4", - "nSigmaTOF_TPCTOF_Pi": "4", - "nSigmaTOF_TPCTOF_Pr": "4", - "nSigmaTPCOnlyKa": "4", - "nSigmaTPCOnlyPi": "4", - "nSigmaTPCOnlyPr": "4", - "nSigmaTPC_TPCTOF_Ka": "5", - "nSigmaTPC_TPCTOF_Pi": "5", - "nSigmaTPC_TPCTOF_Pr": "5", - "sqrt_s_NN": "0", - "trackSelection": "1" - }, - "tree-writer-tpc-v0": { - "applyEvSel": "2", - "downsamplingTsalisElectrons": "-1", - "downsamplingTsalisPions": "-1", - "downsamplingTsalisProtons": "-1", - "dwnSmplFactor_El": "0.01", - "dwnSmplFactor_Pi": "0.001", - "dwnSmplFactor_Pr": "0.02", - "nClNorm": "152", - "nSigmaTOFdautrack": "5", - "sqrt_s_NN": "0", - "trackSelection": "1" - }, - "v0-selector": { - "dcamax": "1e+10", - "dcamin": "0", - "dcav0dau": "0.300000012", - "maxchi2tpc": "4", - "maxpsipair": "1.60000002", - "mincrossedrows": "70", - "v0Rmax": "90", - "v0Rmin": "0", - "v0cospa": "0.998000026", - "v0max_mee": "0.100000001" - } -} diff --git a/MC/config/analysis_testing/json/TPCSkimming/pp/analysis-testing-data.json b/MC/config/analysis_testing/json/TPCSkimming/pp/analysis-testing-data.json deleted file mode 100644 index 5a73aeb23..000000000 --- a/MC/config/analysis_testing/json/TPCSkimming/pp/analysis-testing-data.json +++ /dev/null @@ -1,315 +0,0 @@ -{ - "bc-selection-task": { - "processRun2": "false", - "processRun3": "true", - "triggerBcShift": "999" - }, - "event-selection-task": { - "customDeltaBC": "300", - "isMC": "false", - "muonSelection": "0", - "processRun2": "false", - "processRun3": "true", - "syst": "pp" - }, - "lambdakzero-builder": { - "ccdb-url": "http://alice-ccdb.cern.ch", - "createV0CovMats": "0", - "dQAK0ShortMassWindow": "0.00499999989", - "dQALambdaMassWindow": "0.00499999989", - "dQAMaxPt": "5", - "dQANBinsMass": "400", - "dQANBinsPtCoarse": "10", - "dQANBinsRadius": "500", - "d_UseAbsDCA": "true", - "d_UseAutodetectMode": "false", - "d_UseWeightedPCA": "false", - "d_bz": "-999", - "d_doQA": "false", - "d_doTrackQA": "false", - "dcanegtopv": "0.100000001", - "dcapostopv": "0.100000001", - "dcav0dau": "1", - "geoPath": "GLO/Config/GeometryAligned", - "grpPath": "GLO/GRP/GRP", - "grpmagPath": "GLO/Config/GRPMagField", - "lutPath": "GLO/Param/MatLUT", - "processRun2": "false", - "processRun3": "true", - "rejDiffCollTracks": "0", - "tpcrefit": "0", - "useMatCorrType": "0", - "v0cospa": "0.995", - "v0radius": "0.899999976" - }, - "lambdakzero-initializer": "", - "lambdakzero-preselector": { - "dIfMCgenerateAntiHypertriton": "false", - "dIfMCgenerateAntiLambda": "false", - "dIfMCgenerateGamma": "false", - "dIfMCgenerateHypertriton": "false", - "dIfMCgenerateK0Short": "false", - "dIfMCgenerateLambda": "false", - "dPreselectOnlyBaryons": "false", - "dTPCNCrossedRows": "50", - "ddEdxPreSelectAntiHypertriton": "false", - "ddEdxPreSelectAntiLambda": "true", - "ddEdxPreSelectGamma": "true", - "ddEdxPreSelectHypertriton": "false", - "ddEdxPreSelectK0Short": "true", - "ddEdxPreSelectLambda": "true", - "ddEdxPreSelectionWindow": "7", - "processBuildAll": "true", - "processBuildMCAssociated": "false", - "processBuildValiddEdx": "false", - "processBuildValiddEdxMCAssociated": "false" - }, - "lambdakzero-v0-data-link-builder": "", - "multiplicity-table": { - "doVertexZeq": "1", - "processRun2": "false", - "processRun3": "true" - }, - "pid-multiplicity": { - "processIU": "false", - "processStandard": "true" - }, - "timestamp-task": { - "ccdb-url": "http://alice-ccdb.cern.ch", - "isRun2MC": "false", - "orbit-reset-path": "CTP/Calib/OrbitReset", - "rct-path": "RCT/Info/RunInformation", - "verbose": "false" - }, - "tof-event-time": { - "ccdb-timestamp": "-1", - "ccdb-url": "http://alice-ccdb.cern.ch", - "ccdbPath": "Analysis/PID/TOF", - "maxEvTimeTOF": "100000", - "maxMomentum": "2", - "minMomentum": "0.5", - "param-file": "", - "param-sigma": "TOFReso", - "processFT0": "false", - "processNoFT0": "true", - "processOnlyFT0": "false", - "processRun2": "false" - }, - "tof-pid-beta": { - "tof-expreso": "80" - }, - "tof-pid-full": { - "ccdb-timestamp": "-1", - "ccdb-url": "http://alice-ccdb.cern.ch", - "ccdbPath": "Analysis/PID/TOF", - "enableTimeDependentResponse": "false", - "fatalOnPassNotAvailable": "true", - "param-file": "", - "param-sigma": "TOFResoParams", - "passName": "", - "pid-al": "-1", - "pid-de": "-1", - "pid-el": "-1", - "pid-he": "-1", - "pid-ka": "-1", - "pid-mu": "-1", - "pid-pi": "-1", - "pid-pr": "-1", - "pid-tr": "-1", - "processWSlice": "true", - "processWoSlice": "false", - "processWoSliceDev": "false", - "useParamCollection": "false" - }, - "tof-signal": { - "processRun2": "false", - "processRun3": "true" - }, - "tpc-pid-full": { - "autofetchNetworks": "true", - "ccdb-timestamp": "0", - "ccdb-url": "http://ccdb-test.cern.ch:8080", - "ccdbPath": "Analysis/PID/TPC/Response", - "enableNetworkOptimizations": "true", - "networkPathCCDB": "Analysis/PID/TPC/ML", - "networkPathLocally": "network.onnx", - "networkSetNumThreads": "0", - "param-file": "", - "pid-al": "-1", - "pid-de": "-1", - "pid-el": "-1", - "pid-he": "-1", - "pid-ka": "-1", - "pid-mu": "-1", - "pid-pi": "-1", - "pid-pr": "-1", - "pid-tr": "-1", - "useNetworkCorrection": "false" - }, - "tpc-pid-qa": { - "applyEvSel": "2", - "applyRapidityCut": "false", - "dEdxBins": { - "values": [ - "5000", - "0", - "5000" - ] - }, - "deltaBins": { - "values": [ - "200", - "-1000", - "1000" - ] - }, - "enableDeDxPlot": "true", - "etaBins": { - "values": [ - "100", - "-1", - "1" - ] - }, - "expSigmaBins": { - "values": [ - "200", - "0", - "200" - ] - }, - "logAxis": "1", - "maxP": "20", - "minP": "0.100000001", - "minTPCNcls": "0", - "nBinsP": "200", - "nSigmaBins": { - "values": [ - "401", - "-10.024999618530273", - "10.024999618530273" - ] - }, - "phiBins": { - "values": [ - "100", - "0", - "6.283185307179586" - ] - }, - "processAlpha": "false", - "processDeuteron": "false", - "processElectron": "false", - "processFullAlpha": "true", - "processFullDeuteron": "true", - "processFullElectron": "true", - "processFullHelium3": "true", - "processFullKaon": "true", - "processFullMuon": "true", - "processFullPion": "true", - "processFullProton": "true", - "processFullTriton": "true", - "processFullWithTOFAlpha": "false", - "processFullWithTOFDeuteron": "false", - "processFullWithTOFElectron": "false", - "processFullWithTOFHelium3": "false", - "processFullWithTOFKaon": "false", - "processFullWithTOFMuon": "false", - "processFullWithTOFPion": "false", - "processFullWithTOFProton": "false", - "processFullWithTOFTriton": "false", - "processHelium3": "false", - "processKaon": "false", - "processMuon": "false", - "processPion": "false", - "processProton": "false", - "processTriton": "false", - "splitSignalPerCharge": "true", - "trackLengthBins": { - "values": [ - "100", - "0", - "1000" - ] - }, - "trackSelection": "1" - }, - "track-pid-qa": { - "dcamax": "1e+10", - "dcamin": "0", - "maxchi2tpc": "4", - "mincrossedrows": "70", - "processDummy": "true", - "processQA": "true" - }, - "track-propagation": { - "ccdb-url": "http://alice-ccdb.cern.ch", - "geoPath": "GLO/Config/GeometryAligned", - "grpmagPath": "GLO/Config/GRPMagField", - "lutPath": "GLO/Param/MatLUT", - "mVtxPath": "GLO/Calib/MeanVertex", - "minPropagationDistance": "2.5", - "processCovariance": "true", - "processStandard": "false" - }, - "track-selection": { - "compatibilityIU": "false", - "etaMax": "0.8", - "etaMin": "-0.8", - "isRun3": "true", - "itsMatching": "1", - "produceFBextendedTable": "0", - "ptMax": "1e+10", - "ptMin": "0.1" - }, - "tree-writer-t-p-c-t-o-f": { - "applyEvSel": "2", - "applyTrkSel": "1", - "downsamplingTsalisKaons": "-1", - "downsamplingTsalisPions": "-1", - "downsamplingTsalisProtons": "-1", - "dwnSmplFactor_Ka": "4.0000000000000003e-05", - "dwnSmplFactor_Pi": "4.0000000000000003e-05", - "dwnSmplFactor_Pr": "0.00014999999999999999", - "maxMomTPCOnlyKa": "0.5", - "maxMomTPCOnlyPi": "0.5", - "maxMomTPCOnlyPr": "0.600000024", - "nClNorm": "152", - "nSigmaTOF_TPCTOF_Ka": "4", - "nSigmaTOF_TPCTOF_Pi": "4", - "nSigmaTOF_TPCTOF_Pr": "4", - "nSigmaTPCOnlyKa": "4", - "nSigmaTPCOnlyPi": "4", - "nSigmaTPCOnlyPr": "4", - "nSigmaTPC_TPCTOF_Ka": "5", - "nSigmaTPC_TPCTOF_Pi": "5", - "nSigmaTPC_TPCTOF_Pr": "5", - "sqrt_s_NN": "0", - "trackSelection": "1" - }, - "tree-writer-tpc-v0": { - "applyEvSel": "2", - "downsamplingTsalisElectrons": "-1", - "downsamplingTsalisPions": "-1", - "downsamplingTsalisProtons": "-1", - "dwnSmplFactor_El": "0.01", - "dwnSmplFactor_Pi": "0.001", - "dwnSmplFactor_Pr": "0.02", - "nClNorm": "152", - "nSigmaTOFdautrack": "5", - "sqrt_s_NN": "0", - "trackSelection": "1" - }, - "v0-selector": { - "dcamax": "1e+10", - "dcamin": "0", - "dcav0dau": "0.300000012", - "maxchi2tpc": "4", - "maxpsipair": "1.60000002", - "mincrossedrows": "70", - "v0Rmax": "90", - "v0Rmin": "0", - "v0cospa": "0.998000026", - "v0max_mee": "0.100000001" - } -} diff --git a/MC/config/analysis_testing/json/TPCSkimming/pp/analysis-testing-mc.json b/MC/config/analysis_testing/json/TPCSkimming/pp/analysis-testing-mc.json deleted file mode 100644 index 5a73aeb23..000000000 --- a/MC/config/analysis_testing/json/TPCSkimming/pp/analysis-testing-mc.json +++ /dev/null @@ -1,315 +0,0 @@ -{ - "bc-selection-task": { - "processRun2": "false", - "processRun3": "true", - "triggerBcShift": "999" - }, - "event-selection-task": { - "customDeltaBC": "300", - "isMC": "false", - "muonSelection": "0", - "processRun2": "false", - "processRun3": "true", - "syst": "pp" - }, - "lambdakzero-builder": { - "ccdb-url": "http://alice-ccdb.cern.ch", - "createV0CovMats": "0", - "dQAK0ShortMassWindow": "0.00499999989", - "dQALambdaMassWindow": "0.00499999989", - "dQAMaxPt": "5", - "dQANBinsMass": "400", - "dQANBinsPtCoarse": "10", - "dQANBinsRadius": "500", - "d_UseAbsDCA": "true", - "d_UseAutodetectMode": "false", - "d_UseWeightedPCA": "false", - "d_bz": "-999", - "d_doQA": "false", - "d_doTrackQA": "false", - "dcanegtopv": "0.100000001", - "dcapostopv": "0.100000001", - "dcav0dau": "1", - "geoPath": "GLO/Config/GeometryAligned", - "grpPath": "GLO/GRP/GRP", - "grpmagPath": "GLO/Config/GRPMagField", - "lutPath": "GLO/Param/MatLUT", - "processRun2": "false", - "processRun3": "true", - "rejDiffCollTracks": "0", - "tpcrefit": "0", - "useMatCorrType": "0", - "v0cospa": "0.995", - "v0radius": "0.899999976" - }, - "lambdakzero-initializer": "", - "lambdakzero-preselector": { - "dIfMCgenerateAntiHypertriton": "false", - "dIfMCgenerateAntiLambda": "false", - "dIfMCgenerateGamma": "false", - "dIfMCgenerateHypertriton": "false", - "dIfMCgenerateK0Short": "false", - "dIfMCgenerateLambda": "false", - "dPreselectOnlyBaryons": "false", - "dTPCNCrossedRows": "50", - "ddEdxPreSelectAntiHypertriton": "false", - "ddEdxPreSelectAntiLambda": "true", - "ddEdxPreSelectGamma": "true", - "ddEdxPreSelectHypertriton": "false", - "ddEdxPreSelectK0Short": "true", - "ddEdxPreSelectLambda": "true", - "ddEdxPreSelectionWindow": "7", - "processBuildAll": "true", - "processBuildMCAssociated": "false", - "processBuildValiddEdx": "false", - "processBuildValiddEdxMCAssociated": "false" - }, - "lambdakzero-v0-data-link-builder": "", - "multiplicity-table": { - "doVertexZeq": "1", - "processRun2": "false", - "processRun3": "true" - }, - "pid-multiplicity": { - "processIU": "false", - "processStandard": "true" - }, - "timestamp-task": { - "ccdb-url": "http://alice-ccdb.cern.ch", - "isRun2MC": "false", - "orbit-reset-path": "CTP/Calib/OrbitReset", - "rct-path": "RCT/Info/RunInformation", - "verbose": "false" - }, - "tof-event-time": { - "ccdb-timestamp": "-1", - "ccdb-url": "http://alice-ccdb.cern.ch", - "ccdbPath": "Analysis/PID/TOF", - "maxEvTimeTOF": "100000", - "maxMomentum": "2", - "minMomentum": "0.5", - "param-file": "", - "param-sigma": "TOFReso", - "processFT0": "false", - "processNoFT0": "true", - "processOnlyFT0": "false", - "processRun2": "false" - }, - "tof-pid-beta": { - "tof-expreso": "80" - }, - "tof-pid-full": { - "ccdb-timestamp": "-1", - "ccdb-url": "http://alice-ccdb.cern.ch", - "ccdbPath": "Analysis/PID/TOF", - "enableTimeDependentResponse": "false", - "fatalOnPassNotAvailable": "true", - "param-file": "", - "param-sigma": "TOFResoParams", - "passName": "", - "pid-al": "-1", - "pid-de": "-1", - "pid-el": "-1", - "pid-he": "-1", - "pid-ka": "-1", - "pid-mu": "-1", - "pid-pi": "-1", - "pid-pr": "-1", - "pid-tr": "-1", - "processWSlice": "true", - "processWoSlice": "false", - "processWoSliceDev": "false", - "useParamCollection": "false" - }, - "tof-signal": { - "processRun2": "false", - "processRun3": "true" - }, - "tpc-pid-full": { - "autofetchNetworks": "true", - "ccdb-timestamp": "0", - "ccdb-url": "http://ccdb-test.cern.ch:8080", - "ccdbPath": "Analysis/PID/TPC/Response", - "enableNetworkOptimizations": "true", - "networkPathCCDB": "Analysis/PID/TPC/ML", - "networkPathLocally": "network.onnx", - "networkSetNumThreads": "0", - "param-file": "", - "pid-al": "-1", - "pid-de": "-1", - "pid-el": "-1", - "pid-he": "-1", - "pid-ka": "-1", - "pid-mu": "-1", - "pid-pi": "-1", - "pid-pr": "-1", - "pid-tr": "-1", - "useNetworkCorrection": "false" - }, - "tpc-pid-qa": { - "applyEvSel": "2", - "applyRapidityCut": "false", - "dEdxBins": { - "values": [ - "5000", - "0", - "5000" - ] - }, - "deltaBins": { - "values": [ - "200", - "-1000", - "1000" - ] - }, - "enableDeDxPlot": "true", - "etaBins": { - "values": [ - "100", - "-1", - "1" - ] - }, - "expSigmaBins": { - "values": [ - "200", - "0", - "200" - ] - }, - "logAxis": "1", - "maxP": "20", - "minP": "0.100000001", - "minTPCNcls": "0", - "nBinsP": "200", - "nSigmaBins": { - "values": [ - "401", - "-10.024999618530273", - "10.024999618530273" - ] - }, - "phiBins": { - "values": [ - "100", - "0", - "6.283185307179586" - ] - }, - "processAlpha": "false", - "processDeuteron": "false", - "processElectron": "false", - "processFullAlpha": "true", - "processFullDeuteron": "true", - "processFullElectron": "true", - "processFullHelium3": "true", - "processFullKaon": "true", - "processFullMuon": "true", - "processFullPion": "true", - "processFullProton": "true", - "processFullTriton": "true", - "processFullWithTOFAlpha": "false", - "processFullWithTOFDeuteron": "false", - "processFullWithTOFElectron": "false", - "processFullWithTOFHelium3": "false", - "processFullWithTOFKaon": "false", - "processFullWithTOFMuon": "false", - "processFullWithTOFPion": "false", - "processFullWithTOFProton": "false", - "processFullWithTOFTriton": "false", - "processHelium3": "false", - "processKaon": "false", - "processMuon": "false", - "processPion": "false", - "processProton": "false", - "processTriton": "false", - "splitSignalPerCharge": "true", - "trackLengthBins": { - "values": [ - "100", - "0", - "1000" - ] - }, - "trackSelection": "1" - }, - "track-pid-qa": { - "dcamax": "1e+10", - "dcamin": "0", - "maxchi2tpc": "4", - "mincrossedrows": "70", - "processDummy": "true", - "processQA": "true" - }, - "track-propagation": { - "ccdb-url": "http://alice-ccdb.cern.ch", - "geoPath": "GLO/Config/GeometryAligned", - "grpmagPath": "GLO/Config/GRPMagField", - "lutPath": "GLO/Param/MatLUT", - "mVtxPath": "GLO/Calib/MeanVertex", - "minPropagationDistance": "2.5", - "processCovariance": "true", - "processStandard": "false" - }, - "track-selection": { - "compatibilityIU": "false", - "etaMax": "0.8", - "etaMin": "-0.8", - "isRun3": "true", - "itsMatching": "1", - "produceFBextendedTable": "0", - "ptMax": "1e+10", - "ptMin": "0.1" - }, - "tree-writer-t-p-c-t-o-f": { - "applyEvSel": "2", - "applyTrkSel": "1", - "downsamplingTsalisKaons": "-1", - "downsamplingTsalisPions": "-1", - "downsamplingTsalisProtons": "-1", - "dwnSmplFactor_Ka": "4.0000000000000003e-05", - "dwnSmplFactor_Pi": "4.0000000000000003e-05", - "dwnSmplFactor_Pr": "0.00014999999999999999", - "maxMomTPCOnlyKa": "0.5", - "maxMomTPCOnlyPi": "0.5", - "maxMomTPCOnlyPr": "0.600000024", - "nClNorm": "152", - "nSigmaTOF_TPCTOF_Ka": "4", - "nSigmaTOF_TPCTOF_Pi": "4", - "nSigmaTOF_TPCTOF_Pr": "4", - "nSigmaTPCOnlyKa": "4", - "nSigmaTPCOnlyPi": "4", - "nSigmaTPCOnlyPr": "4", - "nSigmaTPC_TPCTOF_Ka": "5", - "nSigmaTPC_TPCTOF_Pi": "5", - "nSigmaTPC_TPCTOF_Pr": "5", - "sqrt_s_NN": "0", - "trackSelection": "1" - }, - "tree-writer-tpc-v0": { - "applyEvSel": "2", - "downsamplingTsalisElectrons": "-1", - "downsamplingTsalisPions": "-1", - "downsamplingTsalisProtons": "-1", - "dwnSmplFactor_El": "0.01", - "dwnSmplFactor_Pi": "0.001", - "dwnSmplFactor_Pr": "0.02", - "nClNorm": "152", - "nSigmaTOFdautrack": "5", - "sqrt_s_NN": "0", - "trackSelection": "1" - }, - "v0-selector": { - "dcamax": "1e+10", - "dcamin": "0", - "dcav0dau": "0.300000012", - "maxchi2tpc": "4", - "maxpsipair": "1.60000002", - "mincrossedrows": "70", - "v0Rmax": "90", - "v0Rmin": "0", - "v0cospa": "0.998000026", - "v0max_mee": "0.100000001" - } -} diff --git a/MC/config/analysis_testing/json/analyses_config.json b/MC/config/analysis_testing/json/analyses_config.json index 9c060eaa1..bc2998ce4 100644 --- a/MC/config/analysis_testing/json/analyses_config.json +++ b/MC/config/analysis_testing/json/analyses_config.json @@ -6,20 +6,19 @@ "expected_output": ["AnalysisResults.root"], "valid_mc": true, "valid_data": false, - "tasks": ["o2-analysis-timestamp", - "o2-analysis-track-propagation", + "tasks": ["o2-analysis-event-selection-service", + "o2-analysis-propagationservice", "o2-analysistutorial-mc-histograms"] }, { "name": "Efficiency", - "enabled": true, + "enabled": false, "expected_output": ["AnalysisResults.root"], "valid_mc": true, "valid_data": true, - "tasks": ["o2-analysis-timestamp", - "o2-analysis-track-propagation", + "tasks": ["o2-analysis-event-selection-service", + "o2-analysis-propagationservice", "o2-analysis-trackselection", - "o2-analysis-event-selection", "o2-analysis-qa-efficiency"] }, { @@ -28,10 +27,9 @@ "expected_output": ["AnalysisResults.root"], "valid_mc": true, "valid_data": true, - "tasks": ["o2-analysis-timestamp", - "o2-analysis-track-propagation", + "tasks": ["o2-analysis-event-selection-service", + "o2-analysis-propagationservice", "o2-analysis-trackselection", - "o2-analysis-event-selection", "o2-analysis-qa-event-track"] }, { @@ -40,30 +38,16 @@ "expected_output": ["AnalysisResults.root"], "valid_mc": true, "valid_data": true, - "tasks": ["o2-analysis-lf-lambdakzerobuilder", - "o2-analysis-track-propagation", - "o2-analysis-trackselection", - "o2-analysis-pid-tof-base", - "o2-analysis-pid-tof", - "o2-analysis-pid-tof-full", - "o2-analysis-pid-tpc-base", - "o2-analysis-pid-tpc-full", - "o2-analysis-event-selection", - "o2-analysis-timestamp", - "o2-analysis-multiplicity-table", - "o2-analysis-qa-k0s-tracking-efficiency", + "tasks": ["o2-analysis-trackselection", + "o2-analysis-pid-tof-merge", + "o2-analysis-lf-strangenesstofpid", + "o2-analysis-pid-tpc-service", + "o2-analysis-event-selection-service", + "o2-analysis-multcenttable", + "o2-analysis-ft0-corrected-table", + "o2-analysis-propagationservice", "o2-analysis-perf-k0s-resolution"] }, - { - "name": "Validation", - "enabled": false, - "expected_output": ["AnalysisResults.root"], - "valid_mc": true, - "valid_data": true, - "tasks": ["o2-analysis-timestamp", - "o2-analysis-track-propagation", - "o2-analysis-validation"] - }, { "name": "PIDFull", "enabled": true, @@ -71,46 +55,16 @@ "valid_mc": true, "valid_data": true, "tasks": ["o2-analysis-ft0-corrected-table", - "o2-analysis-timestamp", - "o2-analysis-track-propagation", + "o2-analysis-event-selection-service", + "o2-analysis-propagationservice", "o2-analysis-trackselection", - "o2-analysis-event-selection", - "o2-analysis-pid-tof-base", - "o2-analysis-pid-tof", - "o2-analysis-pid-tof-full", + "o2-analysis-multcenttable", + "o2-analysis-pid-tof-merge", "o2-analysis-pid-tof-qa", "o2-analysis-pid-tof-qa-evtime", - "o2-analysis-pid-tof-beta", "o2-analysis-pid-tof-qa-beta", "o2-analysis-pid-tpc-qa", - "o2-analysis-pid-tpc-base", - "o2-analysis-pid-tpc-full"] - }, - { - "name": "TPCSkimming", - "enabled": false, - "expected_output": [ - "AnalysisResults.root", - "AnalysisResults_trees.root" - ], - "valid_mc": true, - "valid_data": true, - "tasks": [ - "o2-analysis-pid-tof-full", - "o2-analysis-pid-tof-beta", - "o2-analysis-pid-tof-base", - "o2-analysis-multiplicity-table", - "o2-analysis-trackselection", - "o2-analysis-track-propagation", - "o2-analysis-pid-tpc-qa", - "o2-analysis-timestamp", - "o2-analysis-lf-lambdakzerobuilder", - "o2-analysis-dq-v0-selector", - "o2-analysis-pid-tpc-full", - "o2-analysis-event-selection", - "o2-analysis-pid-tpc-base", - "o2-analysis-pid-tpc-skimscreation --aod-writer-keep AOD/TPCSKIMV0TREE/0,AOD/TPCTOFSKIMTREE/0" - ] + "o2-analysis-pid-tpc-service"] }, { "name": "PWGMMMFT", @@ -118,11 +72,11 @@ "expected_output": ["AnalysisResults.root"], "valid_mc": true, "valid_data": true, - "tasks": ["o2-analysis-timestamp", - "o2-analysis-track-propagation", + "tasks": ["o2-analysis-event-selection-service", + "o2-analysis-propagationservice", "o2-analysis-trackselection", - "o2-analysis-event-selection", - "o2-analysis-multiplicity-table", + "o2-analysis-multcenttable", + "o2-analysis-fwdtrack-to-collision-associator", "o2-analysis-mm-track-propagation", "o2-analysis-mm-dndeta-mft"] }, @@ -132,20 +86,22 @@ "expected_output": ["AnalysisResults.root"], "valid_mc": true, "valid_data": true, - "tasks": ["o2-analysis-timestamp", - "o2-analysis-track-propagation", - "o2-analysis-event-selection", + "tasks": ["o2-analysis-event-selection-service", + "o2-analysis-propagationservice", "o2-analysis-event-selection-qa"] }, { - "name": "WeakDecayTutorial", - "enabled": false, - "expected_output": null, + "name": "TimeDependentQA", + "enabled": true, + "expected_output": ["AnalysisResults.root"], "valid_mc": true, - "valid_data": false, - "tasks": ["o2-analysis-timestamp", - "o2-analysis-track-propagation", - "o2-analysistutorial-weak-decay-iteration"] + "valid_data": true, + "tasks": ["o2-analysis-event-selection-service", + "o2-analysis-propagationservice", + "o2-analysis-trackselection", + "o2-analysis-multcenttable", + "o2-analysis-ft0-corrected-table", + "o2-analysis-time-dependent-qa"] }, { "name": "CheckDataModelMC", @@ -155,30 +111,13 @@ "valid_data": false, "tasks": ["o2-analysis-check-data-model-mc"] }, - { - "name": "LK0CFFemto", - "enabled": false, - "expected_output": ["AnalysisResults.root", "QAResults.root"], - "valid_mc": true, - "valid_data": false, - "tasks": ["o2-analysis-multiplicity-table --aod-writer-json aodWriterTempConfig.json", - "o2-analysis-timestamp", - "o2-analysis-track-propagation", - "o2-analysis-event-selection", - "o2-analysis-pid-tof-base", - "o2-analysis-pid-tof", - "o2-analysis-pid-tpc-base", - "o2-analysis-pid-tpc", - "o2-analysis-lf-lambdakzerobuilder", - "o2-analysis-cf-femtodream-producer"] - }, { "name": "PWGMMFwdVertexing", "enabled": true, "expected_output": ["AnalysisResults.root"], "valid_mc": true, "valid_data": false, - "tasks": ["o2-analysis-timestamp", + "tasks": ["o2-analysis-event-selection-service", "o2-analysis-mm-vertexing-fwd"] }, { @@ -187,62 +126,60 @@ "expected_output": ["AnalysisResults.root"], "valid_mc": true, "valid_data": false, - "tasks": ["o2-analysis-timestamp", - "o2-analysis-track-propagation", + "tasks": ["o2-analysis-event-selection-service", + "o2-analysis-propagationservice", "o2-analysis-trackselection", - "o2-analysis-event-selection", "o2-analysis-task-mc-simple-qc"] }, { - "name": "PWGMMMDnDeta", + "name": "EMCAL", "enabled": false, "expected_output": ["AnalysisResults.root"], "valid_mc": true, - "valid_data": false, - "tasks": ["o2-analysis-timestamp", - "o2-analysis-track-propagation", - "o2-analysis-event-selection", - "o2-analysis-mm-particles-to-tracks", - "o2-analysis-mm-dndeta"] + "valid_data": true, + "tasks": ["o2-analysis-je-emc-eventselection-qa", + "o2-analysis-je-emc-cellmonitor", + "o2-analysis-je-emcal-correction-task", + "o2-analysis-je-emc-clustermonitor", + "o2-analysis-je-emc-tmmonitor", + "o2-analysis-event-selection-service", + "o2-analysis-propagationservice", + "o2-analysis-trackselection", + "o2-analysis-pid-tpc-service" + ] }, { - "name": "PWGHFD0", - "enabled": false, + "name": "FT0QA", + "enabled": true, "expected_output": ["AnalysisResults.root"], "valid_mc": true, - "valid_data": false, - "tasks": ["o2-analysis-hf-track-index-skims-creator", - "o2-analysis-hf-candidate-creator-2prong", - "o2-analysis-hf-d0-candidate-selector", - "o2-analysis-hf-task-d0", - "o2-analysis-timestamp", - "o2-analysis-track-propagation", + "valid_data": true, + "tasks": ["o2-analysis-event-selection-service", + "o2-analysis-propagationservice", "o2-analysis-trackselection", - "o2-analysis-event-selection", - "o2-analysis-multiplicity-table", - "o2-analysis-pid-tpc-base", - "o2-analysis-pid-tpc-full", - "o2-analysis-pid-tof-base", - "o2-analysis-pid-tof-full"] + "o2-analysis-multcenttable", + "o2-analysis-ft0-corrected-table", + "o2-analysis-ft0-qa"] }, { - "name": "EMCAL", + "name": "HFDzeroQA", "enabled": true, "expected_output": ["AnalysisResults.root"], "valid_mc": true, "valid_data": true, - "tasks": ["o2-analysis-je-emc-eventselection-qa", - "o2-analysis-je-emc-cellmonitor", - "o2-analysis-je-emcal-correction-task", - "o2-analysis-je-emc-clustermonitor", - "o2-analysis-je-emc-tmmonitor", - "o2-analysis-timestamp", - "o2-analysis-track-propagation", + "tasks": ["o2-analysis-hf-task-d0", + "o2-analysis-hf-candidate-selector-d0", + "o2-analysis-hf-candidate-creator-2prong", + "o2-analysis-hf-track-index-skim-creator", + "o2-analysis-track-to-collision-associator", + "o2-analysis-hf-pid-creator", "o2-analysis-trackselection", - "o2-analysis-event-selection", - "o2-analysis-pid-tpc-full", - "o2-analysis-pid-tpc-base" - ] + "o2-analysis-multcenttable", + "o2-analysis-propagationservice", + "o2-analysis-event-selection-service", + "o2-analysis-pid-tpc-service", + "o2-analysis-pid-tof-merge", + "o2-analysis-ft0-corrected-table"] } ] } diff --git a/MC/config/analysis_testing/json/default/pbpb/analysis-testing-data.json b/MC/config/analysis_testing/json/default/pbpb/analysis-testing-data.json deleted file mode 100644 index 6b1ad0ad5..000000000 --- a/MC/config/analysis_testing/json/default/pbpb/analysis-testing-data.json +++ /dev/null @@ -1,567 +0,0 @@ -{ - "EMCClusterMonitorTask": { - "clusterDefinition": "10", - "clustertime-binning": { - "values": [ - 1500, - -600, - 900 - ] - }, - "doEventSel": "0", - "numclusters-binning": { - "values": [ - 201, - -0.5, - 200.5 - ] - }, - "processAmbiguous": "0", - "processCollisions": "1", - "selectBCID": "all", - "vertexCut": "-1", - "vetoBCID": "" - }, - "EMCClusterMonitorTaskAmbiguous": { - "clusterDefinition": "10", - "clustertime-binning": { - "values": [ - 1500, - -600, - 900 - ] - }, - "doEventSel": "0", - "numclusters-binning": { - "values": [ - 201, - -0.5, - 200.5 - ] - }, - "processAmbiguous": "1", - "processCollisions": "0", - "selectBCID": "all", - "vertexCut": "-1", - "vetoBCID": "" - }, - "ambiguous-track-propagation": { - "ccdb-url": "http://alice-ccdb.cern.ch", - "geoPath": "GLO/Config/GeometryAligned", - "grpmagPath": "GLO/Config/GRPMagField", - "mVtxPath": "GLO/Calib/MeanVertex", - "processCentral": "false", - "processMFT": "true" - }, - "bc-selection-task": { - "processRun2": "false", - "processRun3": "true" - }, - "cell-monitor": { - "maxCellTimeMain": "100", - "minCellAmplitude": "0", - "minCellAmplitudeTimeHists": "0.3", - "minCellTimeMain": "-50", - "selectBCID": "all", - "vetoBCID": "" - }, - "emc-tmmonitor": { - "clusterDefinition": "10", - "clustertime-binning": { - "values": [ - 1500, - -600, - 900 - ] - }, - "doEventSel": "0", - "hasPropagatedTracks": "1", - "maxTime": "20", - "minM02": "0.1", - "minTime": "-25", - "processCollisions": "1", - "selectBCID": "all", - "tpcNsigmaBack": { - "values": [ - -10, - -4 - ] - }, - "tpcNsigmaElectron": { - "values": [ - -1, - 3 - ] - }, - "tpcNsigmaPion": { - "values": [ - -3, - 3 - ] - }, - "usePionRejection": "0", - "vertexCut": "-1", - "vetoBCID": "" - }, - "emcal-correction-task": { - "clusterDefinition": "kV3Default", - "disableNonLin": "0", - "exoticCellDiffTime": "1e+06", - "exoticCellFraction": "0.97", - "exoticCellInCrossMinAmplitude": "0.1", - "exoticCellMinAmplitude": "4", - "hasPropagatedTracks": "0", - "hasShaperCorrection": "1", - "isMC": "0", - "logWeight": "4.5", - "maxMatchingDistance": "0.4", - "nonlinearityFunction": "DATA_TestbeamFinal_NoScale", - "processFull": "1", - "processMCFull": "0", - "processStandalone": "0", - "selectedCellType": "1", - "useWeightExotic": "0" - }, - "event-selection-qa-task": { - "isMC": "false", - "processRun2": "false", - "processRun3": "true" - }, - "event-selection-task": { - "isMC": "false", - "muonSelection": "0", - "processRun2": "false", - "processRun3": "true", - "syst": "pp" - }, - "lambdakzero-builder": { - "ccdb-url": "http://alice-ccdb.cern.ch", - "createV0CovMats": "-1", - "dQAK0ShortMassWindow": "0.005", - "dQALambdaMassWindow": "0.005", - "dQAMaxPt": "5", - "dQANBinsMass": "400", - "dQANBinsPtCoarse": "10", - "dQANBinsRadius": "500", - "d_UseAbsDCA": "1", - "d_UseAutodetectMode": "0", - "d_UseWeightedPCA": "0", - "d_bz": "-999", - "d_doQA": "0", - "d_doTrackQA": "0", - "dcanegtopv": "0.1", - "dcapostopv": "0.1", - "dcav0dau": "1", - "geoPath": "GLO/Config/GeometryAligned", - "grpPath": "GLO/GRP/GRP", - "grpmagPath": "GLO/Config/GRPMagField", - "lutPath": "GLO/Param/MatLUT", - "processRun2": "0", - "processRun3": "1", - "rejDiffCollTracks": "0", - "tpcrefit": "0", - "useMatCorrType": "2", - "v0cospa": "0.995", - "v0radius": "1" - }, - "multiplicity-table": { - "processRun2": "false", - "processRun3": "true" - }, - "perf-k0s-resolution": { - "etaBins": { - "values": [ - "20", - "-1", - "1" - ] - }, - "eventSelection": "true", - "mBins": { - "values": [ - "300", - "0.35", - "0.65" - ] - }, - "nSigTPC": "10", - "pTBins": { - "values": [ - "200", - "0", - "10" - ] - }, - "phiBins": { - "values": [ - "54", - "0", - "6.2831853" - ] - }, - "rapidity": "0.5", - "requireTRDneg": "0", - "requireTRDpos": "0", - "v0lifetime": "3", - "v0setting_cospa": "0.995", - "v0setting_dcanegtopv": "0.1", - "v0setting_dcapostopv": "0.1", - "v0setting_dcav0dau": "1", - "v0setting_radius": "0.9" - }, - "pid-multiplicity": { - "processIU": "1", - "processStandard": "0" - }, - "qa-efficiency": { - "applyEvSel": "2", - "do-al": "false", - "do-de": "false", - "do-el": "false", - "do-he": "false", - "do-ka": "false", - "do-mu": "false", - "do-pi": "false", - "do-pr": "false", - "do-tr": "false", - "doNegativePDG": "true", - "doPositivePDG": "true", - "doPtEta": "false", - "etaBins": { - "values": [ - "200", - "-0.8", - "0.8" - ] - }, - "globalTrackSelection": "4", - "log-pt": "0", - "make-eff": "true", - "maxProdRadius": "9999", - "nMinNumberOfContributors": "2", - "noFakesHits": "false", - "phiBins": { - "values": [ - "200", - "0", - "6.2839999198913574" - ] - }, - "processData": "true", - "processMC": "false", - "processMCWithoutCollisions": "false", - "ptBins": { - "values": [ - "200", - "0", - "5" - ] - }, - "trackSelection": "true", - "vertex-z-max": "10", - "vertex-z-min": "-10", - "yBins": { - "values": [ - "200", - "-0.5", - "0.5" - ] - } - }, - "qa-event-track": { - "binsPt": { - "values": [ - "0", - "0", - "0.10000000000000001", - "0.20000000000000001", - "0.29999999999999999", - "0.40000000000000002", - "0.5", - "0.59999999999999998", - "0.69999999999999996", - "0.80000000000000004", - "0.90000000000000002", - "1", - "1.1000000000000001", - "1.2", - "1.3", - "1.3999999999999999", - "1.5", - "2", - "5", - "10", - "20", - "50" - ] - }, - "binsTrackMultiplcity": { - "values": [ - "200", - "0", - "200" - ] - }, - "binsVertexPosXY": { - "values": [ - "500", - "-1", - "1" - ] - }, - "binsVertexPosZ": { - "values": [ - "100", - "-20", - "20" - ] - }, - "fractionOfSampledEvents": "1", - "isRun3": "true", - "maxEta": "2", - "maxPhi": "10", - "maxPt": "1e+10", - "minEta": "-2", - "minPhi": "-1", - "minPt": "-10", - "processData": "true", - "processDataIU": "false", - "processDataIUFiltered": "false", - "processMC": "false", - "processTableData": "false", - "processTableMC": "false", - "selectCharge": "0", - "selectGoodEvents": "true", - "selectMaxVtxZ": "100", - "selectPID": "0", - "selectPrim": "false", - "selectSec": "false", - "targetNumberOfEvents": "10000000", - "trackSelection": "1" - }, - "qa-k0s-tracking-efficiency": { - "eventSelection": "true", - "nSigTPC": "10", - "nSigmaBins": { - "values": [ - "1000", - "-100", - "100" - ] - }, - "processIU": "true", - "rapidity": "0.5", - "v0cospa": "0.995" - }, - "tof-signal": "", - "tpc-pid-qa": { - "etaBins": { - "values": [ - 50, - -1, - 1 - ] - }, - "dEdxBins": { - "values": [ - 3000, - 0, - 1000 - ] - }, - "applyRapidityCut": "0", - "processFullWithTOFDeuteron": "0", - "phiBins": { - "values": [ - 50, - 0, - 6.283185307179586 - ] - }, - "processProton": "0", - "processHelium3": "0", - "processFullElectron": "1", - "processKaon": "0", - "processAlpha": "0", - "processFullWithTOFPion": "0", - "processFullHelium3": "1", - "minP": "0.01", - "enableDeDxPlot": "1", - "processFullWithTOFElectron": "0", - "logAxis": "1", - "processFullWithTOFAlpha": "0", - "processFullKaon": "1", - "processMuon": "0", - "processFullDeuteron": "1", - "processFullWithTOFMuon": "0", - "processFullWithTOFProton": "0", - "maxP": "5", - "processFullAlpha": "0", - "processElectron": "0", - "processDeuteron": "0", - "trackLengthBins": { - "values": [ - 50, - 0, - 1000 - ] - }, - "processFullProton": "1", - "processFullWithTOFKaon": "0", - "minTPCNcls": "0", - "processFullMuon": "1", - "processFullWithTOFTriton": "0", - "nSigmaBins": { - "values": [ - 400, - -10, - 10 - ] - }, - "trackSelection": "1", - "processFullWithTOFHelium3": "0", - "processFullTriton": "1", - "processTriton": "0", - "processPion": "0", - "splitSignalPerCharge": "1", - "expSigmaBins": { - "values": [ - 100, - 0, - 200 - ] - }, - "applyEvSel": "2", - "nBinsP": "400", - "deltaBins": { - "values": [ - 100, - -1000, - 1000 - ] - }, - "processFullPion": "1" - }, - "tof-pid-qa": { - "etaBins": { - "values": [ - 50, - -1, - 1 - ] - }, - "applyRapidityCut": "1", - "phiBins": { - "values": [ - 25, - 0, - 6.283185307179586 - ] - }, - "processProton": "0", - "processHelium3": "0", - "processFullElectron": "0", - "processKaon": "0", - "processAlpha": "0", - "processFullHelium3": "1", - "minP": "0.1", - "logAxis": "0", - "processFullKaon": "1", - "produceDeltaTEtaPhiMap": "0", - "processMuon": "0", - "processFullDeuteron": "1", - "maxP": "5", - "processFullAlpha": "0", - "ptDeltaTEtaPhiMap": "3", - "processElectron": "0", - "processDeuteron": "0", - "trackLengthBins": { - "values": [ - 50, - 0, - 1000 - ] - }, - "processFullProton": "1", - "enableEvTimeSplitting": "0", - "enableVsMomentumHistograms": "0", - "processFullMuon": "0", - "nSigmaBins": { - "values": [ - 400, - -10, - 10 - ] - }, - "trackSelection": "1", - "processFullTriton": "1", - "processTriton": "0", - "processPion": "0", - "splitSignalPerCharge": "1", - "expSigmaBins": { - "values": [ - 200, - 0, - 200 - ] - }, - "applyEvSel": "2", - "nBinsP": "400", - "deltaBins": { - "values": [ - 100, - -1000, - 1000 - ] - }, - "processFullPion": "1" - }, - "tpc-pid-full": { - "autofetchNetworks": "true", - "ccdb-timestamp": "0", - "ccdb-url": "http://alice-ccdb.cern.ch", - "ccdbPath": "Analysis/PID/TPC/Response", - "enableNetworkOptimizations": "true", - "networkPathCCDB": "Analysis/PID/TPC/ML", - "networkPathLocally": "network.onnx", - "networkSetNumThreads": "0", - "param-file": "", - "pid-al": "-1", - "pid-de": "-1", - "pid-el": "-1", - "pid-he": "-1", - "pid-ka": "-1", - "pid-mu": "-1", - "pid-pi": "-1", - "pid-pr": "-1", - "pid-tr": "-1", - "recoPass": "", - "skipTPCOnly": "false", - "useNetworkCorrection": "false" - }, - "track-extension": { - "processRun2": "false", - "processRun3": "true" - }, - "track-propagation": { - "ccdb-url": "http://alice-ccdb.cern.ch", - "geoPath": "GLO/Config/GeometryAligned", - "grpmagPath": "GLO/Config/GRPMagField", - "lutPath": "GLO/Param/MatLUT", - "mVtxPath": "GLO/Calib/MeanVertex", - "processCovariance": "true", - "processStandard": "false" - }, - "track-selection": { - "compatibilityIU": "false", - "dcaSetup": "0", - "etaMax": "0.8", - "etaMin": "-0.8", - "isRun3": "true", - "itsMatching": "1", - "produceFBextendedTable": "-1", - "produceTable": "-1", - "ptMax": "1e+10", - "ptMin": "0.1" - } -} \ No newline at end of file diff --git a/MC/config/analysis_testing/json/default/pbpb/analysis-testing-mc.json b/MC/config/analysis_testing/json/default/pbpb/analysis-testing-mc.json deleted file mode 100644 index 62209a027..000000000 --- a/MC/config/analysis_testing/json/default/pbpb/analysis-testing-mc.json +++ /dev/null @@ -1,1442 +0,0 @@ -{ - "McCollisions": {}, - "McParticles": {}, - "McTrackLabels": {}, - "ambiguous-track-propagation": { - "ccdb-url": "http://alice-ccdb.cern.ch", - "geoPath": "GLO/Config/GeometryAligned", - "grpmagPath": "GLO/Config/GRPMagField", - "mVtxPath": "GLO/Calib/MeanVertex", - "processCentral": "false", - "processMFT": "true" - }, - "bc-selection-task": { - "processRun2": "false", - "processRun3": "true" - }, - "check-mc-particles-indices": { - "debugMode": "0" - }, - "check-mc-particles-indices-grouped": { - "debugMode": "1" - }, - "event-selection-qa-task": { - "isMC": "true", - "processMCRun3": "true", - "processRun2": "false", - "processRun3": "true" - }, - "event-selection-task": { - "isMC": "true", - "muonSelection": "0", - "processRun2": "false", - "processRun3": "true", - "syst": "pp" - }, - "femto-dream-producer-task": { - "ConfDebugOutput": "true", - "ConfEvtOfflineCheck": "false", - "ConfEvtTriggerCheck": "true", - "ConfEvtTriggerSel": "0", - "ConfEvtZvtx": "10", - "ConfIsRun3": "false", - "ConfIsTrigger": "false", - "ConfStoreV0": "true", - "ConfTrkDCAxyMax": { - "values": [ - "0.100000001", - "3.5" - ] - }, - "ConfTrkDCAzMax": { - "values": [ - "0.200000003", - "3.5" - ] - }, - "ConfTrkEtaMax": { - "values": [ - "0.800000012", - "0.699999988", - "0.899999976" - ] - }, - "ConfTrkITSnClsIbMin": { - "values": [ - "-1", - "1" - ] - }, - "ConfTrkITSnClsMin": { - "values": [ - "-1", - "2", - "4" - ] - }, - "ConfTrkPIDnSigmaMax": { - "values": [ - "3.5", - "3", - "2.5" - ] - }, - "ConfTrkPtMin": { - "values": [ - "0.400000006", - "0.600000024", - "0.5" - ] - }, - "ConfTrkSign": { - "values": [ - "-1", - "1" - ] - }, - "ConfTrkTPCcRowsMin": { - "values": [ - "70", - "60", - "80" - ] - }, - "ConfTrkTPCfClsMin": { - "values": [ - "0.699999988", - "0.829999983", - "0.899999976" - ] - }, - "ConfTrkTPCnClsMin": { - "values": [ - "80", - "70", - "60" - ] - }, - "ConfTrkTPCsClsMax": { - "values": [ - "0.100000001", - "160" - ] - }, - "ConfTrkTPIDspecies": { - "values": [ - "2", - "3", - "4", - "5" - ] - }, - "ConfV0CPAMin": { - "values": [ - "0.99000001", - "0.995000005" - ] - }, - "ConfV0DCAdaughMax": { - "values": [ - "1.20000005", - "1.5" - ] - }, - "ConfV0DaughDCAMin": { - "values": [ - "0.0500000007", - "0.0599999987" - ] - }, - "ConfV0DaughPIDnSigmaMax": { - "values": [ - "5", - "4" - ] - }, - "ConfV0DaughTPCnclsMin": { - "values": [ - "80", - "70", - "60" - ] - }, - "ConfV0DecVecMax": { - "values": [ - "100.0" - ] - }, - "ConfV0PtMin": { - "values": [ - "0.300000012", - "0.400000006", - "0.5" - ] - }, - "ConfV0Sign": { - "values": [ - "-1", - "1" - ] - }, - "ConfV0TranRadMax": { - "values": [ - "100.0" - ] - }, - "ConfV0TranRadMin": { - "values": [ - "0.2" - ] - } - }, - "hf-cand-creator-2prong": { - "b_dovalplots": "true", - "b_propdca": "true", - "d_bz": "5.", - "d_maxdzini": "999.", - "d_maxr": "5.", - "d_minparamchange": "0.001", - "d_minrelchi2change": "0.9" - }, - "hf-cand-creator-2prong-expressions": { - "processMC": "true" - }, - "hf-d0-candidate-selector": { - "D0_to_pi_K_cuts": { - "values": [ - [ - "0.4", - "0.035", - "0.8", - "0.5", - "0.5", - "0.1", - "0.1", - "-5.e-5", - "0.80", - "0.", - "0.", - "10.0", - "10.0", - "0.06" - ], - [ - "0.4", - "0.035", - "0.8", - "0.5", - "0.5", - "0.1", - "0.1", - "-5.e-5", - "0.80", - "0.", - "0.", - "10.0", - "10.0", - "0.06" - ], - [ - "0.4", - "0.030", - "0.8", - "0.4", - "0.4", - "0.1", - "0.1", - "-25.e-5", - "0.80", - "0.", - "0.", - "10.0", - "10.0", - "0.06" - ], - [ - "0.4", - "0.030", - "0.8", - "0.4", - "0.4", - "0.1", - "0.1", - "-25.e-5", - "0.80", - "0.", - "0.", - "10.0", - "10.0", - "0.06" - ], - [ - "0.4", - "0.030", - "0.8", - "0.7", - "0.7", - "0.1", - "0.1", - "-20.e-5", - "0.90", - "0.", - "0.", - "10.0", - "10.0", - "0.06" - ], - [ - "0.4", - "0.030", - "0.8", - "0.7", - "0.7", - "0.1", - "0.1", - "-20.e-5", - "0.90", - "0.", - "0.", - "10.0", - "10.0", - "0.06" - ], - [ - "0.4", - "0.030", - "0.8", - "0.7", - "0.7", - "0.1", - "0.1", - "-12.e-5", - "0.85", - "0.", - "0.", - "10.0", - "10.0", - "0.06" - ], - [ - "0.4", - "0.030", - "0.8", - "0.7", - "0.7", - "0.1", - "0.1", - "-12.e-5", - "0.85", - "0.", - "0.", - "10.0", - "10.0", - "0.06" - ], - [ - "0.4", - "0.030", - "0.8", - "0.7", - "0.7", - "0.1", - "0.1", - "-8.e-5", - "0.85", - "0.", - "0.", - "10.0", - "10.0", - "0.06" - ], - [ - "0.4", - "0.030", - "0.8", - "0.7", - "0.7", - "0.1", - "0.1", - "-8.e-5", - "0.85", - "0.", - "0.", - "10.0", - "10.0", - "0.06" - ], - [ - "0.4", - "0.030", - "0.8", - "0.7", - "0.7", - "0.1", - "0.1", - "-8.e-5", - "0.85", - "0.", - "0.", - "10.0", - "10.0", - "0.06" - ], - [ - "0.4", - "0.030", - "0.8", - "0.7", - "0.7", - "0.1", - "0.1", - "-8.e-5", - "0.85", - "0.", - "0.", - "10.0", - "10.0", - "0.06" - ], - [ - "0.4", - "0.030", - "0.8", - "0.7", - "0.7", - "0.1", - "0.1", - "-8.e-5", - "0.85", - "0.", - "0.", - "10.0", - "10.0", - "0.06" - ], - [ - "0.4", - "0.030", - "0.8", - "0.7", - "0.7", - "0.1", - "0.1", - "-8.e-5", - "0.85", - "0.", - "0.", - "10.0", - "10.0", - "0.06" - ], - [ - "0.4", - "0.030", - "0.8", - "0.7", - "0.7", - "0.1", - "0.1", - "-7.e-5", - "0.85", - "0.", - "0.", - "10.0", - "10.0", - "0.06" - ], - [ - "0.4", - "0.030", - "0.8", - "0.7", - "0.7", - "0.1", - "0.1", - "-7.e-5", - "0.85", - "0.", - "0.", - "10.0", - "10.0", - "0.06" - ], - [ - "0.4", - "0.030", - "0.9", - "0.7", - "0.7", - "0.1", - "0.1", - "-5.e-5", - "0.85", - "0.", - "0.", - "10.0", - "10.0", - "0.06" - ], - [ - "0.4", - "0.030", - "0.9", - "0.7", - "0.7", - "0.1", - "0.1", - "-5.e-5", - "0.85", - "0.", - "0.", - "10.0", - "10.0", - "0.06" - ], - [ - "0.4", - "0.030", - "0.9", - "0.7", - "0.7", - "0.1", - "0.1", - "-5.e-5", - "0.85", - "0.", - "0.", - "10.0", - "10.0", - "0.06" - ], - [ - "0.4", - "0.030", - "1.0", - "0.7", - "0.7", - "0.1", - "0.1", - "10.e-5", - "0.85", - "0.", - "0.", - "10.0", - "10.0", - "0.06" - ], - [ - "0.4", - "0.030", - "1.0", - "0.7", - "0.7", - "0.1", - "0.1", - "1000.e-5", - "0.85", - "0.", - "0.", - "10.0", - "10.0", - "0.06" - ], - [ - "0.4", - "0.030", - "1.0", - "0.7", - "0.7", - "0.1", - "0.1", - "1000.e-5", - "0.85", - "0.", - "0.", - "10.0", - "10.0", - "0.06" - ], - [ - "0.4", - "0.030", - "1.0", - "0.7", - "0.7", - "0.1", - "0.1", - "1000.e-5", - "0.85", - "0.", - "0.", - "10.0", - "10.0", - "0.06" - ], - [ - "0.4", - "0.030", - "1.0", - "0.7", - "0.7", - "0.1", - "0.1", - "1000.e-5", - "0.85", - "0.", - "0.", - "10.0", - "10.0", - "0.06" - ], - [ - "0.4", - "0.030", - "1.0", - "0.6", - "0.6", - "0.1", - "0.1", - "1000.e-5", - "0.80", - "0.", - "0.", - "10.0", - "10.0", - "0.06" - ] - ] - }, - "d_TPCNClsFindablePIDCut": "50.", - "d_nSigmaTOF": "3.", - "d_nSigmaTOFCombined": "5.", - "d_nSigmaTPC": "3.", - "d_nSigmaTPCCombined": "5.", - "d_pTCandMax": "50.", - "d_pTCandMin": "0.", - "d_pidTOFMaxpT": "10.", - "d_pidTOFMinpT": "0.15", - "d_pidTPCMaxpT": "10.", - "d_pidTPCMinpT": "0.15", - "pTBins": { - "values": [ - "0", - "0.5", - "1", - "1.5", - "2", - "2.5", - "3", - "3.5", - "4", - "4.5", - "5", - "5.5", - "6", - "6.5", - "7", - "7.5", - "8", - "9", - "10", - "12", - "16", - "20", - "24", - "36", - "50", - "100" - ] - } - }, - "hf-task-d0": { - "cutYCandMax": "0.8", - "d_selectionFlagD0": "0", - "d_selectionFlagD0bar": "0", - "pTBins": { - "values": [ - "0", - "0.5", - "1", - "1.5", - "2", - "2.5", - "3", - "3.5", - "4", - "4.5", - "5", - "5.5", - "6", - "6.5", - "7", - "7.5", - "8", - "9", - "10", - "12", - "16", - "20", - "24", - "36", - "50", - "100" - ] - }, - "processMC": "true" - }, - "hf-track-index-skims-creator": { - "bz": "5.", - "cutsD0ToPiK": { - "values": [ - [ - "1.61", - "2.12", - "0.5", - "0.00" - ], - [ - "1.61", - "2.12", - "0.5", - "0.00" - ] - ] - }, - "cutsDPlusToPiKPi": { - "values": [ - [ - "1.67", - "2.07", - "0.7", - "0.03" - ], - [ - "1.67", - "2.07", - "0.7", - "0.03" - ] - ] - }, - "cutsDsToPiKK": { - "values": [ - [ - "1.74", - "2.18", - "0.94", - "0.02" - ], - [ - "1.74", - "2.18", - "0.94", - "0.02" - ] - ] - }, - "cutsJpsiToEE": { - "values": [ - [ - "2.5", - "4.1", - "-2.", - "1000.00" - ], - [ - "2.5", - "4.1", - "-2.", - "1000.00" - ] - ] - }, - "cutsJpsiToMuMu": { - "values": [ - [ - "2.5", - "4.1", - "-2.", - "1000.00" - ], - [ - "2.5", - "4.1", - "-2.", - "1000.00" - ] - ] - }, - "cutsLcToPKPi": { - "values": [ - [ - "1.98", - "2.58", - "-2.", - "0.0" - ], - [ - "1.98", - "2.58", - "-2.", - "0.0" - ] - ] - }, - "cutsXicToPKPi": { - "values": [ - [ - "2.10", - "2.80", - "-2.", - "0.0" - ], - [ - "2.10", - "2.80", - "-2.", - "0.0" - ] - ] - }, - "do3prong": "1", - "fillHistograms": "true", - "maxDZIni": "999.", - "maxRad": "5.", - "minParamChange": "0.001", - "minRelChi2Change": "0.9", - "pTBinsD0ToPiK": { - "values": [ - "0.", - "5.", - "1000." - ] - }, - "pTBinsDPlusToPiKPi": { - "values": [ - "1.", - "5.", - "1000." - ] - }, - "pTBinsDsToPiKK": { - "values": [ - "1.5", - "5.", - "1000." - ] - }, - "pTBinsJpsiToEE": { - "values": [ - "0.", - "5.", - "1000." - ] - }, - "pTBinsJpsiToMuMu": { - "values": [ - "0.", - "5.", - "1000." - ] - }, - "pTBinsLcToPKPi": { - "values": [ - "4.", - "5.", - "1000." - ] - }, - "pTBinsXicToPKPi": { - "values": [ - "4.", - "5.", - "1000." - ] - }, - "propToDCA": "true" - }, - "lambdakzero-builder": { - "ccdb-url": "http://alice-ccdb.cern.ch", - "createV0CovMats": "-1", - "dQAK0ShortMassWindow": "0.005", - "dQALambdaMassWindow": "0.005", - "dQAMaxPt": "5", - "dQANBinsMass": "400", - "dQANBinsPtCoarse": "10", - "dQANBinsRadius": "500", - "d_UseAbsDCA": "1", - "d_UseAutodetectMode": "0", - "d_UseWeightedPCA": "0", - "d_bz": "-999", - "d_doQA": "0", - "d_doTrackQA": "0", - "dcanegtopv": "0.1", - "dcapostopv": "0.1", - "dcav0dau": "1", - "geoPath": "GLO/Config/GeometryAligned", - "grpPath": "GLO/GRP/GRP", - "grpmagPath": "GLO/Config/GRPMagField", - "lutPath": "GLO/Param/MatLUT", - "processRun2": "0", - "processRun3": "1", - "rejDiffCollTracks": "0", - "tpcrefit": "0", - "useMatCorrType": "2", - "v0cospa": "0.995", - "v0radius": "1" - }, - "lambdakzero-preselector": { - "dIfMCgenerateAntiHypertriton": "0", - "dIfMCgenerateAntiLambda": "0", - "dIfMCgenerateGamma": "0", - "dIfMCgenerateHypertriton": "0", - "dIfMCgenerateK0Short": "1", - "dIfMCgenerateLambda": "0", - "dPreselectOnlyBaryons": "0", - "dTPCNCrossedRows": "50", - "ddEdxPreSelectAntiHypertriton": "0", - "ddEdxPreSelectAntiLambda": "0", - "ddEdxPreSelectGamma": "0", - "ddEdxPreSelectHypertriton": "0", - "ddEdxPreSelectK0Short": "1", - "ddEdxPreSelectLambda": "0", - "ddEdxPreSelectionWindow": "7", - "processBuildAll": "1", - "processBuildMCAssociated": "0", - "processBuildValiddEdx": "0", - "processBuildValiddEdxMCAssociated": "0" - }, - "multiplicity-table": { - "processRun2": "false", - "processRun3": "true" - }, - "particles-to-tracks": { - "processIndexing": "true" - }, - "perf-k0s-resolution": { - "etaBins": { - "values": [ - 10, - -1, - 1 - ] - }, - "eventSelection": "1", - "mBins": { - "values": [ - 300, - 0.35, - 0.65 - ] - }, - "nSigTPC": "10", - "pTBins": { - "values": [ - 200, - 0, - 10 - ] - }, - "phiBins": { - "values": [ - 18, - 0, - 6.2831853 - ] - }, - "rapidity": "0.5", - "v0lifetime": "3", - "v0setting_cospa": "0.995", - "v0setting_dcanegtopv": "0.1", - "v0setting_dcapostopv": "0.1", - "v0setting_dcav0dau": "1", - "v0setting_radius": "0.9" - }, - "pseudorapidity-density": { - "estimatorEta": "1", - "exclusionPhi": { - "values": [ - [ - "4", - "5" - ] - ] - }, - "maxDCAXY": "2.4", - "maxDCAZ": "3.2", - "processGen": "true", - "processTagging": "true", - "processTrackEfficiency": "true", - "useDCAXY": "false", - "useDCAZ": "false", - "useEvSel": "false", - "usePhiCut": "false", - "usePtDCAXY": "false" - }, - "qa-efficiency": { - "applyEvSel": "2", - "do-al": "true", - "do-de": "true", - "do-el": "true", - "do-he": "true", - "do-ka": "true", - "do-mu": "true", - "do-pi": "true", - "do-pr": "true", - "do-tr": "true", - "doNegativePDG": "true", - "doPositivePDG": "true", - "doPtEta": "false", - "etaBins": { - "values": [ - "200", - "-0.8", - "0.8" - ] - }, - "globalTrackSelection": "0", - "log-pt": "0", - "make-eff": "true", - "maxProdRadius": "9999", - "nMinNumberOfContributors": "2", - "noFakesHits": "false", - "phiBins": { - "values": [ - "200", - "0", - "6.2839999198913574" - ] - }, - "processData": "true", - "processMC": "true", - "processMCWithoutCollisions": "false", - "ptBins": { - "values": [ - "200", - "0", - "5" - ] - }, - "trackSelection": "true", - "vertex-z-max": "10", - "vertex-z-min": "-10", - "yBins": { - "values": [ - "200", - "-0.5", - "0.5" - ] - } - }, - "qa-event-track": { - "binsPt": { - "values": [ - "0", - "0", - "0.10000000000000001", - "0.20000000000000001", - "0.29999999999999999", - "0.40000000000000002", - "0.5", - "0.59999999999999998", - "0.69999999999999996", - "0.80000000000000004", - "0.90000000000000002", - "1", - "1.1000000000000001", - "1.2", - "1.3", - "1.3999999999999999", - "1.5", - "2", - "5", - "10", - "20", - "50" - ] - }, - "binsTrackMultiplcity": { - "values": [ - "200", - "0", - "200" - ] - }, - "binsVertexPosXY": { - "values": [ - "500", - "-1", - "1" - ] - }, - "binsVertexPosZ": { - "values": [ - "100", - "-20", - "20" - ] - }, - "fractionOfSampledEvents": "1", - "isRun3": "true", - "maxEta": "2", - "maxPhi": "10", - "maxPt": "1e+10", - "minEta": "-2", - "minPhi": "-1", - "minPt": "-10", - "processData": "true", - "processDataIU": "false", - "processDataIUFiltered": "false", - "processMC": "true", - "processTableData": "false", - "processTableMC": "false", - "selectCharge": "0", - "selectGoodEvents": "true", - "selectMaxVtxZ": "100", - "selectPID": "0", - "selectPrim": "false", - "selectSec": "false", - "targetNumberOfEvents": "10000000", - "trackSelection": "1" - }, - "table-maker-m-c": { - "cfgBarrelLowPt": "1", - "cfgBarrelTrackCuts": "jpsiO2MCdebugCuts,kaonPID", - "cfgDetailedQA": "true", - "cfgEventCuts": "eventStandardNoINT7", - "cfgIsRun2": "false", - "cfgMCsignals": "anyBeautyHadron,everythingFromBeauty,everythingFromEverythingFromBeauty", - "cfgMuonCuts": "muonQualityCuts,muonTightQualityCutsForTests", - "cfgMuonLowPt": "1", - "cfgNoQA": "false", - "processBarrelOnly": "false", - "processBarrelOnlyWithCent": "false", - "processBarrelOnlyWithCov": "true", - "processFull": "false", - "processMuonOnlyWithCent": "false", - "processMuonOnlyWithCov": "false", - "processOnlyBCs": "true" - }, - "tof-signal": "", - "tpc-pid-qa": { - "etaBins": { - "values": [ - 50, - -1, - 1 - ] - }, - "dEdxBins": { - "values": [ - 3000, - 0, - 1000 - ] - }, - "applyRapidityCut": "0", - "processFullWithTOFDeuteron": "0", - "phiBins": { - "values": [ - 50, - 0, - 6.283185307179586 - ] - }, - "processProton": "0", - "processHelium3": "0", - "processFullElectron": "1", - "processKaon": "0", - "processAlpha": "0", - "processFullWithTOFPion": "0", - "processFullHelium3": "1", - "minP": "0.01", - "enableDeDxPlot": "1", - "processFullWithTOFElectron": "0", - "logAxis": "1", - "processFullWithTOFAlpha": "0", - "processFullKaon": "1", - "processMuon": "0", - "processFullDeuteron": "1", - "processFullWithTOFMuon": "0", - "processFullWithTOFProton": "0", - "maxP": "5", - "processFullAlpha": "0", - "processElectron": "0", - "processDeuteron": "0", - "trackLengthBins": { - "values": [ - 50, - 0, - 1000 - ] - }, - "processFullProton": "1", - "processFullWithTOFKaon": "0", - "minTPCNcls": "0", - "processFullMuon": "1", - "processFullWithTOFTriton": "0", - "nSigmaBins": { - "values": [ - 400, - -10, - 10 - ] - }, - "trackSelection": "1", - "processFullWithTOFHelium3": "0", - "processFullTriton": "1", - "processTriton": "0", - "processPion": "0", - "splitSignalPerCharge": "1", - "expSigmaBins": { - "values": [ - 100, - 0, - 200 - ] - }, - "applyEvSel": "2", - "nBinsP": "400", - "deltaBins": { - "values": [ - 100, - -1000, - 1000 - ] - }, - "processFullPion": "1" - }, - "tof-pid-qa": { - "etaBins": { - "values": [ - 50, - -1, - 1 - ] - }, - "applyRapidityCut": "1", - "phiBins": { - "values": [ - 25, - 0, - 6.283185307179586 - ] - }, - "processProton": "0", - "processHelium3": "0", - "processFullElectron": "0", - "processKaon": "0", - "processAlpha": "0", - "processFullHelium3": "1", - "minP": "0.1", - "logAxis": "0", - "processFullKaon": "1", - "produceDeltaTEtaPhiMap": "0", - "processMuon": "0", - "processFullDeuteron": "1", - "maxP": "5", - "processFullAlpha": "0", - "ptDeltaTEtaPhiMap": "3", - "processElectron": "0", - "processDeuteron": "0", - "trackLengthBins": { - "values": [ - 50, - 0, - 1000 - ] - }, - "processFullProton": "1", - "enableEvTimeSplitting": "0", - "enableVsMomentumHistograms": "0", - "processFullMuon": "0", - "nSigmaBins": { - "values": [ - 400, - -10, - 10 - ] - }, - "trackSelection": "1", - "processFullTriton": "1", - "processTriton": "0", - "processPion": "0", - "splitSignalPerCharge": "1", - "expSigmaBins": { - "values": [ - 200, - 0, - 200 - ] - }, - "applyEvSel": "2", - "nBinsP": "400", - "deltaBins": { - "values": [ - 100, - -1000, - 1000 - ] - }, - "processFullPion": "1" - }, - "tpc-pid-full": { - "autofetchNetworks": "1", - "ccdb-timestamp": "0", - "ccdb-url": "http://alice-ccdb.cern.ch", - "ccdbPath": "Analysis/PID/TPC/Response", - "enableNetworkOptimizations": "1", - "networkPathCCDB": "Analysis/PID/TPC/ML", - "networkPathLocally": "network.onnx", - "networkSetNumThreads": "0", - "param-file": "", - "pid-al": "-1", - "pid-de": "-1", - "pid-el": "-1", - "pid-he": "-1", - "pid-ka": "-1", - "pid-mu": "-1", - "pid-pi": "-1", - "pid-pr": "-1", - "pid-tr": "-1", - "useNetworkCorrection": "0" - }, - "pid-multiplicity": { - "processIU": "0", - "processStandard": "1" - }, - "track-extension": { - "processRun2": "false", - "processRun3": "true" - }, - "track-propagation": { - "ccdb-url": "http://alice-ccdb.cern.ch", - "geoPath": "GLO/Config/GeometryAligned", - "grpmagPath": "GLO/Config/GRPMagField", - "lutPath": "GLO/Param/MatLUT", - "mVtxPath": "GLO/Calib/MeanVertex", - "processCovariance": "true", - "processStandard": "false" - }, - "track-selection": { - "compatibilityIU": "false", - "etaMax": "0.8", - "etaMin": "-0.8", - "isRun3": "true", - "itsMatching": "1", - "produceFBextendedTable": "-1", - "ptMax": "1e+10", - "ptMin": "0.1" - }, - "emcal-correction-task": { - "nonlinearityFunction": "MC_TestbeamFinal", - "logWeight": "4.5", - "exoticCellMinAmplitude": "4", - "clusterDefinition": "kV3Default", - "useWeightExotic": "0", - "disableNonLin": "0", - "exoticCellDiffTime": "1e+06", - "exoticCellInCrossMinAmplitude": "0.1", - "maxMatchingDistance": "0.4", - "exoticCellFraction": "0.97", - "hasShaperCorrection": "1", - "isMC": "1", - "hasPropagatedTracks": "0", - "processFull": "0", - "selectedCellType": "1", - "processStandalone": "0", - "processMCFull": "1" - }, - "cell-monitor": { - "minCellTimeMain": "-50", - "maxCellTimeMain": "100", - "minCellAmplitudeTimeHists": "0.3", - "vetoBCID": "", - "selectBCID": "all", - "minCellAmplitude": "0" - }, - "EMCClusterMonitorTaskAmbiguous": { - "clustertime-binning": { - "values": [ - 1500, - -600, - 900 - ] - }, - "processCollisions": "0", - "doEventSel": "0", - "processAmbiguous": "1", - "clusterDefinition": "10", - "numclusters-binning": { - "values": [ - 201, - -0.5, - 200.5 - ] - }, - "vetoBCID": "", - "selectBCID": "all", - "vertexCut": "-1" - }, - "EMCClusterMonitorTask": { - "clustertime-binning": { - "values": [ - 1500, - -600, - 900 - ] - }, - "processCollisions": "1", - "doEventSel": "0", - "processAmbiguous": "0", - "clusterDefinition": "10", - "numclusters-binning": { - "values": [ - 201, - -0.5, - 200.5 - ] - }, - "vetoBCID": "", - "selectBCID": "all", - "vertexCut": "-1" - }, - "emc-tmmonitor": { - "clustertime-binning": { - "values": [ - 1500, - -600, - 900 - ] - }, - "doEventSel": "0", - "maxTime": "20", - "clusterDefinition": "10", - "minM02": "0.1", - "vetoBCID": "", - "processCollisions": "1", - "hasPropagatedTracks": "1", - "usePionRejection": "0", - "minTime": "-25", - "tpcNsigmaPion": { - "values": [ - -3, - 3 - ] - }, - "selectBCID": "all", - "tpcNsigmaElectron": { - "values": [ - -1, - 3 - ] - }, - "vertexCut": "-1", - "tpcNsigmaBack": { - "values": [ - -10, - -4 - ] - } - } -} diff --git a/MC/config/analysis_testing/json/default/pp/analysis-testing-data.json b/MC/config/analysis_testing/json/default/pp/analysis-testing-data.json deleted file mode 100644 index 6b1ad0ad5..000000000 --- a/MC/config/analysis_testing/json/default/pp/analysis-testing-data.json +++ /dev/null @@ -1,567 +0,0 @@ -{ - "EMCClusterMonitorTask": { - "clusterDefinition": "10", - "clustertime-binning": { - "values": [ - 1500, - -600, - 900 - ] - }, - "doEventSel": "0", - "numclusters-binning": { - "values": [ - 201, - -0.5, - 200.5 - ] - }, - "processAmbiguous": "0", - "processCollisions": "1", - "selectBCID": "all", - "vertexCut": "-1", - "vetoBCID": "" - }, - "EMCClusterMonitorTaskAmbiguous": { - "clusterDefinition": "10", - "clustertime-binning": { - "values": [ - 1500, - -600, - 900 - ] - }, - "doEventSel": "0", - "numclusters-binning": { - "values": [ - 201, - -0.5, - 200.5 - ] - }, - "processAmbiguous": "1", - "processCollisions": "0", - "selectBCID": "all", - "vertexCut": "-1", - "vetoBCID": "" - }, - "ambiguous-track-propagation": { - "ccdb-url": "http://alice-ccdb.cern.ch", - "geoPath": "GLO/Config/GeometryAligned", - "grpmagPath": "GLO/Config/GRPMagField", - "mVtxPath": "GLO/Calib/MeanVertex", - "processCentral": "false", - "processMFT": "true" - }, - "bc-selection-task": { - "processRun2": "false", - "processRun3": "true" - }, - "cell-monitor": { - "maxCellTimeMain": "100", - "minCellAmplitude": "0", - "minCellAmplitudeTimeHists": "0.3", - "minCellTimeMain": "-50", - "selectBCID": "all", - "vetoBCID": "" - }, - "emc-tmmonitor": { - "clusterDefinition": "10", - "clustertime-binning": { - "values": [ - 1500, - -600, - 900 - ] - }, - "doEventSel": "0", - "hasPropagatedTracks": "1", - "maxTime": "20", - "minM02": "0.1", - "minTime": "-25", - "processCollisions": "1", - "selectBCID": "all", - "tpcNsigmaBack": { - "values": [ - -10, - -4 - ] - }, - "tpcNsigmaElectron": { - "values": [ - -1, - 3 - ] - }, - "tpcNsigmaPion": { - "values": [ - -3, - 3 - ] - }, - "usePionRejection": "0", - "vertexCut": "-1", - "vetoBCID": "" - }, - "emcal-correction-task": { - "clusterDefinition": "kV3Default", - "disableNonLin": "0", - "exoticCellDiffTime": "1e+06", - "exoticCellFraction": "0.97", - "exoticCellInCrossMinAmplitude": "0.1", - "exoticCellMinAmplitude": "4", - "hasPropagatedTracks": "0", - "hasShaperCorrection": "1", - "isMC": "0", - "logWeight": "4.5", - "maxMatchingDistance": "0.4", - "nonlinearityFunction": "DATA_TestbeamFinal_NoScale", - "processFull": "1", - "processMCFull": "0", - "processStandalone": "0", - "selectedCellType": "1", - "useWeightExotic": "0" - }, - "event-selection-qa-task": { - "isMC": "false", - "processRun2": "false", - "processRun3": "true" - }, - "event-selection-task": { - "isMC": "false", - "muonSelection": "0", - "processRun2": "false", - "processRun3": "true", - "syst": "pp" - }, - "lambdakzero-builder": { - "ccdb-url": "http://alice-ccdb.cern.ch", - "createV0CovMats": "-1", - "dQAK0ShortMassWindow": "0.005", - "dQALambdaMassWindow": "0.005", - "dQAMaxPt": "5", - "dQANBinsMass": "400", - "dQANBinsPtCoarse": "10", - "dQANBinsRadius": "500", - "d_UseAbsDCA": "1", - "d_UseAutodetectMode": "0", - "d_UseWeightedPCA": "0", - "d_bz": "-999", - "d_doQA": "0", - "d_doTrackQA": "0", - "dcanegtopv": "0.1", - "dcapostopv": "0.1", - "dcav0dau": "1", - "geoPath": "GLO/Config/GeometryAligned", - "grpPath": "GLO/GRP/GRP", - "grpmagPath": "GLO/Config/GRPMagField", - "lutPath": "GLO/Param/MatLUT", - "processRun2": "0", - "processRun3": "1", - "rejDiffCollTracks": "0", - "tpcrefit": "0", - "useMatCorrType": "2", - "v0cospa": "0.995", - "v0radius": "1" - }, - "multiplicity-table": { - "processRun2": "false", - "processRun3": "true" - }, - "perf-k0s-resolution": { - "etaBins": { - "values": [ - "20", - "-1", - "1" - ] - }, - "eventSelection": "true", - "mBins": { - "values": [ - "300", - "0.35", - "0.65" - ] - }, - "nSigTPC": "10", - "pTBins": { - "values": [ - "200", - "0", - "10" - ] - }, - "phiBins": { - "values": [ - "54", - "0", - "6.2831853" - ] - }, - "rapidity": "0.5", - "requireTRDneg": "0", - "requireTRDpos": "0", - "v0lifetime": "3", - "v0setting_cospa": "0.995", - "v0setting_dcanegtopv": "0.1", - "v0setting_dcapostopv": "0.1", - "v0setting_dcav0dau": "1", - "v0setting_radius": "0.9" - }, - "pid-multiplicity": { - "processIU": "1", - "processStandard": "0" - }, - "qa-efficiency": { - "applyEvSel": "2", - "do-al": "false", - "do-de": "false", - "do-el": "false", - "do-he": "false", - "do-ka": "false", - "do-mu": "false", - "do-pi": "false", - "do-pr": "false", - "do-tr": "false", - "doNegativePDG": "true", - "doPositivePDG": "true", - "doPtEta": "false", - "etaBins": { - "values": [ - "200", - "-0.8", - "0.8" - ] - }, - "globalTrackSelection": "4", - "log-pt": "0", - "make-eff": "true", - "maxProdRadius": "9999", - "nMinNumberOfContributors": "2", - "noFakesHits": "false", - "phiBins": { - "values": [ - "200", - "0", - "6.2839999198913574" - ] - }, - "processData": "true", - "processMC": "false", - "processMCWithoutCollisions": "false", - "ptBins": { - "values": [ - "200", - "0", - "5" - ] - }, - "trackSelection": "true", - "vertex-z-max": "10", - "vertex-z-min": "-10", - "yBins": { - "values": [ - "200", - "-0.5", - "0.5" - ] - } - }, - "qa-event-track": { - "binsPt": { - "values": [ - "0", - "0", - "0.10000000000000001", - "0.20000000000000001", - "0.29999999999999999", - "0.40000000000000002", - "0.5", - "0.59999999999999998", - "0.69999999999999996", - "0.80000000000000004", - "0.90000000000000002", - "1", - "1.1000000000000001", - "1.2", - "1.3", - "1.3999999999999999", - "1.5", - "2", - "5", - "10", - "20", - "50" - ] - }, - "binsTrackMultiplcity": { - "values": [ - "200", - "0", - "200" - ] - }, - "binsVertexPosXY": { - "values": [ - "500", - "-1", - "1" - ] - }, - "binsVertexPosZ": { - "values": [ - "100", - "-20", - "20" - ] - }, - "fractionOfSampledEvents": "1", - "isRun3": "true", - "maxEta": "2", - "maxPhi": "10", - "maxPt": "1e+10", - "minEta": "-2", - "minPhi": "-1", - "minPt": "-10", - "processData": "true", - "processDataIU": "false", - "processDataIUFiltered": "false", - "processMC": "false", - "processTableData": "false", - "processTableMC": "false", - "selectCharge": "0", - "selectGoodEvents": "true", - "selectMaxVtxZ": "100", - "selectPID": "0", - "selectPrim": "false", - "selectSec": "false", - "targetNumberOfEvents": "10000000", - "trackSelection": "1" - }, - "qa-k0s-tracking-efficiency": { - "eventSelection": "true", - "nSigTPC": "10", - "nSigmaBins": { - "values": [ - "1000", - "-100", - "100" - ] - }, - "processIU": "true", - "rapidity": "0.5", - "v0cospa": "0.995" - }, - "tof-signal": "", - "tpc-pid-qa": { - "etaBins": { - "values": [ - 50, - -1, - 1 - ] - }, - "dEdxBins": { - "values": [ - 3000, - 0, - 1000 - ] - }, - "applyRapidityCut": "0", - "processFullWithTOFDeuteron": "0", - "phiBins": { - "values": [ - 50, - 0, - 6.283185307179586 - ] - }, - "processProton": "0", - "processHelium3": "0", - "processFullElectron": "1", - "processKaon": "0", - "processAlpha": "0", - "processFullWithTOFPion": "0", - "processFullHelium3": "1", - "minP": "0.01", - "enableDeDxPlot": "1", - "processFullWithTOFElectron": "0", - "logAxis": "1", - "processFullWithTOFAlpha": "0", - "processFullKaon": "1", - "processMuon": "0", - "processFullDeuteron": "1", - "processFullWithTOFMuon": "0", - "processFullWithTOFProton": "0", - "maxP": "5", - "processFullAlpha": "0", - "processElectron": "0", - "processDeuteron": "0", - "trackLengthBins": { - "values": [ - 50, - 0, - 1000 - ] - }, - "processFullProton": "1", - "processFullWithTOFKaon": "0", - "minTPCNcls": "0", - "processFullMuon": "1", - "processFullWithTOFTriton": "0", - "nSigmaBins": { - "values": [ - 400, - -10, - 10 - ] - }, - "trackSelection": "1", - "processFullWithTOFHelium3": "0", - "processFullTriton": "1", - "processTriton": "0", - "processPion": "0", - "splitSignalPerCharge": "1", - "expSigmaBins": { - "values": [ - 100, - 0, - 200 - ] - }, - "applyEvSel": "2", - "nBinsP": "400", - "deltaBins": { - "values": [ - 100, - -1000, - 1000 - ] - }, - "processFullPion": "1" - }, - "tof-pid-qa": { - "etaBins": { - "values": [ - 50, - -1, - 1 - ] - }, - "applyRapidityCut": "1", - "phiBins": { - "values": [ - 25, - 0, - 6.283185307179586 - ] - }, - "processProton": "0", - "processHelium3": "0", - "processFullElectron": "0", - "processKaon": "0", - "processAlpha": "0", - "processFullHelium3": "1", - "minP": "0.1", - "logAxis": "0", - "processFullKaon": "1", - "produceDeltaTEtaPhiMap": "0", - "processMuon": "0", - "processFullDeuteron": "1", - "maxP": "5", - "processFullAlpha": "0", - "ptDeltaTEtaPhiMap": "3", - "processElectron": "0", - "processDeuteron": "0", - "trackLengthBins": { - "values": [ - 50, - 0, - 1000 - ] - }, - "processFullProton": "1", - "enableEvTimeSplitting": "0", - "enableVsMomentumHistograms": "0", - "processFullMuon": "0", - "nSigmaBins": { - "values": [ - 400, - -10, - 10 - ] - }, - "trackSelection": "1", - "processFullTriton": "1", - "processTriton": "0", - "processPion": "0", - "splitSignalPerCharge": "1", - "expSigmaBins": { - "values": [ - 200, - 0, - 200 - ] - }, - "applyEvSel": "2", - "nBinsP": "400", - "deltaBins": { - "values": [ - 100, - -1000, - 1000 - ] - }, - "processFullPion": "1" - }, - "tpc-pid-full": { - "autofetchNetworks": "true", - "ccdb-timestamp": "0", - "ccdb-url": "http://alice-ccdb.cern.ch", - "ccdbPath": "Analysis/PID/TPC/Response", - "enableNetworkOptimizations": "true", - "networkPathCCDB": "Analysis/PID/TPC/ML", - "networkPathLocally": "network.onnx", - "networkSetNumThreads": "0", - "param-file": "", - "pid-al": "-1", - "pid-de": "-1", - "pid-el": "-1", - "pid-he": "-1", - "pid-ka": "-1", - "pid-mu": "-1", - "pid-pi": "-1", - "pid-pr": "-1", - "pid-tr": "-1", - "recoPass": "", - "skipTPCOnly": "false", - "useNetworkCorrection": "false" - }, - "track-extension": { - "processRun2": "false", - "processRun3": "true" - }, - "track-propagation": { - "ccdb-url": "http://alice-ccdb.cern.ch", - "geoPath": "GLO/Config/GeometryAligned", - "grpmagPath": "GLO/Config/GRPMagField", - "lutPath": "GLO/Param/MatLUT", - "mVtxPath": "GLO/Calib/MeanVertex", - "processCovariance": "true", - "processStandard": "false" - }, - "track-selection": { - "compatibilityIU": "false", - "dcaSetup": "0", - "etaMax": "0.8", - "etaMin": "-0.8", - "isRun3": "true", - "itsMatching": "1", - "produceFBextendedTable": "-1", - "produceTable": "-1", - "ptMax": "1e+10", - "ptMin": "0.1" - } -} \ No newline at end of file diff --git a/MC/config/analysis_testing/json/default/pp/analysis-testing-mc.json b/MC/config/analysis_testing/json/default/pp/analysis-testing-mc.json deleted file mode 100644 index 62209a027..000000000 --- a/MC/config/analysis_testing/json/default/pp/analysis-testing-mc.json +++ /dev/null @@ -1,1442 +0,0 @@ -{ - "McCollisions": {}, - "McParticles": {}, - "McTrackLabels": {}, - "ambiguous-track-propagation": { - "ccdb-url": "http://alice-ccdb.cern.ch", - "geoPath": "GLO/Config/GeometryAligned", - "grpmagPath": "GLO/Config/GRPMagField", - "mVtxPath": "GLO/Calib/MeanVertex", - "processCentral": "false", - "processMFT": "true" - }, - "bc-selection-task": { - "processRun2": "false", - "processRun3": "true" - }, - "check-mc-particles-indices": { - "debugMode": "0" - }, - "check-mc-particles-indices-grouped": { - "debugMode": "1" - }, - "event-selection-qa-task": { - "isMC": "true", - "processMCRun3": "true", - "processRun2": "false", - "processRun3": "true" - }, - "event-selection-task": { - "isMC": "true", - "muonSelection": "0", - "processRun2": "false", - "processRun3": "true", - "syst": "pp" - }, - "femto-dream-producer-task": { - "ConfDebugOutput": "true", - "ConfEvtOfflineCheck": "false", - "ConfEvtTriggerCheck": "true", - "ConfEvtTriggerSel": "0", - "ConfEvtZvtx": "10", - "ConfIsRun3": "false", - "ConfIsTrigger": "false", - "ConfStoreV0": "true", - "ConfTrkDCAxyMax": { - "values": [ - "0.100000001", - "3.5" - ] - }, - "ConfTrkDCAzMax": { - "values": [ - "0.200000003", - "3.5" - ] - }, - "ConfTrkEtaMax": { - "values": [ - "0.800000012", - "0.699999988", - "0.899999976" - ] - }, - "ConfTrkITSnClsIbMin": { - "values": [ - "-1", - "1" - ] - }, - "ConfTrkITSnClsMin": { - "values": [ - "-1", - "2", - "4" - ] - }, - "ConfTrkPIDnSigmaMax": { - "values": [ - "3.5", - "3", - "2.5" - ] - }, - "ConfTrkPtMin": { - "values": [ - "0.400000006", - "0.600000024", - "0.5" - ] - }, - "ConfTrkSign": { - "values": [ - "-1", - "1" - ] - }, - "ConfTrkTPCcRowsMin": { - "values": [ - "70", - "60", - "80" - ] - }, - "ConfTrkTPCfClsMin": { - "values": [ - "0.699999988", - "0.829999983", - "0.899999976" - ] - }, - "ConfTrkTPCnClsMin": { - "values": [ - "80", - "70", - "60" - ] - }, - "ConfTrkTPCsClsMax": { - "values": [ - "0.100000001", - "160" - ] - }, - "ConfTrkTPIDspecies": { - "values": [ - "2", - "3", - "4", - "5" - ] - }, - "ConfV0CPAMin": { - "values": [ - "0.99000001", - "0.995000005" - ] - }, - "ConfV0DCAdaughMax": { - "values": [ - "1.20000005", - "1.5" - ] - }, - "ConfV0DaughDCAMin": { - "values": [ - "0.0500000007", - "0.0599999987" - ] - }, - "ConfV0DaughPIDnSigmaMax": { - "values": [ - "5", - "4" - ] - }, - "ConfV0DaughTPCnclsMin": { - "values": [ - "80", - "70", - "60" - ] - }, - "ConfV0DecVecMax": { - "values": [ - "100.0" - ] - }, - "ConfV0PtMin": { - "values": [ - "0.300000012", - "0.400000006", - "0.5" - ] - }, - "ConfV0Sign": { - "values": [ - "-1", - "1" - ] - }, - "ConfV0TranRadMax": { - "values": [ - "100.0" - ] - }, - "ConfV0TranRadMin": { - "values": [ - "0.2" - ] - } - }, - "hf-cand-creator-2prong": { - "b_dovalplots": "true", - "b_propdca": "true", - "d_bz": "5.", - "d_maxdzini": "999.", - "d_maxr": "5.", - "d_minparamchange": "0.001", - "d_minrelchi2change": "0.9" - }, - "hf-cand-creator-2prong-expressions": { - "processMC": "true" - }, - "hf-d0-candidate-selector": { - "D0_to_pi_K_cuts": { - "values": [ - [ - "0.4", - "0.035", - "0.8", - "0.5", - "0.5", - "0.1", - "0.1", - "-5.e-5", - "0.80", - "0.", - "0.", - "10.0", - "10.0", - "0.06" - ], - [ - "0.4", - "0.035", - "0.8", - "0.5", - "0.5", - "0.1", - "0.1", - "-5.e-5", - "0.80", - "0.", - "0.", - "10.0", - "10.0", - "0.06" - ], - [ - "0.4", - "0.030", - "0.8", - "0.4", - "0.4", - "0.1", - "0.1", - "-25.e-5", - "0.80", - "0.", - "0.", - "10.0", - "10.0", - "0.06" - ], - [ - "0.4", - "0.030", - "0.8", - "0.4", - "0.4", - "0.1", - "0.1", - "-25.e-5", - "0.80", - "0.", - "0.", - "10.0", - "10.0", - "0.06" - ], - [ - "0.4", - "0.030", - "0.8", - "0.7", - "0.7", - "0.1", - "0.1", - "-20.e-5", - "0.90", - "0.", - "0.", - "10.0", - "10.0", - "0.06" - ], - [ - "0.4", - "0.030", - "0.8", - "0.7", - "0.7", - "0.1", - "0.1", - "-20.e-5", - "0.90", - "0.", - "0.", - "10.0", - "10.0", - "0.06" - ], - [ - "0.4", - "0.030", - "0.8", - "0.7", - "0.7", - "0.1", - "0.1", - "-12.e-5", - "0.85", - "0.", - "0.", - "10.0", - "10.0", - "0.06" - ], - [ - "0.4", - "0.030", - "0.8", - "0.7", - "0.7", - "0.1", - "0.1", - "-12.e-5", - "0.85", - "0.", - "0.", - "10.0", - "10.0", - "0.06" - ], - [ - "0.4", - "0.030", - "0.8", - "0.7", - "0.7", - "0.1", - "0.1", - "-8.e-5", - "0.85", - "0.", - "0.", - "10.0", - "10.0", - "0.06" - ], - [ - "0.4", - "0.030", - "0.8", - "0.7", - "0.7", - "0.1", - "0.1", - "-8.e-5", - "0.85", - "0.", - "0.", - "10.0", - "10.0", - "0.06" - ], - [ - "0.4", - "0.030", - "0.8", - "0.7", - "0.7", - "0.1", - "0.1", - "-8.e-5", - "0.85", - "0.", - "0.", - "10.0", - "10.0", - "0.06" - ], - [ - "0.4", - "0.030", - "0.8", - "0.7", - "0.7", - "0.1", - "0.1", - "-8.e-5", - "0.85", - "0.", - "0.", - "10.0", - "10.0", - "0.06" - ], - [ - "0.4", - "0.030", - "0.8", - "0.7", - "0.7", - "0.1", - "0.1", - "-8.e-5", - "0.85", - "0.", - "0.", - "10.0", - "10.0", - "0.06" - ], - [ - "0.4", - "0.030", - "0.8", - "0.7", - "0.7", - "0.1", - "0.1", - "-8.e-5", - "0.85", - "0.", - "0.", - "10.0", - "10.0", - "0.06" - ], - [ - "0.4", - "0.030", - "0.8", - "0.7", - "0.7", - "0.1", - "0.1", - "-7.e-5", - "0.85", - "0.", - "0.", - "10.0", - "10.0", - "0.06" - ], - [ - "0.4", - "0.030", - "0.8", - "0.7", - "0.7", - "0.1", - "0.1", - "-7.e-5", - "0.85", - "0.", - "0.", - "10.0", - "10.0", - "0.06" - ], - [ - "0.4", - "0.030", - "0.9", - "0.7", - "0.7", - "0.1", - "0.1", - "-5.e-5", - "0.85", - "0.", - "0.", - "10.0", - "10.0", - "0.06" - ], - [ - "0.4", - "0.030", - "0.9", - "0.7", - "0.7", - "0.1", - "0.1", - "-5.e-5", - "0.85", - "0.", - "0.", - "10.0", - "10.0", - "0.06" - ], - [ - "0.4", - "0.030", - "0.9", - "0.7", - "0.7", - "0.1", - "0.1", - "-5.e-5", - "0.85", - "0.", - "0.", - "10.0", - "10.0", - "0.06" - ], - [ - "0.4", - "0.030", - "1.0", - "0.7", - "0.7", - "0.1", - "0.1", - "10.e-5", - "0.85", - "0.", - "0.", - "10.0", - "10.0", - "0.06" - ], - [ - "0.4", - "0.030", - "1.0", - "0.7", - "0.7", - "0.1", - "0.1", - "1000.e-5", - "0.85", - "0.", - "0.", - "10.0", - "10.0", - "0.06" - ], - [ - "0.4", - "0.030", - "1.0", - "0.7", - "0.7", - "0.1", - "0.1", - "1000.e-5", - "0.85", - "0.", - "0.", - "10.0", - "10.0", - "0.06" - ], - [ - "0.4", - "0.030", - "1.0", - "0.7", - "0.7", - "0.1", - "0.1", - "1000.e-5", - "0.85", - "0.", - "0.", - "10.0", - "10.0", - "0.06" - ], - [ - "0.4", - "0.030", - "1.0", - "0.7", - "0.7", - "0.1", - "0.1", - "1000.e-5", - "0.85", - "0.", - "0.", - "10.0", - "10.0", - "0.06" - ], - [ - "0.4", - "0.030", - "1.0", - "0.6", - "0.6", - "0.1", - "0.1", - "1000.e-5", - "0.80", - "0.", - "0.", - "10.0", - "10.0", - "0.06" - ] - ] - }, - "d_TPCNClsFindablePIDCut": "50.", - "d_nSigmaTOF": "3.", - "d_nSigmaTOFCombined": "5.", - "d_nSigmaTPC": "3.", - "d_nSigmaTPCCombined": "5.", - "d_pTCandMax": "50.", - "d_pTCandMin": "0.", - "d_pidTOFMaxpT": "10.", - "d_pidTOFMinpT": "0.15", - "d_pidTPCMaxpT": "10.", - "d_pidTPCMinpT": "0.15", - "pTBins": { - "values": [ - "0", - "0.5", - "1", - "1.5", - "2", - "2.5", - "3", - "3.5", - "4", - "4.5", - "5", - "5.5", - "6", - "6.5", - "7", - "7.5", - "8", - "9", - "10", - "12", - "16", - "20", - "24", - "36", - "50", - "100" - ] - } - }, - "hf-task-d0": { - "cutYCandMax": "0.8", - "d_selectionFlagD0": "0", - "d_selectionFlagD0bar": "0", - "pTBins": { - "values": [ - "0", - "0.5", - "1", - "1.5", - "2", - "2.5", - "3", - "3.5", - "4", - "4.5", - "5", - "5.5", - "6", - "6.5", - "7", - "7.5", - "8", - "9", - "10", - "12", - "16", - "20", - "24", - "36", - "50", - "100" - ] - }, - "processMC": "true" - }, - "hf-track-index-skims-creator": { - "bz": "5.", - "cutsD0ToPiK": { - "values": [ - [ - "1.61", - "2.12", - "0.5", - "0.00" - ], - [ - "1.61", - "2.12", - "0.5", - "0.00" - ] - ] - }, - "cutsDPlusToPiKPi": { - "values": [ - [ - "1.67", - "2.07", - "0.7", - "0.03" - ], - [ - "1.67", - "2.07", - "0.7", - "0.03" - ] - ] - }, - "cutsDsToPiKK": { - "values": [ - [ - "1.74", - "2.18", - "0.94", - "0.02" - ], - [ - "1.74", - "2.18", - "0.94", - "0.02" - ] - ] - }, - "cutsJpsiToEE": { - "values": [ - [ - "2.5", - "4.1", - "-2.", - "1000.00" - ], - [ - "2.5", - "4.1", - "-2.", - "1000.00" - ] - ] - }, - "cutsJpsiToMuMu": { - "values": [ - [ - "2.5", - "4.1", - "-2.", - "1000.00" - ], - [ - "2.5", - "4.1", - "-2.", - "1000.00" - ] - ] - }, - "cutsLcToPKPi": { - "values": [ - [ - "1.98", - "2.58", - "-2.", - "0.0" - ], - [ - "1.98", - "2.58", - "-2.", - "0.0" - ] - ] - }, - "cutsXicToPKPi": { - "values": [ - [ - "2.10", - "2.80", - "-2.", - "0.0" - ], - [ - "2.10", - "2.80", - "-2.", - "0.0" - ] - ] - }, - "do3prong": "1", - "fillHistograms": "true", - "maxDZIni": "999.", - "maxRad": "5.", - "minParamChange": "0.001", - "minRelChi2Change": "0.9", - "pTBinsD0ToPiK": { - "values": [ - "0.", - "5.", - "1000." - ] - }, - "pTBinsDPlusToPiKPi": { - "values": [ - "1.", - "5.", - "1000." - ] - }, - "pTBinsDsToPiKK": { - "values": [ - "1.5", - "5.", - "1000." - ] - }, - "pTBinsJpsiToEE": { - "values": [ - "0.", - "5.", - "1000." - ] - }, - "pTBinsJpsiToMuMu": { - "values": [ - "0.", - "5.", - "1000." - ] - }, - "pTBinsLcToPKPi": { - "values": [ - "4.", - "5.", - "1000." - ] - }, - "pTBinsXicToPKPi": { - "values": [ - "4.", - "5.", - "1000." - ] - }, - "propToDCA": "true" - }, - "lambdakzero-builder": { - "ccdb-url": "http://alice-ccdb.cern.ch", - "createV0CovMats": "-1", - "dQAK0ShortMassWindow": "0.005", - "dQALambdaMassWindow": "0.005", - "dQAMaxPt": "5", - "dQANBinsMass": "400", - "dQANBinsPtCoarse": "10", - "dQANBinsRadius": "500", - "d_UseAbsDCA": "1", - "d_UseAutodetectMode": "0", - "d_UseWeightedPCA": "0", - "d_bz": "-999", - "d_doQA": "0", - "d_doTrackQA": "0", - "dcanegtopv": "0.1", - "dcapostopv": "0.1", - "dcav0dau": "1", - "geoPath": "GLO/Config/GeometryAligned", - "grpPath": "GLO/GRP/GRP", - "grpmagPath": "GLO/Config/GRPMagField", - "lutPath": "GLO/Param/MatLUT", - "processRun2": "0", - "processRun3": "1", - "rejDiffCollTracks": "0", - "tpcrefit": "0", - "useMatCorrType": "2", - "v0cospa": "0.995", - "v0radius": "1" - }, - "lambdakzero-preselector": { - "dIfMCgenerateAntiHypertriton": "0", - "dIfMCgenerateAntiLambda": "0", - "dIfMCgenerateGamma": "0", - "dIfMCgenerateHypertriton": "0", - "dIfMCgenerateK0Short": "1", - "dIfMCgenerateLambda": "0", - "dPreselectOnlyBaryons": "0", - "dTPCNCrossedRows": "50", - "ddEdxPreSelectAntiHypertriton": "0", - "ddEdxPreSelectAntiLambda": "0", - "ddEdxPreSelectGamma": "0", - "ddEdxPreSelectHypertriton": "0", - "ddEdxPreSelectK0Short": "1", - "ddEdxPreSelectLambda": "0", - "ddEdxPreSelectionWindow": "7", - "processBuildAll": "1", - "processBuildMCAssociated": "0", - "processBuildValiddEdx": "0", - "processBuildValiddEdxMCAssociated": "0" - }, - "multiplicity-table": { - "processRun2": "false", - "processRun3": "true" - }, - "particles-to-tracks": { - "processIndexing": "true" - }, - "perf-k0s-resolution": { - "etaBins": { - "values": [ - 10, - -1, - 1 - ] - }, - "eventSelection": "1", - "mBins": { - "values": [ - 300, - 0.35, - 0.65 - ] - }, - "nSigTPC": "10", - "pTBins": { - "values": [ - 200, - 0, - 10 - ] - }, - "phiBins": { - "values": [ - 18, - 0, - 6.2831853 - ] - }, - "rapidity": "0.5", - "v0lifetime": "3", - "v0setting_cospa": "0.995", - "v0setting_dcanegtopv": "0.1", - "v0setting_dcapostopv": "0.1", - "v0setting_dcav0dau": "1", - "v0setting_radius": "0.9" - }, - "pseudorapidity-density": { - "estimatorEta": "1", - "exclusionPhi": { - "values": [ - [ - "4", - "5" - ] - ] - }, - "maxDCAXY": "2.4", - "maxDCAZ": "3.2", - "processGen": "true", - "processTagging": "true", - "processTrackEfficiency": "true", - "useDCAXY": "false", - "useDCAZ": "false", - "useEvSel": "false", - "usePhiCut": "false", - "usePtDCAXY": "false" - }, - "qa-efficiency": { - "applyEvSel": "2", - "do-al": "true", - "do-de": "true", - "do-el": "true", - "do-he": "true", - "do-ka": "true", - "do-mu": "true", - "do-pi": "true", - "do-pr": "true", - "do-tr": "true", - "doNegativePDG": "true", - "doPositivePDG": "true", - "doPtEta": "false", - "etaBins": { - "values": [ - "200", - "-0.8", - "0.8" - ] - }, - "globalTrackSelection": "0", - "log-pt": "0", - "make-eff": "true", - "maxProdRadius": "9999", - "nMinNumberOfContributors": "2", - "noFakesHits": "false", - "phiBins": { - "values": [ - "200", - "0", - "6.2839999198913574" - ] - }, - "processData": "true", - "processMC": "true", - "processMCWithoutCollisions": "false", - "ptBins": { - "values": [ - "200", - "0", - "5" - ] - }, - "trackSelection": "true", - "vertex-z-max": "10", - "vertex-z-min": "-10", - "yBins": { - "values": [ - "200", - "-0.5", - "0.5" - ] - } - }, - "qa-event-track": { - "binsPt": { - "values": [ - "0", - "0", - "0.10000000000000001", - "0.20000000000000001", - "0.29999999999999999", - "0.40000000000000002", - "0.5", - "0.59999999999999998", - "0.69999999999999996", - "0.80000000000000004", - "0.90000000000000002", - "1", - "1.1000000000000001", - "1.2", - "1.3", - "1.3999999999999999", - "1.5", - "2", - "5", - "10", - "20", - "50" - ] - }, - "binsTrackMultiplcity": { - "values": [ - "200", - "0", - "200" - ] - }, - "binsVertexPosXY": { - "values": [ - "500", - "-1", - "1" - ] - }, - "binsVertexPosZ": { - "values": [ - "100", - "-20", - "20" - ] - }, - "fractionOfSampledEvents": "1", - "isRun3": "true", - "maxEta": "2", - "maxPhi": "10", - "maxPt": "1e+10", - "minEta": "-2", - "minPhi": "-1", - "minPt": "-10", - "processData": "true", - "processDataIU": "false", - "processDataIUFiltered": "false", - "processMC": "true", - "processTableData": "false", - "processTableMC": "false", - "selectCharge": "0", - "selectGoodEvents": "true", - "selectMaxVtxZ": "100", - "selectPID": "0", - "selectPrim": "false", - "selectSec": "false", - "targetNumberOfEvents": "10000000", - "trackSelection": "1" - }, - "table-maker-m-c": { - "cfgBarrelLowPt": "1", - "cfgBarrelTrackCuts": "jpsiO2MCdebugCuts,kaonPID", - "cfgDetailedQA": "true", - "cfgEventCuts": "eventStandardNoINT7", - "cfgIsRun2": "false", - "cfgMCsignals": "anyBeautyHadron,everythingFromBeauty,everythingFromEverythingFromBeauty", - "cfgMuonCuts": "muonQualityCuts,muonTightQualityCutsForTests", - "cfgMuonLowPt": "1", - "cfgNoQA": "false", - "processBarrelOnly": "false", - "processBarrelOnlyWithCent": "false", - "processBarrelOnlyWithCov": "true", - "processFull": "false", - "processMuonOnlyWithCent": "false", - "processMuonOnlyWithCov": "false", - "processOnlyBCs": "true" - }, - "tof-signal": "", - "tpc-pid-qa": { - "etaBins": { - "values": [ - 50, - -1, - 1 - ] - }, - "dEdxBins": { - "values": [ - 3000, - 0, - 1000 - ] - }, - "applyRapidityCut": "0", - "processFullWithTOFDeuteron": "0", - "phiBins": { - "values": [ - 50, - 0, - 6.283185307179586 - ] - }, - "processProton": "0", - "processHelium3": "0", - "processFullElectron": "1", - "processKaon": "0", - "processAlpha": "0", - "processFullWithTOFPion": "0", - "processFullHelium3": "1", - "minP": "0.01", - "enableDeDxPlot": "1", - "processFullWithTOFElectron": "0", - "logAxis": "1", - "processFullWithTOFAlpha": "0", - "processFullKaon": "1", - "processMuon": "0", - "processFullDeuteron": "1", - "processFullWithTOFMuon": "0", - "processFullWithTOFProton": "0", - "maxP": "5", - "processFullAlpha": "0", - "processElectron": "0", - "processDeuteron": "0", - "trackLengthBins": { - "values": [ - 50, - 0, - 1000 - ] - }, - "processFullProton": "1", - "processFullWithTOFKaon": "0", - "minTPCNcls": "0", - "processFullMuon": "1", - "processFullWithTOFTriton": "0", - "nSigmaBins": { - "values": [ - 400, - -10, - 10 - ] - }, - "trackSelection": "1", - "processFullWithTOFHelium3": "0", - "processFullTriton": "1", - "processTriton": "0", - "processPion": "0", - "splitSignalPerCharge": "1", - "expSigmaBins": { - "values": [ - 100, - 0, - 200 - ] - }, - "applyEvSel": "2", - "nBinsP": "400", - "deltaBins": { - "values": [ - 100, - -1000, - 1000 - ] - }, - "processFullPion": "1" - }, - "tof-pid-qa": { - "etaBins": { - "values": [ - 50, - -1, - 1 - ] - }, - "applyRapidityCut": "1", - "phiBins": { - "values": [ - 25, - 0, - 6.283185307179586 - ] - }, - "processProton": "0", - "processHelium3": "0", - "processFullElectron": "0", - "processKaon": "0", - "processAlpha": "0", - "processFullHelium3": "1", - "minP": "0.1", - "logAxis": "0", - "processFullKaon": "1", - "produceDeltaTEtaPhiMap": "0", - "processMuon": "0", - "processFullDeuteron": "1", - "maxP": "5", - "processFullAlpha": "0", - "ptDeltaTEtaPhiMap": "3", - "processElectron": "0", - "processDeuteron": "0", - "trackLengthBins": { - "values": [ - 50, - 0, - 1000 - ] - }, - "processFullProton": "1", - "enableEvTimeSplitting": "0", - "enableVsMomentumHistograms": "0", - "processFullMuon": "0", - "nSigmaBins": { - "values": [ - 400, - -10, - 10 - ] - }, - "trackSelection": "1", - "processFullTriton": "1", - "processTriton": "0", - "processPion": "0", - "splitSignalPerCharge": "1", - "expSigmaBins": { - "values": [ - 200, - 0, - 200 - ] - }, - "applyEvSel": "2", - "nBinsP": "400", - "deltaBins": { - "values": [ - 100, - -1000, - 1000 - ] - }, - "processFullPion": "1" - }, - "tpc-pid-full": { - "autofetchNetworks": "1", - "ccdb-timestamp": "0", - "ccdb-url": "http://alice-ccdb.cern.ch", - "ccdbPath": "Analysis/PID/TPC/Response", - "enableNetworkOptimizations": "1", - "networkPathCCDB": "Analysis/PID/TPC/ML", - "networkPathLocally": "network.onnx", - "networkSetNumThreads": "0", - "param-file": "", - "pid-al": "-1", - "pid-de": "-1", - "pid-el": "-1", - "pid-he": "-1", - "pid-ka": "-1", - "pid-mu": "-1", - "pid-pi": "-1", - "pid-pr": "-1", - "pid-tr": "-1", - "useNetworkCorrection": "0" - }, - "pid-multiplicity": { - "processIU": "0", - "processStandard": "1" - }, - "track-extension": { - "processRun2": "false", - "processRun3": "true" - }, - "track-propagation": { - "ccdb-url": "http://alice-ccdb.cern.ch", - "geoPath": "GLO/Config/GeometryAligned", - "grpmagPath": "GLO/Config/GRPMagField", - "lutPath": "GLO/Param/MatLUT", - "mVtxPath": "GLO/Calib/MeanVertex", - "processCovariance": "true", - "processStandard": "false" - }, - "track-selection": { - "compatibilityIU": "false", - "etaMax": "0.8", - "etaMin": "-0.8", - "isRun3": "true", - "itsMatching": "1", - "produceFBextendedTable": "-1", - "ptMax": "1e+10", - "ptMin": "0.1" - }, - "emcal-correction-task": { - "nonlinearityFunction": "MC_TestbeamFinal", - "logWeight": "4.5", - "exoticCellMinAmplitude": "4", - "clusterDefinition": "kV3Default", - "useWeightExotic": "0", - "disableNonLin": "0", - "exoticCellDiffTime": "1e+06", - "exoticCellInCrossMinAmplitude": "0.1", - "maxMatchingDistance": "0.4", - "exoticCellFraction": "0.97", - "hasShaperCorrection": "1", - "isMC": "1", - "hasPropagatedTracks": "0", - "processFull": "0", - "selectedCellType": "1", - "processStandalone": "0", - "processMCFull": "1" - }, - "cell-monitor": { - "minCellTimeMain": "-50", - "maxCellTimeMain": "100", - "minCellAmplitudeTimeHists": "0.3", - "vetoBCID": "", - "selectBCID": "all", - "minCellAmplitude": "0" - }, - "EMCClusterMonitorTaskAmbiguous": { - "clustertime-binning": { - "values": [ - 1500, - -600, - 900 - ] - }, - "processCollisions": "0", - "doEventSel": "0", - "processAmbiguous": "1", - "clusterDefinition": "10", - "numclusters-binning": { - "values": [ - 201, - -0.5, - 200.5 - ] - }, - "vetoBCID": "", - "selectBCID": "all", - "vertexCut": "-1" - }, - "EMCClusterMonitorTask": { - "clustertime-binning": { - "values": [ - 1500, - -600, - 900 - ] - }, - "processCollisions": "1", - "doEventSel": "0", - "processAmbiguous": "0", - "clusterDefinition": "10", - "numclusters-binning": { - "values": [ - 201, - -0.5, - 200.5 - ] - }, - "vetoBCID": "", - "selectBCID": "all", - "vertexCut": "-1" - }, - "emc-tmmonitor": { - "clustertime-binning": { - "values": [ - 1500, - -600, - 900 - ] - }, - "doEventSel": "0", - "maxTime": "20", - "clusterDefinition": "10", - "minM02": "0.1", - "vetoBCID": "", - "processCollisions": "1", - "hasPropagatedTracks": "1", - "usePionRejection": "0", - "minTime": "-25", - "tpcNsigmaPion": { - "values": [ - -3, - 3 - ] - }, - "selectBCID": "all", - "tpcNsigmaElectron": { - "values": [ - -1, - 3 - ] - }, - "vertexCut": "-1", - "tpcNsigmaBack": { - "values": [ - -10, - -4 - ] - } - } -} diff --git a/MC/config/analysis_testing/json/dpl/o2-analysis-bc-selection.json b/MC/config/analysis_testing/json/dpl/o2-analysis-bc-selection.json new file mode 100644 index 000000000..824be71a9 --- /dev/null +++ b/MC/config/analysis_testing/json/dpl/o2-analysis-bc-selection.json @@ -0,0 +1,6 @@ +{ + "bc-selection-task": { + "processRun2": "false", + "processRun3": "true" + } +} diff --git a/MC/config/analysis_testing/json/dpl/o2-analysis-centrality-table.json b/MC/config/analysis_testing/json/dpl/o2-analysis-centrality-table.json new file mode 100644 index 000000000..dd342777e --- /dev/null +++ b/MC/config/analysis_testing/json/dpl/o2-analysis-centrality-table.json @@ -0,0 +1,460 @@ +{ + "centrality-table": { + "processRun2": "false", + "processRun3": "true", + "processRun3Complete": "false", + "processRun3FT0": "false", + "embedINELgtZEROselection": "false", + "produceHistograms": "0", + "ccdbUrl": "http://alice-ccdb.cern.ch", + "ccdbPath": "Centrality/Estimators", + "genName": "", + "doNotCrashOnNull": "1", + "reconstructionPass": "", + "binsPercentile": { + "values": [ + 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.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, + 1.1, + 1.2, + 1.3, + 1.4, + 1.5, + 1.6, + 1.7, + 1.8, + 1.9, + 2, + 2.1, + 2.2, + 2.3, + 2.4, + 2.5, + 2.6, + 2.7, + 2.8, + 2.9, + 3, + 3.1, + 3.2, + 3.3, + 3.4, + 3.5, + 3.6, + 3.7, + 3.8, + 3.9, + 4, + 4.1, + 4.2, + 4.3, + 4.4, + 4.5, + 4.6, + 4.7, + 4.8, + 4.9, + 5, + 5.1, + 5.2, + 5.3, + 5.4, + 5.5, + 5.6, + 5.7, + 5.8, + 5.9, + 6, + 6.1, + 6.2, + 6.3, + 6.4, + 6.5, + 6.6, + 6.7, + 6.8, + 6.9, + 7, + 7.1, + 7.2, + 7.3, + 7.4, + 7.5, + 7.6, + 7.7, + 7.8, + 7.9, + 8, + 8.1, + 8.2, + 8.3, + 8.4, + 8.5, + 8.6, + 8.7, + 8.8, + 8.9, + 9, + 9.1, + 9.2, + 9.3, + 9.4, + 9.5, + 9.6, + 9.7, + 9.8, + 9.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 + ] + }, + "enabledTables": { + "labels_rows": [ + "CentRun2V0Ms", + "CentRun2V0As", + "CentRun2SPDTrks", + "CentRun2SPDClss", + "CentRun2CL0s", + "CentRun2CL1s", + "CentFV0As", + "CentFT0Ms", + "CentFT0As", + "CentFT0Cs", + "CentFT0CVariant1s", + "CentFDDMs", + "CentNTPVs", + "CentNGlobals", + "CentMFTs" + ], + "labels_cols": [ + "Enable" + ], + "values": [ + [ + -1 + ], + [ + -1 + ], + [ + -1 + ], + [ + -1 + ], + [ + -1 + ], + [ + -1 + ], + [ + -1 + ], + [ + -1 + ], + [ + -1 + ], + [ + -1 + ], + [ + -1 + ], + [ + -1 + ], + [ + -1 + ], + [ + -1 + ], + [ + -1 + ] + ] + } + } +} diff --git a/MC/config/analysis_testing/json/dpl/o2-analysis-event-selection-qa.json b/MC/config/analysis_testing/json/dpl/o2-analysis-event-selection-qa.json new file mode 100644 index 000000000..010a6e301 --- /dev/null +++ b/MC/config/analysis_testing/json/dpl/o2-analysis-event-selection-qa.json @@ -0,0 +1,9 @@ +{ + "event-selection-qa-task": { + "isMC": "!ANALYSIS_QC_is_mc!", + "processMCRun3": "!ANALYSIS_QC_is_mc!", + "isLowFlux": "!ISLOWFLUX!", + "processRun2": "false", + "processRun3": "true" + } +} diff --git a/MC/config/analysis_testing/json/dpl/o2-analysis-event-selection-service.json b/MC/config/analysis_testing/json/dpl/o2-analysis-event-selection-service.json new file mode 100644 index 000000000..c17cdcb3c --- /dev/null +++ b/MC/config/analysis_testing/json/dpl/o2-analysis-event-selection-service.json @@ -0,0 +1,57 @@ +{ + "event-selection-service": { + "timestamp": { + "verbose": "0", + "fatalOnInvalidTimestamp": "0", + "rct-path": "RCT/Info/RunInformation", + "orbit-reset-path": "CTP/Calib/OrbitReset", + "isRun2MC": "-1" + }, + "bcselOpts": { + "amIneeded": "-1", + "triggerBcShift": "0", + "ITSROFrameStartBorderMargin": "-1", + "ITSROFrameEndBorderMargin": "-1", + "TimeFrameStartBorderMargin": "-1", + "TimeFrameEndBorderMargin": "-1", + "checkRunDurationLimits": "0", + "maxInactiveChipsPerLayer": { + "values": [ + "8", + "8", + "8", + "111", + "111", + "195", + "195" + ] + }, + "NumberOfOrbitsPerTF": "-1" + }, + "evselOpts": { + "amIneeded": "-1", + "muonSelection": "0", + "maxDiffZvtxFT0vsPV": "1", + "isMC": "-1", + "confSigmaBCforHighPtTracks": "4", + "TimeIntervalForOccupancyCalculationMin": "-40", + "TimeIntervalForOccupancyCalculationMax": "100", + "TimeRangeVetoOnCollStandard": "10", + "TimeRangeVetoOnCollNarrow": "2", + "FT0CamplPerCollCutVetoOnCollInTimeRange": "8000", + "FT0CamplPerCollCutVetoOnCollInROF": "5000", + "EpsilonVzDiffVetoInROF": "0.300000012", + "UseWeightsForOccupancyEstimator": "1", + "NumberOfOrbitsPerTF": "-1", + "TestAlternativeBcMatching": "0", + "TestModifyTimeVetoOnNearbyColl": "0", + "TestVetoOnTRDinPast": "0", + "TestVzDiffNsigma": "3", + "TestVzDiffMargin": "0.200000003" + }, + "lumiOpts": { + "amIneeded": "-1" + }, + "ccdburl": "http://alice-ccdb.cern.ch" + } +} \ No newline at end of file diff --git a/MC/config/analysis_testing/json/dpl/o2-analysis-event-selection.json b/MC/config/analysis_testing/json/dpl/o2-analysis-event-selection.json new file mode 100644 index 000000000..3fc3aac53 --- /dev/null +++ b/MC/config/analysis_testing/json/dpl/o2-analysis-event-selection.json @@ -0,0 +1,9 @@ +{ + "event-selection-task": { + "isMC": "-1", + "muonSelection": "0", + "processRun2": "false", + "processRun3": "true", + "syst": "pp" + } +} diff --git a/MC/config/analysis_testing/json/dpl/o2-analysis-ft0-corrected-table.json b/MC/config/analysis_testing/json/dpl/o2-analysis-ft0-corrected-table.json new file mode 100644 index 000000000..5fe5e456b --- /dev/null +++ b/MC/config/analysis_testing/json/dpl/o2-analysis-ft0-corrected-table.json @@ -0,0 +1,13 @@ +{ + "ft0-corrected-table": { + "resoFT0A": "20", + "resoFT0C": "20", + "addHistograms": "0", + "collisionSystem": "-2", + "ccdb-url": "http://alice-ccdb.cern.ch", + "ccdb-path-grplhcif": "GLO/Config/GRPLHCIF", + "ccdb-timestamp": "-1", + "processStandard": "1", + "processWithBypassFT0timeInMC": "0" + } +} \ No newline at end of file diff --git a/MC/config/analysis_testing/json/dpl/o2-analysis-ft0-qa.json b/MC/config/analysis_testing/json/dpl/o2-analysis-ft0-qa.json new file mode 100644 index 000000000..db4e27987 --- /dev/null +++ b/MC/config/analysis_testing/json/dpl/o2-analysis-ft0-qa.json @@ -0,0 +1,5 @@ +{ + "ft0-qa": { + "isLowFlux": "false" + } +} diff --git a/MC/config/analysis_testing/json/dpl/o2-analysis-fwdtrack-to-collision-associator.json b/MC/config/analysis_testing/json/dpl/o2-analysis-fwdtrack-to-collision-associator.json new file mode 100644 index 000000000..8273cfdf4 --- /dev/null +++ b/MC/config/analysis_testing/json/dpl/o2-analysis-fwdtrack-to-collision-associator.json @@ -0,0 +1,13 @@ +{ + "fwd-track-to-collision-association": { + "nSigmaForTimeCompat": "4", + "timeMargin": "0", + "includeUnassigned": "0", + "fillTableOfCollIdsPerTrack": "1", + "bcWindowForOneSigma": "115", + "processFwdAssocWithTime": "0", + "processFwdStandardAssoc": "0", + "processMFTAssocWithTime": "1", + "processMFTStandardAssoc": "0" + } +} \ No newline at end of file diff --git a/MC/config/analysis_testing/json/dpl/o2-analysis-hf-candidate-creator-2prong.json b/MC/config/analysis_testing/json/dpl/o2-analysis-hf-candidate-creator-2prong.json new file mode 100644 index 000000000..d45ab8eb0 --- /dev/null +++ b/MC/config/analysis_testing/json/dpl/o2-analysis-hf-candidate-creator-2prong.json @@ -0,0 +1,96 @@ +{ + "hf-candidate-creator-2prong": { + "constrainKfToPv": "false", + "propagateToPCA": "true", + "useAbsDCA": "false", + "useWeightedFinalPCA": "false", + "maxR": "200", + "maxDZIni": "4", + "minParamChange": "0.001", + "minRelChi2Change": "0.90000000000000002", + "fillHistograms": "true", + "isRun2": "false", + "ccdbUrl": "http:\/\/alice-ccdb.cern.ch", + "ccdbPathGrp": "GLO\/GRP\/GRP", + "ccdbPathGrpMag": "GLO\/Config\/GRPMagField", + "hfEvSel": { + "centralityMin": "-10", + "centralityMax": "110", + "useSel8Trigger": "true", + "triggerClass": "-1", + "useTvxTrigger": "false", + "useTimeFrameBorderCut": "false", + "useItsRofBorderCut": "false", + "useIsGoodZvtxFT0vsPV": "false", + "useNoSameBunchPileup": "true", + "useOccupancyCut": "false", + "occEstimator": "1", + "occupancyMin": "0", + "occupancyMax": "1000000", + "nPvContributorsMin": "0", + "chi2PvMax": "-1", + "zPvPosMin": "-10", + "zPvPosMax": "10", + "useNoCollInTimeRangeNarrow": "false", + "useNoCollInTimeRangeStandard": "false", + "useNoCollInRofStandard": "false", + "softwareTrigger": "", + "bcMarginForSoftwareTrigger": "100", + "ccdbPathSoftwareTrigger": "Users\/m\/mpuccio\/EventFiltering\/OTS\/", + "th2ConfigAxisCent": { + "values": [ + "100", + "0", + "100" + ] + }, + "th2ConfigAxisOccupancy": { + "values": [ + "24", + "0", + "24000" + ] + }, + "requireGoodRct": "true", + "rctLabel": "CBT_hadronPID", + "rctCheckZDC": "false", + "rctTreatLimitedAcceptanceAsBad": "false" + }, + "axisMass": { + "values": [ + "500", + "1.6", + "2.1" + ] + }, + "processPvRefitWithDCAFitterN": "false", + "processNoPvRefitWithDCAFitterN": "false", + "processPvRefitWithKFParticle": "false", + "processNoPvRefitWithKFParticle": "false", + "processPvRefitWithDCAFitterNCentFT0C": "false", + "processNoPvRefitWithDCAFitterNCentFT0C": "true", + "processPvRefitWithKFParticleCentFT0C": "false", + "processNoPvRefitWithKFParticleCentFT0C": "false", + "processPvRefitWithDCAFitterNCentFT0M": "false", + "processNoPvRefitWithDCAFitterNCentFT0M": "false", + "processPvRefitWithKFParticleCentFT0M": "false", + "processNoPvRefitWithKFParticleCentFT0M": "false", + "processPvRefitWithDCAFitterNUpc": "false", + "processNoPvRefitWithDCAFitterNUpc": "false", + "processPvRefitWithKFParticleUpc": "false", + "processNoPvRefitWithKFParticleUpc": "false", + "processCollisions": "false", + "processCollisionsCentFT0C": "true", + "processCollisionsCentFT0M": "false", + "processCollisionsUpc": "false" + }, + "hf-candidate-creator-2prong-expressions": { + "rejectBackground": "false", + "matchKinkedDecayTopology": "false", + "matchInteractionsWithMaterial": "false", + "matchCorrelatedBackground": "false", + "processMc": "false", + "processMcCentFT0C": "false", + "processMcCentFT0M": "false" + } +} diff --git a/MC/config/analysis_testing/json/dpl/o2-analysis-hf-candidate-selector-d0.json b/MC/config/analysis_testing/json/dpl/o2-analysis-hf-candidate-selector-d0.json new file mode 100644 index 000000000..008b1676c --- /dev/null +++ b/MC/config/analysis_testing/json/dpl/o2-analysis-hf-candidate-selector-d0.json @@ -0,0 +1,706 @@ +{ + "hf-candidate-selector-d0": { + "ptCandMin": "0", + "ptCandMax": "50", + "usePid": "true", + "ptPidTpcMin": "0.40000000000000002", + "ptPidTpcMax": "50", + "nSigmaTpcMax": "3", + "nSigmaTpcCombinedMax": "3", + "usePidTpcOnly": "false", + "ptPidTofMin": "0.40000000000000002", + "ptPidTofMax": "50", + "nSigmaTofMax": "3", + "nSigmaTofCombinedMax": "3", + "usePidTpcAndTof": "true", + "itsNClustersFoundMin": "0", + "itsChi2PerClusterMax": "1e+10", + "tpcNClustersFoundMin": "70", + "tpcNCrossedRowsMin": "80", + "tpcNCrossedRowsOverFindableClustersMin": "0.800000012", + "tpcChi2PerClusterMax": "1e+10", + "keepOnlySidebandCandidates": "false", + "distanceFromD0MassForSidebands": "0.14999999999999999", + "binsPt": { + "values": [ + "0", + "1", + "2", + "3", + "4", + "6", + "8", + "12", + "24", + "50", + "100" + ] + }, + "cuts": { + "labels_rows": [ + "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", + "pT bin 12", + "pT bin 13", + "pT bin 14", + "pT bin 15", + "pT bin 16", + "pT bin 17", + "pT bin 18", + "pT bin 19", + "pT bin 20", + "pT bin 21", + "pT bin 22", + "pT bin 23", + "pT bin 24" + ], + "labels_cols": [ + "m", + "DCA", + "cos theta*", + "pT K", + "pT Pi", + "d0K", + "d0pi", + "d0d0", + "cos pointing angle", + "cos pointing angle xy", + "min norm decay length XY", + "max decay length", + "max decay length XY", + "min decay length", + "norm dauImpPar XY" + ], + "values": [ + [ + "0.20000000000000001", + "0.040000000000000001", + "0.80000000000000004", + "0.5", + "0.5", + "1", + "1", + "1", + "0.97999999999999998", + "0.98499999999999999", + "0", + "10", + "10", + "0", + "0.10000000000000001" + ], + [ + "0.20000000000000001", + "0.040000000000000001", + "0.80000000000000004", + "0.5", + "0.5", + "1", + "1", + "1", + "0.98999999999999999", + "0.995", + "0", + "10", + "10", + "0", + "0.10000000000000001" + ], + [ + "0.20000000000000001", + "0.029999999999999999", + "0.80000000000000004", + "0.5", + "0.5", + "1", + "1", + "1", + "0.97999999999999998", + "0.98499999999999999", + "0", + "10", + "10", + "0", + "0.10000000000000001" + ], + [ + "0.20000000000000001", + "0.029999999999999999", + "0.80000000000000004", + "0.59999999999999998", + "0.59999999999999998", + "1", + "1", + "1", + "0.96999999999999997", + "0.97499999999999998", + "0", + "10", + "10", + "0", + "0.10000000000000001" + ], + [ + "0.20000000000000001", + "0.029999999999999999", + "0.80000000000000004", + "0.59999999999999998", + "0.59999999999999998", + "1", + "1", + "1", + "0.95999999999999996", + "0.96499999999999997", + "0", + "10", + "10", + "0", + "0.10000000000000001" + ], + [ + "0.20000000000000001", + "0.029999999999999999", + "0.80000000000000004", + "0.59999999999999998", + "0.59999999999999998", + "1", + "1", + "1", + "0.94999999999999996", + "0.95499999999999996", + "0", + "10", + "10", + "0", + "0.10000000000000001" + ], + [ + "0.20000000000000001", + "0.029999999999999999", + "0.80000000000000004", + "0.69999999999999996", + "0.69999999999999996", + "1", + "1", + "1", + "0.93999999999999995", + "0.94499999999999995", + "0", + "10", + "10", + "0", + "0.10000000000000001" + ], + [ + "0.20000000000000001", + "0.029999999999999999", + "0.80000000000000004", + "0.69999999999999996", + "0.69999999999999996", + "1", + "1", + "1", + "0.92000000000000004", + "0.92500000000000004", + "0", + "10", + "10", + "0", + "0.10000000000000001" + ], + [ + "0.20000000000000001", + "0.029999999999999999", + "0.80000000000000004", + "0.69999999999999996", + "0.69999999999999996", + "1", + "1", + "1", + "0.91000000000000003", + "0.91500000000000004", + "0", + "10", + "10", + "0", + "0.10000000000000001" + ], + [ + "0.20000000000000001", + "0.029999999999999999", + "0.80000000000000004", + "0.69999999999999996", + "0.69999999999999996", + "1", + "1", + "1", + "0.90000000000000002", + "0.90500000000000003", + "0", + "10", + "10", + "0", + "0.10000000000000001" + ], + [ + "0.20000000000000001", + "0.029999999999999999", + "1", + "0.40000000000000002", + "0.40000000000000002", + "1", + "1", + "1", + "0.90000000000000002", + "0.90000000000000002", + "0", + "10", + "10", + "0", + "0.10000000000000001" + ], + [ + "0.20000000000000001", + "0.029999999999999999", + "1", + "0.40000000000000002", + "0.40000000000000002", + "1", + "1", + "1", + "0.84999999999999998", + "0.84999999999999998", + "0", + "10", + "10", + "0", + "0.10000000000000001" + ], + [ + "0.20000000000000001", + "0.029999999999999999", + "1", + "0.40000000000000002", + "0.40000000000000002", + "1", + "1", + "1", + "0.84999999999999998", + "0.84999999999999998", + "0", + "10", + "10", + "0", + "0.10000000000000001" + ], + [ + "0.20000000000000001", + "0.035000000000000003", + "1", + "0.40000000000000002", + "0.40000000000000002", + "0.10000000000000001", + "0.10000000000000001", + "0", + "0.84999999999999998", + "0.84999999999999998", + "0", + "10", + "10", + "0", + "0.5" + ], + [ + "0.20000000000000001", + "0.029999999999999999", + "1", + "0.40000000000000002", + "0.40000000000000002", + "0.10000000000000001", + "0.10000000000000001", + "0", + "0.84999999999999998", + "0.84999999999999998", + "0", + "10", + "10", + "0", + "0.5" + ], + [ + "0.20000000000000001", + "0.029999999999999999", + "0.90000000000000002", + "0.40000000000000002", + "0.40000000000000002", + "0.10000000000000001", + "0.10000000000000001", + "0", + "0.84999999999999998", + "0.84999999999999998", + "0", + "10", + "10", + "0", + "0.5" + ], + [ + "0.20000000000000001", + "0.029999999999999999", + "0.90000000000000002", + "0.40000000000000002", + "0.40000000000000002", + "0.10000000000000001", + "0.10000000000000001", + "-5.0000000000000002e-05", + "0.84999999999999998", + "0.84999999999999998", + "0", + "10", + "10", + "0", + "0.5" + ], + [ + "0.20000000000000001", + "0.029999999999999999", + "0.90000000000000002", + "0.69999999999999996", + "0.69999999999999996", + "0.10000000000000001", + "0.10000000000000001", + "-5.0000000000000002e-05", + "0.84999999999999998", + "0.84999999999999998", + "0", + "10", + "10", + "0.059999999999999998", + "0.5" + ], + [ + "0.20000000000000001", + "0.029999999999999999", + "0.90000000000000002", + "0.69999999999999996", + "0.69999999999999996", + "0.10000000000000001", + "0.10000000000000001", + "-5.0000000000000002e-05", + "0.84999999999999998", + "0.84999999999999998", + "0", + "10", + "10", + "0.059999999999999998", + "0.5" + ], + [ + "0.20000000000000001", + "0.029999999999999999", + "1", + "0.69999999999999996", + "0.69999999999999996", + "0.10000000000000001", + "0.10000000000000001", + "0.0001", + "0.84999999999999998", + "0.84999999999999998", + "0", + "10", + "10", + "0.059999999999999998", + "0.5" + ], + [ + "0.40000000000000002", + "0.035000000000000003", + "1", + "0.69999999999999996", + "0.69999999999999996", + "0.10000000000000001", + "0.10000000000000001", + "0.0099999900000000003", + "0.84999999999999998", + "0.84999999999999998", + "0", + "10", + "10", + "0.059999999999999998", + "0.5" + ], + [ + "0.40000000000000002", + "0.035000000000000003", + "1", + "0.69999999999999996", + "0.69999999999999996", + "0.10000000000000001", + "0.10000000000000001", + "0.0099999900000000003", + "0.84999999999999998", + "0.84999999999999998", + "0", + "10", + "10", + "0.059999999999999998", + "0.5" + ], + [ + "0.40000000000000002", + "0.030000000000000002", + "1", + "0.69999999999999996", + "0.69999999999999996", + "0.10000000000000001", + "0.10000000000000001", + "0.0099999900000000003", + "0.84999999999999998", + "0.84999999999999998", + "0", + "10", + "10", + "0.059999999999999998", + "0.5" + ], + [ + "0.40000000000000002", + "0.030000000000000002", + "1", + "0.69999999999999996", + "0.69999999999999996", + "0.10000000000000001", + "0.10000000000000001", + "0.0099999900000000003", + "0.84999999999999998", + "0.84999999999999998", + "0", + "10", + "10", + "0.059999999999999998", + "0.5" + ], + [ + "0.40000000000000002", + "0.030000000000000002", + "1", + "0.59999999999999998", + "0.59999999999999998", + "0.10000000000000001", + "0.10000000000000001", + "0.0099999900000000003", + "0.84999999999999998", + "0.84999999999999998", + "0", + "10", + "10", + "0.059999999999999998", + "0.5" + ] + ] + }, + "applyMl": "true", + "binsPtMl": { + "values": [ + "0", + "1", + "2", + "3", + "4", + "6", + "8", + "12", + "24", + "50", + "100" + ] + }, + "cutDirMl": { + "values": [ + "0", + "1", + "1" + ] + }, + "cutsMl": { + "labels_rows": [ + "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" + ], + "labels_cols": [ + "score class 1", + "score class 2", + "score class 3" + ], + "values": [ + [ + "0.0025", + "0", + "0" + ], + [ + "0.005", + "0", + "0" + ], + [ + "0.006", + "0", + "0" + ], + [ + "0.008", + "0", + "0" + ], + [ + "0.01", + "0", + "0" + ], + [ + "0.1", + "0", + "0" + ], + [ + "0.2", + "0", + "0" + ], + [ + "0.4", + "0", + "0" + ], + [ + "1", + "0", + "0" + ], + [ + "1", + "0", + "0" + ], + [ + "1", + "0", + "0" + ], + [ + "1", + "0", + "0" + ] + ] + }, + "nClassesMl": "3", + "enableDebugMl": "true", + "namesInputFeatures": { + "values": [ + "impactParameterXY0", + "impactParameterXY1", + "chi2PCA", + "cpa", + "cpaXY", + "decayLengthXY", + "decayLength", + "impactParameterProduct", + "nSigTpcTofPiExpPi", + "nSigTpcTofKaExpKa", + "nSigTpcPiExpPi", + "nSigTpcKaExpPi", + "nSigTpcTofKaExpPi", + "nSigTpcPiExpKa", + "nSigTpcKaExpKa", + "nSigTpcTofPiExpKa" + ] + }, + "ccdbUrl": "http:\/\/alice-ccdb.cern.ch", + "modelPathsCCDB": { + "values": [ + "Users\/b\/bizhang\/BDT\/D0\/23_PbPb_020_new\/pt0_1", + "Users\/b\/bizhang\/BDT\/D0\/23_PbPb_020_new\/pt1_2", + "Users\/b\/bizhang\/BDT\/D0\/23_PbPb_020_new\/pt2_3", + "Users\/b\/bizhang\/BDT\/D0\/23_PbPb_020_new\/pt3_4", + "Users\/b\/bizhang\/BDT\/D0\/23_PbPb_020_new\/pt4_6", + "Users\/b\/bizhang\/BDT\/D0\/23_PbPb_020_new\/pt6_8", + "Users\/b\/bizhang\/BDT\/D0\/23_PbPb_020_new\/pt8_12", + "Users\/b\/bizhang\/BDT\/D0\/23_PbPb_020_new\/pt12_24", + "Users\/b\/bizhang\/BDT\/D0\/23_PbPb_020_new\/pt24_50", + "Users\/b\/bizhang\/BDT\/D0\/23_PbPb_020_new\/pt24_50" + ] + }, + "onnxFileNames": { + "values": [ + "ModelHandler_pT_0_1.onnx", + "ModelHandler_pT_1_2.onnx", + "ModelHandler_pT_2_3.onnx", + "ModelHandler_pT_3_4.onnx", + "ModelHandler_pT_4_6.onnx", + "ModelHandler_pT_6_8.onnx", + "ModelHandler_pT_8_12.onnx", + "ModelHandler_pT_12_24.onnx", + "ModelHandler_pT_24_50.onnx", + "ModelHandler_pT_24_50.onnx" + ] + }, + "timestampCCDB": "-1", + "loadModelsFromCCDB": "true", + "useTriggerMassCut": "false", + "hfTrigger2ProngCuts": { + "nSigmaMax": "2", + "ptDeltaMassMax": "10", + "ptMassCutMax": "9999", + "deltaMassPars": { + "labels_rows": "", + "labels_cols": [ + "constant", + "linear" + ], + "values": [ + [ + "-0.00249999994", + "9.99999975e-05" + ] + ] + }, + "sigmaPars": { + "labels_rows": "", + "labels_cols": [ + "constant", + "linear" + ], + "values": [ + [ + "0.0142400004", + "0.00178000005" + ] + ] + } + }, + "processWithDCAFitterN": "true", + "processWithKFParticle": "false" + } +} \ No newline at end of file diff --git a/MC/config/analysis_testing/json/dpl/o2-analysis-hf-pid-creator.json b/MC/config/analysis_testing/json/dpl/o2-analysis-hf-pid-creator.json new file mode 100644 index 000000000..dfb171a57 --- /dev/null +++ b/MC/config/analysis_testing/json/dpl/o2-analysis-hf-pid-creator.json @@ -0,0 +1,15 @@ +{ + "hf-pid-creator": { + "processDummy": "false", + "processFullEl": "false", + "processTinyEl": "false", + "processFullMu": "false", + "processTinyMu": "false", + "processFullPi": "true", + "processTinyPi": "false", + "processFullKa": "true", + "processTinyKa": "false", + "processFullPr": "false", + "processTinyPr": "false" + } +} \ No newline at end of file diff --git a/MC/config/analysis_testing/json/dpl/o2-analysis-hf-task-d0.json b/MC/config/analysis_testing/json/dpl/o2-analysis-hf-task-d0.json new file mode 100644 index 000000000..ae1a0be18 --- /dev/null +++ b/MC/config/analysis_testing/json/dpl/o2-analysis-hf-task-d0.json @@ -0,0 +1,161 @@ +{ + "hf-task-d0": { + "selectionFlagD0": "1", + "selectionFlagD0bar": "1", + "yCandGenMax": "0.5", + "yCandRecoMax": "0.80000000000000004", + "selectionFlagHf": "1", + "selectionTopol": "1", + "selectionCand": "1", + "selectionPid": "1", + "binsPt": { + "values": [ + "0", + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "10", + "12", + "16", + "24", + "36", + "50" + ] + }, + "centEstimator": "2", + "occEstimator": "1", + "storeCentrality": "true", + "storeOccupancy": "true", + "storeTrackQuality": "false", + "applyMl": "true", + "thnConfigAxisBkgScore": { + "values": [ + "5000", + "0", + "0.5" + ] + }, + "thnConfigAxisNonPromptScore": { + "values": [ + "100", + "0", + "1" + ] + }, + "thnConfigAxisPromptScore": { + "values": [ + "100", + "0", + "1" + ] + }, + "thnConfigAxisMass": { + "values": [ + "120", + "1.5848", + "2.1848" + ] + }, + "thnConfigAxisPtB": { + "values": [ + "1000", + "0", + "100" + ] + }, + "thnConfigAxisPt": { + "values": [ + "500", + "0", + "50" + ] + }, + "thnConfigAxisY": { + "values": [ + "20", + "-1", + "1" + ] + }, + "thnConfigAxisOrigin": { + "values": [ + "3", + "-0.5", + "2.5" + ] + }, + "thnConfigAxisCandType": { + "values": [ + "6", + "-0.5", + "5.5" + ] + }, + "thnConfigAxisGenPtD": { + "values": [ + "500", + "0", + "50" + ] + }, + "thnConfigAxisGenPtB": { + "values": [ + "1000", + "0", + "100" + ] + }, + "thnConfigAxisNumPvContr": { + "values": [ + "800", + "-0.5", + "7999.5" + ] + }, + "thnConfigAxisCent": { + "values": [ + "110", + "0", + "110" + ] + }, + "thnConfigAxisOccupancy": { + "values": [ + "24", + "0", + "24000" + ] + }, + "thnConfigAxisMinItsNCls": { + "values": [ + "5", + "3", + "8" + ] + }, + "thnConfigAxisMinTpcNCrossedRows": { + "values": [ + "10", + "70", + "180" + ] + }, + "processDataWithDCAFitterN": "false", + "processDataWithDCAFitterNCent": "false", + "processDataWithKFParticle": "false", + "processDataWithDCAFitterNMl": "false", + "processDataWithDCAFitterNMlCent": "true", + "processDataWithKFParticleMl": "false", + "processMcWithDCAFitterN": "false", + "processMcWithDCAFitterNCent": "false", + "processMcWithKFParticle": "false", + "processMcWithDCAFitterNMl": "false", + "processMcWithDCAFitterNMlCent": "false", + "processMcWithKFParticleMl": "false" + } +} \ No newline at end of file diff --git a/MC/config/analysis_testing/json/dpl/o2-analysis-hf-track-index-skim-creator.json b/MC/config/analysis_testing/json/dpl/o2-analysis-hf-track-index-skim-creator.json new file mode 100644 index 000000000..3d941b71e --- /dev/null +++ b/MC/config/analysis_testing/json/dpl/o2-analysis-hf-track-index-skim-creator.json @@ -0,0 +1,895 @@ +{ + "hf-track-index-skim-creator-tag-sel-collisions": { + "fillHistograms": "true", + "triggerClassName": "", + "hfEvSel": { + "centralityMin": "-10", + "centralityMax": "110", + "useSel8Trigger": "true", + "triggerClass": "-1", + "useTvxTrigger": "false", + "useTimeFrameBorderCut": "false", + "useItsRofBorderCut": "false", + "useIsGoodZvtxFT0vsPV": "false", + "useNoSameBunchPileup": "false", + "useOccupancyCut": "false", + "occEstimator": "1", + "occupancyMin": "0", + "occupancyMax": "1000000", + "nPvContributorsMin": "0", + "chi2PvMax": "-1", + "zPvPosMin": "-10", + "zPvPosMax": "10", + "useNoCollInTimeRangeNarrow": "false", + "useNoCollInTimeRangeStandard": "false", + "useNoCollInRofStandard": "false", + "softwareTrigger": "", + "bcMarginForSoftwareTrigger": "100", + "ccdbPathSoftwareTrigger": "Users\/m\/mpuccio\/EventFiltering\/OTS\/Chunked\/", + "th2ConfigAxisCent": { + "values": [ + "100", + "0", + "100" + ] + }, + "th2ConfigAxisOccupancy": { + "values": [ + "14", + "0", + "14000" + ] + }, + "requireGoodRct": "false", + "rctLabel": "CBT_hadronPID", + "rctCheckZDC": "false", + "rctTreatLimitedAcceptanceAsBad": "false" + }, + "processTrigAndCentFT0ASel": "false", + "processTrigAndCentFT0CSel": "true", + "processTrigAndCentFT0MSel": "false", + "processTrigAndCentFV0ASel": "false", + "processTrigSel": "false", + "processNoTrigSel": "false", + "processUpcSel": "false" + }, + "hf-pid-creator": { + "processDummy": "false", + "processFullEl": "false", + "processTinyEl": "false", + "processFullMu": "false", + "processTinyMu": "false", + "processFullPi": "true", + "processTinyPi": "false", + "processFullKa": "true", + "processTinyKa": "false", + "processFullPr": "false", + "processTinyPr": "false" + }, + "hf-track-index-skim-creator-tag-sel-tracks": { + "isRun2": "false", + "doPvRefit": "false", + "fillHistograms": "true", + "debugPvRefit": "false", + "doCutQuality": "true", + "useIsGlobalTrack": "false", + "useIsGlobalTrackWoDCA": "true", + "tpcNClsFoundMin": "70", + "binsPtTrack": { + "values": [ + "0", + "0.5", + "1", + "1.5", + "2", + "3", + "1000" + ] + }, + "ptMinTrack2Prong": "0.40000000000000002", + "cutsTrack2Prong": { + "labels_rows": "", + "labels_cols": [ + "min_dcaxytoprimary", + "max_dcaxytoprimary", + "min_dcaztoprimary", + "max_dcaztoprimary" + ], + "values": [ + [ + "0.002", + "10", + "0", + "100" + ], + [ + "0.002", + "10", + "0", + "100" + ], + [ + "0.002", + "10", + "0", + "100" + ], + [ + "0.002", + "10", + "0", + "100" + ], + [ + "0", + "10", + "0", + "100" + ], + [ + "0", + "10", + "0", + "100" + ] + ] + }, + "etaMinTrack2Prong": "-0.80000000000000004", + "etaMaxTrack2Prong": "0.80000000000000004", + "ptMinTrack3Prong": "0.59999999999999998", + "cutsTrack3Prong": { + "labels_rows": "", + "labels_cols": [ + "min_dcaxytoprimary", + "max_dcaxytoprimary", + "min_dcaztoprimary", + "max_dcaztoprimary" + ], + "values": [ + [ + "0.0040000000000000001", + "10", + "0", + "100" + ], + [ + "0.0040000000000000001", + "10", + "0", + "100" + ], + [ + "0.0040000000000000001", + "10", + "0", + "100" + ], + [ + "0.0040000000000000001", + "10", + "0", + "100" + ], + [ + "0", + "10", + "0", + "100" + ], + [ + "0", + "10", + "0", + "100" + ] + ] + }, + "etaMinTrack3Prong": "-0.80000000000000004", + "etaMaxTrack3Prong": "0.80000000000000004", + "ptMinTrackBach": "0", + "cutsTrackBach": { + "labels_rows": "", + "labels_cols": [ + "min_dcaxytoprimary", + "max_dcaxytoprimary", + "min_dcaztoprimary", + "max_dcaztoprimary" + ], + "values": [ + [ + "0", + "10", + "0", + "100" + ], + [ + "0", + "10", + "0", + "100" + ], + [ + "0", + "10", + "0", + "100" + ], + [ + "0", + "10", + "0", + "100" + ], + [ + "0", + "10", + "0", + "100" + ], + [ + "0", + "10", + "0", + "100" + ] + ] + }, + "etaMinTrackBach": "-0.80000000000000004", + "etaMaxTrackBach": "0.80000000000000004", + "ptMinTrackBachLfCasc": "0.10000000000000001", + "cutsTrackBachLfCasc": { + "labels_rows": "", + "labels_cols": [ + "min_dcaxytoprimary", + "max_dcaxytoprimary", + "min_dcaztoprimary", + "max_dcaztoprimary" + ], + "values": [ + [ + "0", + "10", + "0", + "100" + ], + [ + "0", + "10", + "0", + "100" + ], + [ + "0", + "10", + "0", + "100" + ], + [ + "0", + "10", + "0", + "100" + ], + [ + "0", + "10", + "0", + "100" + ], + [ + "0", + "10", + "0", + "100" + ] + ] + }, + "etaMinTrackBachLfCasc": "-99999", + "etaMaxTrackBachLfCasc": "1.1000000000000001", + "useIsGlobalTrackForBachLfCasc": "false", + "useIsGlobalTrackWoDCAForBachLfCasc": "false", + "useIsQualityTrackITSForBachLfCasc": "true", + "ptMinSoftPionForDstar": "0.10000000000000001", + "etaMinSoftPionForDstar": "-0.80000000000000004", + "etaMaxSoftPionForDstar": "0.80000000000000004", + "cutsTrackDstar": { + "labels_rows": "", + "labels_cols": [ + "min_dcaxytoprimary", + "max_dcaxytoprimary", + "min_dcaztoprimary", + "max_dcaztoprimary" + ], + "values": [ + [ + "0", + "2", + "0", + "100" + ], + [ + "0", + "2", + "0", + "100" + ], + [ + "0", + "2", + "0", + "100" + ], + [ + "0", + "2", + "0", + "100" + ], + [ + "0", + "2", + "0", + "100" + ], + [ + "0", + "2", + "0", + "100" + ] + ] + }, + "useIsGlobalTrackForSoftPion": "false", + "useIsGlobalTrackWoDCAForSoftPion": "true", + "useIsQualityTrackITSForSoftPion": "false", + "selectionsPid": { + "labels_rows": [ + "ProtonInLcToPKPi", + "ProtonInXicToPKPi", + "ProtonInLcToPK0S", + "KaonIn3Prongs" + ], + "labels_cols": [ + "minPtTpc", + "maxPtTpc", + "nSigmaMaxTpc", + "minPtTof", + "maxPtTof", + "nSigmaMaxTof" + ], + "values": [ + [ + "0", + "1000", + "4", + "0", + "1000", + "5" + ], + [ + "0", + "1000", + "4", + "0", + "1000", + "5" + ], + [ + "0", + "1000", + "4", + "0", + "1000", + "5" + ], + [ + "0", + "1000", + "4", + "0", + "1000", + "5" + ] + ] + }, + "ccdbUrl": "http:\/\/alice-ccdb.cern.ch", + "ccdbPathLut": "GLO\/Param\/MatLUT", + "ccdbPathGrp": "GLO\/GRP\/GRP", + "ccdbPathGrpMag": "GLO\/Config\/GRPMagField", + "axisPvRefitDeltaX": { + "values": [ + "1000", + "-0.5", + "0.5" + ] + }, + "axisPvRefitDeltaY": { + "values": [ + "1000", + "-0.5", + "0.5" + ] + }, + "axisPvRefitDeltaZ": { + "values": [ + "1000", + "-0.5", + "0.5" + ] + }, + "processNoPid": "false", + "processProtonPidTpc": "true", + "processProtonPidTof": "false", + "processProtonPidTpcOrTof": "false", + "processProtonPidTpcAndTof": "false" + }, + "hf-track-index-skim-creator": { + "isRun2": "false", + "do3Prong": "true", + "doDstar": "true", + "debug": "false", + "debugPvRefit": "false", + "fillHistograms": "true", + "ptTolerance": "0.10000000000000001", + "minTwoTrackDecayLengthFor3Prongs": "0.01", + "maxTwoTrackChi2PcaFor3Prongs": "10000000000", + "propagateToPCA": "true", + "useAbsDCA": "false", + "useWeightedFinalPCA": "false", + "maxR": "200", + "maxDZIni": "4", + "minParamChange": "0.001", + "minRelChi2Change": "0.90000000000000002", + "ccdbUrl": "http:\/\/alice-ccdb.cern.ch", + "ccdbPathLut": "GLO\/Param\/MatLUT", + "ccdbPathGrp": "GLO\/GRP\/GRP", + "ccdbPathGrpMag": "GLO\/Config\/GRPMagField", + "binsPtD0ToPiK": { + "values": [ + "0", + "5", + "1000" + ] + }, + "cutsD0ToPiK": { + "labels_rows": "", + "labels_cols": [ + "massMin", + "massMax", + "cosp", + "d0d0" + ], + "values": [ + [ + "1.6499999999999999", + "2.1000000000000001", + "0.90000000000000002", + "0" + ], + [ + "1.6499999999999999", + "2.25", + "0.90000000000000002", + "0" + ] + ] + }, + "binsPtJpsiToEE": { + "values": [ + "0", + "5", + "1000" + ] + }, + "cutsJpsiToEE": { + "labels_rows": "", + "labels_cols": [ + "massMin", + "massMax", + "cosp", + "d0d0" + ], + "values": [ + [ + "0", + "0", + "2", + "0" + ], + [ + "0", + "0", + "2", + "0" + ] + ] + }, + "binsPtJpsiToMuMu": { + "values": [ + "0", + "5", + "1000" + ] + }, + "cutsJpsiToMuMu": { + "labels_rows": "", + "labels_cols": [ + "massMin", + "massMax", + "cosp", + "d0d0" + ], + "values": [ + [ + "0", + "0", + "2", + "0" + ], + [ + "0", + "0", + "2", + "0" + ] + ] + }, + "binsPtDplusToPiKPi": { + "values": [ + "1", + "5", + "1000" + ] + }, + "cutsDplusToPiKPi": { + "labels_rows": "", + "labels_cols": [ + "massMin", + "massMax", + "cosp", + "decL" + ], + "values": [ + [ + "1.7", + "2.0499999999999998", + "0.95999999999999996", + "0.050000000000000003" + ], + [ + "1.6499999999999999", + "2.2000000000000002", + "0.94999999999999996", + "0.050000000000000003" + ] + ] + }, + "binsPtDsToKKPi": { + "values": [ + "1", + "5", + "1000" + ] + }, + "cutsDsToKKPi": { + "labels_rows": "", + "labels_cols": [ + "massMin", + "massMax", + "cosp", + "decL", + "deltaMassKK" + ], + "values": [ + [ + "1.7", + "2.1499999999999999", + "0.93999999999999995", + "0.02", + "0.02" + ], + [ + "1.6499999999999999", + "2.25", + "0.92000000000000004", + "0.02", + "0.02" + ] + ] + }, + "binsPtLcToPKPi": { + "values": [ + "2", + "8", + "1000" + ] + }, + "cutsLcToPKPi": { + "labels_rows": "", + "labels_cols": [ + "massMin", + "massMax", + "cosp", + "decL" + ], + "values": [ + [ + "2.1000000000000001", + "2.5", + "0.92000000000000004", + "0.01" + ], + [ + "2", + "2.6000000000000001", + "0.90000000000000002", + "0.02" + ] + ] + }, + "binsPtXicToPKPi": { + "values": [ + "2", + "8", + "1000" + ] + }, + "cutsXicToPKPi": { + "labels_rows": "", + "labels_cols": [ + "massMin", + "massMax", + "cosp", + "decL" + ], + "values": [ + [ + "2.1000000000000001", + "2.6499999999999999", + "0.93999999999999995", + "0.02" + ], + [ + "2", + "2.7000000000000002", + "0.92000000000000004", + "0.02" + ] + ] + }, + "binsPtDstarToD0Pi": { + "values": [ + "2", + "5", + "1000" + ] + }, + "cutsDstarToD0Pi": { + "labels_rows": "", + "labels_cols": [ + "deltaMassMax", + "deltaMassD0" + ], + "values": [ + [ + "0.17000000000000001", + "0.070000000000000007" + ], + [ + "0.17000000000000001", + "0.10000000000000001" + ] + ] + }, + "applyProtonPidForLcToPKPi": "true", + "applyProtonPidForXicToPKPi": "true", + "applyKaonPidIn3Prongs": "true", + "applyMlForHfFilters": "false", + "mlModelPathCCDB": "EventFiltering\/PWGHF\/BDTSmeared", + "timestampCcdbForHfFilters": "1657032422771", + "loadMlModelsFromCCDB": "true", + "onnxFileNames": { + "labels_rows": [ + "D0", + "Dplus", + "Lc", + "Ds", + "Xic" + ], + "labels_cols": [ + "Model path" + ], + "values": [ + [ + "ModelHandler_onnx_D0ToKPi.onnx" + ], + [ + "ModelHandler_onnx_DplusToPiKPi.onnx" + ], + [ + "ModelHandler_onnx_LcToPKPi.onnx" + ], + [ + "ModelHandler_onnx_DsToKKPi.onnx" + ], + [ + "" + ] + ] + }, + "thresholdMlScoreD0ToKPi": { + "labels_rows": "", + "labels_cols": [ + "BDTbkg", + "BDTprompt", + "BDTnonprompt" + ], + "values": [ + [ + "0.10000000000000001", + "0.5", + "0.5" + ] + ] + }, + "thresholdMlScoreDplusToPiKPi": { + "labels_rows": "", + "labels_cols": [ + "BDTbkg", + "BDTprompt", + "BDTnonprompt" + ], + "values": [ + [ + "0.10000000000000001", + "0.5", + "0.5" + ] + ] + }, + "thresholdMlScoreDsToPiKK": { + "labels_rows": "", + "labels_cols": [ + "BDTbkg", + "BDTprompt", + "BDTnonprompt" + ], + "values": [ + [ + "0.10000000000000001", + "0.5", + "0.5" + ] + ] + }, + "thresholdMlScoreLcToPiKP": { + "labels_rows": "", + "labels_cols": [ + "BDTbkg", + "BDTprompt", + "BDTnonprompt" + ], + "values": [ + [ + "0.10000000000000001", + "0.5", + "0.5" + ] + ] + }, + "thresholdMlScoreXicToPiKP": { + "labels_rows": "", + "labels_cols": [ + "BDTbkg", + "BDTprompt", + "BDTnonprompt" + ], + "values": [ + [ + "0.10000000000000001", + "0.5", + "0.5" + ] + ] + }, + "axisPvRefitDeltaX": { + "values": [ + "1000", + "-0.5", + "0.5" + ] + }, + "axisPvRefitDeltaY": { + "values": [ + "1000", + "-0.5", + "0.5" + ] + }, + "axisPvRefitDeltaZ": { + "values": [ + "1000", + "-0.5", + "0.5" + ] + }, + "processNo2And3Prongs": "false", + "process2And3ProngsWithPvRefit": "false", + "process2And3ProngsNoPvRefit": "true", + "process2And3ProngsWithPvRefitWithPidForHfFiltersBdt": "false", + "process2And3ProngsNoPvRefitWithPidForHfFiltersBdt": "false" + }, + "hf-track-index-skim-creator-lf-cascades": { + "fillHistograms": "true", + "do3Prong": "true", + "rejDiffCollTrack": "true", + "ptTolerance": "0.10000000000000001", + "massXiPiMin": "2.1000000000000001", + "massXiPiMax": "3", + "massOmegaCharmBachelorMin": "2.3999999999999999", + "massOmegaCharmBachelorMax": "3", + "massXiPiPiMin": "2.1000000000000001", + "massXiPiPiMax": "2.7999999999999998", + "propagateToPCA": "true", + "useAbsDCA": "true", + "useWeightedFinalPCA": "true", + "maxR": "200", + "maxDZIni": "4", + "maxDXYIni": "4", + "minParamChange": "0.001", + "minRelChi2Change": "0.90000000000000002", + "maxChi2": "100", + "refitWithMatCorr": "true", + "ptMinOmegacZeroToOmegaPiLfCasc": "0", + "ptMinOmegaczeroToOmegaKaLfCasc": "0", + "ptMinXicZeroOmegacZeroToXiPiLfCasc": "0", + "ptMinXicplusLfCasc": "0", + "v0TransvRadius": "1", + "cascTransvRadius": "0.400000006", + "decayLengthXicMin": "-1", + "dcaBachToPv": "0.0500000007", + "dcaV0ToPv": "0.0500000007", + "v0CosPA": "0.94999999999999996", + "cascCosPA": "0.94999999999999996", + "xicCosPA": "0.94999999999999996", + "dcaV0Dau": "2", + "dcaCascDau": "1", + "dcaNegToPv": "0.0500000007", + "dcaPosToPv": "0.0500000007", + "v0MassWindow": "0.00800000038", + "cascadeMassWindow": "0.00999999978", + "isRun2": "false", + "ccdbUrl": "http:\/\/alice-ccdb.cern.ch", + "ccdbPathLut": "GLO\/Param\/MatLUT", + "ccdbPathGrp": "GLO\/GRP\/GRP", + "ccdbPathGrpMag": "GLO\/Config\/GRPMagField", + "processNoLfCascades": "true", + "processLfCascades": "false" + }, + "hf-track-index-skim-creator-cascades": { + "isRun2": "false", + "fillHistograms": "true", + "useDCAFitter": "true", + "propagateToPCA": "true", + "maxR": "200", + "maxDZIni": "4", + "minParamChange": "0.001", + "minRelChi2Change": "0.90000000000000002", + "useAbsDCA": "true", + "useWeightedFinalPCA": "true", + "etaMinV0Daugh": "-99999", + "etaMaxV0Daugh": "1.1000000000000001", + "ptMinV0Daugh": "0.050000000000000003", + "cpaV0Min": "0.995", + "cutInvMassV0": "0.050000000000000003", + "ptCascCandMin": "-1", + "cutInvMassCascLc": "1", + "applyProtonPid": "false", + "ccdbUrl": "http:\/\/alice-ccdb.cern.ch", + "ccdbPathLut": "GLO\/Param\/MatLUT", + "ccdbPathGrp": "GLO\/GRP\/GRP", + "ccdbPathGrpMag": "GLO\/Config\/GRPMagField", + "processNoCascades": "true", + "processCascades": "false" + } +} diff --git a/MC/config/analysis_testing/json/dpl/o2-analysis-je-emc-clustermonitor.json b/MC/config/analysis_testing/json/dpl/o2-analysis-je-emc-clustermonitor.json new file mode 100644 index 000000000..c9ea2a730 --- /dev/null +++ b/MC/config/analysis_testing/json/dpl/o2-analysis-je-emc-clustermonitor.json @@ -0,0 +1,48 @@ +{ + "EMCClusterMonitorTask": { + "clusterDefinition": "10", + "clustertime-binning": { + "values": [ + 1500, + -600, + 900 + ] + }, + "doEventSel": "0", + "numclusters-binning": { + "values": [ + 201, + -0.5, + 200.5 + ] + }, + "processAmbiguous": "0", + "processCollisions": "1", + "selectBCID": "all", + "vertexCut": "-1", + "vetoBCID": "" + }, + "EMCClusterMonitorTaskAmbiguous": { + "clusterDefinition": "10", + "clustertime-binning": { + "values": [ + 1500, + -600, + 900 + ] + }, + "doEventSel": "0", + "numclusters-binning": { + "values": [ + 201, + -0.5, + 200.5 + ] + }, + "processAmbiguous": "1", + "processCollisions": "0", + "selectBCID": "all", + "vertexCut": "-1", + "vetoBCID": "" + } +} diff --git a/MC/config/analysis_testing/json/dpl/o2-analysis-je-emc-tmmonitor.json b/MC/config/analysis_testing/json/dpl/o2-analysis-je-emc-tmmonitor.json new file mode 100644 index 000000000..b4b0f9d9a --- /dev/null +++ b/MC/config/analysis_testing/json/dpl/o2-analysis-je-emc-tmmonitor.json @@ -0,0 +1,40 @@ +{ + "emc-tmmonitor": { + "clusterDefinition": "10", + "clustertime-binning": { + "values": [ + 1500, + -600, + 900 + ] + }, + "doEventSel": "0", + "hasPropagatedTracks": "1", + "maxTime": "20", + "minM02": "0.1", + "minTime": "-25", + "processCollisions": "1", + "selectBCID": "all", + "tpcNsigmaBack": { + "values": [ + -10, + -4 + ] + }, + "tpcNsigmaElectron": { + "values": [ + -1, + 3 + ] + }, + "tpcNsigmaPion": { + "values": [ + -3, + 3 + ] + }, + "usePionRejection": "0", + "vertexCut": "-1", + "vetoBCID": "" + } +} diff --git a/MC/config/analysis_testing/json/dpl/o2-analysis-je-emcal-correction-task.json b/MC/config/analysis_testing/json/dpl/o2-analysis-je-emcal-correction-task.json new file mode 100644 index 000000000..2ffbf1070 --- /dev/null +++ b/MC/config/analysis_testing/json/dpl/o2-analysis-je-emcal-correction-task.json @@ -0,0 +1,21 @@ +{ + "emcal-correction-task": { + "clusterDefinition": "kV3Default", + "disableNonLin": "0", + "exoticCellDiffTime": "1e+06", + "exoticCellFraction": "0.97", + "exoticCellInCrossMinAmplitude": "0.1", + "exoticCellMinAmplitude": "4", + "hasPropagatedTracks": "0", + "hasShaperCorrection": "1", + "isMC": "!ANALYSIS_QC_is_mc!", + "logWeight": "4.5", + "maxMatchingDistance": "0.4", + "nonlinearityFunction": "MC_TestbeamFinal", + "processFull": "0", + "processMCFull": "!ANALYSIS_QC_is_mc!", + "processStandalone": "0", + "selectedCellType": "1", + "useWeightExotic": "0" + } +} diff --git a/MC/config/analysis_testing/json/dpl/o2-analysis-lf-lambdakzerobuilder.json b/MC/config/analysis_testing/json/dpl/o2-analysis-lf-lambdakzerobuilder.json new file mode 100644 index 000000000..0ee7eea1f --- /dev/null +++ b/MC/config/analysis_testing/json/dpl/o2-analysis-lf-lambdakzerobuilder.json @@ -0,0 +1,53 @@ +{ + "lambdakzero-builder": { + "ccdb-url": "http://alice-ccdb.cern.ch", + "createV0CovMats": "-1", + "dQAK0ShortMassWindow": "0.005", + "dQALambdaMassWindow": "0.005", + "dQAMaxPt": "5", + "dQANBinsMass": "400", + "dQANBinsPtCoarse": "10", + "dQANBinsRadius": "500", + "d_UseAbsDCA": "1", + "d_UseAutodetectMode": "0", + "d_UseWeightedPCA": "0", + "d_bz": "-999", + "d_doQA": "0", + "d_doTrackQA": "0", + "dcanegtopv": "0.1", + "dcapostopv": "0.1", + "dcav0dau": "1", + "geoPath": "GLO/Config/GeometryAligned", + "grpPath": "GLO/GRP/GRP", + "grpmagPath": "GLO/Config/GRPMagField", + "lutPath": "GLO/Param/MatLUT", + "processRun2": "0", + "processRun3": "1", + "rejDiffCollTracks": "0", + "tpcrefit": "0", + "useMatCorrType": "2", + "v0cospa": "0.995", + "v0radius": "1" + }, + "lambdakzero-preselector": { + "dIfMCgenerateAntiHypertriton": "0", + "dIfMCgenerateAntiLambda": "0", + "dIfMCgenerateGamma": "0", + "dIfMCgenerateHypertriton": "0", + "dIfMCgenerateK0Short": "1", + "dIfMCgenerateLambda": "0", + "dPreselectOnlyBaryons": "0", + "dTPCNCrossedRows": "50", + "ddEdxPreSelectAntiHypertriton": "0", + "ddEdxPreSelectAntiLambda": "0", + "ddEdxPreSelectGamma": "0", + "ddEdxPreSelectHypertriton": "0", + "ddEdxPreSelectK0Short": "1", + "ddEdxPreSelectLambda": "0", + "ddEdxPreSelectionWindow": "7", + "processBuildAll": "1", + "processBuildMCAssociated": "0", + "processBuildValiddEdx": "0", + "processBuildValiddEdxMCAssociated": "0" + } +} diff --git a/MC/config/analysis_testing/json/dpl/o2-analysis-lf-strangenesstofpid.json b/MC/config/analysis_testing/json/dpl/o2-analysis-lf-strangenesstofpid.json new file mode 100644 index 000000000..f168e5634 --- /dev/null +++ b/MC/config/analysis_testing/json/dpl/o2-analysis-lf-strangenesstofpid.json @@ -0,0 +1,166 @@ +{ + "strangenesstofpid": { + "calculationMethod": "0", + "calculateV0s": "-1", + "calculateCascades": "-1", + "calculateV0TOFPIDs": "-1", + "calculateV0TOFBetas": "-1", + "calculateV0TOFDebugs": "-1", + "calculateCascTOFPIDs": "-1", + "d_bz": "-999", + "tofPosition": "377.93399", + "doQA": "0", + "doNSigmas": "1", + "doQANSigma": "0", + "v0Calibration": { + "qaDCADau": "0.5", + "qaCosPA": "0.999000013", + "qaMassWindow": "0.00499999989", + "qaTPCNSigma": "5" + }, + "cascadeCalibration": { + "qaV0DCADau": "0.5", + "qaCascDCADau": "0.5", + "qaV0CosPA": "0.995000005", + "qaCascCosPA": "0.995000005", + "qaMassWindow": "0.00499999989", + "qaTPCNSigma": "5" + }, + "ccdb": { + "ccdb-url": "http://alice-ccdb.cern.ch", + "grpPath": "GLO/GRP/GRP", + "grpmagPath": "GLO/Config/GRPMagField", + "lutPath": "GLO/Param/MatLUT", + "nSigmaPath": "Users/d/ddobrigk/stratof", + "mVtxPath": "GLO/Calib/MeanVertex" + }, + "useCustomRunNumber": "1", + "manualRunNumber": "544122", + "axisPosition": { + "values": [ + "400", + "-400", + "400" + ] + }, + "axisEta": { + "values": [ + "20", + "-1", + "1" + ] + }, + "axisDeltaTime": { + "values": [ + "2000", + "-1000", + "1000" + ] + }, + "axisDeltaTimeVsPrimaryCalculation": { + "values": [ + "500", + "-500", + "500" + ] + }, + "axisTime": { + "values": [ + "400", + "10000", + "50000" + ] + }, + "axisNSigma": { + "values": [ + "200", + "-10", + "10" + ] + }, + "axisRatioMethods": { + "values": [ + "400", + "0.8999999761581421", + "1.899999976158142" + ] + }, + "axisSnp": { + "values": [ + "220", + "-1.100000023841858", + "1.100000023841858" + ] + }, + "axisP": { + "values": [ + "0", + "0", + "0.10000000149011612", + "0.20000000298023224", + "0.30000001192092896", + "0.4000000059604645", + "0.5", + "0.6000000238418579", + "0.699999988079071", + "0.800000011920929", + "0.8999999761581421", + "1", + "1.100000023841858", + "1.2000000476837158", + "1.2999999523162842", + "1.399999976158142", + "1.5", + "1.600000023841858", + "1.7000000476837158", + "1.7999999523162842", + "1.899999976158142", + "2", + "2.200000047683716", + "2.4000000953674316", + "2.5999999046325684", + "2.799999952316284", + "3", + "3.200000047683716", + "3.4000000953674316", + "3.5999999046325684", + "3.799999952316284", + "4", + "4.400000095367432", + "4.800000190734863", + "5.199999809265137", + "5.599999904632568", + "6", + "6.5", + "7", + "7.5", + "8", + "9", + "10", + "11", + "12", + "13", + "14", + "15", + "17", + "19", + "21", + "23", + "25", + "30", + "35", + "40", + "50" + ] + }, + "axisSmallP": { + "values": [ + "250", + "0", + "2.5" + ] + }, + "processStandardData": "1", + "processDerivedData": "0" + } +} \ No newline at end of file diff --git a/MC/config/analysis_testing/json/dpl/o2-analysis-mm-track-propagation.json b/MC/config/analysis_testing/json/dpl/o2-analysis-mm-track-propagation.json new file mode 100644 index 000000000..d8e6cce90 --- /dev/null +++ b/MC/config/analysis_testing/json/dpl/o2-analysis-mm-track-propagation.json @@ -0,0 +1,29 @@ +{ + "ambiguous-track-propagation": { + "ccdburl": "http://alice-ccdb.cern.ch", + "geoPath": "GLO/Config/GeometryAligned", + "grpmagPath": "GLO/Config/GRPMagField", + "mVtxPath": "GLO/Calib/MeanVertex", + "produceExtra": "0", + "produceHistos": "0", + "removeTrivialAssoc": "0", + "binsDCAxy": { + "values": [ + "200", + "-1", + "1" + ] + }, + "binsDCAz": { + "values": [ + "200", + "-1", + "1" + ] + }, + "processCentral": "0", + "processMFT": "0", + "processMFTReassoc": "1", + "processMFTReassoc3D": "0" + } +} \ No newline at end of file diff --git a/MC/config/analysis_testing/json/dpl/o2-analysis-multcenttable.json b/MC/config/analysis_testing/json/dpl/o2-analysis-multcenttable.json new file mode 100644 index 000000000..43a938bf2 --- /dev/null +++ b/MC/config/analysis_testing/json/dpl/o2-analysis-multcenttable.json @@ -0,0 +1,145 @@ +{ + "mult-cent-table": { + "generatorName": "", + "processRun3WithGlobalCounters": "0", + "processMFT": "0", + "processRun3": "1", + "processCentralityRun2": "0", + "processRun2": "0", + "processMonteCarlo2Mults": "0", + "enabledTables": { + "values": [ + [ + -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 + ], + [ + -1 + ], + [ + -1 + ], + [ + -1 + ], + [ + -1 + ], + [ + -1 + ], + [ + -1 + ], + [ + -1 + ], + [ + -1 + ], + [ + -1 + ], + [ + -1 + ], + [ + -1 + ], + [ + -1 + ], + [ + -1 + ] + ] + }, + "maxPtGlobalTrack": "1e+10", + "embedINELgtZEROselection": "0", + "minPtGlobalTrack": "0.15", + "autoConfigureProcess": "0", + "ccdbPathCentrality": "Centrality/Estimators", + "doVertexZeq": "1", + "ccdbPathVtxZ": "Centrality/Calibration", + "minNclsITSibGlobalTrack": "1", + "reconstructionPass": "", + "ccdburl": "http://alice-ccdb.cern.ch", + "processCentralityRun3": "1", + "minNclsITSGlobalTrack": "5", + "processMonteCarlo": "0" + } +} \ No newline at end of file diff --git a/MC/config/analysis_testing/json/dpl/o2-analysis-multiplicity-table.json b/MC/config/analysis_testing/json/dpl/o2-analysis-multiplicity-table.json new file mode 100644 index 000000000..622786cf9 --- /dev/null +++ b/MC/config/analysis_testing/json/dpl/o2-analysis-multiplicity-table.json @@ -0,0 +1,11 @@ +{ + "multiplicity-table": { + "processRun2": "false", + "processRun3": "true", + "processMC": "!ANALYSIS_QC_is_mc!" + }, + "pid-multiplicity": { + "processIU": "0", + "processStandard": "1" + } +} diff --git a/MC/config/analysis_testing/json/dpl/o2-analysis-perf-k0s-resolution.json b/MC/config/analysis_testing/json/dpl/o2-analysis-perf-k0s-resolution.json new file mode 100644 index 000000000..5bf02b5bd --- /dev/null +++ b/MC/config/analysis_testing/json/dpl/o2-analysis-perf-k0s-resolution.json @@ -0,0 +1,173 @@ +{ + "perf-k0s-resolution": { + "etaBins": { + "values": [ + 10, + -1, + 1 + ] + }, + "eventSelection": "1", + "mBins": { + "values": [ + 300, + 0.35, + 0.65 + ] + }, + "nSigTPC": "10", + "pTBins": { + "values": [ + 200, + 0, + 10 + ] + }, + "phiBins": { + "values": [ + 18, + 0, + 6.2831853 + ] + }, + "relpTResBins": { + "values": [ + "200", + "0", + "0.5" + ] + }, + "centralityAxis": { + "values": [ + "0", + "0", + "5", + "10", + "20", + "30", + "40", + "50", + "60", + "70", + "80", + "90", + "100", + "101" + ] + }, + "occupancyAxis": { + "values": [ + "0", + "-1", + "0", + "250", + "500", + "750", + "1000", + "1500", + "2000", + "3000", + "4500", + "6000", + "8000", + "10000", + "50000" + ] + }, + "eventSelections": { + "requireSel8": "0", + "requireTriggerTVX": "1", + "rejectITSROFBorder": "1", + "rejectTFBorder": "1", + "requireIsVertexITSTPC": "0", + "requireIsGoodZvtxFT0VsPV": "0", + "requireIsVertexTOFmatched": "0", + "requireIsVertexTRDmatched": "0", + "rejectSameBunchPileup": "1", + "requireNoCollInTimeRangeStd": "0", + "requireNoCollInTimeRangeStrict": "0", + "requireNoCollInTimeRangeNarrow": "0", + "requireNoCollInROFStd": "0", + "requireNoCollInROFStrict": "0", + "requireINEL0": "0", + "requireINEL1": "0", + "maxZVtxPosition": "10", + "centralityEstimator": "0", + "useFT0CbasedOccupancy": "0", + "minOccupancy": "-1", + "maxOccupancy": "-1" + }, + "v0Selections": { + "v0TypeSelection": "1", + "rapidityCut": "0.5", + "daughterEtaCut": "0.800000012", + "v0cospa": "0.995000005", + "dcav0dau": "1", + "dcanegtopv": "0.100000001", + "dcapostopv": "0.100000001", + "v0radius": "1.20000005", + "v0radiusMax": "100000", + "lifetimecut": { + "labels_rows": "", + "labels_cols": [ + "lifetimecutLambda", + "lifetimecutK0S" + ], + "values": [ + [ + "30", + "20" + ] + ] + }, + "armPodCut": "5", + "minTPCrows": "70", + "minITSclusters": "-1", + "minTPCrowsOverFindableClusters": "-1", + "minTPCfoundOverFindableClusters": "-1", + "maxFractionTPCSharedClusters": "1e+09", + "maxITSchi2PerNcls": "1e+09", + "maxTPCchi2PerNcls": "1e+09", + "requirePosITSib": "0", + "requireNegITSib": "0", + "requirePosITSafterburner": "0", + "requireNegITSafterburner": "0", + "trdSelectionPos": "0", + "trdSelectionNeg": "0", + "tofSelectionPos": "0", + "tofSelectionNeg": "0", + "pidHypoPos": "-1", + "pidHypoNeg": "-1", + "tpcPidNsigmaCut": "1e+09", + "tofPidNsigmaCut": "1000000" + }, + "useMultidimHisto": "0", + "enableTPCPlot": "0", + "requireTrueK0s": "0", + "doTreatPiToMuon": "0", + "trackTuner": { + "debugInfo": "0", + "updateTrackDCAs": "0", + "updateTrackCovMat": "0", + "updateCurvature": "0", + "updateCurvatureIU": "0", + "updatePulls": "0", + "isInputFileFromCCDB": "0", + "pathInputFile": "", + "nameInputFile": "", + "pathFileQoverPt": "", + "nameFileQoverPt": "", + "usePvRefitCorrections": "0", + "qOverPtMC": "-1", + "qOverPtData": "-1", + "nPhiBins": "0" + }, + "useTrackTuner": "0", + "trackTunerParams": "debugInfo=0|updateTrackCovMat=0|updateCurvature=1|updatePulls=0|isInputFileFromCCDB=1|pathInputFile=Users/m/mfaggin/test/inputsTrackTuner/PbPb2022|nameInputFile=trackTuner_DataLHC22sPass5_McLHC22l1b2_run529397.root|usePvRefitCorrections=0|oneOverPtCurrent=1|oneOverPtUpgr=1.2", + "lutPath": "GLO/Param/MatLUTInner", + "ccdbUrl": "http://alice-ccdb.cern.ch", + "grpmagPath": "GLO/Config/GRPMagField", + "processData": "1", + "processMC": "0" + } +} diff --git a/MC/config/analysis_testing/json/dpl/o2-analysis-pid-tof-merge.json b/MC/config/analysis_testing/json/dpl/o2-analysis-pid-tof-merge.json new file mode 100644 index 000000000..f4a68dbee --- /dev/null +++ b/MC/config/analysis_testing/json/dpl/o2-analysis-pid-tof-merge.json @@ -0,0 +1,96 @@ +{ + "tof-pid-merge": { + "enableQaHistograms": "0", + "enableTOFParamsForBetaMass": "0", + "enableParticle": { + "labels_rows": [ + "El", + "Mu", + "Pi", + "Ka", + "Pr", + "De", + "Tr", + "He", + "Al" + ], + "labels_cols": [ + "Enable", + "EnableFull" + ], + "values": [ + [ + "-1", + "-1" + ], + [ + "-1", + "-1" + ], + [ + "-1", + "-1" + ], + [ + "-1", + "-1" + ], + [ + "-1", + "-1" + ], + [ + "-1", + "-1" + ], + [ + "-1", + "-1" + ], + [ + "-1", + "-1" + ], + [ + "-1", + "-1" + ] + ] + }, + "processRun3": "1", + "processRun2": "0", + "processRun2BetaM": "0", + "processRun3BetaM": "1" + }, + "tof-signal": { + "enableQaHistograms": "0", + "ccdb-url": "http://alice-ccdb.cern.ch", + "ccdb-path-grplhcif": "GLO/Config/GRPLHCIF", + "ccdb-timestamp": "-1", + "timeShiftCCDBPathPos": "Analysis/PID/TOFOffsetPos", + "timeShiftCCDBPathNeg": "Analysis/PID/TOFOffsetPos", + "timeShiftCCDBPathPosMC": "", + "timeShiftCCDBPathNegMC": "", + "paramFileName": "", + "parametrizationPath": "TOF/Calib/Params", + "reconstructionPass": "metadata", + "reconstructionPassDefault": "unanchored", + "fatalOnPassNotAvailable": "0", + "enableTimeDependentResponse": "1", + "collisionSystem": "-1", + "autoSetProcessFunctions": "1", + "processRun3": "1", + "processRun2": "0" + }, + "tof-event-time": { + "minMomentum": "0.5", + "maxMomentum": "2", + "maxEvTimeTOF": "100000", + "sel8TOFEvTime": "0", + "computeEvTimeWithTOF": "1", + "computeEvTimeWithFT0": "1", + "maxNtracksInSet": "10", + "processRun2": "0", + "processRun3": "1" + } +} \ No newline at end of file diff --git a/MC/config/analysis_testing/json/dpl/o2-analysis-pid-tof-qa.json b/MC/config/analysis_testing/json/dpl/o2-analysis-pid-tof-qa.json new file mode 100644 index 000000000..b97eca22b --- /dev/null +++ b/MC/config/analysis_testing/json/dpl/o2-analysis-pid-tof-qa.json @@ -0,0 +1,76 @@ +{ + "tof-pid-qa": { + "applyEvSel": "2", + "applyRapidityCut": "1", + "deltaBins": { + "values": [ + 100, + -1000, + 1000 + ] + }, + "enableEvTimeSplitting": "0", + "enableVsMomentumHistograms": "0", + "etaBins": { + "values": [ + 50, + -1, + 1 + ] + }, + "expSigmaBins": { + "values": [ + 200, + 0, + 200 + ] + }, + "logAxis": "0", + "maxP": "5", + "minP": "0.1", + "nBinsP": "400", + "nSigmaBins": { + "values": [ + 400, + -10, + 10 + ] + }, + "phiBins": { + "values": [ + 25, + 0, + 6.283185307179586 + ] + }, + "processAlpha": "0", + "processDeuteron": "0", + "processElectron": "0", + "processFullAlpha": "0", + "processFullDeuteron": "1", + "processFullElectron": "0", + "processFullHelium3": "1", + "processFullKaon": "1", + "processFullMuon": "0", + "processFullPion": "1", + "processFullProton": "1", + "processFullTriton": "1", + "processHelium3": "0", + "processKaon": "0", + "processMuon": "0", + "processPion": "0", + "processProton": "0", + "processTriton": "0", + "produceDeltaTEtaPhiMap": "0", + "ptDeltaTEtaPhiMap": "3", + "splitSignalPerCharge": "1", + "trackLengthBins": { + "values": [ + 50, + 0, + 1000 + ] + }, + "trackSelection": "1" + } +} diff --git a/MC/config/analysis_testing/json/dpl/o2-analysis-pid-tpc-qa.json b/MC/config/analysis_testing/json/dpl/o2-analysis-pid-tpc-qa.json new file mode 100644 index 000000000..6aea099bb --- /dev/null +++ b/MC/config/analysis_testing/json/dpl/o2-analysis-pid-tpc-qa.json @@ -0,0 +1,90 @@ +{ + "tpc-pid-qa": { + "applyEvSel": "2", + "applyRapidityCut": "0", + "dEdxBins": { + "values": [ + 3000, + 0, + 1000 + ] + }, + "deltaBins": { + "values": [ + 100, + -1000, + 1000 + ] + }, + "enableDeDxPlot": "1", + "etaBins": { + "values": [ + 50, + -1, + 1 + ] + }, + "expSigmaBins": { + "values": [ + 100, + 0, + 200 + ] + }, + "logAxis": "1", + "maxP": "5", + "minP": "0.01", + "minTPCNcls": "0", + "nBinsP": "400", + "nSigmaBins": { + "values": [ + 400, + -10, + 10 + ] + }, + "phiBins": { + "values": [ + 50, + 0, + 6.283185307179586 + ] + }, + "processAlpha": "0", + "processDeuteron": "0", + "processElectron": "0", + "processFullAlpha": "0", + "processFullDeuteron": "1", + "processFullElectron": "1", + "processFullHelium3": "1", + "processFullKaon": "1", + "processFullMuon": "1", + "processFullPion": "1", + "processFullProton": "1", + "processFullTriton": "1", + "processFullWithTOFAlpha": "0", + "processFullWithTOFDeuteron": "0", + "processFullWithTOFElectron": "0", + "processFullWithTOFHelium3": "0", + "processFullWithTOFKaon": "0", + "processFullWithTOFMuon": "0", + "processFullWithTOFPion": "0", + "processFullWithTOFProton": "0", + "processFullWithTOFTriton": "0", + "processHelium3": "0", + "processKaon": "0", + "processMuon": "0", + "processPion": "0", + "processProton": "0", + "processTriton": "0", + "splitSignalPerCharge": "1", + "trackLengthBins": { + "values": [ + 50, + 0, + 1000 + ] + }, + "trackSelection": "1" + } +} diff --git a/MC/config/analysis_testing/json/dpl/o2-analysis-pid-tpc-service.json b/MC/config/analysis_testing/json/dpl/o2-analysis-pid-tpc-service.json new file mode 100644 index 000000000..5069a4eb2 --- /dev/null +++ b/MC/config/analysis_testing/json/dpl/o2-analysis-pid-tpc-service.json @@ -0,0 +1,52 @@ +{ + "pid-tpc-service": { + "ccdburl": "http://alice-ccdb.cern.ch", + "pidTPC": { + "param-file": "", + "ccdbPath": "Analysis/PID/TPC/Response", + "recoPass": "", + "ccdb-timestamp": "0", + "useNetworkCorrection": "0", + "autofetchNetworks": "1", + "skipTPCOnly": "0", + "networkPathLocally": "network.onnx", + "networkPathCCDB": "Analysis/PID/TPC/ML", + "enableNetworkOptimizations": "1", + "networkSetNumThreads": "0", + "savedEdxsCorrected": "-1", + "useCorrecteddEdx": "0", + "pid-full-el": "-1", + "pid-full-mu": "-1", + "pid-full-pi": "-1", + "pid-full-ka": "-1", + "pid-full-pr": "-1", + "pid-full-de": "-1", + "pid-full-tr": "-1", + "pid-full-he": "-1", + "pid-full-al": "-1", + "pid-tiny-el": "-1", + "pid-tiny-mu": "-1", + "pid-tiny-pi": "-1", + "pid-tiny-ka": "-1", + "pid-tiny-pr": "-1", + "pid-tiny-de": "-1", + "pid-tiny-tr": "-1", + "pid-tiny-he": "-1", + "pid-tiny-al": "-1", + "enableTuneOnDataTable": "-1", + "useNetworkEl": "1", + "useNetworkMu": "0", + "useNetworkPi": "1", + "useNetworkKa": "1", + "useNetworkPr": "1", + "useNetworkDe": "0", + "useNetworkTr": "0", + "useNetworkHe": "0", + "useNetworkAl": "0", + "networkBetaGammaCutoff": "0.449999988" + }, + "processTracksIU": "1", + "processTracksIUWithTracksQA": "0", + "processTracksMCIU": "0" + } +} \ No newline at end of file diff --git a/MC/config/analysis_testing/json/dpl/o2-analysis-pid-tpc.json b/MC/config/analysis_testing/json/dpl/o2-analysis-pid-tpc.json new file mode 100644 index 000000000..ea1387c2c --- /dev/null +++ b/MC/config/analysis_testing/json/dpl/o2-analysis-pid-tpc.json @@ -0,0 +1,27 @@ +{ + "tpc-pid": { + "autofetchNetworks": "1", + "ccdb-timestamp": "0", + "ccdb-url": "http://alice-ccdb.cern.ch", + "ccdbPath": "Analysis/PID/TPC/Response", + "enableNetworkOptimizations": "1", + "enableTuneOnDataTable": "1", + "networkPathCCDB": "Analysis/PID/TPC/ML", + "networkPathLocally": "network.onnx", + "networkSetNumThreads": "0", + "param-file": "", + "pid-al": "-1", + "pid-de": "-1", + "pid-el": "-1", + "pid-he": "-1", + "pid-ka": "-1", + "pid-mu": "-1", + "pid-pi": "-1", + "pid-pr": "-1", + "pid-tr": "-1", + "processMcTuneOnData": "!ANALYSIS_QC_is_mc!", + "processStandard": "!ANALYSIS_QC_is_data!", + "skipTPCOnly": "1", + "useNetworkCorrection": "0" + } +} diff --git a/MC/config/analysis_testing/json/dpl/o2-analysis-propagation-service.json b/MC/config/analysis_testing/json/dpl/o2-analysis-propagation-service.json new file mode 100644 index 000000000..05c23a7f0 --- /dev/null +++ b/MC/config/analysis_testing/json/dpl/o2-analysis-propagation-service.json @@ -0,0 +1,403 @@ +{ + "propagation-service": { + "ccdburl": "http://alice-ccdb.cern.ch", + "ccdb": { + "ccdb-url": "http://alice-ccdb.cern.ch", + "lutPath": "GLO/Param/MatLUTInner", + "grpmagPath": "GLO/Config/GRPMagField", + "mVtxPath": "GLO/Calib/MeanVertex" + }, + "enabledTables": { + "labels_rows": [ + "V0Indices", + "V0CoresBase", + "V0Covs", + "CascIndices", + "KFCascIndices", + "TraCascIndices", + "StoredCascCores", + "StoredKFCascCores", + "StoredTraCascCores", + "CascCovs", + "KFCascCovs", + "TraCascCovs", + "V0TrackXs", + "CascTrackXs", + "CascBBs", + "V0DauCovs", + "V0DauCovIUs", + "V0TraPosAtDCAs", + "V0TraPosAtIUs", + "V0Ivanovs", + "McV0Labels", + "V0MCCores", + "V0CoreMCLabels", + "V0MCCollRefs", + "McCascLabels", + "McKFCascLabels", + "McTraCascLabels", + "McCascBBTags", + "CascMCCores", + "CascCoreMCLabels", + "CascMCCollRefs", + "CascToTraRefs", + "CascToKFRefs", + "TraToCascRefs", + "KFToCascRefs", + "V0FoundTags", + "CascFoundTags" + ], + "labels_cols": [ + "enable" + ], + "values": [ + [ + "-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" + ], + [ + "-1" + ], + [ + "-1" + ], + [ + "1" + ], + [ + "1" + ], + [ + "1" + ], + [ + "-1" + ], + [ + "-1" + ], + [ + "-1" + ], + [ + "-1" + ], + [ + "-1" + ], + [ + "-1" + ] + ] + }, + "deduplicationAlgorithm": "1", + "useV0BufferForCascades": "0", + "mc_findableMode": "0", + "refitWithMaterialCorrection": "0", + "v0BuilderOpts": { + "generatePhotonCandidates": "0", + "moveTPCOnlyTracks": "1", + "minCrossedRows": "60", + "dcanegtopv": "0.0500000007", + "dcapostopv": "0.0500000007", + "v0cospa": "0.96999999999999997", + "dcav0dau": "1.5", + "v0radius": "0.899999976", + "maxDaughterEta": "0.800000012", + "mc_populateV0MCCoresSymmetric": "0", + "mc_populateV0MCCoresAsymmetric": "1", + "mc_treatPiToMuDecays": "1", + "mc_rapidityWindow": "0.5", + "mc_keepOnlyPhysicalPrimary": "1", + "mc_addGeneratedK0Short": "1", + "mc_addGeneratedLambda": "1", + "mc_addGeneratedAntiLambda": "1", + "mc_addGeneratedGamma": "0", + "mc_addGeneratedGammaMakeCollinear": "0", + "mc_findableDetachedV0": "0" + }, + "cascadeBuilderOpts": { + "useCascadeMomentumAtPrimVtx": "0", + "minCrossedRows": "60", + "dcabachtopv": "0.0500000007", + "cascradius": "0.899999976", + "casccospa": "0.970000029", + "dcacascdau": "1.5", + "lambdaMassWindow": "0.00999999978", + "maxDaughterEta": "0.800000012", + "kfTuneForOmega": "0", + "kfConstructMethod": "2", + "kfUseV0MassConstraint": "1", + "kfUseCascadeMassConstraint": "0", + "kfDoDCAFitterPreMinimV0": "1", + "kfDoDCAFitterPreMinimCasc": "1", + "mc_populateCascMCCoresSymmetric": "0", + "mc_populateCascMCCoresAsymmetric": "1", + "mc_addGeneratedXiMinus": "1", + "mc_addGeneratedXiPlus": "1", + "mc_addGeneratedOmegaMinus": "1", + "mc_addGeneratedOmegaPlus": "1", + "mc_treatPiToMuDecays": "1", + "mc_rapidityWindow": "0.5", + "mc_keepOnlyPhysicalPrimary": "1", + "mc_findableDetachedCascade": "0" + }, + "preSelectOpts": { + "preselectOnlyDesiredV0s": "0", + "preselectOnlyDesiredCascades": "0", + "lifetimeCut": { + "labels_rows": "", + "labels_cols": [ + "lifetimeCutK0S", + "lifetimeCutLambda", + "lifetimeCutXi", + "lifetimeCutOmega" + ], + "values": [ + [ + "20", + "60", + "40", + "20" + ] + ] + }, + "massCutPhoton": "0.300000012", + "massCutK0": { + "labels_rows": "", + "labels_cols": [ + "constant", + "linear", + "expoConstant", + "expoRelax" + ], + "values": [ + [ + "0.00281882007", + "0.00114057004", + "0.00172138005", + "0.500262022" + ] + ] + }, + "massCutLambda": { + "labels_rows": "", + "labels_cols": [ + "constant", + "linear", + "expoConstant", + "expoRelax" + ], + "values": [ + [ + "0.00117517996", + "0.000124098995", + "0.00547936978", + "0.308008999" + ] + ] + }, + "massCutXi": { + "labels_rows": "", + "labels_cols": [ + "constant", + "linear", + "expoConstant", + "expoRelax" + ], + "values": [ + [ + "0.00143209996", + "0.000203560994", + "0.00243186997", + "0.799668014" + ] + ] + }, + "massCutOm": { + "labels_rows": "", + "labels_cols": [ + "constant", + "linear", + "expoConstant", + "expoRelax" + ], + "values": [ + [ + "0.00143209996", + "0.000203560994", + "0.00243186997", + "0.799668014" + ] + ] + }, + "massWindownumberOfSigmas": "20", + "massWindowSafetyMargin": "0.00100000005", + "maxTPCpidNsigma": "5" + }, + "trackTuner": { + "debugInfo": "0", + "updateTrackDCAs": "0", + "updateTrackCovMat": "0", + "updateCurvature": "0", + "updateCurvatureIU": "0", + "updatePulls": "0", + "isInputFileFromCCDB": "0", + "pathInputFile": "", + "nameInputFile": "", + "pathFileQoverPt": "", + "nameFileQoverPt": "", + "usePvRefitCorrections": "0", + "qOverPtMC": "-1", + "qOverPtData": "-1", + "nPhiBins": "0" + }, + "trackPropagation": { + "minPropagationDistance": "5", + "useTrackTuner": "0", + "useTrkPid": "0", + "fillTrackTunerTable": "0", + "trackTunerConfigSource": "1", + "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.", + "axisPtQA": { + "values": [ + "0", + "0", + "0.10000000149011612", + "0.20000000298023224", + "0.30000001192092896", + "0.4000000059604645", + "0.5", + "0.6000000238418579", + "0.699999988079071", + "0.800000011920929", + "0.8999999761581421", + "1", + "1.100000023841858", + "1.2000000476837158", + "1.2999999523162842", + "1.399999976158142", + "1.5", + "1.600000023841858", + "1.7000000476837158", + "1.7999999523162842", + "1.899999976158142", + "2", + "2.200000047683716", + "2.4000000953674316", + "2.5999999046325684", + "2.799999952316284", + "3", + "3.200000047683716", + "3.4000000953674316", + "3.5999999046325684", + "3.799999952316284", + "4", + "4.400000095367432", + "4.800000190734863", + "5.199999809265137", + "5.599999904632568", + "6", + "6.5", + "7", + "7.5", + "8", + "9", + "10", + "11", + "12", + "13", + "14", + "15", + "17", + "19", + "21", + "23", + "25", + "30", + "35", + "40", + "50" + ] + } + }, + "processRealData": "0", + "processMonteCarlo": "0", + "processRealDataWithPID": "1", + "processMonteCarloWithPID": "0" + } +} \ No newline at end of file diff --git a/MC/config/analysis_testing/json/dpl/o2-analysis-qa-efficiency.json b/MC/config/analysis_testing/json/dpl/o2-analysis-qa-efficiency.json new file mode 100644 index 000000000..16be97864 --- /dev/null +++ b/MC/config/analysis_testing/json/dpl/o2-analysis-qa-efficiency.json @@ -0,0 +1,24 @@ +{ + "qa-efficiency": { + "applyEvSel": "2", + "do-al": "true", + "do-de": "true", + "do-el": "true", + "do-he": "true", + "do-ka": "true", + "do-mu": "true", + "do-pi": "true", + "do-pr": "true", + "do-tr": "true", + "doNegativePDG": "true", + "doPositivePDG": "true", + "doPtEta": "false", + "etaBins": { + "values": [ + "200", + "-0.8", + "0.8" + ] + } + } +} diff --git a/MC/config/analysis_testing/json/dpl/o2-analysis-qa-event-track.json b/MC/config/analysis_testing/json/dpl/o2-analysis-qa-event-track.json new file mode 100644 index 000000000..0c4adc582 --- /dev/null +++ b/MC/config/analysis_testing/json/dpl/o2-analysis-qa-event-track.json @@ -0,0 +1,138 @@ +{ + "qa-event-track": { + "maxEta": "2", + "checkOnlyPVContributor": "1", + "minPhi": "-1", + "binsPt": { + "values": [ + 0, + 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, + 2, + 5, + 10, + 20, + 50 + ] + }, + "minPt": "-10", + "selectPrim": "0", + "selectSec": "0", + "forceTRD": "0", + "processDataIU": "0", + "doDebug": "0", + "minTPCcrossedRows": "70", + "processTrackMatch": "!ANALYSIS_QC_is_data!", + "binsDeltaPt": { + "values": [ + 100, + -0.495, + 0.505 + ] + }, + "selectPID": "0", + "PartIdentifier": "2", + "forceNotTRD": "0", + "binsInvPt": { + "values": [ + 0, + 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, + 2, + 5, + 10, + 20, + 50 + ] + }, + "minEta": "-2", + "doExtraPIDqa": "0", + "tfCut": "1", + "processRun2ConvertedData": "0", + "addRunInfo": "1", + "maxPhi": "10", + "binsVertexPosZ": { + "values": [ + 100, + -20, + 20 + ] + }, + "selectGoodEvents": "1", + "maxPt": "1e+10", + "binsDeltaSigned1Pt": { + "values": [ + 100, + -0.495, + 0.505 + ] + }, + "binsSigned1Pt": { + "values": [ + 300, + -5, + 5 + ] + }, + "binsVertexNumContrib": { + "values": [ + 200, + 0, + 200 + ] + }, + "binsTrackMultiplicity": { + "values": [ + 1000, + 0, + 1000 + ] + }, + "checkFakeMatches": "0", + "overwriteAxisRangeForPbPb": "!OVERWRITEAXISRANGEFORPBPBVALUE!", + "activateChecksTRD": "0", + "binsVertexPosXY": { + "values": [ + 500, + -1, + 1 + ] + }, + "processData": "!ANALYSIS_QC_is_data!", + "trackSelection": "1", + "selectCharge": "0", + "checkPIDforTracking": "0", + "processRun2ConvertedMC": "0", + "isRun3": "1", + "processMC": "!ANALYSIS_QC_is_mc!", + "processDataIUFiltered": "0" + } +} diff --git a/MC/config/analysis_testing/json/dpl/o2-analysis-time-dependent-qa.json b/MC/config/analysis_testing/json/dpl/o2-analysis-time-dependent-qa.json new file mode 100644 index 000000000..296243dd8 --- /dev/null +++ b/MC/config/analysis_testing/json/dpl/o2-analysis-time-dependent-qa.json @@ -0,0 +1,6 @@ +{ + "time-dependent-qa-task": { + "FlagCheckQoverPtHist": "0", + "FlagFillPhiVsTimeHist": "0" + } +} diff --git a/MC/config/analysis_testing/json/dpl/o2-analysis-timestamp.json b/MC/config/analysis_testing/json/dpl/o2-analysis-timestamp.json new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/MC/config/analysis_testing/json/dpl/o2-analysis-timestamp.json @@ -0,0 +1 @@ +{} diff --git a/MC/config/analysis_testing/json/dpl/o2-analysis-track-propagation.json b/MC/config/analysis_testing/json/dpl/o2-analysis-track-propagation.json new file mode 100644 index 000000000..a05342f63 --- /dev/null +++ b/MC/config/analysis_testing/json/dpl/o2-analysis-track-propagation.json @@ -0,0 +1,19 @@ +{ + "track-propagation": { + "ccdb-url": "http://alice-ccdb.cern.ch", + "geoPath": "GLO/Config/GeometryAligned", + "grpmagPath": "GLO/Config/GRPMagField", + "lutPath": "GLO/Param/MatLUT", + "mVtxPath": "GLO/Calib/MeanVertex", + "processCovariance": "true", + "processStandard": "false" + }, + "ambiguous-track-propagation": { + "ccdb-url": "http://alice-ccdb.cern.ch", + "geoPath": "GLO/Config/GeometryAligned", + "grpmagPath": "GLO/Config/GRPMagField", + "mVtxPath": "GLO/Calib/MeanVertex", + "processCentral": "false", + "processMFT": "true" + } +} diff --git a/MC/config/analysis_testing/json/dpl/o2-analysis-track-to-collision-associator.json b/MC/config/analysis_testing/json/dpl/o2-analysis-track-to-collision-associator.json new file mode 100644 index 000000000..5a796328a --- /dev/null +++ b/MC/config/analysis_testing/json/dpl/o2-analysis-track-to-collision-associator.json @@ -0,0 +1,13 @@ +{ + "track-to-collision-association": { + "nSigmaForTimeCompat": "4", + "timeMargin": "500", + "setTrackSelections": "1", + "usePVAssociation": "true", + "includeUnassigned": "true", + "fillTableOfCollIdsPerTrack": "false", + "bcWindowForOneSigma": "60", + "processAssocWithTime": "true", + "processStandardAssoc": "false" + } +} diff --git a/MC/config/analysis_testing/json/dpl/o2-analysis-trackselection.json b/MC/config/analysis_testing/json/dpl/o2-analysis-trackselection.json new file mode 100644 index 000000000..dc26e77f7 --- /dev/null +++ b/MC/config/analysis_testing/json/dpl/o2-analysis-trackselection.json @@ -0,0 +1,14 @@ +{ + "track-selection": { + "isRun3": "1", + "produceTable": "-1", + "produceFBextendedTable": "-1", + "compatibilityIU": "0", + "itsMatching": "1", + "dcaSetup": "0", + "ptMin": "0.100000001", + "ptMax": "1e+10", + "etaMin": "-1", + "etaMax": "1" + } +} \ No newline at end of file diff --git a/MC/config/analysis_testing/json/dpl/o2-analysistutorial-mc-histograms.json b/MC/config/analysis_testing/json/dpl/o2-analysistutorial-mc-histograms.json new file mode 100644 index 000000000..d60d3166b --- /dev/null +++ b/MC/config/analysis_testing/json/dpl/o2-analysistutorial-mc-histograms.json @@ -0,0 +1,11 @@ +{ + "access-mc-data": { + "reduce-output" : 2 + }, + "access-mc-truth": { + "reduce-output" : 2 + }, + "loop-over-mc-matched": { + "reduce-output" : 2 + } +} diff --git a/MC/config/analysis_testing/json/format_json.sh b/MC/config/analysis_testing/json/format_json.sh index aac459674..e3b8b34d5 100755 --- a/MC/config/analysis_testing/json/format_json.sh +++ b/MC/config/analysis_testing/json/format_json.sh @@ -2,7 +2,7 @@ # Script to format the json files -for i in $(ls *.json); do +for i in $(find . | grep "\.json"); do case "$i" in *analyses_config.json*) continue diff --git a/MC/config/common/TPCloopers/ScalerComptonParams.json b/MC/config/common/TPCloopers/ScalerComptonParams.json new file mode 100644 index 000000000..d8e654847 --- /dev/null +++ b/MC/config/common/TPCloopers/ScalerComptonParams.json @@ -0,0 +1,28 @@ +{ + "normal": { + "min": [ + -0.0108811147511005, + -0.0098758740350604, + -0.0103233363479375, + -260.0542297363281, + -259.80059814453125 + ], + "max": [ + 0.0108060473576188, + 0.0103057539090514, + 0.0106524610891938, + 260.0343933105469, + 259.62890625 + ] + }, + "outlier": { + "center": [ + -71.39387130737305, + 96791.23828125 + ], + "scale": [ + 265.9389114379883, + 230762.30981445312 + ] + } +} \ No newline at end of file diff --git a/MC/config/common/TPCloopers/ScalerPairParams.json b/MC/config/common/TPCloopers/ScalerPairParams.json new file mode 100644 index 000000000..61434bfa2 --- /dev/null +++ b/MC/config/common/TPCloopers/ScalerPairParams.json @@ -0,0 +1,34 @@ +{ + "normal": { + "min": [ + -0.0073022879660129, + -0.0077305701561272, + -0.0076750442385673, + -0.0082916170358657, + -0.0079681202769279, + -0.0077468422241508, + -255.6164093017578, + -252.9441680908203 + ], + "max": [ + 0.007688719779253, + 0.0077241472899913, + 0.0075828479602932, + 0.00813714787364, + 0.0083825681358575, + 0.0073839174583554, + 256.2904968261719, + 253.4925842285156 + ] + }, + "outlier": { + "center": [ + -79.66580963134766, + 141535.640625 + ], + "scale": [ + 250.8921127319336, + 222363.16015625 + ] + } +} \ No newline at end of file diff --git a/MC/config/common/TPCloopers/gaussian_params.csv b/MC/config/common/TPCloopers/gaussian_params.csv new file mode 100644 index 000000000..8e07c22dd --- /dev/null +++ b/MC/config/common/TPCloopers/gaussian_params.csv @@ -0,0 +1,4 @@ +9.611554230339172022e+01 +1.963570744941765867e+01 +4.300000000000000000e+01 +1.690000000000000000e+02 diff --git a/MC/config/common/TPCloopers/poisson_params.csv b/MC/config/common/TPCloopers/poisson_params.csv new file mode 100644 index 000000000..ef26bd973 --- /dev/null +++ b/MC/config/common/TPCloopers/poisson_params.csv @@ -0,0 +1,3 @@ +3.165383056343737511e+00 +1.000000000000000000e+00 +1.200000000000000000e+01 diff --git a/MC/config/common/ampt/generator/OO_536TeV_AMPT.ampt b/MC/config/common/ampt/generator/OO_536TeV_AMPT.ampt new file mode 100644 index 000000000..d6d7a38b9 --- /dev/null +++ b/MC/config/common/ampt/generator/OO_536TeV_AMPT.ampt @@ -0,0 +1,151 @@ +5360 ! EFRM (sqrt(S_NN) in GeV if FRAME is CMS) +CMS ! FRAME +A ! PROJ +A ! TARG +16 ! IAP (projectile A number) +8 ! IZP (projectile Z number) +16 ! IAT (target A number) +8 ! IZT (target Z number) +1 ! NEVNT (total number of events) - overwritten by generator_AMPT.C +0. ! BMIN (mininum impact parameter in fm) +20. ! BMAX (maximum impact parameter in fm, also see below) +4 ! ISOFT (D=4): select Default AMPT or String Melting(see below) +1000 ! NTMAX: number of timesteps (D=150), see below +0.2 ! DT: timestep in fm (hadron cascade time= DT*NTMAX) (D=0.2) +0.30 ! PARJ(41): parameter a in Lund symmetric splitting function +0.15 ! PARJ(42): parameter b in Lund symmetric splitting function +1 ! (D=1,yes;0,no) flag for popcorn mechanism(netbaryon stopping) +1.0 ! PARJ(5) to control BMBbar vs BBbar in popcorn (D=1.0) +1 ! shadowing flag (Default=1,yes; 0,no) +0 ! quenching flag (D=0,no; 1,yes) +2.0 ! quenching parameter -dE/dx (GeV/fm) in case quenching flag=1 +2.0 ! p0 cutoff in HIJING for minijet productions (D=2.0) +3.2264d0 ! parton screening mass in fm^(-1) (D=2.265d0), see below +0 ! IZPC: (D=0 forward-angle parton scatterings; 100,isotropic) +0.33d0 ! alpha in parton cascade (D=0.33d0), see parton screening mass +1d6 ! dpcoal in GeV +1d6 ! drcoal in fm +0 ! ihjsed: take HIJING seed from below (D=0)or at runtime(11) - overwritten by generator_AMPT.C +13150909 ! random seed for HIJING - overwritten by generator_AMPT.C +8 ! random seed for parton cascade - overwritten by generator_AMPT.C +0 ! flag for K0s weak decays (D=0,no; 1,yes) +1 ! flag for phi decays at end of hadron cascade (D=1,yes; 0,no) +0 ! flag for pi0 decays at end of hadron cascade (D=0,no; 1,yes) +0 ! optional OSCAR output (D=0,no; 1,yes; 2&3,more parton info) +0 ! flag for perturbative deuteron calculation (D=0,no; 1or2,yes) +1 ! integer factor for perturbative deuterons(>=1 & <=10000) +1 ! choice of cross section assumptions for deuteron reactions +-7. ! Pt in GeV: generate events with >=1 minijet above this value +1000 ! maxmiss (D=1000): maximum # of tries to repeat a HIJING event +3 ! flag on initial and final state radiation (D=3,both yes; 0,no) +1 ! flag on Kt kick (D=1,yes; 0,no) +0 ! flag to turn on quark pair embedding (D=0,no; 1,yes) +7., 0. ! Initial Px and Py values (GeV) of the embedded quark (u or d) +0., 0. ! Initial x & y values (fm) of the embedded back-to-back q/qbar +1, 5., 0. ! nsembd(D=0), psembd (in GeV),tmaxembd (in radian). +0 ! Flag to enable users to modify shadowing (D=0,no; 1,yes) +1.d0 ! Factor used to modify nuclear shadowing +1 ! Flag for random orientation of reaction plane (D=0,no; 1,yes) + +%%%%%%%%%% O2DPG notes: +OO at sqrt(s_NN) = 5.36 TeV, minimum bias (b = 0-20 fm), String Melting +with the LHC settings of arXiv:1403.6321 (a=0.30, b=0.15/GeV^2, 1.5 mb parton +cross section: alpha=0.33 and screening mass 3.2264/fm). +Values are read by AMPT line by line: do NOT add or remove lines above. +NEVNT (line 9), ihjsed (line 28) and the two seeds (lines 29-30) are +overwritten by MC/config/common/external/generator/generator_AMPT.C. +%%%%%%%%%% Further explanations: +BMAX: the upper limit HIPR1(34)+HIPR1(35)=19.87fm (dAu), 25.60fm(AuAu). +ISOFT: 1 Default, + 4 String Melting. +PARJ(41) & (42): for string melting AMPT, 0.55 & 0.15/GeV^2 are recommended + for top RHIC energies and 0.30 & 0.15/GeV^2 are recommended for + LHC energies (see arXiv:1403.6321 for details). +NTMAX: number of time-steps for hadron cascade. + Use a large value (e.g. 1000) for LHC studies or HBT studies at RHIC. + Using NTMAX=2 or 3 effectively turns off hadronic cascade. +parton screening mass (in 1/fm): its square is inversely proportional to + the parton cross section. Use D=2.265d0 for 3mb cross section + when alpha in parton cascade is set to 0.33; + (note: 3.2264d0 for 3mb cross section when alpha is set to 0.47). + Using 1d4 effectively turns off parton cascade. +ihjsed: if =11, take HIJING random seed at runtime so that + every run may be automatically different (see file 'exec'). +iksdcy: flag for K0s weak decays for comparison with data. +iphidcy: flag for phi meson decays at the end of hadron cascade for comparison + with data; default is yes; use 0 to turn off these decays. + Note: phi meson decay during hadron cascade is always enabled. +ipi0dcy: flag for pi0 electromagnetic decays at the end of hadron cascade for + comparison with data; set to 1 to turn on pi0 decays. +ioscar: 0 Dafault, + 1 Write output in the OSCAR format, + 2 Write out the complete parton information + (ana/parton-initial-afterPropagation.dat) + right after string melting (before parton cascade), + 3 Write out several more files on parton information (see readme). +idpert: flag for perturbative deuteron and antideuteron calculations + with results in ana/ampt_pert.dat: + 0 No perturbative calculations, + 1 Trigger a production of NPERTD perturbative deuterons + in each NN collision, + 2 Trigger a production of NPERTD perturbative deuterons only in + an NN collision where a conventional deuteron is produced. + Note: conventional deuteron calculations are always performed + with results in ana/ampt.dat. +NPERTD: number of perturbative deuterons produced in each triggered collision; + setting it to 0 turns off perturbative deuteron productions. +idxsec: choose a cross section model for deuteron inelastic/elastic collisions: + 1: same |matrix element|**2/s (after averaging over initial spins + and isospins) for B+B -> deuteron+meson at the same sqrt(s); + 2: same |matrix element|**2/s for B+B -> deuteron+meson + at the same sqrt(s)-threshold; + 3: same |matrix element|**2/s for deuteron+meson -> B+B + at the same sqrt(s); + 4: same |matrix element|**2/s for deuteron+meson -> B+B + at the same sqrt(s)-threshold; + 1 or 3 also chooses the same cross section for deuteron+meson or baryon + elastic collision at the same sqrt(s); + 2 or 4 also chooses the same cross section for deuteron+meson or baryon + elastic collision at the same sqrt(s)-threshold. +%%%%%%%%%% For jet studies: +pttrig: generate events with at least 1 initial minijet parton above this Pt + value, otherwise repeat HIJING event until reaching maxmiss tries; + use a negative value to disable this requirement and get normal events. +maxmiss: maximum number of tries for the repetition of a HIJING event to obtain + a minijet above the Pt value of pttrig; increase maxmiss if some events + fail to generate at least 1 initial minijet parton above pttrig. + it is safer to set a large value for high pttrig and/or large b value + and/or smaller colliding nuclei. +IHPR2(2): flag to turn off initial and final state radiation: + 0 both radiation off, 1 only final off, 2 only initial off, 3 both on. +IHPR2(5): flag to turn off Pt kick due to soft interactions: 0 off, 1 on. + Setting both IHPR2(2) and IHPR2(5) to zero makes it more likely to + have two high-Pt minijet partons that are close to back-to-back. +%%%%%%%%%% To embed a back-to-back light q/qbar jet pair +%%%%%%%%%% and a given number of soft pions along each jet into each event: +iembed: flag to turn on quark pair embedding: + 1: on with fixed position(xembd,pembd) and Pt(pxqembd,pyqembd); + 2: on with fixed position(xembd,pembd) and random azimuthal angle + with Pt-magnitude given by sqrt(pxqembd^2+pyqembd^2); + 3: on with random position and fixed Pt(pxqembd,pyqembd); + 4: on with random position and random random azimuthal angle + with Pt-magnitude given by sqrt(pxqembd^2+pyqembd^2); + for iembed=3 or 4: need a position file "embed-jet-xy.txt"; + Other integers: off. +pxqembd, pyqembd: sqrt(pxqembd^2+pyqembd^2) > 70MeV/c is required; + the embedded quark and antiquark have pz=0. +xembd, yembd: the embedded quark and antiquark jets have z=0 initially. Note: + the x-axis is defined as the direction along the impact parameter. +nsembd: number of soft pions to be embedded with each high-Pt parton + in the embedded jet pair. +psembd: Momentum of each embedded soft pion in GeV. +tmaxembd: maximum angle(rad) of embedded soft pions relative to high-Pt parton. +%%%%%%%%%% User modification of nuclear shadowing: +ishadow: set to 1 to enable users to adjust nuclear shadowing + provided the shadowing flag IHPR2(6) is turned on; default value is 0. +dshadow: valid when ishadow=1; this parameter modifies the HIJING shadowing + parameterization Ra(x,r)==1+fa(x,r) via Ra(x,r)==1+fa(x,r)*dshadow, + so the value of 0.d0 turns off shadowing + and the value of 1.d0 uses the default HIJING shadowing; + currently limited to 0.d0<=dshadow<=1.d0 to make sure Ra(x,r)>0. +iphirp: set to 1 to turn on random orientation of reaction plane (D=0) diff --git a/MC/config/common/ampt/generator/PbPb_536TeV_AMPT.ampt b/MC/config/common/ampt/generator/PbPb_536TeV_AMPT.ampt new file mode 100644 index 000000000..31eb92c85 --- /dev/null +++ b/MC/config/common/ampt/generator/PbPb_536TeV_AMPT.ampt @@ -0,0 +1,151 @@ +5360 ! EFRM (sqrt(S_NN) in GeV if FRAME is CMS) +CMS ! FRAME +A ! PROJ +A ! TARG +208 ! IAP (projectile A number) +82 ! IZP (projectile Z number) +208 ! IAT (target A number) +82 ! IZT (target Z number) +1 ! NEVNT (total number of events) - overwritten by generator_AMPT.C +0. ! BMIN (mininum impact parameter in fm) +20. ! BMAX (maximum impact parameter in fm, also see below) +4 ! ISOFT (D=4): select Default AMPT or String Melting(see below) +1000 ! NTMAX: number of timesteps (D=150), see below +0.2 ! DT: timestep in fm (hadron cascade time= DT*NTMAX) (D=0.2) +0.30 ! PARJ(41): parameter a in Lund symmetric splitting function +0.15 ! PARJ(42): parameter b in Lund symmetric splitting function +1 ! (D=1,yes;0,no) flag for popcorn mechanism(netbaryon stopping) +1.0 ! PARJ(5) to control BMBbar vs BBbar in popcorn (D=1.0) +1 ! shadowing flag (Default=1,yes; 0,no) +0 ! quenching flag (D=0,no; 1,yes) +2.0 ! quenching parameter -dE/dx (GeV/fm) in case quenching flag=1 +2.0 ! p0 cutoff in HIJING for minijet productions (D=2.0) +3.2264d0 ! parton screening mass in fm^(-1) (D=2.265d0), see below +0 ! IZPC: (D=0 forward-angle parton scatterings; 100,isotropic) +0.33d0 ! alpha in parton cascade (D=0.33d0), see parton screening mass +1d6 ! dpcoal in GeV +1d6 ! drcoal in fm +0 ! ihjsed: take HIJING seed from below (D=0)or at runtime(11) - overwritten by generator_AMPT.C +13150909 ! random seed for HIJING - overwritten by generator_AMPT.C +8 ! random seed for parton cascade - overwritten by generator_AMPT.C +0 ! flag for K0s weak decays (D=0,no; 1,yes) +1 ! flag for phi decays at end of hadron cascade (D=1,yes; 0,no) +0 ! flag for pi0 decays at end of hadron cascade (D=0,no; 1,yes) +0 ! optional OSCAR output (D=0,no; 1,yes; 2&3,more parton info) +0 ! flag for perturbative deuteron calculation (D=0,no; 1or2,yes) +1 ! integer factor for perturbative deuterons(>=1 & <=10000) +1 ! choice of cross section assumptions for deuteron reactions +-7. ! Pt in GeV: generate events with >=1 minijet above this value +1000 ! maxmiss (D=1000): maximum # of tries to repeat a HIJING event +3 ! flag on initial and final state radiation (D=3,both yes; 0,no) +1 ! flag on Kt kick (D=1,yes; 0,no) +0 ! flag to turn on quark pair embedding (D=0,no; 1,yes) +7., 0. ! Initial Px and Py values (GeV) of the embedded quark (u or d) +0., 0. ! Initial x & y values (fm) of the embedded back-to-back q/qbar +1, 5., 0. ! nsembd(D=0), psembd (in GeV),tmaxembd (in radian). +0 ! Flag to enable users to modify shadowing (D=0,no; 1,yes) +1.d0 ! Factor used to modify nuclear shadowing +1 ! Flag for random orientation of reaction plane (D=0,no; 1,yes) + +%%%%%%%%%% O2DPG notes: +Pb-Pb at sqrt(s_NN) = 5.36 TeV, minimum bias (b = 0-20 fm), String Melting +with the LHC settings of arXiv:1403.6321 (a=0.30, b=0.15/GeV^2, 1.5 mb parton +cross section: alpha=0.33 and screening mass 3.2264/fm). +Values are read by AMPT line by line: do NOT add or remove lines above. +NEVNT (line 9), ihjsed (line 28) and the two seeds (lines 29-30) are +overwritten by MC/config/common/external/generator/generator_AMPT.C. +%%%%%%%%%% Further explanations: +BMAX: the upper limit HIPR1(34)+HIPR1(35)=19.87fm (dAu), 25.60fm(AuAu). +ISOFT: 1 Default, + 4 String Melting. +PARJ(41) & (42): for string melting AMPT, 0.55 & 0.15/GeV^2 are recommended + for top RHIC energies and 0.30 & 0.15/GeV^2 are recommended for + LHC energies (see arXiv:1403.6321 for details). +NTMAX: number of time-steps for hadron cascade. + Use a large value (e.g. 1000) for LHC studies or HBT studies at RHIC. + Using NTMAX=2 or 3 effectively turns off hadronic cascade. +parton screening mass (in 1/fm): its square is inversely proportional to + the parton cross section. Use D=2.265d0 for 3mb cross section + when alpha in parton cascade is set to 0.33; + (note: 3.2264d0 for 3mb cross section when alpha is set to 0.47). + Using 1d4 effectively turns off parton cascade. +ihjsed: if =11, take HIJING random seed at runtime so that + every run may be automatically different (see file 'exec'). +iksdcy: flag for K0s weak decays for comparison with data. +iphidcy: flag for phi meson decays at the end of hadron cascade for comparison + with data; default is yes; use 0 to turn off these decays. + Note: phi meson decay during hadron cascade is always enabled. +ipi0dcy: flag for pi0 electromagnetic decays at the end of hadron cascade for + comparison with data; set to 1 to turn on pi0 decays. +ioscar: 0 Dafault, + 1 Write output in the OSCAR format, + 2 Write out the complete parton information + (ana/parton-initial-afterPropagation.dat) + right after string melting (before parton cascade), + 3 Write out several more files on parton information (see readme). +idpert: flag for perturbative deuteron and antideuteron calculations + with results in ana/ampt_pert.dat: + 0 No perturbative calculations, + 1 Trigger a production of NPERTD perturbative deuterons + in each NN collision, + 2 Trigger a production of NPERTD perturbative deuterons only in + an NN collision where a conventional deuteron is produced. + Note: conventional deuteron calculations are always performed + with results in ana/ampt.dat. +NPERTD: number of perturbative deuterons produced in each triggered collision; + setting it to 0 turns off perturbative deuteron productions. +idxsec: choose a cross section model for deuteron inelastic/elastic collisions: + 1: same |matrix element|**2/s (after averaging over initial spins + and isospins) for B+B -> deuteron+meson at the same sqrt(s); + 2: same |matrix element|**2/s for B+B -> deuteron+meson + at the same sqrt(s)-threshold; + 3: same |matrix element|**2/s for deuteron+meson -> B+B + at the same sqrt(s); + 4: same |matrix element|**2/s for deuteron+meson -> B+B + at the same sqrt(s)-threshold; + 1 or 3 also chooses the same cross section for deuteron+meson or baryon + elastic collision at the same sqrt(s); + 2 or 4 also chooses the same cross section for deuteron+meson or baryon + elastic collision at the same sqrt(s)-threshold. +%%%%%%%%%% For jet studies: +pttrig: generate events with at least 1 initial minijet parton above this Pt + value, otherwise repeat HIJING event until reaching maxmiss tries; + use a negative value to disable this requirement and get normal events. +maxmiss: maximum number of tries for the repetition of a HIJING event to obtain + a minijet above the Pt value of pttrig; increase maxmiss if some events + fail to generate at least 1 initial minijet parton above pttrig. + it is safer to set a large value for high pttrig and/or large b value + and/or smaller colliding nuclei. +IHPR2(2): flag to turn off initial and final state radiation: + 0 both radiation off, 1 only final off, 2 only initial off, 3 both on. +IHPR2(5): flag to turn off Pt kick due to soft interactions: 0 off, 1 on. + Setting both IHPR2(2) and IHPR2(5) to zero makes it more likely to + have two high-Pt minijet partons that are close to back-to-back. +%%%%%%%%%% To embed a back-to-back light q/qbar jet pair +%%%%%%%%%% and a given number of soft pions along each jet into each event: +iembed: flag to turn on quark pair embedding: + 1: on with fixed position(xembd,pembd) and Pt(pxqembd,pyqembd); + 2: on with fixed position(xembd,pembd) and random azimuthal angle + with Pt-magnitude given by sqrt(pxqembd^2+pyqembd^2); + 3: on with random position and fixed Pt(pxqembd,pyqembd); + 4: on with random position and random random azimuthal angle + with Pt-magnitude given by sqrt(pxqembd^2+pyqembd^2); + for iembed=3 or 4: need a position file "embed-jet-xy.txt"; + Other integers: off. +pxqembd, pyqembd: sqrt(pxqembd^2+pyqembd^2) > 70MeV/c is required; + the embedded quark and antiquark have pz=0. +xembd, yembd: the embedded quark and antiquark jets have z=0 initially. Note: + the x-axis is defined as the direction along the impact parameter. +nsembd: number of soft pions to be embedded with each high-Pt parton + in the embedded jet pair. +psembd: Momentum of each embedded soft pion in GeV. +tmaxembd: maximum angle(rad) of embedded soft pions relative to high-Pt parton. +%%%%%%%%%% User modification of nuclear shadowing: +ishadow: set to 1 to enable users to adjust nuclear shadowing + provided the shadowing flag IHPR2(6) is turned on; default value is 0. +dshadow: valid when ishadow=1; this parameter modifies the HIJING shadowing + parameterization Ra(x,r)==1+fa(x,r) via Ra(x,r)==1+fa(x,r)*dshadow, + so the value of 0.d0 turns off shadowing + and the value of 1.d0 uses the default HIJING shadowing; + currently limited to 0.d0<=dshadow<=1.d0 to make sure Ra(x,r)>0. +iphirp: set to 1 to turn on random orientation of reaction plane (D=0) diff --git a/MC/config/common/ampt/generator/pp_536TeV_AMPT.ampt b/MC/config/common/ampt/generator/pp_536TeV_AMPT.ampt new file mode 100644 index 000000000..740d085ed --- /dev/null +++ b/MC/config/common/ampt/generator/pp_536TeV_AMPT.ampt @@ -0,0 +1,155 @@ +5360 ! EFRM (sqrt(S_NN) in GeV if FRAME is CMS) +CMS ! FRAME +A ! PROJ +A ! TARG +1 ! IAP (projectile A number) +1 ! IZP (projectile Z number) +1 ! IAT (target A number) +1 ! IZT (target Z number) +1 ! NEVNT (total number of events) - overwritten by generator_AMPT.C +0.0 ! BMIN (mininum impact parameter in fm) +2.5 ! BMAX (maximum impact parameter in fm, also see below) +4 ! ISOFT (D=4): select Default AMPT or String Melting(see below) +150 ! NTMAX: number of timesteps (D=150), see below +0.2 ! DT: timestep in fm (hadron cascade time= DT*NTMAX) (D=0.2) +0.30 ! PARJ(41): parameter a in Lund symmetric splitting function +0.15 ! PARJ(42): parameter b in Lund symmetric splitting function +1 ! (D=1,yes;0,no) flag for popcorn mechanism(netbaryon stopping) +1.0 ! PARJ(5) to control BMBbar vs BBbar in popcorn (D=1.0) +1 ! shadowing flag (Default=1,yes; 0,no) +0 ! quenching flag (D=0,no; 1,yes) +2.0 ! quenching parameter -dE/dx (GeV/fm) in case quenching flag=1 +2.0 ! p0 cutoff in HIJING for minijet productions (D=2.0) +2.265d0 ! parton screening mass in fm^(-1) (D=2.265d0), see below +0 ! IZPC: (D=0 forward-angle parton scatterings; 100,isotropic) +0.33d0 ! alpha in parton cascade (D=0.33d0), see parton screening mass +1d6 ! dpcoal in GeV +1d6 ! drcoal in fm +0 ! ihjsed: take HIJING seed from below (D=0)or at runtime(11) - overwritten by generator_AMPT.C +13150909 ! random seed for HIJING - overwritten by generator_AMPT.C +8 ! random seed for parton cascade - overwritten by generator_AMPT.C +0 ! flag for K0s weak decays (D=0,no; 1,yes) +1 ! flag for phi decays at end of hadron cascade (D=1,yes; 0,no) +0 ! flag for pi0 decays at end of hadron cascade (D=0,no; 1,yes) +0 ! optional OSCAR output (D=0,no; 1,yes; 2&3,more parton info) +0 ! flag for perturbative deuteron calculation (D=0,no; 1or2,yes) +1 ! integer factor for perturbative deuterons(>=1 & <=10000) +1 ! choice of cross section assumptions for deuteron reactions +-7. ! Pt in GeV: generate events with >=1 minijet above this value +1000 ! maxmiss (D=1000): maximum # of tries to repeat a HIJING event +3 ! flag on initial and final state radiation (D=3,both yes; 0,no) +1 ! flag on Kt kick (D=1,yes; 0,no) +0 ! flag to turn on quark pair embedding (D=0,no; 1,yes) +7., 0. ! Initial Px and Py values (GeV) of the embedded quark (u or d) +0., 0. ! Initial x & y values (fm) of the embedded back-to-back q/qbar +1, 5., 0. ! nsembd(D=0), psembd (in GeV),tmaxembd (in radian). +0 ! Flag to enable users to modify shadowing (D=0,no; 1,yes) +1.d0 ! Factor used to modify nuclear shadowing +0 ! Flag for random orientation of reaction plane (D=0,no; 1,yes) +0 ! iexotnp, Flag for exotic nuclear structure of projectile (=0, no; =1,yes) +0 ! iexotnt, Flag for exotic nuclear structure of target (=0, no; =1,yes) +0 ! nstrucp, nucl. struct. of projectile, C12, 1(Triangle), 2(Chain); O16, 1(Tetrahedron), 2(Square), 3(Kite), 4(Chain) +0 ! nstruct, nucl. struct. of target, C12, 1(Triangle), 2(Chain); O16, 1(Tetrahedron), 2(Square), 3(Kite), 4(Chain) + +%%%%%%%%%% O2DPG notes: +Pb-Pb at sqrt(s_NN) = 5.36 TeV, minimum bias (b = 0-20 fm), String Melting +with the LHC settings of arXiv:1403.6321 (a=0.30, b=0.15/GeV^2, 1.5 mb parton +cross section: alpha=0.33 and screening mass 3.2264/fm). +Values are read by AMPT line by line: do NOT add or remove lines above. +NEVNT (line 9), ihjsed (line 28) and the two seeds (lines 29-30) are +overwritten by MC/config/common/external/generator/generator_AMPT.C. +%%%%%%%%%% Further explanations: +BMAX: the upper limit HIPR1(34)+HIPR1(35)=19.87fm (dAu), 25.60fm(AuAu). +ISOFT: 1 Default, + 4 String Melting. +PARJ(41) & (42): for string melting AMPT, 0.55 & 0.15/GeV^2 are recommended + for top RHIC energies and 0.30 & 0.15/GeV^2 are recommended for + LHC energies (see arXiv:1403.6321 for details). +NTMAX: number of time-steps for hadron cascade. + Use a large value (e.g. 1000) for LHC studies or HBT studies at RHIC. + Using NTMAX=2 or 3 effectively turns off hadronic cascade. +parton screening mass (in 1/fm): its square is inversely proportional to + the parton cross section. Use D=2.265d0 for 3mb cross section + when alpha in parton cascade is set to 0.33; + (note: 3.2264d0 for 3mb cross section when alpha is set to 0.47). + Using 1d4 effectively turns off parton cascade. +ihjsed: if =11, take HIJING random seed at runtime so that + every run may be automatically different (see file 'exec'). +iksdcy: flag for K0s weak decays for comparison with data. +iphidcy: flag for phi meson decays at the end of hadron cascade for comparison + with data; default is yes; use 0 to turn off these decays. + Note: phi meson decay during hadron cascade is always enabled. +ipi0dcy: flag for pi0 electromagnetic decays at the end of hadron cascade for + comparison with data; set to 1 to turn on pi0 decays. +ioscar: 0 Dafault, + 1 Write output in the OSCAR format, + 2 Write out the complete parton information + (ana/parton-initial-afterPropagation.dat) + right after string melting (before parton cascade), + 3 Write out several more files on parton information (see readme). +idpert: flag for perturbative deuteron and antideuteron calculations + with results in ana/ampt_pert.dat: + 0 No perturbative calculations, + 1 Trigger a production of NPERTD perturbative deuterons + in each NN collision, + 2 Trigger a production of NPERTD perturbative deuterons only in + an NN collision where a conventional deuteron is produced. + Note: conventional deuteron calculations are always performed + with results in ana/ampt.dat. +NPERTD: number of perturbative deuterons produced in each triggered collision; + setting it to 0 turns off perturbative deuteron productions. +idxsec: choose a cross section model for deuteron inelastic/elastic collisions: + 1: same |matrix element|**2/s (after averaging over initial spins + and isospins) for B+B -> deuteron+meson at the same sqrt(s); + 2: same |matrix element|**2/s for B+B -> deuteron+meson + at the same sqrt(s)-threshold; + 3: same |matrix element|**2/s for deuteron+meson -> B+B + at the same sqrt(s); + 4: same |matrix element|**2/s for deuteron+meson -> B+B + at the same sqrt(s)-threshold; + 1 or 3 also chooses the same cross section for deuteron+meson or baryon + elastic collision at the same sqrt(s); + 2 or 4 also chooses the same cross section for deuteron+meson or baryon + elastic collision at the same sqrt(s)-threshold. +%%%%%%%%%% For jet studies: +pttrig: generate events with at least 1 initial minijet parton above this Pt + value, otherwise repeat HIJING event until reaching maxmiss tries; + use a negative value to disable this requirement and get normal events. +maxmiss: maximum number of tries for the repetition of a HIJING event to obtain + a minijet above the Pt value of pttrig; increase maxmiss if some events + fail to generate at least 1 initial minijet parton above pttrig. + it is safer to set a large value for high pttrig and/or large b value + and/or smaller colliding nuclei. +IHPR2(2): flag to turn off initial and final state radiation: + 0 both radiation off, 1 only final off, 2 only initial off, 3 both on. +IHPR2(5): flag to turn off Pt kick due to soft interactions: 0 off, 1 on. + Setting both IHPR2(2) and IHPR2(5) to zero makes it more likely to + have two high-Pt minijet partons that are close to back-to-back. +%%%%%%%%%% To embed a back-to-back light q/qbar jet pair +%%%%%%%%%% and a given number of soft pions along each jet into each event: +iembed: flag to turn on quark pair embedding: + 1: on with fixed position(xembd,pembd) and Pt(pxqembd,pyqembd); + 2: on with fixed position(xembd,pembd) and random azimuthal angle + with Pt-magnitude given by sqrt(pxqembd^2+pyqembd^2); + 3: on with random position and fixed Pt(pxqembd,pyqembd); + 4: on with random position and random random azimuthal angle + with Pt-magnitude given by sqrt(pxqembd^2+pyqembd^2); + for iembed=3 or 4: need a position file "embed-jet-xy.txt"; + Other integers: off. +pxqembd, pyqembd: sqrt(pxqembd^2+pyqembd^2) > 70MeV/c is required; + the embedded quark and antiquark have pz=0. +xembd, yembd: the embedded quark and antiquark jets have z=0 initially. Note: + the x-axis is defined as the direction along the impact parameter. +nsembd: number of soft pions to be embedded with each high-Pt parton + in the embedded jet pair. +psembd: Momentum of each embedded soft pion in GeV. +tmaxembd: maximum angle(rad) of embedded soft pions relative to high-Pt parton. +%%%%%%%%%% User modification of nuclear shadowing: +ishadow: set to 1 to enable users to adjust nuclear shadowing + provided the shadowing flag IHPR2(6) is turned on; default value is 0. +dshadow: valid when ishadow=1; this parameter modifies the HIJING shadowing + parameterization Ra(x,r)==1+fa(x,r) via Ra(x,r)==1+fa(x,r)*dshadow, + so the value of 0.d0 turns off shadowing + and the value of 1.d0 uses the default HIJING shadowing; + currently limited to 0.d0<=dshadow<=1.d0 to make sure Ra(x,r)>0. +iphirp: set to 1 to turn on random orientation of reaction plane (D=0) diff --git a/MC/config/common/epos4/generator/NeNe_536TeV_EPOS4.optns b/MC/config/common/epos4/generator/NeNe_536TeV_EPOS4.optns new file mode 100644 index 000000000..ca9538d55 --- /dev/null +++ b/MC/config/common/epos4/generator/NeNe_536TeV_EPOS4.optns @@ -0,0 +1,33 @@ +!-------------------------------------------------------------------- +! Neon-Neon collisions without hydro and with hadronic cascade +!-------------------------------------------------------------------- + +!--------------------------------------- +! Define run +!--------------------------------------- + +application hadron !hadron-hadron, hadron-nucleus, or nucleus-nucleus +set laproj 10 !projectile atomic number +set maproj 20 !projectile mass number +set latarg 10 !target atomic number +set matarg 20 !target mass number +set ecms 5360 !sqrt(s)_pp +set istmax 25 !max status considered for storage + +ftime on !string formation time non-zero +!suppressed decays: +nodecays + 110 20 2130 -2130 2230 -2230 1130 -1130 1330 -1330 2330 -2330 3331 -3331 +end + +set ninicon 1 !number of initial conditions used for hydro evolution +core full !core/corona activated +hydro off !hydro disabled +eos off !eos disabled (epos standard EoS) +hacas full !hadronic cascade activated (UrQMD) +set nfreeze 1 !number of freeze out events per hydro event +set modsho 1 !printout every modsho events +set centrality 0 !0=min bias +set ihepmc 2 !HepMC output enabled on stdout +fillTree4(C1) !C1 sets impact parameter as centrality variable +set nfull 10 diff --git a/MC/config/common/epos4/generator/OO_536TeV_EPOS4.optns b/MC/config/common/epos4/generator/OO_536TeV_EPOS4.optns new file mode 100644 index 000000000..55c84a98e --- /dev/null +++ b/MC/config/common/epos4/generator/OO_536TeV_EPOS4.optns @@ -0,0 +1,33 @@ +!-------------------------------------------------------------------- +! Oxygen-Oxygen collisions without hydro and with hadronic cascade +!-------------------------------------------------------------------- + +!--------------------------------------- +! Define run +!--------------------------------------- + +application hadron !hadron-hadron, hadron-nucleus, or nucleus-nucleus +set laproj 8 !projectile atomic number +set maproj 16 !projectile mass number +set latarg 8 !target atomic number +set matarg 16 !target mass number +set ecms 5360 !sqrt(s)_pp +set istmax 25 !max status considered for storage + +ftime on !string formation time non-zero +!suppressed decays: +nodecays + 110 20 2130 -2130 2230 -2230 1130 -1130 1330 -1330 2330 -2330 3331 -3331 +end + +set ninicon 1 !number of initial conditions used for hydro evolution +core full !core/corona activated +hydro off !hydro disabled +eos off !eos disabled (epos standard EoS) +hacas full !hadronic cascade activated (UrQMD) +set nfreeze 1 !number of freeze out events per hydro event +set modsho 1 !printout every modsho events +set centrality 0 !0=min bias +set ihepmc 2 !HepMC output enabled on stdout +fillTree4(C1) !C1 sets impact parameter as centrality variable +set nfull 10 diff --git a/MC/config/common/epos4/generator/pO_962TeV_EPOS4.optns b/MC/config/common/epos4/generator/pO_962TeV_EPOS4.optns new file mode 100644 index 000000000..a54feecb4 --- /dev/null +++ b/MC/config/common/epos4/generator/pO_962TeV_EPOS4.optns @@ -0,0 +1,34 @@ +!-------------------------------------------------------------------- +! proton-Oxigen collisions at 9.62 TeV without hydro and with hadronic cascade +!-------------------------------------------------------------------- + +!--------------------------------------- +! Define run +!--------------------------------------- + +application hadron !hadron-hadron, hadron-nucleus, or nucleus-nucleus +set laproj 1 !projectile atomic number +set maproj 1 !projectile mass number +set latarg 8 !target atomic number +set matarg 16 !target mass number +set ecms 9620 !sqrt(s_NN) +set istmax 25 !max status considered for storage + +ftime on !string formation time non-zero +!suppressed decays: +nodecays + 110 20 2130 -2130 2230 -2230 1130 -1130 1330 -1330 2330 -2330 3331 -3331 +end + +set ninicon 1 !number of initial conditions used for hydro evolution +core full !core/corona activated +hydro off !hydro disabled +eos off !eos disabled (epos standard EoS) +hacas full !hadronic cascade activated (UrQMD) +set nfreeze 1 !number of freeze out events per hydro event +set modsho 1 !printout every modsho events +set centrality 0 !0=min bias +set ihepmc 2 !HepMC output enabled on stdout +fillTree4(C1) !C1 sets impact parameter as centrality variable +hepmc_rapcms 0.346225 !boost output to real asymmetric p/O beam frame (p: 6800 GeV, O: 3400 GeV) +set nfull 10 diff --git a/MC/config/common/epos4/generator/pO_962TeV_EPOS4_hydro.optns b/MC/config/common/epos4/generator/pO_962TeV_EPOS4_hydro.optns new file mode 100644 index 000000000..ee97644c4 --- /dev/null +++ b/MC/config/common/epos4/generator/pO_962TeV_EPOS4_hydro.optns @@ -0,0 +1,34 @@ +!-------------------------------------------------------------------- +! proton-Oxigen collisions at 9.62 TeV with hydro and with hadronic cascade +!-------------------------------------------------------------------- + +!--------------------------------------- +! Define run +!--------------------------------------- + +application hadron !hadron-hadron, hadron-nucleus, or nucleus-nucleus +set laproj 1 !projectile atomic number +set maproj 1 !projectile mass number +set latarg 8 !target atomic number +set matarg 16 !target mass number +set ecms 9620 !sqrt(s_NN) +set istmax 25 !max status considered for storage + +ftime on !string formation time non-zero +!suppressed decays: +nodecays + 110 20 2130 -2130 2230 -2230 1130 -1130 1330 -1330 2330 -2330 3331 -3331 +end + +set ninicon 1 !number of initial conditions used for hydro evolution +core full !core/corona activated +hydro hlle !hydro activated +eos x3ff !eos activated (epos standard EoS) +hacas full !hadronic cascade activated (UrQMD) +set nfreeze 1 !number of freeze out events per hydro event +set modsho 1 !printout every modsho events +set centrality 0 !0=min bias +set ihepmc 2 !HepMC output enabled on stdout +fillTree4(C1) !C1 sets impact parameter as centrality variable +hepmc_rapcms 0.346225 !boost output to real asymmetric p/O beam frame (p: 6800 GeV, O: 3400 GeV) +set nfull 10 diff --git a/MC/config/common/external/generator/CoalescencePythia8.h b/MC/config/common/external/generator/CoalescencePythia8.h new file mode 100644 index 000000000..4daacfe64 --- /dev/null +++ b/MC/config/common/external/generator/CoalescencePythia8.h @@ -0,0 +1,198 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does 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 "Pythia8/Pythia.h" +#include "fairlogger/Logger.h" +#include "TParticlePDG.h" +#include "TDatabasePDG.h" +#include "TSystem.h" +#include "TMath.h" +#include +#include +#include +#include +using namespace Pythia8; + +/// Coalescence afterburner for Pythia8 +/// Utility to compute naive coalescence afterburner as done in PRL 126, 101101 (2021) + +enum NucleiBits { + kDeuteron = 0, + kTriton = 1, + kHe3 = 2, + kHyperTriton = 3, + kHe4 = 4, +}; + +std::vector pdgList = {1000010020, 1000010030, 1000020030, 1010010030, 1000020040}; +std::vector massList = {1.875612, 2.80892113298, 2.808391, 2.991134, 3.727379}; + +bool doCoal(Pythia8::Event& event, int charge, int pdgCode, float mass, bool trivialCoal, double coalescenceRadius, bool nuclFromDecay, int iD1, int iD2, int iD3 = -1, int iD4 = -1) +{ + std::vector nucleonIDs = std::vector{iD1, iD2}; + // add A=3 and A=4 nuclei if enabled + if (iD3 > 0) { + nucleonIDs.push_back(iD3); + } + if (iD4 > 0) { + nucleonIDs.push_back(iD4); + } + Pythia8::Vec4 p; + for (auto nID : nucleonIDs) { + if (event[nID].status() < 0) { + // nucleon already used in coalescence + return false; + } + p += event[nID].p(); + } + bool isCoalescence = true; + for (auto nID : nucleonIDs) { + auto pN = event[nID].p(); + pN.bstback(p); + // trivial coal does not check the distance of the nucleons + if (pN.pAbs() > coalescenceRadius && !trivialCoal) { + isCoalescence = false; + break; + } + } + if (!isCoalescence) { + return false; + } + p.e(std::hypot(p.pAbs(), mass)); + + if (!nuclFromDecay) { + /// keep the original nucleons with negative status, store the mother with status 94 + event.append((charge * 2 - 1) * pdgCode, 94, 0, 0, 0, 0, 0, 0, p.px(), p.py(), p.pz(), p.e(), mass); + for (auto nID : nucleonIDs) { + event[nID].statusNeg(); + event[nID].daughter1(event.size() - 1); + } + } else { + // first nucleon will be replaced by the nucleus, the others will be removed + bool swap = true; + int nRemoved = 0; + for (auto iPart{0}; iPart < event.size(); ++iPart) { + for (auto nID : nucleonIDs) { + if (iPart == nID && swap) { + // replace the nucleon with the nucleus + LOG(debug) << "Replacing nucleon with index " << iPart << " and pdg code " << event[iPart].id() << " with nucleus with pdg code " << (charge * 2 - 1) * pdgCode; + event[iPart].id((charge * 2 - 1) * pdgCode); + event[iPart].status(94); + event[iPart].px(p.px()); + event[iPart].py(p.py()); + event[iPart].pz(p.pz()); + event[iPart].e(std::hypot(p.pAbs(), mass)); + event[iPart].m(mass); + swap = false; + } else if (iPart == nID - nRemoved && !swap) { + LOG(debug) << "Removing nucleon with index " << iPart << " and pdg code " << event[iPart].id(); + event.remove(iPart, iPart, true); + nRemoved++; + } + } + } + } + LOG(debug) << "Adding a " << (charge * 2 - 1) * pdgCode << " with p = " << p.px() << ", " << p.py() << ", " << p.pz() << ", E = " << p.e(); + return true; +} + +bool CoalescencePythia8(Pythia8::Event& event, std::vector inputPdgList = {}, bool trivialCoal = false, double coalMomentum = 0.4, int firstDauID = -1, int lastDauId = -1, float maxRapidity = 1.) +{ + const double coalescenceRadius{0.5 * 1.122462 * coalMomentum}; + + // if coalescence from a heavy hadron, loop only between firstDauID and lastDauID + int loopStart = firstDauID > -1 ? firstDauID : 0; + int loopEnd = lastDauId > -1 ? lastDauId : event.size() - 1; + // fill the nuclear mask + uint8_t nuclearMask = 0; + for (auto nuclPdg : inputPdgList) { + if (nuclPdg == pdgList[NucleiBits::kDeuteron]) { + nuclearMask |= (1 << kDeuteron); + } else if (nuclPdg == pdgList[NucleiBits::kTriton]) { + nuclearMask |= (1 << kTriton); + } else if (nuclPdg == pdgList[NucleiBits::kHe3]) { + nuclearMask |= (1 << kHe3); + } else if (nuclPdg == pdgList[NucleiBits::kHyperTriton]) { + nuclearMask |= (1 << kHyperTriton); + } else if (nuclPdg == pdgList[NucleiBits::kHe4]) { + nuclearMask |= (1 << kHe4); + } else { + LOG(fatal) << "Unknown pdg code for coalescence generator: " << nuclPdg; + return false; + } + } + // fill nucleon pools + std::vector protons[2], neutrons[2], lambdas[2]; + for (auto iPart{loopStart}; iPart <= loopEnd; ++iPart) { + if (std::abs(event[iPart].y()) > maxRapidity) // skip particles with y > ymax + { + continue; + } + if (std::abs(event[iPart].id()) == 2212) { + protons[event[iPart].id() > 0].push_back(iPart); + } else if (std::abs(event[iPart].id()) == 2112) { + neutrons[event[iPart].id() > 0].push_back(iPart); + } else if (std::abs(event[iPart].id()) == 3122 && (nuclearMask & (1 << kHyperTriton))) { + lambdas[event[iPart].id() > 0].push_back(iPart); + } + } + // Run coalescence + bool nuclFromDecay = firstDauID > -1; + bool coalHappened = false; + + for (int iC{0}; iC < 2; ++iC) { + for (int iP{0}; iP < protons[iC].size(); ++iP) { + for (int iN{0}; iN < neutrons[iC].size(); ++iN) { + if (nuclearMask & (1 << kDeuteron)) { + coalHappened |= doCoal(event, iC, pdgList[kDeuteron], massList[kDeuteron], trivialCoal, coalescenceRadius, nuclFromDecay, protons[iC][iP], neutrons[iC][iN]); + if (nuclFromDecay && coalHappened) { + return true; + } + } + if (nuclearMask & (1 << kTriton)) { + for (int iN2{iN + 1}; iN2 < neutrons[iC].size(); ++iN2) { + coalHappened |= doCoal(event, iC, pdgList[kTriton], massList[kTriton], trivialCoal, coalescenceRadius, nuclFromDecay, protons[iC][iP], neutrons[iC][iN], neutrons[iC][iN2]); + if (nuclFromDecay && coalHappened) { + return true; + } + } + } + if (nuclearMask & (1 << kHe3)) { + for (int iP2{iP + 1}; iP2 < protons[iC].size(); ++iP2) { + coalHappened |= doCoal(event, iC, pdgList[kHe3], massList[kHe3], trivialCoal, coalescenceRadius, nuclFromDecay, protons[iC][iP], protons[iC][iP2], neutrons[iC][iN]); + if (nuclFromDecay && coalHappened) { + return true; + } + } + } + if (nuclearMask & (1 << kHyperTriton)) { + for (int iL{0}; iL < lambdas[iC].size(); ++iL) { + coalHappened |= doCoal(event, iC, pdgList[kHyperTriton], massList[kHyperTriton], trivialCoal, coalescenceRadius, nuclFromDecay, protons[iC][iP], neutrons[iC][iN], lambdas[iC][iL]); + if (nuclFromDecay && coalHappened) { + return true; + } + } + } + if (nuclearMask & (1 << kHe4)) { + for (int iP2{iP + 1}; iP2 < protons[iC].size(); ++iP2) { + for (int iN2{iN + 1}; iN2 < neutrons[iC].size(); ++iN2) { + coalHappened |= doCoal(event, iC, pdgList[kHe4], massList[kHe4], trivialCoal, coalescenceRadius, nuclFromDecay, protons[iC][iP], protons[iC][iP2], neutrons[iC][iN], neutrons[iC][iN2]); + if (nuclFromDecay && coalHappened) { + return true; + } + } + } + } + } + } + } + return coalHappened; +} diff --git a/MC/config/common/external/generator/GeneratorHGPythia8.C b/MC/config/common/external/generator/GeneratorHGPythia8.C new file mode 100644 index 000000000..1e2a1d5c1 --- /dev/null +++ b/MC/config/common/external/generator/GeneratorHGPythia8.C @@ -0,0 +1,445 @@ +// HG-PYTHIA: heavy-ion events built from a Glauber model with eikonal NN +// interactions and minijet (hard scattering) counting, where each NN +// collision is represented by a PYTHIA 8 pp event with the matching number +// of multiparton interactions. The heavy-ion event is the sum of the pp +// events. +// +// Model: C. Loizides, A. Morsch, Phys. Lett. B773 (2017) 408 (arXiv:1705.08856) +// Reference implementation: https://github.com/abaty/HGPythia (A. Baty) +// +// The PYTHIA pp configuration (beams at sqrt(s_NN), SoftQCD:inelastic, +// decays...) is given through the usual GeneratorPythia8 parameters, e.g. +// GeneratorPythia8.config=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/generator/pythia8_inel_536.cfg +// The collision energy used for the Glauber model is taken from PYTHIA. +// +// Differences with respect to the reference implementation: +// - the unused J/psi and bookkeeping parts are not ported +// +/// @author Marco Giacalone (marco.giacalone@cern.ch) +/// @date 09-2026 + +#include "Generators/GeneratorPythia8.h" +#include "SimConfig/SimConfig.h" +#include "SimulationDataFormat/MCEventHeader.h" +#include "SimulationDataFormat/MCGenProperties.h" +#include "SimulationDataFormat/ParticleStatus.h" +#include "Pythia8/Pythia.h" +#include "TF1.h" +#include "TF2.h" +#include "TMath.h" +#include "TParticle.h" +#include "TRandom3.h" +#include +#include +#include +#include +#include + +namespace o2 +{ +namespace eventgen +{ + +class GeneratorHGPythia8 : public GeneratorPythia8 +{ + public: + /// Number of bins for the hard scatterings per NN collision (and MPIs per PYTHIA event), as in the + /// reference implementation: collisions with 20 or more hard scatterings are counted in the last bin (19) + static constexpr int kMaxMPI = 20; + /// Maximum number of consecutive PYTHIA events not matching any requested number of MPIs + static constexpr long kMaxRejected = 10000000; + + GeneratorHGPythia8(int A = 208, int B = 208) : GeneratorPythia8(), mA(A), mB(B) + { + // the PYTHIA interface only describes the last NN collision: do not expose it as "pythia8" + mInterfaceName = "hgpythia8"; + // defaults, overridden by the PYTHIA configuration file (GeneratorPythia8.config) read in Init + readString("Beams:idA 2212"); + readString("Beams:idB 2212"); + readString("SoftQCD:inelastic on"); + readString("ParticleDecays:limitTau0 on"); + readString("ParticleDecays:tau0Max 10."); + } + ~GeneratorHGPythia8() override = default; + + /// Impact parameter range in fm. A non-positive bMax selects o2-sim --bMax, + /// if given, otherwise 20 fm (10 fm for p-A, 5 fm for pp) + void setImpactParameterRange(double bMin, double bMax) + { + mBMin = bMin; + mBMax = bMax; + } + /// Hard (minijet) NN cross-section in mb, <0 takes it from the built-in energy table + void setSigmaHard(double sigma) { mSigmaHardIn = sigma; } + /// Soft NN cross-section in mb + void setSigmaSoft(double sigma) { mSigmaSoft = sigma; } + /// Include elastic NN scattering in the eikonal + void setElastic(bool val) { mElastic = val; } + + /// Glauber information of the current event + double getImpactParameter() const { return mImpactParameter; } + int getNcoll() const { return mNcoll; } + int getNcollHard() const { return mNcollHard; } + int getNhard() const { return mNhard; } + int getNpartProjectile() const { return mNpartProj; } + int getNpartTarget() const { return mNpartTarg; } + int getNpartBlackDisc() const { return mNpartBlackDisc; } + int getNcollBlackDisc() const { return mNcollBlackDisc; } + double getEccentricity() const { return mEccentricity; } + + Bool_t Init() override + { + if (mA < 1 || mA > 208 || mB < 1 || mB > 208) { + LOG(fatal) << "GeneratorHGPythia8: mass numbers must be between 1 and 208, got A = " << mA << ", B = " << mB; + return false; + } + // PYTHIA configuration, seeding and initialisation + if (!GeneratorPythia8::Init()) { + return false; + } + if (mPythia.settings.mode("Beams:idA") != 2212 || mPythia.settings.mode("Beams:idB") != 2212) { + LOG(warn) << "GeneratorHGPythia8: PYTHIA is expected to generate pp collisions"; + } + // Glauber random numbers seeded from the PYTHIA seed (0 means time dependent in both cases) + mRandom = std::make_unique(mPythia.settings.mode("Random:seed")); + + const double energy = mPythia.info.eCM(); + mSigmaHard = mSigmaHardIn >= 0 ? mSigmaHardIn : sigmaHardFromTable(energy); + if (mSigmaHard < 0) { + LOG(fatal) << "GeneratorHGPythia8: no hard cross-section available for sqrt(s_NN) = " << energy + << " GeV, please set it with setSigmaHard()"; + return false; + } + if (mBMax <= 0) { + const auto simBMax = o2::conf::SimConfig::Instance().getBMax(); + mBMax = simBMax > 0 ? simBMax : ((mA == 1 && mB == 1) ? 5. : ((mA == 1 || mB == 1) ? 10. : 20.)); + } + mDensity[0].reset(makeNucleonDensity(mA, "HGPythia8A")); + mDensity[1].reset(makeNucleonDensity(mB, "HGPythia8B")); + for (int j = 0; j < 2; ++j) { + mX[j].resize(j == 0 ? mA : mB); + mY[j].resize(j == 0 ? mA : mB); + mWounded[j].resize(j == 0 ? mA : mB); + mWoundedBlackDisc[j].resize(j == 0 ? mA : mB); + } + mHIEvent.init("HG-PYTHIA event", &mPythia.particleData); + + LOG(info) << "GeneratorHGPythia8: A = " << mA << ", B = " << mB << ", sqrt(s_NN) = " << energy + << " GeV, sigma_hard = " << mSigmaHard << " mb, sigma_soft = " << mSigmaSoft + << " mb, b in [" << mBMin << ", " << mBMax << "] fm, elastic " << mElastic; + return true; + } + + Bool_t generateEvent() override + { + std::array nMPI{}; + sampleGlauber(nMPI); + + // Generate PYTHIA events until each NN collision has a partner with the + // matching number of MPIs, and sum them + mHIEvent.reset(); + int nNeeded = mNcoll; + long nRejected = 0; + const bool prune = !mGenConfig.includePartonEvent; + auto select = [this](const Pythia8::Particle& p) { + const int st = p.statusHepMC(); + return (st == 1 || st == 2 || st == 4) && mUserFilterFcn(p); + }; + while (nNeeded > 0) { + if (!mPythia.next()) { + continue; + } + const int mpi = mPythia.info.nMPI(); + if (mpi >= kMaxMPI || nMPI[mpi] == 0) { + if (++nRejected > kMaxRejected) { + LOG(fatal) << "GeneratorHGPythia8: " << kMaxRejected << " consecutive PYTHIA events do not match the " + << "requested number of MPIs, check the PYTHIA configuration (SoftQCD:inelastic needed)"; + return false; + } + continue; + } + nRejected = 0; + nMPI[mpi]--; + nNeeded--; + if (prune) { + pruneEvent(mPythia.event, select); + } + mHIEvent += mPythia.event; + } + return true; + } + + Bool_t importParticles() override + { + // same conversion as GeneratorPythia8::importParticles, without pruning again the summed event + for (int i = 1; i < mHIEvent.size(); ++i) { + const auto& particle = mHIEvent[i]; + auto st = o2::mcgenstatus::MCGenStatusEncoding(particle.statusHepMC(), particle.status()).fullEncoding; + mParticles.push_back(TParticle(particle.id(), st, + particle.mother1() - 1, particle.mother2() - 1, + particle.daughter1() - 1, particle.daughter2() - 1, + particle.px(), particle.py(), particle.pz(), particle.e(), + particle.xProd(), particle.yProd(), particle.zProd(), particle.tProd())); + mParticles.back().SetBit(ParticleStatus::kToBeDone, particle.statusHepMC() == 1); + } + return true; + } + + void updateHeader(o2::dataformats::MCEventHeader* eventHeader) override + { + using Key = o2::dataformats::MCInfoKeys; + eventHeader->putInfo(Key::generator, "hgpythia8"); + eventHeader->putInfo(Key::generatorVersion, PYTHIA_VERSION_INTEGER); + eventHeader->SetB(mImpactParameter); + eventHeader->putInfo(Key::impactParameter, mImpactParameter); + eventHeader->putInfo(Key::planeAngle, 0.); // impact parameter along x + eventHeader->putInfo(Key::nColl, mNcoll); + eventHeader->putInfo(Key::nCollHard, mNcollHard); + eventHeader->putInfo(Key::nPart, mNpartProj + mNpartTarg); + eventHeader->putInfo(Key::nPartProjectile, mNpartProj); + eventHeader->putInfo(Key::nPartTarget, mNpartTarg); + eventHeader->putInfo(Key::sigmaInelNN, mSigmaSoft + mSigmaHard); + eventHeader->putInfo("nHard", mNhard); + eventHeader->putInfo("Npart_blackdisc", mNpartBlackDisc); + eventHeader->putInfo("Ncoll_blackdisc", mNcollBlackDisc); + eventHeader->putInfo("eccentricity", mEccentricity); + } + + private: + /// Hard cross-section (pT > 2 GeV) in mb as a function of sqrt(s_NN), from the reference implementation + double sigmaHardFromTable(double energy) const + { + if (energy < 20) return 0.161440969; + if (energy < 40) return 1.07414019; + if (energy < 64) return 2.32993174; + if (energy < 201) return 11.6641903; + if (energy < 2800) return 85.2298813; + if (energy < 5100) return 124.296341; + if (energy < 5500) return 130.82; + if (energy < 6400) return 144.17; + if (energy < 8100) return 166.184998; + if (energy < 9700) return 180.87; + return -1; + } + + /// Radial (radial and polar for deformed nuclei) nucleon density; a single nucleon (A = 1) is + /// sampled from the exponential proton profile with R = 0.234 fm (rms charge radius), as in TGlauberMC + static TF1* makeNucleonDensity(int A, const std::string& suffix) + { + auto name = [&suffix](const char* n) { return std::string(n) + suffix; }; + if (A == 208) { + return new TF1(name("wsPb").c_str(), "7.208e-4*4.*TMath::Pi()*x^2/(1+exp((x-6.62)/0.546))", 0., 20.); + } + if (A == 197) { + return new TF1(name("wsAu").c_str(), "8.596e-04*4.*TMath::Pi()*x^2/(1+exp((x-6.38)/0.535))", 0., 20.); + } + if (A == 129) { + auto f = new TF2(name("wsXe2a").c_str(), "x*x*TMath::Sin(y)/(1+exp((x-[0]*(1+[2]*0.315*(3*pow(cos(y),2)-1.0)+[3]*0.105*(35*pow(cos(y),4)-30*pow(cos(y),2)+3)))/[1]))", 0, 15, 0.0, TMath::Pi()); + f->SetNpx(120); + f->SetNpy(120); + f->SetParameters(5.36, 0.59, 0.18, 0); + return f; + } + if (A == 40) { + return new TF1(name("wsAr").c_str(), "1.*TMath::Pi()*x^2/(1+exp((x-3.53)/0.542))", 0., 15.); + } + if (A == 20) { + auto f = new TF1(name("wsNe").c_str(), "x*x*(1+[2]*(x/[0])**2)/(1+exp((x-[0])/[1]))", 0, 10.); + f->SetParameters(2.791, 0.698, -0.168); + return f; + } + if (A == 16) { + auto f = new TF1(name("wsO").c_str(), "x*x*(1+[2]*(x/[0])**2)/(1+exp((x-[0])/[1]))", 0, 10.); + f->SetParameters(2.608, 0.513, -0.051); + return f; + } + if (A == 6) { + return new TF1(name("wsC").c_str(), "7.208e-4*4.*TMath::Pi()*x^2*(1.-0.149*(x/2.46)**2)/(1+exp((x-2.46)/0.522))", 0., 10.); + } + if (A == 3) { + return new TF1(name("wsHe").c_str(), "7.208e-4*4.*TMath::Pi()*x^2*(1.+0.517*(x/0.964)**2)/(1+exp((x-0.964)/0.322))", 0., 10.); + } + if (A == 1) { + return new TF1(name("prot").c_str(), "x*x*exp(-x/0.234)", 0., 5.); + } + LOG(fatal) << "GeneratorHGPythia8: nucleus with A = " << A << " not supported (1, 3, 6, 16, 20, 40, 129, 197, 208)"; + return nullptr; + } + + /// Matter distribution in the proton (eikonal) + static double eikonal(double x) + { + constexpr double p0 = 3.9, p1 = 96.; + return p0 * p0 / p1 * TMath::Power(p0 * x, 3) * TMath::BesselK(3, p0 * x); + } + + /// Sample the nucleon positions in the transverse plane for nucleus j, centred at x = dx + void sampleNucleus(int j, double dx) + { + auto density = mDensity[j].get(); + auto f2 = dynamic_cast(density); + for (size_t k = 0; k < mX[j].size(); ++k) { + double x = 0., y = 0.; + if (f2) { + double r, theta; + f2->GetRandom2(r, theta, mRandom.get()); + const double phi = 2. * TMath::Pi() * mRandom->Rndm(); + x = r * TMath::Sin(phi) * TMath::Sin(theta); + y = r * TMath::Cos(phi) * TMath::Sin(theta); + } else if (density) { + const double r = density->GetRandom(mRandom.get()); + const double phi = 2. * TMath::Pi() * mRandom->Rndm(); + const double costh = 2. * mRandom->Rndm() - 1.; + const double sinth = costh * costh < 1. ? TMath::Sqrt(1. - costh * costh) : 0.; + x = r * sinth * TMath::Cos(phi); + y = r * sinth * TMath::Sin(phi); + } + mX[j][k] = x + dx; + mY[j][k] = y; + } + } + + /// Sample a Glauber configuration with at least one inelastic NN collision and count, for + /// each NN collision, the number of hard scatterings (nMPI[0]: collisions without any) + void sampleGlauber(std::array& nMPI) + { + constexpr double dmax = 1.43; // black-disc NN distance (fm) + const double b02 = 0.5 * mSigmaSoft * 0.1 / TMath::Pi(); + do { + nMPI.fill(0); + const double b = TMath::Sqrt(mBMin * mBMin + mRandom->Rndm() * (mBMax * mBMax - mBMin * mBMin)); + sampleNucleus(0, b / 2.); + sampleNucleus(1, -b / 2.); + for (int j = 0; j < 2; ++j) { + std::fill(mWounded[j].begin(), mWounded[j].end(), 0); + std::fill(mWoundedBlackDisc[j].begin(), mWoundedBlackDisc[j].end(), 0); + } + const double gstot0 = mElastic ? 2. * (1. - TMath::Exp(-(mSigmaSoft + mSigmaHard) / mSigmaSoft * eikonal(0.001))) : 1.; + + mImpactParameter = b; + mNcoll = mNcollHard = mNhard = mNcollBlackDisc = 0; + for (int i = 0; i < mA; ++i) { + for (int j = 0; j < mB; ++j) { + const double dx = mX[0][i] - mX[1][j]; + const double dy = mY[0][i] - mY[1][j]; + double r2 = dx * dx + dy * dy; + if (r2 < dmax * dmax) { + mWoundedBlackDisc[0][i] = 1; + mWoundedBlackDisc[1][j] = 1; + mNcollBlackDisc++; + } + if (r2 > 25.) { + continue; + } + // interaction probability + r2 /= b02; + r2 /= gstot0; + const double chi = eikonal(TMath::Sqrt(r2)); + const double gs = 1. - TMath::Exp(-2. * (mSigmaSoft + mSigmaHard) / mSigmaSoft * chi); + const double gstot = 2. * (1. - TMath::Sqrt(1. - gs)); + const double rantot = mRandom->Rndm() * gstot0; + if (rantot > gstot && mElastic) { + continue; + } + if (rantot > gs) { + continue; + } + mWounded[0][i] = 1; + mWounded[1][j] = 1; + mNcoll++; + // minijets + const double tt = 2. * chi * mSigmaHard / mSigmaSoft; + const double ts = 2. * chi; + if (rantot < TMath::Exp(-tt) * (1. - TMath::Exp(-ts))) { + nMPI[0]++; + continue; + } + double xr = -TMath::Log(TMath::Exp(-tt) + mRandom->Rndm() * (1. - TMath::Exp(-tt))); + int njet = 0; + while (true) { + njet++; + xr -= TMath::Log(mRandom->Rndm()); + if (xr > tt) { + break; + } + } + njet = TMath::Min(njet, kMaxMPI - 1); + nMPI[njet]++; + mNhard += njet; + mNcollHard++; + } + } + } while (mNcoll < 1); + + // participants and participant eccentricity + mNpartProj = mNpartTarg = mNpartBlackDisc = 0; + double mx = 0., my = 0., mx2 = 0., my2 = 0., mxy = 0.; + for (int j = 0; j < 2; ++j) { + for (size_t k = 0; k < mX[j].size(); ++k) { + mNpartBlackDisc += mWoundedBlackDisc[j][k]; + if (!mWounded[j][k]) { + continue; + } + (j == 0 ? mNpartProj : mNpartTarg)++; + mx += mX[j][k]; + my += mY[j][k]; + mx2 += mX[j][k] * mX[j][k]; + my2 += mY[j][k] * mY[j][k]; + mxy += mX[j][k] * mY[j][k]; + } + } + const double iw = mNpartProj + mNpartTarg; + mx2 -= mx * mx / iw; + my2 -= my * my / iw; + mxy -= mx * my / iw; + mEccentricity = (mx2 + my2) > 0 ? TMath::Sqrt((my2 - mx2) * (my2 - mx2) + 4. * mxy * mxy) / (mx2 + my2) : 0.; + } + + // configuration + int mA = 208; + int mB = 208; + double mBMin = 0.; + double mBMax = -1.; + double mSigmaHardIn = -1.; + double mSigmaHard = -1.; + double mSigmaSoft = 57.; + bool mElastic = false; + + // Glauber state + std::unique_ptr mRandom; + std::array, 2> mDensity; + std::array, 2> mX; + std::array, 2> mY; + std::array, 2> mWounded; + std::array, 2> mWoundedBlackDisc; + double mImpactParameter = 0.; + int mNcoll = 0; + int mNcollHard = 0; + int mNhard = 0; + int mNpartProj = 0; + int mNpartTarg = 0; + int mNpartBlackDisc = 0; + int mNcollBlackDisc = 0; + double mEccentricity = 0.; + + // sum of the PYTHIA events of the current heavy-ion event + Pythia8::Event mHIEvent; +}; + +} // namespace eventgen +} // namespace o2 + +/// HG-PYTHIA generator for nuclei with mass numbers A (along +z, PYTHIA beam A) and B. +/// bMax <= 0: o2-sim --bMax if given, otherwise 20 fm (10 fm for p-A, 5 fm for pp). +/// sigmaHard < 0: hard cross-section from the built-in sqrt(s_NN) table (up to 8.1 TeV). +FairGenerator* generateHGPythia8(int A = 208, int B = 208, double bMin = 0., double bMax = -1., + double sigmaHard = -1., double sigmaSoft = 57., + bool elastic = false) +{ + auto gen = new o2::eventgen::GeneratorHGPythia8(A, B); + gen->setImpactParameterRange(bMin, bMax); + gen->setSigmaHard(sigmaHard); + gen->setSigmaSoft(sigmaSoft); + gen->setElastic(elastic); + return gen; +} diff --git a/MC/config/common/external/generator/QEDepem.C b/MC/config/common/external/generator/QEDepem.C index 4ed3cdcc4..f34cfe839 100644 --- a/MC/config/common/external/generator/QEDepem.C +++ b/MC/config/common/external/generator/QEDepem.C @@ -29,6 +29,8 @@ o2::eventgen::GeneratorTGenerator* QEDepem() genBg->SetPtRange(qedParam.ptMin, qedParam.ptMax); // Set pt limits (GeV) for e+-: 1MeV corresponds to max R=13.3mm at 5kGaus genBg->SetOrigin(diamond.position[0], diamond.position[1], diamond.position[2]); // vertex position in space genBg->SetSigma(diamond.width[0], diamond.width[1], diamond.width[2]); // vertex sigma + genBg->SetCMEnergy(qedParam.cmEnergy); // center of mass energy per nucleon pair in GeV + genBg->SetZ(qedParam.Z); // atomic number of the projectile/target (only symmetric systems are compatible for now) genBg->SetTimeOrigin(0.); // vertex position in time genBg->Init(); diff --git a/MC/config/common/external/generator/TPCLoopers.C b/MC/config/common/external/generator/TPCLoopers.C new file mode 100644 index 000000000..8253fe091 --- /dev/null +++ b/MC/config/common/external/generator/TPCLoopers.C @@ -0,0 +1,787 @@ +#include +#include +#include +#include +#include +#include "CCDB/CCDBTimeStampUtils.h" +#include "CCDB/CcdbApi.h" +#include "DetectorsRaw/HBFUtils.h" + +//** This external generator is now used for development purposes only. +//** Fast simulated TPC loopers are automatically integrated as detector effect in O2 +//** starting from the O2PDPSuite::MC-prod-2026-v3-1 official release (05/02/2026) +//** Previous cocktails configurations using this generator must not be used anymore for recent tags, +//** as they will increase the default TPC loopers contribution. +//** For support: Marco Giacalone . + +// Static Ort::Env instance for multiple onnx model loading +static Ort::Env global_env(ORT_LOGGING_LEVEL_WARNING, "GlobalEnv"); + +// This class is responsible for loading the scaler parameters from a JSON file +// and applying the inverse transformation to the generated data. +struct Scaler_debug +{ + std::vector normal_min; + std::vector normal_max; + std::vector outlier_center; + std::vector outlier_scale; + + void load(const std::string &filename) + { + std::ifstream file(filename); + if (!file.is_open()) + { + throw std::runtime_error("Error: Could not open scaler file!"); + } + + std::string json_str((std::istreambuf_iterator(file)), std::istreambuf_iterator()); + file.close(); + + rapidjson::Document doc; + doc.Parse(json_str.c_str()); + + if (doc.HasParseError()) + { + throw std::runtime_error("Error: JSON parsing failed!"); + } + + normal_min = jsonArrayToVector(doc["normal"]["min"]); + normal_max = jsonArrayToVector(doc["normal"]["max"]); + outlier_center = jsonArrayToVector(doc["outlier"]["center"]); + outlier_scale = jsonArrayToVector(doc["outlier"]["scale"]); + } + + std::vector inverse_transform(const std::vector &input) + { + std::vector output; + for (int i = 0; i < input.size(); ++i) + { + if (i < input.size() - 2) + output.push_back(input[i] * (normal_max[i] - normal_min[i]) + normal_min[i]); + else + output.push_back(input[i] * outlier_scale[i - (input.size() - 2)] + outlier_center[i - (input.size() - 2)]); + } + + return output; + } + +private: + std::vector jsonArrayToVector(const rapidjson::Value &jsonArray) + { + std::vector vec; + for (int i = 0; i < jsonArray.Size(); ++i) + { + vec.push_back(jsonArray[i].GetDouble()); + } + return vec; + } +}; + +// This class loads the ONNX model and generates samples using it. +class ONNXGenerator_debug +{ +public: + ONNXGenerator_debug(Ort::Env &shared_env, const std::string &model_path) + : env(shared_env), session(env, model_path.c_str(), Ort::SessionOptions{}) + { + // Create session options + Ort::SessionOptions session_options; + session = Ort::Session(env, model_path.c_str(), session_options); + } + + std::vector generate_sample() + { + Ort::AllocatorWithDefaultOptions allocator; + + // Generate a latent vector (z) + std::vector z(100); + for (auto &v : z) + v = rand_gen.Gaus(0.0, 1.0); + + // Prepare input tensor + std::vector input_shape = {1, 100}; + // Get memory information + Ort::MemoryInfo memory_info = Ort::MemoryInfo::CreateCpu(OrtArenaAllocator, OrtMemTypeDefault); + + // Create input tensor correctly + Ort::Value input_tensor = Ort::Value::CreateTensor( + memory_info, z.data(), z.size(), input_shape.data(), input_shape.size()); + // Run inference + const char *input_names[] = {"z"}; + const char *output_names[] = {"output"}; + auto output_tensors = session.Run(Ort::RunOptions{nullptr}, input_names, &input_tensor, 1, output_names, 1); + + // Extract output + float *output_data = output_tensors.front().GetTensorMutableData(); + // Get the size of the output tensor + auto output_tensor_info = output_tensors.front().GetTensorTypeAndShapeInfo(); + size_t output_data_size = output_tensor_info.GetElementCount(); // Total number of elements in the tensor + std::vector output; + for (int i = 0; i < output_data_size; ++i) + { + output.push_back(output_data[i]); + } + + return output; + } + +private: + Ort::Env &env; + Ort::Session session; + TRandom3 rand_gen; +}; + +namespace o2 +{ +namespace eventgen +{ + +class GenTPCLoopers_debug : public Generator +{ + public: + GenTPCLoopers_debug(std::string model_pairs = "tpcloopmodel.onnx", std::string model_compton = "tpcloopmodelcompton.onnx", + std::string poisson = "poisson.csv", std::string gauss = "gauss.csv", std::string scaler_pair = "scaler_pair.json", + std::string scaler_compton = "scaler_compton.json") + { + // Checking if the model files exist and are not empty + std::ifstream model_file[2]; + model_file[0].open(model_pairs); + model_file[1].open(model_compton); + if (!model_file[0].is_open() || model_file[0].peek() == std::ifstream::traits_type::eof()) + { + LOG(fatal) << "Error: Pairs model file is empty or does not exist!"; + exit(1); + } + if (!model_file[1].is_open() || model_file[1].peek() == std::ifstream::traits_type::eof()) + { + LOG(fatal) << "Error: Compton model file is empty or does not exist!"; + exit(1); + } + model_file[0].close(); + model_file[1].close(); + // Checking if the scaler files exist and are not empty + std::ifstream scaler_file[2]; + scaler_file[0].open(scaler_pair); + scaler_file[1].open(scaler_compton); + if (!scaler_file[0].is_open() || scaler_file[0].peek() == std::ifstream::traits_type::eof()) + { + LOG(fatal) << "Error: Pairs scaler file is empty or does not exist!"; + exit(1); + } + if (!scaler_file[1].is_open() || scaler_file[1].peek() == std::ifstream::traits_type::eof()) + { + LOG(fatal) << "Error: Compton scaler file is empty or does not exist!"; + exit(1); + } + scaler_file[0].close(); + scaler_file[1].close(); + // Checking if the poisson file exists and it's not empty + if (poisson != "") + { + std::ifstream poisson_file(poisson); + if (!poisson_file.is_open() || poisson_file.peek() == std::ifstream::traits_type::eof()) + { + LOG(fatal) << "Error: Poisson file is empty or does not exist!"; + exit(1); + } + else + { + poisson_file >> mPoisson[0] >> mPoisson[1] >> mPoisson[2]; + poisson_file.close(); + mPoissonSet = true; + } + } + // Checking if the gauss file exists and it's not empty + if (gauss != "") + { + std::ifstream gauss_file(gauss); + if (!gauss_file.is_open() || gauss_file.peek() == std::ifstream::traits_type::eof()) + { + LOG(fatal) << "Error: Gauss file is empty or does not exist!"; + exit(1); + } + else + { + gauss_file >> mGauss[0] >> mGauss[1] >> mGauss[2] >> mGauss[3]; + gauss_file.close(); + mGaussSet = true; + } + } + mONNX_pair = std::make_unique(global_env, model_pairs); + mScaler_pair = std::make_unique(); + mScaler_pair->load(scaler_pair); + mONNX_compton = std::make_unique(global_env, model_compton); + mScaler_compton = std::make_unique(); + mScaler_compton->load(scaler_compton); + Generator::setTimeUnit(1.0); + Generator::setPositionUnit(1.0); + } + + Bool_t generateEvent() override + { + // Clear the vector of pairs + mGenPairs.clear(); + // Clear the vector of compton electrons + mGenElectrons.clear(); + if (mFlatGas) + { + unsigned int nLoopers, nLoopersPairs, nLoopersCompton; + LOG(debug) << "mCurrentEvent is " << mCurrentEvent; + LOG(debug) << "Current event time: " << ((mCurrentEvent < mInteractionTimeRecords.size() - 1) ? std::to_string(mInteractionTimeRecords[mCurrentEvent + 1].bc2ns() - mInteractionTimeRecords[mCurrentEvent].bc2ns()) : std::to_string(mTimeEnd - mInteractionTimeRecords[mCurrentEvent].bc2ns())) << " ns"; + LOG(debug) << "Current time offset wrt BC: " << mInteractionTimeRecords[mCurrentEvent].getTimeOffsetWrtBC() << " ns"; + mTimeLimit = (mCurrentEvent < mInteractionTimeRecords.size() - 1) ? mInteractionTimeRecords[mCurrentEvent + 1].bc2ns() - mInteractionTimeRecords[mCurrentEvent].bc2ns() : mTimeEnd - mInteractionTimeRecords[mCurrentEvent].bc2ns(); + // With flat gas the number of loopers are adapted based on time interval widths + // The denominator is either the LHC orbit (if mFlatGasOrbit is true) or the mean interaction time record interval + nLoopers = mFlatGasOrbit ? (mFlatGasNumber * (mTimeLimit / o2::constants::lhc::LHCOrbitNS)) : (mFlatGasNumber * (mTimeLimit / mIntTimeRecMean)); + nLoopersPairs = static_cast(std::round(nLoopers * mLoopsFractionPairs)); + nLoopersCompton = nLoopers - nLoopersPairs; + SetNLoopers(nLoopersPairs, nLoopersCompton); + LOG(info) << "Flat gas loopers: " << nLoopers << " (pairs: " << nLoopersPairs << ", compton: " << nLoopersCompton << ")"; + generateEvent(mTimeLimit); + mCurrentEvent++; + } else { + // Set number of loopers if poissonian params are available + if (mPoissonSet) + { + mNLoopersPairs = static_cast(std::round(mMultiplier[0] * PoissonPairs())); + } + if (mGaussSet) + { + mNLoopersCompton = static_cast(std::round(mMultiplier[1] * GaussianElectrons())); + } + // Generate pairs + for (int i = 0; i < mNLoopersPairs; ++i) + { + std::vector pair = mONNX_pair->generate_sample(); + // Apply the inverse transformation using the scaler + std::vector transformed_pair = mScaler_pair->inverse_transform(pair); + mGenPairs.push_back(transformed_pair); + } + // Generate compton electrons + for (int i = 0; i < mNLoopersCompton; ++i) + { + std::vector electron = mONNX_compton->generate_sample(); + // Apply the inverse transformation using the scaler + std::vector transformed_electron = mScaler_compton->inverse_transform(electron); + mGenElectrons.push_back(transformed_electron); + } + } + return true; + } + + Bool_t generateEvent(double &time_limit) + { + LOG(info) << "Time constraint for loopers: " << time_limit << " ns"; + // Generate pairs + for (int i = 0; i < mNLoopersPairs; ++i) + { + std::vector pair = mONNX_pair->generate_sample(); + // Apply the inverse transformation using the scaler + std::vector transformed_pair = mScaler_pair->inverse_transform(pair); + transformed_pair[9] = gRandom->Uniform(0., time_limit); // Regenerate time, scaling is not needed because time_limit is already in nanoseconds + mGenPairs.push_back(transformed_pair); + } + // Generate compton electrons + for (int i = 0; i < mNLoopersCompton; ++i) + { + std::vector electron = mONNX_compton->generate_sample(); + // Apply the inverse transformation using the scaler + std::vector transformed_electron = mScaler_compton->inverse_transform(electron); + transformed_electron[6] = gRandom->Uniform(0., time_limit); // Regenerate time, scaling is not needed because time_limit is already in nanoseconds + mGenElectrons.push_back(transformed_electron); + } + LOG(info) << "Generated Particles with time limit"; + return true; + } + + Bool_t importParticles() override + { + // Get looper pairs from the event + for (auto &pair : mGenPairs) + { + double px_e, py_e, pz_e, px_p, py_p, pz_p; + double vx, vy, vz, time; + double e_etot, p_etot; + px_e = pair[0]; + py_e = pair[1]; + pz_e = pair[2]; + px_p = pair[3]; + py_p = pair[4]; + pz_p = pair[5]; + vx = pair[6]; + vy = pair[7]; + vz = pair[8]; + time = pair[9]; + e_etot = TMath::Sqrt(px_e * px_e + py_e * py_e + pz_e * pz_e + mMass_e * mMass_e); + p_etot = TMath::Sqrt(px_p * px_p + py_p * py_p + pz_p * pz_p + mMass_p * mMass_p); + // Push the electron + TParticle electron(11, 1, -1, -1, -1, -1, px_e, py_e, pz_e, e_etot, vx, vy, vz, time / 1e9); + electron.SetStatusCode(o2::mcgenstatus::MCGenStatusEncoding(electron.GetStatusCode(), 0).fullEncoding); + electron.SetBit(ParticleStatus::kToBeDone, // + o2::mcgenstatus::getHepMCStatusCode(electron.GetStatusCode()) == 1); + mParticles.push_back(electron); + // Push the positron + TParticle positron(-11, 1, -1, -1, -1, -1, px_p, py_p, pz_p, p_etot, vx, vy, vz, time / 1e9); + positron.SetStatusCode(o2::mcgenstatus::MCGenStatusEncoding(positron.GetStatusCode(), 0).fullEncoding); + positron.SetBit(ParticleStatus::kToBeDone, // + o2::mcgenstatus::getHepMCStatusCode(positron.GetStatusCode()) == 1); + mParticles.push_back(positron); + } + // Get compton electrons from the event + for (auto &compton : mGenElectrons) + { + double px, py, pz; + double vx, vy, vz, time; + double etot; + px = compton[0]; + py = compton[1]; + pz = compton[2]; + vx = compton[3]; + vy = compton[4]; + vz = compton[5]; + time = compton[6]; + etot = TMath::Sqrt(px * px + py * py + pz * pz + mMass_e * mMass_e); + // Push the electron + TParticle electron(11, 1, -1, -1, -1, -1, px, py, pz, etot, vx, vy, vz, time / 1e9); + electron.SetStatusCode(o2::mcgenstatus::MCGenStatusEncoding(electron.GetStatusCode(), 0).fullEncoding); + electron.SetBit(ParticleStatus::kToBeDone, // + o2::mcgenstatus::getHepMCStatusCode(electron.GetStatusCode()) == 1); + mParticles.push_back(electron); + } + + return true; + } + + unsigned int PoissonPairs() + { + unsigned int poissonValue; + do + { + // Generate a Poisson-distributed random number with mean mPoisson[0] + poissonValue = mRandGen.Poisson(mPoisson[0]); + } while (poissonValue < mPoisson[1] || poissonValue > mPoisson[2]); // Regenerate if out of range + + return poissonValue; + } + + unsigned int GaussianElectrons() + { + unsigned int gaussValue; + do + { + // Generate a Normal-distributed random number with mean mGass[0] and stddev mGauss[1] + gaussValue = mRandGen.Gaus(mGauss[0], mGauss[1]); + } while (gaussValue < mGauss[2] || gaussValue > mGauss[3]); // Regenerate if out of range + + return gaussValue; + } + + void SetNLoopers(unsigned int &nsig_pair, unsigned int &nsig_compton) + { + if(mPoissonSet) { + LOG(info) << "Poissonian parameters correctly loaded."; + } else { + mNLoopersPairs = nsig_pair; + } + if(mGaussSet) { + LOG(info) << "Gaussian parameters correctly loaded."; + } else { + mNLoopersCompton = nsig_compton; + } + } + + void SetMultiplier(std::array &mult) + { + // Multipliers will work only if the poissonian and gaussian parameters are set + // otherwise they will be ignored + if (mult[0] < 0 || mult[1] < 0) + { + LOG(fatal) << "Error: Multiplier values must be non-negative!"; + exit(1); + } else { + LOG(info) << "Multiplier values set to: Pair = " << mult[0] << ", Compton = " << mult[1]; + mMultiplier[0] = mult[0]; + mMultiplier[1] = mult[1]; + } + } + + void setFlatGas(Bool_t &flat, const Int_t &number = -1, const Int_t &nloopers_orbit = -1) + { + mFlatGas = flat; + if (mFlatGas) + { + if(nloopers_orbit > 0) + { + mFlatGasOrbit = true; + mFlatGasNumber = nloopers_orbit; + LOG(info) << "Flat gas loopers will be generated using orbit reference."; + } else { + mFlatGasOrbit = false; + if (number < 0) + { + LOG(warn) << "Warning: Number of loopers per event must be non-negative! Switching option off."; + mFlatGas = false; + mFlatGasNumber = -1; + } else { + mFlatGasNumber = number; + } + } + if (mFlatGas){ + // Check if mContextFile is already opened + if(!mContextFile) + { + mContextFile = std::filesystem::exists("collisioncontext.root") ? TFile::Open("collisioncontext.root") : nullptr; + } + if(!mCollisionContext) + { + mCollisionContext = mContextFile ? (o2::steer::DigitizationContext *)mContextFile->Get("DigitizationContext") : nullptr; + } + mInteractionTimeRecords = mCollisionContext ? mCollisionContext->getEventRecords() : std::vector{}; + if (mInteractionTimeRecords.empty()) + { + LOG(error) << "Error: No interaction time records found in the collision context!"; + exit(1); + } else { + LOG(info) << "Interaction Time records has " << mInteractionTimeRecords.size() << " entries."; + mCollisionContext->printCollisionSummary(); + } + for (int c = 0; c < mInteractionTimeRecords.size() - 1; c++) + { + mIntTimeRecMean += mInteractionTimeRecords[c + 1].bc2ns() - mInteractionTimeRecords[c].bc2ns(); + } + mIntTimeRecMean /= (mInteractionTimeRecords.size() - 1); // Average interaction time record used as reference + const auto &hbfUtils = o2::raw::HBFUtils::Instance(); + // Get the start time of the second orbit after the last interaction record + const auto &lastIR = mInteractionTimeRecords.back(); + o2::InteractionRecord finalOrbitIR(0, lastIR.orbit + 2); // Final orbit, BC = 0 + mTimeEnd = finalOrbitIR.bc2ns(); + LOG(debug) << "Final orbit start time: " << mTimeEnd << " ns while last interaction record time is " << mInteractionTimeRecords.back().bc2ns() << " ns"; + } + } else { + mFlatGasNumber = -1; + } + LOG(info) << "Flat gas loopers: " << (mFlatGas ? "ON" : "OFF") << ", Reference loopers number per " << (mFlatGasOrbit ? "orbit " : "event ") << mFlatGasNumber; + } + + void setFractionPairs(float &fractionPairs) + { + if (fractionPairs < 0 || fractionPairs > 1) + { + LOG(fatal) << "Error: Loops fraction for pairs must be in the range [0, 1]."; + exit(1); + } + mLoopsFractionPairs = fractionPairs; + LOG(info) << "Pairs fraction set to: " << mLoopsFractionPairs; + } + + void SetRate(const std::string &rateFile, const bool isPbPb = true, const int &intRate = 50000) + { + // Checking if the rate file exists and is not empty + TFile rate_file(rateFile.c_str(), "READ"); + if (!rate_file.IsOpen() || rate_file.IsZombie()) + { + LOG(fatal) << "Error: Rate file is empty or does not exist!"; + exit(1); + } + const char* fitName = isPbPb ? "fitPbPb" : "fitpp"; + auto fit = (TF1 *)rate_file.Get(fitName); + if (!fit) + { + LOG(fatal) << "Error: Could not find fit function '" << fitName << "' in rate file!"; + exit(1); + } + mInteractionRate = intRate; + if(mInteractionRate < 0) + { + mContextFile = std::filesystem::exists("collisioncontext.root") ? TFile::Open("collisioncontext.root") : nullptr; + if(!mContextFile || mContextFile->IsZombie()) + { + LOG(fatal) << "Error: Interaction rate not provided and collision context file not found!"; + exit(1); + } + mCollisionContext = (o2::steer::DigitizationContext *)mContextFile->Get("DigitizationContext"); + mInteractionRate = std::floor(mCollisionContext->getDigitizerInteractionRate()); + LOG(info) << "Interaction rate retrieved from collision context: " << mInteractionRate << " Hz"; + if (mInteractionRate < 0) + { + LOG(fatal) << "Error: Invalid interaction rate retrieved from collision context!"; + exit(1); + } + } + auto ref = static_cast(std::floor(fit->Eval(mInteractionRate / 1000.))); // fit expects rate in kHz + rate_file.Close(); + if (ref <= 0) + { + LOG(fatal) << "Computed flat gas number reference per orbit is <=0"; + exit(1); + } else { + LOG(info) << "Set flat gas number to " << ref << " loopers per orbit using " << fitName << " from " << mInteractionRate << " Hz interaction rate."; + auto flat = true; + setFlatGas(flat, -1, ref); + } + } + + void SetAdjust(const float &adjust = 0.f) + { + if (mFlatGas && mFlatGasOrbit && adjust >= -1.f && adjust != 0.f) + { + LOG(info) << "Adjusting flat gas number per orbit by " << adjust * 100.f << "%"; + mFlatGasNumber = static_cast(std::round(mFlatGasNumber * (1.f + adjust))); + LOG(info) << "New flat gas number per orbit: " << mFlatGasNumber; + } + } + + private: + std::unique_ptr mONNX_pair = nullptr; + std::unique_ptr mONNX_compton = nullptr; + std::unique_ptr mScaler_pair = nullptr; + std::unique_ptr mScaler_compton = nullptr; + double mPoisson[3] = {0.0, 0.0, 0.0}; // Mu, Min and Max of Poissonian + double mGauss[4] = {0.0, 0.0, 0.0, 0.0}; // Mean, Std, Min, Max + std::vector> mGenPairs; + std::vector> mGenElectrons; + unsigned int mNLoopersPairs = -1; + unsigned int mNLoopersCompton = -1; + std::array mMultiplier = {1., 1.}; + bool mPoissonSet = false; + bool mGaussSet = false; + // Random number generator + TRandom3 mRandGen; + // Masses of the electrons and positrons + TDatabasePDG *mPDG = TDatabasePDG::Instance(); + double mMass_e = mPDG->GetParticle(11)->Mass(); + double mMass_p = mPDG->GetParticle(-11)->Mass(); + int mCurrentEvent = 0; // Current event number, used for adaptive loopers + TFile *mContextFile = nullptr; // Input collision context file + o2::steer::DigitizationContext *mCollisionContext = nullptr; // Pointer to the digitization context + std::vector mInteractionTimeRecords; // Interaction time records from collision context + Bool_t mFlatGas = false; // Flag to indicate if flat gas loopers are used + Bool_t mFlatGasOrbit = false; // Flag to indicate if flat gas loopers are per orbit + Int_t mFlatGasNumber = -1; // Number of flat gas loopers per event + double mIntTimeRecMean = 1.0; // Average interaction time record used for the reference + double mTimeLimit = 0.0; // Time limit for the current event + double mTimeEnd = 0.0; // Time limit for the last event + float mLoopsFractionPairs = 0.08; // Fraction of loopers from Pairs + std::string mRateFile = ""; // File with clusters/rate information per orbit + int mInteractionRate = 38000; // Interaction rate in Hz +}; + +} // namespace eventgen +} // namespace o2 + +// ONNX model files can be local, on AliEn or in the ALICE CCDB. +// For local and alien files it is mandatory to provide the filenames, for the CCDB instead the +// path to the object in the CCDB is sufficient. The model files will be downloaded locally. +// Example of CCDB path: "ccdb://Users/n/name/test" +// Example of alien path: "alien:///alice/cern.ch/user/n/name/test/test.onnx" +FairGenerator * + Generator_TPCLoopers(std::string model_pairs = "tpcloopmodel.onnx", std::string model_compton = "tpcloopmodelcompton.onnx", + std::string poisson = "poisson.csv", std::string gauss = "gauss.csv", std::string scaler_pair = "scaler_pair.json", + std::string scaler_compton = "scaler_compton.json", std::array mult = {1., 1.}, unsigned int nloopers_pairs = 1, + unsigned int nloopers_compton = 1) +{ + // Expand all environment paths + model_pairs = gSystem->ExpandPathName(model_pairs.c_str()); + model_compton = gSystem->ExpandPathName(model_compton.c_str()); + poisson = gSystem->ExpandPathName(poisson.c_str()); + gauss = gSystem->ExpandPathName(gauss.c_str()); + scaler_pair = gSystem->ExpandPathName(scaler_pair.c_str()); + scaler_compton = gSystem->ExpandPathName(scaler_compton.c_str()); + const std::array models = {model_pairs, model_compton}; + const std::array local_names = {"WGANpair.onnx", "WGANcompton.onnx"}; + const std::array isAlien = {models[0].starts_with("alien://"), models[1].starts_with("alien://")}; + const std::array isCCDB = {models[0].starts_with("ccdb://"), models[1].starts_with("ccdb://")}; + if (std::any_of(isAlien.begin(), isAlien.end(), [](bool v) { return v; })) + { + if (!gGrid) { + TGrid::Connect("alien://"); + if (!gGrid) { + LOG(fatal) << "AliEn connection failed, check token."; + exit(1); + } + } + for (size_t i = 0; i < models.size(); ++i) + { + if (isAlien[i] && !TFile::Cp(models[i].c_str(), local_names[i].c_str())) + { + LOG(fatal) << "Error: Model file " << models[i] << " does not exist!"; + exit(1); + } + } + } + if (std::any_of(isCCDB.begin(), isCCDB.end(), [](bool v) { return v; })) + { + o2::ccdb::CcdbApi ccdb_api; + ccdb_api.init("http://alice-ccdb.cern.ch"); + for (size_t i = 0; i < models.size(); ++i) + { + if (isCCDB[i]) + { + auto model_path = models[i].substr(7); // Remove "ccdb://" + // Treat filename if provided in the CCDB path + auto extension = model_path.find(".onnx"); + if (extension != std::string::npos) + { + auto last_slash = model_path.find_last_of('/'); + model_path = model_path.substr(0, last_slash); + } + std::map filter; + if(!ccdb_api.retrieveBlob(model_path, "./" , filter, o2::ccdb::getCurrentTimestamp(), false, local_names[i].c_str())) + { + LOG(fatal) << "Error: issues in retrieving " << model_path << " from CCDB!"; + exit(1); + } + } + } + } + model_pairs = isAlien[0] || isCCDB[0] ? local_names[0] : model_pairs; + model_compton = isAlien[1] || isCCDB[1] ? local_names[1] : model_compton; + auto generator = new o2::eventgen::GenTPCLoopers_debug(model_pairs, model_compton, poisson, gauss, scaler_pair, scaler_compton); + generator->SetNLoopers(nloopers_pairs, nloopers_compton); + generator->SetMultiplier(mult); + return generator; +} + +// Generator with flat gas loopers. Number of loopers starts from a reference value and changes +// based on the BC time intervals in each event. +FairGenerator * +Generator_TPCLoopersFlat(std::string model_pairs = "tpcloopmodel.onnx", std::string model_compton = "tpcloopmodelcompton.onnx", + std::string scaler_pair = "scaler_pair.json", std::string scaler_compton = "scaler_compton.json", + bool flat_gas = true, const int loops_num = 500, float fraction_pairs = 0.08, const int nloopers_orbit = -1) +{ + // Expand all environment paths + model_pairs = gSystem->ExpandPathName(model_pairs.c_str()); + model_compton = gSystem->ExpandPathName(model_compton.c_str()); + scaler_pair = gSystem->ExpandPathName(scaler_pair.c_str()); + scaler_compton = gSystem->ExpandPathName(scaler_compton.c_str()); + const std::array models = {model_pairs, model_compton}; + const std::array local_names = {"WGANpair.onnx", "WGANcompton.onnx"}; + const std::array isAlien = {models[0].starts_with("alien://"), models[1].starts_with("alien://")}; + const std::array isCCDB = {models[0].starts_with("ccdb://"), models[1].starts_with("ccdb://")}; + if (std::any_of(isAlien.begin(), isAlien.end(), [](bool v) + { return v; })) + { + if (!gGrid) + { + TGrid::Connect("alien://"); + if (!gGrid) + { + LOG(fatal) << "AliEn connection failed, check token."; + exit(1); + } + } + for (size_t i = 0; i < models.size(); ++i) + { + if (isAlien[i] && !TFile::Cp(models[i].c_str(), local_names[i].c_str())) + { + LOG(fatal) << "Error: Model file " << models[i] << " does not exist!"; + exit(1); + } + } + } + if (std::any_of(isCCDB.begin(), isCCDB.end(), [](bool v) + { return v; })) + { + o2::ccdb::CcdbApi ccdb_api; + ccdb_api.init("http://alice-ccdb.cern.ch"); + for (size_t i = 0; i < models.size(); ++i) + { + if (isCCDB[i]) + { + auto model_path = models[i].substr(7); // Remove "ccdb://" + // Treat filename if provided in the CCDB path + auto extension = model_path.find(".onnx"); + if (extension != std::string::npos) + { + auto last_slash = model_path.find_last_of('/'); + model_path = model_path.substr(0, last_slash); + } + std::map filter; + if (!ccdb_api.retrieveBlob(model_path, "./", filter, o2::ccdb::getCurrentTimestamp(), false, local_names[i].c_str())) + { + LOG(fatal) << "Error: issues in retrieving " << model_path << " from CCDB!"; + exit(1); + } + } + } + } + model_pairs = isAlien[0] || isCCDB[0] ? local_names[0] : model_pairs; + model_compton = isAlien[1] || isCCDB[1] ? local_names[1] : model_compton; + auto generator = new o2::eventgen::GenTPCLoopers_debug(model_pairs, model_compton, "", "", scaler_pair, scaler_compton); + generator->setFractionPairs(fraction_pairs); + generator->setFlatGas(flat_gas, loops_num, nloopers_orbit); + return generator; +} + +// Generator with flat gas loopers. Reference number of loopers is provided per orbit via external file +FairGenerator * +Generator_TPCLoopersOrbitRef(std::string model_pairs = "tpcloopmodel.onnx", std::string model_compton = "tpcloopmodelcompton.onnx", + std::string scaler_pair = "scaler_pair.json", std::string scaler_compton = "scaler_compton.json", + std::string nclxrate = "nclxrate.root", bool isPbPb = true, const int intrate = -1, const float adjust = 0.f) +{ + // Expand all environment paths + model_pairs = gSystem->ExpandPathName(model_pairs.c_str()); + model_compton = gSystem->ExpandPathName(model_compton.c_str()); + scaler_pair = gSystem->ExpandPathName(scaler_pair.c_str()); + scaler_compton = gSystem->ExpandPathName(scaler_compton.c_str()); + nclxrate = gSystem->ExpandPathName(nclxrate.c_str()); + const std::array models = {model_pairs, model_compton, nclxrate}; + const std::array local_names = {"WGANpair.onnx", "WGANcompton.onnx", "nclxrate.root"}; + const std::array isAlien = {models[0].starts_with("alien://"), models[1].starts_with("alien://"), nclxrate.starts_with("alien://")}; + const std::array isCCDB = {models[0].starts_with("ccdb://"), models[1].starts_with("ccdb://"), nclxrate.starts_with("ccdb://")}; + if (std::any_of(isAlien.begin(), isAlien.end(), [](bool v) + { return v; })) + { + if (!gGrid) + { + TGrid::Connect("alien://"); + if (!gGrid) + { + LOG(fatal) << "AliEn connection failed, check token."; + exit(1); + } + } + for (size_t i = 0; i < models.size(); ++i) + { + if (isAlien[i] && !TFile::Cp(models[i].c_str(), local_names[i].c_str())) + { + LOG(fatal) << "Error: Model file " << models[i] << " does not exist!"; + exit(1); + } + } + } + if (std::any_of(isCCDB.begin(), isCCDB.end(), [](bool v) + { return v; })) + { + o2::ccdb::CcdbApi ccdb_api; + ccdb_api.init("http://alice-ccdb.cern.ch"); + for (size_t i = 0; i < models.size(); ++i) + { + if (isCCDB[i]) + { + auto model_path = models[i].substr(7); // Remove "ccdb://" + // Treat filename if provided in the CCDB path + auto extension = model_path.find(".onnx"); + if (extension != std::string::npos) + { + auto last_slash = model_path.find_last_of('/'); + model_path = model_path.substr(0, last_slash); + } + std::map filter; + if (!ccdb_api.retrieveBlob(model_path, "./", filter, o2::ccdb::getCurrentTimestamp(), false, local_names[i].c_str())) + { + LOG(fatal) << "Error: issues in retrieving " << model_path << " from CCDB!"; + exit(1); + } + } + } + } + model_pairs = isAlien[0] || isCCDB[0] ? local_names[0] : model_pairs; + model_compton = isAlien[1] || isCCDB[1] ? local_names[1] : model_compton; + nclxrate = isAlien[2] || isCCDB[2] ? local_names[2] : nclxrate; + auto generator = new o2::eventgen::GenTPCLoopers_debug(model_pairs, model_compton, "", "", scaler_pair, scaler_compton); + generator->SetRate(nclxrate, isPbPb, intrate); + // Adjust can be negative (-1 maximum) or positive to decrease or increase the number of loopers per orbit + generator->SetAdjust(adjust); + return generator; +} \ No newline at end of file diff --git a/MC/config/common/external/generator/generator_AMPT.C b/MC/config/common/external/generator/generator_AMPT.C new file mode 100644 index 000000000..2de19ffeb --- /dev/null +++ b/MC/config/common/external/generator/generator_AMPT.C @@ -0,0 +1,245 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "TRandom.h" +#include "TParticle.h" +#include "TString.h" +#include "Generators/Generator.h" +#include "Generators/GeneratorFileOrCmd.h" +#include "CommonUtils/FileSystemUtils.h" +#include "SimulationDataFormat/MCEventHeader.h" +#include "SimulationDataFormat/MCUtils.h" + +/// AMPT adapted external generator +/// +/// @author Marco Giacalone (marco.giacalone@cern.ch) +/// @date 09/26 +// The AMPT output (ana/ampt.dat) is converted directly into TParticles, without any HepMC step, as done in the past. +// Two modes are available: +// - "run" : the path is an AMPT input card (input.ampt). AMPT is started in the background in a dedicated +// working directory, where ana/ampt.dat is a named pipe, so events are streamed to O2 as soon as +// they are produced. NEVNT and the random seeds of the card are replaced by the macro. +// - "file" : the path is an existing ampt.dat file, whose events are read sequentially. +// All the particles are transported, including the spectator nucleons (needed by the ZDC). +// This could be changed in case it's actually not needed. To verify + +// o2-sim -g external --noGeant -n 2 --configFile ${O2DPG_MC_CONFIG_ROOT}/MC/config/common/ini/GeneratorAMPTPbPb536TeV.ini +// or, reading an existing AMPT output +// o2-sim -g external --noGeant -n 2 --configKeyValues "GeneratorExternal.fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/external/generator/generator_AMPT.C;GeneratorExternal.funcName=generateAMPT(\"/path/to/ampt.dat\",\"file\")" + +namespace o2 +{ +namespace eventgen +{ + +// Inheriting from GeneratorFileOrCmd as well to use the pipe mechanism +class GeneratorAMPT : public Generator, public GeneratorFileOrCmd +{ + public: + GeneratorAMPT(const std::string& path, bool runAMPT, unsigned int nEvents) : mPath(path) + { + setNEvents(nEvents); + if (runAMPT) { + // AMPT does NSEED = 2 * NSEED + 1 internally, hence the seed must stay below 2^30 + setSeed(gRandom->Integer(536870911) + 1); + mWorkDir = Form("ampt_%lu", mSeed); + // AMPT always reads a number from stdin (used as seed only when ihjsed = 11). If AMPT fails, + // the pipe is opened and closed by the shell, so the reader gets an end of file instead of hanging. + // The timeout avoids a shell blocked forever on the pipe when the reader is gone (this was seen if o2-sim crashed mid-run for some reason) + setCmd("cd " + mWorkDir + " && { echo 0 | \"$AMPT_ROOT/bin/ampt\" > ampt.log 2>&1 || timeout 60 sh -c ': > ana/ampt.dat'; }"); + } + } + ~GeneratorAMPT() override + { + stop(); + if (not mCmd.empty()) { + removeTemp(); + } + } + + Bool_t Init() override + { + if (mCmd.empty()) { + mInput.open(mPath); + } else { + if (!getenv("AMPT_ROOT")) { + LOG(fatal) << "AMPT_ROOT is not set, load the AMPT package"; + return false; + } + if (!prepareWorkDir() || !makeFifo() || !executeCmdLine(mCmd)) { + return false; + } + LOG(info) << "AMPT started in " << mWorkDir << " with seed " << mSeed << " for " << mNEvents << " events"; + mInput.open(mTemporary); // blocks until AMPT opens the pipe for writing + } + if (!mInput.is_open()) { + LOG(fatal) << "Cannot open AMPT output " << (mCmd.empty() ? mPath : mTemporary); + return false; + } + return Generator::Init(); + } + + // Reads the next event from ampt.dat: one header line with 11 fields followed by + // exactly nParticles lines with 9 fields (pdg px py pz m x y z t) + Bool_t generateEvent() override + { + std::string line; + if (!nextLine(line)) { + LOG(fatal) << "AMPT output ended after " << mEventCounter << " events (" << mNEvents << " requested)" + << (mCmd.empty() ? "" : ", see " + mWorkDir + "/ampt.log"); + return false; + } + std::istringstream header(line); + header >> mHeader.event >> mHeader.run >> mHeader.nParticles >> mHeader.b >> mHeader.nPartProj >> mHeader.nPartTarg >> mHeader.nElP >> mHeader.nInP >> mHeader.nElT >> mHeader.nInT >> mHeader.phiRP; + if (header.fail() || mHeader.nParticles < 0) { + LOG(fatal) << "Malformed AMPT event header: " << line; + return false; + } + mTracks.clear(); + mTracks.reserve(mHeader.nParticles); + for (int i = 0; i < mHeader.nParticles; ++i) { + AMPTTrack t; + if (!nextLine(line)) { + LOG(fatal) << "AMPT event " << mHeader.event << " is truncated: " << i << " out of " << mHeader.nParticles << " particles"; + return false; + } + std::istringstream particle(line); + particle >> t.pdg >> t.px >> t.py >> t.pz >> t.m; + if (particle.fail()) { + LOG(fatal) << "Malformed AMPT particle line: " << line; + return false; + } + mTracks.push_back(t); + } + mEventCounter++; + LOG(info) << "AMPT event " << mEventCounter << "/" << mNEvents << ": " << mHeader.nParticles << " particles, b = " << mHeader.b << " fm"; + return true; + } + + Bool_t importParticles() override + { + mParticles.clear(); + for (const auto& t : mTracks) { + // AMPT uses PDG codes, apart from (anti)deuterons + int pdg = std::abs(t.pdg) == 42 ? (t.pdg > 0 ? 1 : -1) * 1000010020 : t.pdg; + double e = std::sqrt(t.px * t.px + t.py * t.py + t.pz * t.pz + t.m * t.m); + // Freeze-out coordinates are in fm, hence negligible: particles are produced at the interaction vertex + TParticle particle(pdg, 1, -1, -1, -1, -1, t.px, t.py, t.pz, e, 0., 0., 0., 0.); + o2::mcutils::MCGenHelper::encodeParticleStatusAndTracking(particle, true); + mParticles.push_back(particle); + } + return true; + } + + void updateHeader(o2::dataformats::MCEventHeader* eventHeader) override + { + using Key = o2::dataformats::MCInfoKeys; + eventHeader->putInfo(Key::generator, "ampt"); + eventHeader->SetB(mHeader.b); + eventHeader->putInfo(Key::impactParameter, mHeader.b); + eventHeader->putInfo(Key::nPart, mHeader.nPartProj + mHeader.nPartTarg); + eventHeader->putInfo(Key::nPartProjectile, mHeader.nPartProj); + eventHeader->putInfo(Key::nPartTarget, mHeader.nPartTarg); + eventHeader->putInfo(Key::planeAngle, mHeader.phiRP); + // Participant nucleons from elastic and inelastic collisions in projectile and target + eventHeader->putInfo("ampt_NELP", mHeader.nElP); + eventHeader->putInfo("ampt_NINP", mHeader.nInP); + eventHeader->putInfo("ampt_NELT", mHeader.nElT); + eventHeader->putInfo("ampt_NINTHJ", mHeader.nInT); + } + + void stop() override + { + mInput.close(); + if (not mCmd.empty()) { + // AMPT exits by itself once all the events are written, otherwise it is terminated + terminateCmd(sStopGraceMillis); + } + } + + private: + struct AMPTHeader { + int event = 0, run = 0, nParticles = 0; + double b = 0.; + int nPartProj = 0, nPartTarg = 0, nElP = 0, nInP = 0, nElT = 0, nInT = 0; + double phiRP = 0.; + }; + struct AMPTTrack { + int pdg = 0; + double px = 0., py = 0., pz = 0., m = 0.; + }; + + // Creates the working directory with the edited card. ana/ampt.dat becomes the named pipe, while + // the outputs growing with the number of events and not needed here points to /dev/null + bool prepareWorkDir() + { + std::filesystem::create_directories(mWorkDir + "/ana"); + // AMPT reads the card sequentially, one value per line: lines are replaced by their position + const std::map replacements = { + {9, std::to_string(mNEvents) + "\t\t! NEVNT (set by generator_AMPT.C)"}, + {28, "0\t\t! ihjsed (set by generator_AMPT.C)"}, + {29, std::to_string(mSeed) + "\t\t! random seed for HIJING (set by generator_AMPT.C)"}, + {30, std::to_string(gRandom->Integer(536870911) + 1) + "\t\t! random seed for parton cascade (set by generator_AMPT.C)"}}; + std::ifstream src(mPath); + std::ofstream dst(mWorkDir + "/input.ampt"); + std::string line; + for (int lineNumber = 1; std::getline(src, line); ++lineNumber) { + auto replacement = replacements.find(lineNumber); + dst << (replacement != replacements.end() ? replacement->second : line) << "\n"; + } + for (const auto& file : {"zpc.dat", "npart-xy.dat"}) { + std::filesystem::remove(mWorkDir + "/ana/" + file); + std::filesystem::create_symlink("/dev/null", mWorkDir + "/ana/" + file); + } + // Named pipe used by makeFifo and removed by removeTemp + mTemporary = mWorkDir + "/ana/ampt.dat"; + mFileNames = {mTemporary}; + return true; + } + + bool nextLine(std::string& line) + { + while (std::getline(mInput, line)) { + if (line.find_first_not_of(" \t\r") != std::string::npos) { + return true; + } + } + return false; + } + + std::string mPath; + std::string mWorkDir; + std::ifstream mInput; + unsigned int mEventCounter = 0; + AMPTHeader mHeader; + std::vector mTracks; +}; + +} // namespace eventgen +} // namespace o2 + +// path : AMPT input card (mode "run") or existing ampt.dat file (mode "file") +// maxEvents : number of events for AMPT when the total number of events is not known (e.g. hyperloop) +FairGenerator* generateAMPT(std::string path, std::string mode = "run", int maxEvents = 2147483647) +{ + path = o2::utils::expandShellVarsInFileName(path); + if (mode != "run" && mode != "file") { + LOG(fatal) << "Unknown AMPT generator mode " << mode << ", use \"run\" or \"file\""; + return nullptr; + } + if (!std::filesystem::exists(path)) { + LOG(fatal) << "AMPT " << (mode == "run" ? "input card " : "output file ") << path << " does not exist"; + return nullptr; + } + unsigned int nEvents = o2::eventgen::Generator::getTotalNEvents(); + if (nEvents == 0) { + nEvents = maxEvents; + } + LOG(info) << "AMPT generator in \"" << mode << "\" mode using " << path; + return new o2::eventgen::GeneratorAMPT(path, mode == "run", nEvents); +} diff --git a/MC/config/common/external/generator/perfConf.json b/MC/config/common/external/generator/perfConf.json new file mode 100644 index 000000000..c3bdfae0b --- /dev/null +++ b/MC/config/common/external/generator/perfConf.json @@ -0,0 +1,23 @@ +{ + "generators": [ + { + "cocktail": [ + { + "name": "pythia8pp", + "config": "" + }, + { + "name": "external", + "config": { + "fileName": "${O2DPG_MC_CONFIG_ROOT}/MC/config/common/external/generator/performanceGenerator.C", + "funcName": "Generator_Performance()", + "iniFile": "" + } + } + ] + } + ], + "fractions": [ + 1 + ] +} diff --git a/MC/config/common/external/generator/performanceGenerator.C b/MC/config/common/external/generator/performanceGenerator.C new file mode 100644 index 000000000..d592d6f25 --- /dev/null +++ b/MC/config/common/external/generator/performanceGenerator.C @@ -0,0 +1,397 @@ +// External generator requested in https://its.cern.ch/jira/browse/O2-6235 +// for multidimensional performance studies +// Example usage: +// o2-sim -j 8 -o test -n 100 --seed 612 -g hybrid --configKeyValues "GeneratorHybrid.configFile=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/external/generator/perfConf.json" +namespace o2 +{ + namespace eventgen + { + + class GenPerf : public Generator + { + public: + GenPerf(float fraction = 0.03f, unsigned short int nsig = 100, unsigned short int tag = 1) + { + if (fraction == -1) { + LOG(info) << nsig << " Signal particles will be generated in each event"; + mNSig = nsig; + mFraction = -1.f; + } else if (fraction >= 0) { + LOG(info) << "Fraction based signal generation is enabled"; + LOG(info) << fraction << "*nUE tracks per event will be generated"; + mFraction = fraction; + } else { + LOG(fatal) << "Wrong fraction selected. Accepted values are:"; + LOG(fatal) << "\t -1 => fixed number of tracks per event"; + LOG(fatal) << ">=0 => fraction based signal generation over the number of UE tracks per event"; + exit(1); + } + initGenMap(); + if (genMap.find(tag) == genMap.end()) { + LOG(fatal) << "Wrong tag selected. Accepted values are:"; + for (const auto& [key, _] : genMap) { + LOG(fatal) << "\t" << key; + } + exit(1); + } else { + mTag = tag; + LOG(info) << "Generator with tag " << mTag << " is selected"; + } + LOG(info) << "Z0 decays are handled with Pythia8"; + mPythia = std::make_unique(); + // Turn off all event generation - we only want to decay our Z0 + mPythia->readString("ProcessLevel:all = off"); + // Disable standard event checks since we're manually building the event + mPythia->readString("Check:event = off"); + mPythia->init(); // Initialize + Generator::setTimeUnit(1.0); + Generator::setPositionUnit(1.0); + } + + Bool_t generateEvent() override + { + return kTRUE; + } + + Bool_t importParticles() override + { + mNUE = 0; + if ( mFraction != -1) { + // This line assumes that the current generator is run in a cocktail with another generator + // which is run before the current one in a sequential way + if (!mGenList) { + auto &hybridInstance = GeneratorHybrid::Instance(); + mGenList = &hybridInstance.getGenerators(); + } + if (!mGenList->empty()) { + mNUE = mGenList->front()->getParticles().size(); + LOG(debug) << "Number of tracks from UE is " << mNUE; + } + } + unsigned short nSig = (mFraction == -1) ? mNSig : std::lround(mFraction * mNUE); + LOG(debug) << "Generating additional " << nSig << " particles"; + for (int k = 0; k < nSig; k++){ + auto part = genMap[mTag](); + if(part.GetPdgCode() == 23) { + auto daughters = decayZ0(part); + for (auto &dau : daughters) + { + mParticles.push_back(dau); + } + } else { + mParticles.push_back(part); + } + } + return kTRUE; + } + + private: + float mFraction = 0.03f; // Fraction based generation + unsigned short int mNSig = 0; // Number of particles to generate + unsigned int mNUE = 0; // Number of tracks in the Underlying event + unsigned short int mTag = 1; // Tag to select the generation function + std::unique_ptr mPythia; // Pythia8 instance for particle decays not present in the physics list of Geant4 (like Z0) + const std::vector>* mGenList = nullptr; // Cached generators list + std::map> genMap; + UInt_t mGenID = 42; + + // This is performance test generator with uniform weighting for PDG + TParticle generateParticle0() + { + // 1. Get the singleton instances + TDatabasePDG *pdg = TDatabasePDG::Instance(); + // 2. Define the list of PDG codes + const int ncodes = 13; + const int pdgCodes[ncodes] = { + 310, // K0_s + 421, // D0 + 3122, // Lambda + -3122, // Anti-Lambda + 443, // J/psi + 13, // mu- + 22, // gamma + 23, // Z0 + 1, 2, 3, 4, 5 // Quarks: d, u, s, c, b (t-quark is 6, often excluded for kinematics) + }; + // 3. Randomly select and validate a PDG code + // TMath::Nint(gRandom->Rndm() * ncodes) selects an index from 0 to ncodes-1 safely. + int index = TMath::Nint(gRandom->Rndm() * (ncodes - 1)); + int pdgCode = pdgCodes[index]; + // Check if the particle exists and switch to antiparticle if needed + if (pdg->GetParticle(pdgCode) == nullptr) + { + if (pdg->GetParticle(-pdgCode) != nullptr) + { + pdgCode *= -1; // Use the negative code (antiparticle) + } + else + { + LOG(error) << "Error: PDG code " << pdgCode << " not found in TDatabasePDG. Using Muon (13)."; + pdgCode = 13; + } + } + // 4. Generate Kinematics (p_T, phi, eta) + float pt = 1 / (gRandom->Rndm()); // flat 1/pt distribution + float phi = gRandom->Rndm() * 2.0f * TMath::Pi(); + float eta = 3.0f * (gRandom->Rndm() - 0.5f); // eta from -1.5 to 1.5 + // Initial position (origin) + float xyz[3] = {0.0f, 0.0f, 0.0f}; + // if cosmic, you might want to randomize the vertex position + if (pdgCode == 13 || pdgCode == -13) + { + xyz[0] = (gRandom->Rndm() - 0.5) * 300.0f; // x from -100 to 100 cm + xyz[1] = (gRandom->Rndm() - 0.5) * 300.0f; // y from -100 to 100 cm + xyz[2] = 400; + pt = 1 / (gRandom->Rndm() + 0.01); + eta = gRandom->Gaus() * 0.2; + } + // + // Convert spherical coordinates (pt, phi, eta) to Cartesian (px, py, pz) + float pz = pt * TMath::SinH(eta); + float px = pt * TMath::Cos(phi); + float py = pt * TMath::Sin(phi); + // 5. Calculate Energy (E) from Mass (M) + TParticlePDG *particleInfo = pdg->GetParticle(pdgCode); + double mass = particleInfo ? particleInfo->Mass() : 0.1056; // Default to muon mass if lookup fails + double energy = TMath::Sqrt(px * px + py * py + pz * pz + mass * mass); + + // 6. Create and return the TParticle object by value + // TParticle(pdgCode, trackIndex, Mother, Daughter1, Daughter2, Px, Py, Pz, E, Vx, Vy, Vz, Time) + int status = -1; // Status code, -1 for undefined + // Set your custom performance generator ID (e.g., ID 42) + TParticle generatedParticle(pdgCode, status, -1, -1, -1, -1, px, py, pz, energy, xyz[0], xyz[1], xyz[2], 0.0); + generatedParticle.SetStatusCode(o2::mcgenstatus::MCGenStatusEncoding(generatedParticle.GetStatusCode(), 0).fullEncoding); + generatedParticle.SetUniqueID(mGenID); + if (pdgCode == 23) { + generatedParticle.SetBit(ParticleStatus::kToBeDone, false); // Force Z0 to be decayed by the transport + } else { + generatedParticle.SetBit(ParticleStatus::kToBeDone, // + o2::mcgenstatus::getHepMCStatusCode(generatedParticle.GetStatusCode()) == 1); + } + return generatedParticle; + } + + // Particle configuration for ALICE O2 performance testing + struct ParticleSpec + { + int pdgCode; + float fraction; // Relative probability for probe statistics + float pTScale; // Scales pt + }; + + // Optimized for rare probes (J/psi, D0, jets) with flat distributions + const std::vector g_particle_specs = { + // PDG | Fraction | pTScale + {22, 1.0f, 1.0f}, // Photon: High yield for PID/calo + {13, 1.f, 1.0f}, // Muon: Cosmic override applied + {-13, 1.f, 1.0f}, // Anti-muon + {23, 0.1f, 10.0f}, // Z0: Rare, + {310, 1.f, 1.0f}, // K0_s: Common hadron + {421, 0.2f, 1.5f}, // D0 + {443, 0.1f, 5.0f}, // J/psi: Boosted for candle + {3122, 0.5f, 1.0f}, // Lambda + {-3122, 0.5f, 1.0f}, // Anti-Lambda + {211, 1.0f, 1.0f}, // Pi+ + {-211, 1.0f, 1.0f}, // Pi-: + // + {21, 0.1f, 3.0f}, // Gluon: Jet proxy (status=11) + {1, 0.1f, 3.0f}, // d quark: Jet proxy + {-1, 0.1f, 3.0f}, // anti-d + {2, 0.1f, 3.0f}, // u quark: Jet proxy + {-2, 0.1f, 3.0f}, // anti-u + {3, 0.1f, 5.0f}, // s quark: Strange + {-3, 0.1f, 5.0f}, // anti-s + {4, 0.1f, 5.0f}, // c quark: Heavy flavor + {-4, 0.1f, 5.0f}, // anti-c + {5, 0.1f, 8.0f}, // b quark: Very hard + {-5, 0.1f, 8.0f} // anti-b + }; + + // pT bounds: Max pT ~5 TeV (ALICE Pb-Pb energy) + const float kMaxInvPt = 2.f; // Min pT = 0.5 GeV + const float kMinInvPt = 2e-4f; // Max pT = 5000 GeV (unscaled) + + // Check if particle is a parton (quark/gluon, status=11) + bool isParton(int& pdgCode) + { + int absCode = TMath::Abs(pdgCode); + return (absCode >= 1 && absCode <= 5) || absCode == 21; + } + + // Generator for flat distributions in pT, eta for calibration + TParticle generateParticle1() + { + TDatabasePDG *pdg = TDatabasePDG::Instance(); + // 1. Weighted Random Selection + static float totalWeight = 0.0f; + if (totalWeight == 0.0f) + { + totalWeight = std::accumulate(g_particle_specs.begin(), g_particle_specs.end(), 0.0f, + [](float sum, const ParticleSpec &spec) + { return sum + spec.fraction; }); + } + float randVal = gRandom->Rndm() * totalWeight; + float cumulativeWeight = 0.0f; + const ParticleSpec *selectedSpec = nullptr; + for (const auto &spec : g_particle_specs) + { + cumulativeWeight += spec.fraction; + if (randVal <= cumulativeWeight) + { + selectedSpec = &spec; + break; + } + } + if (!selectedSpec) + selectedSpec = &g_particle_specs.back(); + int pdgCode = selectedSpec->pdgCode; + float pTScale = selectedSpec->pTScale; + // 2. PDG Validation + if (!pdg->GetParticle(pdgCode)) + { + if (pdg->GetParticle(-pdgCode)) + pdgCode *= -1; + else + { + LOG(error) << "Error: PDG " << pdgCode << " not found. Using muon (13).\n"; + pdgCode = 13; + pTScale = 1.0f; + } + } + // 3. Status: 11 for partons (jets), 1 for final-state + int status = isParton(pdgCode) ? 11 : 1; + // 4. Kinematics (flat 1/pT, max ~5000 GeV / pTScale) + float inv_pt = gRandom->Rndm() * (kMaxInvPt - kMinInvPt) + kMinInvPt; + float pt = 1.0f / inv_pt; + float phi = gRandom->Rndm() * 2.0f * TMath::Pi(); + float eta = gRandom->Rndm() * 3.0f - 1.5f; // ALICE TPC: -1.5 to 1.5 + // Vertex: Delta (embedding handles smearing) + float xyz[3] = {0.0f, 0.0f, 0.0f}; + // 5. Cosmic Muon Override + if (TMath::Abs(pdgCode) == 13) + { + xyz[0] = (gRandom->Rndm() - 0.5f) * 300.0f; + xyz[1] = (gRandom->Rndm() - 0.5f) * 300.0f; + xyz[2] = 400.0f; + inv_pt = (gRandom->Rndm() + 0.01f) / pTScale; // Apply pTScale + pt = 1.0f / inv_pt; + eta = TMath::Max(-4.0, TMath::Min(4.0, gRandom->Gaus(0.0, 0.2))); + status = 1; + } + // 6. Momentum and Energy + float pz = pt * TMath::SinH(eta); + float px = pt * TMath::Cos(phi); + float py = pt * TMath::Sin(phi); + TParticlePDG *particleInfo = pdg->GetParticle(pdgCode); + double mass = particleInfo ? particleInfo->Mass() : 0.1056; + double energy = TMath::Sqrt(px * px + py * py + pz * pz + mass * mass); + // 7. TParticle Creation (quarks/gluons need fragmentation in O2) + TParticle generatedParticle(pdgCode, status, -1, -1, -1, -1, px, py, pz, energy, xyz[0], xyz[1], xyz[2], 0.0); + generatedParticle.SetStatusCode(o2::mcgenstatus::MCGenStatusEncoding(generatedParticle.GetStatusCode(), 0).fullEncoding); + generatedParticle.SetUniqueID(mGenID); + if (pdgCode == 23) { + generatedParticle.SetBit(ParticleStatus::kToBeDone, false); + // Z0 will follow another decay procedure + } else { + generatedParticle.SetBit(ParticleStatus::kToBeDone, // + o2::mcgenstatus::getHepMCStatusCode(generatedParticle.GetStatusCode()) == 1); + } + return generatedParticle; + } + + void initGenMap() + { + genMap[0] = [this]() + { return generateParticle0(); }; + genMap[1] = [this]() + { return generateParticle1(); }; + } + + std::vector decayZ0(TParticle &z0) + { + std::vector subparts; + auto &event = mPythia->event; + // Reset event record for new decay + event.reset(); + // Add the Z0 particle to the event record + // Arguments: id, status, mother1, mother2, daughter1, daughter2, + // col, acol, px, py, pz, e, m, scale, pol + // Status code: 91 = incoming particles (needed for proper decay handling) + int iZ0 = event.append(23, 91, 0, 0, 0, 0, 0, 0, + z0.Px(), z0.Py(), z0.Pz(), z0.Energy(), z0.GetMass()); + // Set production vertex + event[iZ0].vProd(z0.Vx(), z0.Vy(), z0.Vz(), 0.0); + // Forcing decay by calling hadron level function + if (!mPythia->forceHadronLevel()) + { + cout << "Warning: Z0 decay failed!" << endl; + } + for (int j = 0; j < event.size(); ++j) + { + const Pythia8::Particle &p = event[j]; + if (p.id() == 23) // PDG code for Z0 + { + // Push Z0 itself + subparts.push_back(TParticle(p.id(), p.status(), + -1, -1, -1, -1, + p.px(), p.py(), + p.pz(), p.e(), + z0.Vx(), z0.Vy(), z0.Vz(), 0.0)); + subparts.back().SetStatusCode(o2::mcgenstatus::MCGenStatusEncoding(p.status(), 0).fullEncoding); + subparts.back().SetUniqueID(mGenID); + subparts.back().SetBit(ParticleStatus::kToBeDone, false); + // Navigate through intermediate Z0s to find final decay products + int iZ0 = j; + while (event[iZ0].daughter1() != 0 && + event[event[iZ0].daughter1()].id() == 23) + { + iZ0 = event[iZ0].daughter1(); + } + // Recursively collect all final-state descendants + std::function collectAllDescendants = [&](int idx) + { + const Pythia8::Particle &particle = event[idx]; + subparts.push_back(TParticle(particle.id(), particle.status(), + -1, -1, -1, -1, + particle.px(), particle.py(), + particle.pz(), particle.e(), + p.xProd(), p.yProd(), p.zProd(), p.tProd())); + subparts.back().SetStatusCode(o2::mcgenstatus::MCGenStatusEncoding(particle.status(), 0).fullEncoding); + subparts.back().SetUniqueID(mGenID + 1); + subparts.back().SetBit(ParticleStatus::kToBeDone, + o2::mcgenstatus::getHepMCStatusCode(subparts.back().GetStatusCode()) == 1); + // Not final-state, recurse through daughters + if (!particle.isFinal()) + { + int d1 = particle.daughter1(); + int d2 = particle.daughter2(); + if (d1 > 0) + { + for (int k = d1; k <= d2; ++k) + { + collectAllDescendants(k); + } + } + } + }; + // Start collecting from the final Z0 + collectAllDescendants(iZ0); + break; // Found and processed the Z0 + } + } + return subparts; + } + }; + + } // namespace eventgen +} // namespace o2 + +// Performance test generator +// fraction == -1 enables the fixed number of signal particles per event (nsig) +// tag selects the generator type to be used +FairGenerator * +Generator_Performance(const float fraction = 0.03f, const unsigned short int nsig = 100, unsigned short int tag = 1) +{ + auto generator = new o2::eventgen::GenPerf(fraction, nsig, tag); + return generator; +} \ No newline at end of file diff --git a/MC/config/common/ini/GeneratorAMPTOO536TeV.ini b/MC/config/common/ini/GeneratorAMPTOO536TeV.ini new file mode 100644 index 000000000..948c7a356 --- /dev/null +++ b/MC/config/common/ini/GeneratorAMPTOO536TeV.ini @@ -0,0 +1,6 @@ +#NEV_TEST> 2 +### AMPT OO 5.36 TeV (String Melting), converted directly to MCTracks without HepMC +### Use generateAMPT("/path/to/ampt.dat", "file") to read an existing AMPT output instead +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/external/generator/generator_AMPT.C +funcName=generateAMPT("${O2DPG_MC_CONFIG_ROOT}/MC/config/common/ampt/generator/OO_536TeV_AMPT.ampt") diff --git a/MC/config/common/ini/GeneratorAMPTPbPb536TeV.ini b/MC/config/common/ini/GeneratorAMPTPbPb536TeV.ini new file mode 100644 index 000000000..2900caf2b --- /dev/null +++ b/MC/config/common/ini/GeneratorAMPTPbPb536TeV.ini @@ -0,0 +1,6 @@ +#NEV_TEST> 2 +### AMPT Pb-Pb 5.36 TeV (String Melting), converted directly to MCTracks without HepMC +### Use generateAMPT("/path/to/ampt.dat", "file") to read an existing AMPT output instead +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/external/generator/generator_AMPT.C +funcName=generateAMPT("${O2DPG_MC_CONFIG_ROOT}/MC/config/common/ampt/generator/PbPb_536TeV_AMPT.ampt") diff --git a/MC/config/common/ini/GeneratorAMPTpp536TeV.ini b/MC/config/common/ini/GeneratorAMPTpp536TeV.ini new file mode 100644 index 000000000..b59152f81 --- /dev/null +++ b/MC/config/common/ini/GeneratorAMPTpp536TeV.ini @@ -0,0 +1,3 @@ +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/external/generator/generator_AMPT.C +funcName=generateAMPT("${O2DPG_MC_CONFIG_ROOT}/MC/config/common/ampt/generator/pp_536TeV_AMPT.ampt") diff --git a/MC/config/common/ini/GeneratorEPOS4NeNe536TeV.ini b/MC/config/common/ini/GeneratorEPOS4NeNe536TeV.ini new file mode 100644 index 000000000..efc2b466a --- /dev/null +++ b/MC/config/common/ini/GeneratorEPOS4NeNe536TeV.ini @@ -0,0 +1,12 @@ +#NEV_TEST> 10 +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/examples/epos4/generator_EPOS4.C +funcName=generateEPOS4("${O2DPG_MC_CONFIG_ROOT}/MC/config/common/epos4/generator/NeNe_536TeV_EPOS4.optns", 2147483647) + +[GeneratorFileOrCmd] +cmd=${O2DPG_MC_CONFIG_ROOT}/MC/config/examples/epos4/epos.sh +bMaxSwitch=none + +# Set to version 2 if EPOS4.0.0 is used +[HepMC] +version=3 diff --git a/MC/config/common/ini/GeneratorEPOS4OO536TeV.ini b/MC/config/common/ini/GeneratorEPOS4OO536TeV.ini new file mode 100644 index 000000000..0daaa3eb3 --- /dev/null +++ b/MC/config/common/ini/GeneratorEPOS4OO536TeV.ini @@ -0,0 +1,12 @@ +#NEV_TEST> 10 +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/examples/epos4/generator_EPOS4.C +funcName=generateEPOS4("${O2DPG_MC_CONFIG_ROOT}/MC/config/common/epos4/generator/OO_536TeV_EPOS4.optns", 2147483647) + +[GeneratorFileOrCmd] +cmd=${O2DPG_MC_CONFIG_ROOT}/MC/config/examples/epos4/epos.sh +bMaxSwitch=none + +# Set to version 2 if EPOS4.0.0 is used +[HepMC] +version=3 diff --git a/MC/config/common/ini/GeneratorEPOS4pO962TeV.ini b/MC/config/common/ini/GeneratorEPOS4pO962TeV.ini new file mode 100644 index 000000000..23afdd552 --- /dev/null +++ b/MC/config/common/ini/GeneratorEPOS4pO962TeV.ini @@ -0,0 +1,12 @@ +#NEV_TEST> 10 +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/examples/epos4/generator_EPOS4.C +funcName=generateEPOS4("${O2DPG_MC_CONFIG_ROOT}/MC/config/common/epos4/generator/pO_962TeV_EPOS4.optns", 2147483647) + +[GeneratorFileOrCmd] +cmd=${O2DPG_MC_CONFIG_ROOT}/MC/config/examples/epos4/epos.sh +bMaxSwitch=none + +# Set to version 2 if EPOS4.0.0 is used +[HepMC] +version=3 diff --git a/MC/config/common/ini/GeneratorEPOS4pO962TeV_hydro.ini b/MC/config/common/ini/GeneratorEPOS4pO962TeV_hydro.ini new file mode 100644 index 000000000..aff71abaa --- /dev/null +++ b/MC/config/common/ini/GeneratorEPOS4pO962TeV_hydro.ini @@ -0,0 +1,13 @@ +#NEV_TEST> 2 +#---> GeneratorEPOS4pO962TeV +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/examples/epos4/generator_EPOS4.C +funcName=generateEPOS4("${O2DPG_MC_CONFIG_ROOT}/MC/config/common/epos4/generator/pO_962TeV_EPOS4_hydro.optns", 2147483647) + +[GeneratorFileOrCmd] +cmd=${O2DPG_MC_CONFIG_ROOT}/MC/config/examples/epos4/epos.sh +bMaxSwitch=none + +# Set to version 2 if EPOS4.0.0 is used +[HepMC] +version=3 diff --git a/MC/config/common/ini/GeneratorHGPythia8PbPb536TeV.ini b/MC/config/common/ini/GeneratorHGPythia8PbPb536TeV.ini new file mode 100644 index 000000000..ea1e57c76 --- /dev/null +++ b/MC/config/common/ini/GeneratorHGPythia8PbPb536TeV.ini @@ -0,0 +1,8 @@ +#NEV_TEST> 5 +# HG-PYTHIA (Glauber + PYTHIA 8 pp collisions) Pb-Pb at sqrt(s_NN) = 5.36 TeV, minimum bias +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/external/generator/GeneratorHGPythia8.C +funcName=generateHGPythia8(208, 208) + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/generator/pythia8_inel_536.cfg diff --git a/MC/config/common/ini/GeneratorLoopersFlatFile.ini b/MC/config/common/ini/GeneratorLoopersFlatFile.ini new file mode 100644 index 000000000..4143989ab --- /dev/null +++ b/MC/config/common/ini/GeneratorLoopersFlatFile.ini @@ -0,0 +1,8 @@ +# TPC loopers injector using fit to calculate reference loopers number per orbit. File with fit function is pulled from the CCDB. +# Three additional parameters are available in the function: (isPbPb = true, intRate = -1, adjust = 0.) +# isPbPb and intRate must be set in case the collision system is not PbPb and the IR is not taken from collisioncontext, while adjust can be used to decrease/increase +# the number of loopers per orbit obtained from the reference (e.g. -0.1 reduces the loopers by 10%) +#---> GeneratorTPCloopers +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/common/external/generator/TPCLoopers.C +funcName = Generator_TPCLoopersOrbitRef("ccdb://Users/m/mgiacalo/WGAN_ExtGenPair", "ccdb://Users/m/mgiacalo/WGAN_ExtGenCompton", "${O2DPG_MC_CONFIG_ROOT}/MC/config/common/TPCloopers/ScalerPairParams.json", "${O2DPG_MC_CONFIG_ROOT}/MC/config/common/TPCloopers/ScalerComptonParams.json","ccdb://Users/m/mgiacalo/ClustersTrackRatio") diff --git a/MC/config/common/ini/GeneratorLoopersFlatGas.ini b/MC/config/common/ini/GeneratorLoopersFlatGas.ini new file mode 100644 index 000000000..68c7648ae --- /dev/null +++ b/MC/config/common/ini/GeneratorLoopersFlatGas.ini @@ -0,0 +1,5 @@ +# TPC loopers injector +#---> GeneratorTPCloopers +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/common/external/generator/TPCLoopers.C +funcName = Generator_TPCLoopersFlat("ccdb://Users/m/mgiacalo/WGAN_ExtGenPair", "ccdb://Users/m/mgiacalo/WGAN_ExtGenCompton", "${O2DPG_MC_CONFIG_ROOT}/MC/config/common/TPCloopers/ScalerPairParams.json", "${O2DPG_MC_CONFIG_ROOT}/MC/config/common/TPCloopers/ScalerComptonParams.json") \ No newline at end of file diff --git a/MC/config/common/ini/GeneratorPerformance.ini b/MC/config/common/ini/GeneratorPerformance.ini new file mode 100644 index 000000000..c4a4efb03 --- /dev/null +++ b/MC/config/common/ini/GeneratorPerformance.ini @@ -0,0 +1,4 @@ +# Performance generator test using hybrid configuration with Pythia8 +# underlying event generator +[GeneratorHybrid] +configFile = ${O2DPG_MC_CONFIG_ROOT}/MC/config/common/external/generator/perfConf.json diff --git a/MC/config/common/ini/GeneratorPerformanceFix.ini b/MC/config/common/ini/GeneratorPerformanceFix.ini new file mode 100644 index 000000000..7c0ce5f0d --- /dev/null +++ b/MC/config/common/ini/GeneratorPerformanceFix.ini @@ -0,0 +1,8 @@ +# Test performance generator for multidimensional studies using fixed number of signal particles per event +# Parameters are in order: fraction of signal particles, fixed number of signal particles per event, tag to select the generator type +# Setting fraction = -1 enables the fixed number of signal particles per event (nsig). +# An hybrid configuration JSON file is provided in ${O2DPG_MC_CONFIG_ROOT}/MC/config/common/external/generator/perfConf.json to run the generator +# in fraction based mode +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/common/external/generator/performanceGenerator.C +funcName = Generator_Performance(-1, 100, 1) diff --git a/MC/config/common/ini/GeneratorTPCloopers.ini b/MC/config/common/ini/GeneratorTPCloopers.ini new file mode 100644 index 000000000..276a086c8 --- /dev/null +++ b/MC/config/common/ini/GeneratorTPCloopers.ini @@ -0,0 +1,4 @@ +# Example of tpc loopers generator with a poisson distribution of pairs and gaussian distribution of compton electrons +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/common/external/generator/TPCLoopers.C +funcName = Generator_TPCLoopers("ccdb://Users/m/mgiacalo/WGAN_ExtGenPair", "ccdb://Users/m/mgiacalo/WGAN_ExtGenCompton", "${O2DPG_MC_CONFIG_ROOT}/MC/config/common/TPCloopers/poisson_params.csv", "${O2DPG_MC_CONFIG_ROOT}/MC/config/common/TPCloopers/gaussian_params.csv", "${O2DPG_MC_CONFIG_ROOT}/MC/config/common/TPCloopers/ScalerPairParams.json", "${O2DPG_MC_CONFIG_ROOT}/MC/config/common/TPCloopers/ScalerComptonParams.json") \ No newline at end of file diff --git a/MC/config/common/ini/GeneratorTPCloopers_fixNPairs.ini b/MC/config/common/ini/GeneratorTPCloopers_fixNPairs.ini new file mode 100644 index 000000000..4223ecf40 --- /dev/null +++ b/MC/config/common/ini/GeneratorTPCloopers_fixNPairs.ini @@ -0,0 +1,6 @@ +# Example of tpc loopers generator with a fixed number of pairs and compton electrons (10) +# Multiplier values are ignored in this case, but kept to 1 for consistency +#---> GeneratorTPCloopers +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/common/external/generator/TPCLoopers.C +funcName = Generator_TPCLoopers("ccdb://Users/m/mgiacalo/WGAN_ExtGenPair", "ccdb://Users/m/mgiacalo/WGAN_ExtGenCompton", "", "", "${O2DPG_MC_CONFIG_ROOT}/MC/config/common/TPCloopers/ScalerPairParams.json", "${O2DPG_MC_CONFIG_ROOT}/MC/config/common/TPCloopers/ScalerComptonParams.json",{1.,1.}, 10,10) diff --git a/MC/config/common/ini/basic.ini b/MC/config/common/ini/basic.ini index 9de066840..f55b2acdf 100644 --- a/MC/config/common/ini/basic.ini +++ b/MC/config/common/ini/basic.ini @@ -10,4 +10,4 @@ width[2]=6.0 ### The setup uses the base configuration of the decayer which is loaded from the file specified by config[0]. [DecayerPythia8] -config[0] = ${O2DPG_ROOT}/MC/config/common/pythia8/decayer/base.cfg +config[0] = ${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/decayer/base.cfg diff --git a/MC/config/common/ini/pythia8_NeNe_536.ini b/MC/config/common/ini/pythia8_NeNe_536.ini new file mode 100644 index 000000000..c279dd980 --- /dev/null +++ b/MC/config/common/ini/pythia8_NeNe_536.ini @@ -0,0 +1,9 @@ +[Diamond] +width[2]=6.0 + +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator_pythia8_ALICE3.C +funcName=generator_pythia8_ALICE3() + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/generator/pythia8_NeNe_536.cfg diff --git a/MC/config/common/ini/pythia8_OO_536.ini b/MC/config/common/ini/pythia8_OO_536.ini new file mode 100644 index 000000000..ade15add1 --- /dev/null +++ b/MC/config/common/ini/pythia8_OO_536.ini @@ -0,0 +1,9 @@ +[Diamond] +width[2]=6.0 + +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator_pythia8_ALICE3.C +funcName=generator_pythia8_ALICE3() + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/generator/pythia8_OO_536.cfg diff --git a/MC/config/common/ini/pythia8_OO_rescattering_536.ini b/MC/config/common/ini/pythia8_OO_rescattering_536.ini new file mode 100644 index 000000000..a0f2fc2e5 --- /dev/null +++ b/MC/config/common/ini/pythia8_OO_rescattering_536.ini @@ -0,0 +1,9 @@ +[Diamond] +width[2]=6.0 + +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator_pythia8_ALICE3.C +funcName=generator_pythia8_ALICE3() + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/generator/pythia8_OO_rescattering_536.cfg diff --git a/MC/config/common/ini/pythia8_PbPb_rescattering_536.ini b/MC/config/common/ini/pythia8_PbPb_rescattering_536.ini new file mode 100644 index 000000000..1ed1278ef --- /dev/null +++ b/MC/config/common/ini/pythia8_PbPb_rescattering_536.ini @@ -0,0 +1,10 @@ +#NEV_TEST> 1 +[Diamond] +width[2]=6.0 + +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator_pythia8_ALICE3.C +funcName=generator_pythia8_ALICE3() + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/generator/pythia8_PbPb_rescattering_536.cfg diff --git a/MC/config/common/ini/pythia8_inel_difftuned.ini b/MC/config/common/ini/pythia8_inel_difftuned.ini new file mode 100644 index 000000000..65ff9b972 --- /dev/null +++ b/MC/config/common/ini/pythia8_inel_difftuned.ini @@ -0,0 +1,2 @@ +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/generator/pythia8_inel_difftuned.cfg diff --git a/MC/config/common/ini/pythia8_pO_961.ini b/MC/config/common/ini/pythia8_pO_961.ini new file mode 100644 index 000000000..db8ccff48 --- /dev/null +++ b/MC/config/common/ini/pythia8_pO_961.ini @@ -0,0 +1,9 @@ +[Diamond] +width[2]=6.0 + +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/pythia8/generator_pythia8_ALICE3.C +funcName=generator_pythia8_ALICE3() + +[GeneratorPythia8] +config=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/generator/pythia8_pO_961.cfg diff --git a/MC/config/common/ini/tests/GeneratorAMPTOO536TeV.C b/MC/config/common/ini/tests/GeneratorAMPTOO536TeV.C new file mode 100644 index 000000000..c48f72d63 --- /dev/null +++ b/MC/config/common/ini/tests/GeneratorAMPTOO536TeV.C @@ -0,0 +1,69 @@ +int External() +{ + std::string path{"o2sim_Kine.root"}; + // Check that file exists, can be opened and has the correct tree + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + auto tree = (TTree*)file.Get("o2sim"); + if (!tree) { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + o2::dataformats::MCEventHeader* eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + // Check if there are 2 events, as simulated in the o2dpg-test + auto nEvents = tree->GetEntries(); + if (nEvents != 2) { + std::cerr << "Expected 2 events, got " << nEvents << "\n"; + return 1; + } + // Beam momentum per nucleon for OO at sqrt(s_NN) = 5.36 TeV + const double beamMomentum = std::sqrt(2680. * 2680. - 0.938 * 0.938); + for (Long64_t i = 0; i < nEvents; ++i) { + tree->GetEntry(i); + if (tracks->empty()) { + std::cerr << "Empty entry found at event " << i << "\n"; + return 1; + } + // Check the heavy-ion information of the header + bool isValid = false; + auto nPart = eventHeader->getInfo(o2::dataformats::MCInfoKeys::nPart, isValid); + if (!isValid || nPart <= 0) { + std::cerr << "Event " << i << " has no valid number of participants\n"; + return 1; + } + if (eventHeader->GetB() < 0. || eventHeader->GetB() > 20.) { + std::cerr << "Event " << i << " has impact parameter " << eventHeader->GetB() << " outside the [0, 20] fm range\n"; + return 1; + } + // Spectator nucleons (pT = 0, beam momentum) must be kept for both beams. + // AMPT writes momenta with 4 decimals, hence the tolerance + int nSpectatorsA = 0, nSpectatorsC = 0; + for (const auto& track : *tracks) { + if (track.GetPdgCode() != 2212 && track.GetPdgCode() != 2112) { + continue; + } + if (track.GetPt() == 0. && std::abs(std::abs(track.GetStartVertexMomentumZ()) - beamMomentum) < 1e-2) { + (track.GetStartVertexMomentumZ() > 0 ? nSpectatorsA : nSpectatorsC)++; + } + } + // The total number of nucleons in each nucleus must be conserved + if (nSpectatorsA + nSpectatorsC + nPart > 2 * 208) { + std::cerr << "Event " << i << " has more nucleons than the colliding nuclei\n"; + return 1; + } + if (nSpectatorsA == 0 || nSpectatorsC == 0) { + std::cerr << "Event " << i << " has no spectator nucleons (A side: " << nSpectatorsA << ", C side: " << nSpectatorsC << ")\n"; + return 1; + } + std::cout << "Event " << i << ": " << tracks->size() << " tracks, b = " << eventHeader->GetB() << " fm, Npart = " << nPart + << ", spectators A/C = " << nSpectatorsA << "/" << nSpectatorsC << "\n"; + } + return 0; +} diff --git a/MC/config/common/ini/tests/GeneratorAMPTPbPb536TeV.C b/MC/config/common/ini/tests/GeneratorAMPTPbPb536TeV.C new file mode 100644 index 000000000..1d68673f4 --- /dev/null +++ b/MC/config/common/ini/tests/GeneratorAMPTPbPb536TeV.C @@ -0,0 +1,69 @@ +int External() +{ + std::string path{"o2sim_Kine.root"}; + // Check that file exists, can be opened and has the correct tree + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + auto tree = (TTree*)file.Get("o2sim"); + if (!tree) { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + o2::dataformats::MCEventHeader* eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + // Check if there are 2 events, as simulated in the o2dpg-test + auto nEvents = tree->GetEntries(); + if (nEvents != 2) { + std::cerr << "Expected 2 events, got " << nEvents << "\n"; + return 1; + } + // Beam momentum per nucleon for Pb-Pb at sqrt(s_NN) = 5.36 TeV + const double beamMomentum = std::sqrt(2680. * 2680. - 0.938 * 0.938); + for (Long64_t i = 0; i < nEvents; ++i) { + tree->GetEntry(i); + if (tracks->empty()) { + std::cerr << "Empty entry found at event " << i << "\n"; + return 1; + } + // Check the heavy-ion information of the header + bool isValid = false; + auto nPart = eventHeader->getInfo(o2::dataformats::MCInfoKeys::nPart, isValid); + if (!isValid || nPart <= 0) { + std::cerr << "Event " << i << " has no valid number of participants\n"; + return 1; + } + if (eventHeader->GetB() < 0. || eventHeader->GetB() > 20.) { + std::cerr << "Event " << i << " has impact parameter " << eventHeader->GetB() << " outside the [0, 20] fm range\n"; + return 1; + } + // Spectator nucleons (pT = 0, beam momentum) must be kept for both beams. + // AMPT writes momenta with 4 decimals, hence the tolerance + int nSpectatorsA = 0, nSpectatorsC = 0; + for (const auto& track : *tracks) { + if (track.GetPdgCode() != 2212 && track.GetPdgCode() != 2112) { + continue; + } + if (track.GetPt() == 0. && std::abs(std::abs(track.GetStartVertexMomentumZ()) - beamMomentum) < 1e-2) { + (track.GetStartVertexMomentumZ() > 0 ? nSpectatorsA : nSpectatorsC)++; + } + } + // The total number of nucleons in each nucleus must be conserved + if (nSpectatorsA + nSpectatorsC + nPart > 2 * 208) { + std::cerr << "Event " << i << " has more nucleons than the colliding nuclei\n"; + return 1; + } + if (nSpectatorsA == 0 || nSpectatorsC == 0) { + std::cerr << "Event " << i << " has no spectator nucleons (A side: " << nSpectatorsA << ", C side: " << nSpectatorsC << ")\n"; + return 1; + } + std::cout << "Event " << i << ": " << tracks->size() << " tracks, b = " << eventHeader->GetB() << " fm, Npart = " << nPart + << ", spectators A/C = " << nSpectatorsA << "/" << nSpectatorsC << "\n"; + } + return 0; +} diff --git a/MC/config/common/ini/tests/GeneratorAMPTpp536TeV.C b/MC/config/common/ini/tests/GeneratorAMPTpp536TeV.C new file mode 100644 index 000000000..78a31725e --- /dev/null +++ b/MC/config/common/ini/tests/GeneratorAMPTpp536TeV.C @@ -0,0 +1,35 @@ +int External() +{ + std::string path{"o2sim_Kine.root"}; + // Check that file exists, can be opened and has the correct tree + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + auto tree = (TTree*)file.Get("o2sim"); + if (!tree) { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + o2::dataformats::MCEventHeader* eventHeader = nullptr; + tree->SetBranchAddress("MCEventHeader.", &eventHeader); + + // Check if there are 100 events, as simulated in the o2dpg-test + auto nEvents = tree->GetEntries(); + if (nEvents != 100) { + std::cerr << "Expected 100 events, got " << nEvents << "\n"; + return 1; + } + // Beam momentum per nucleon for Pb-Pb at sqrt(s_NN) = 5.36 TeV + for (Long64_t i = 0; i < nEvents; ++i) { + tree->GetEntry(i); + if (tracks->empty()) { + std::cerr << "Empty entry found at event " << i << "\n"; + return 1; + } + } + return 0; +} diff --git a/MC/config/common/ini/tests/GeneratorEPOS4NeNe536TeV.C b/MC/config/common/ini/tests/GeneratorEPOS4NeNe536TeV.C new file mode 100644 index 000000000..24e857dbc --- /dev/null +++ b/MC/config/common/ini/tests/GeneratorEPOS4NeNe536TeV.C @@ -0,0 +1,78 @@ +int External() +{ + std::string path{"o2sim_Kine.root"}; + + // Check that file exists, can be opened and has the correct tree + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) + { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree*)file.Get("o2sim"); + if (!tree) + { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + // Check if all events are filled + auto nEvents = tree->GetEntries(); + for (Long64_t i = 0; i < nEvents; ++i) + { + tree->GetEntry(i); + if (tracks->empty()) + { + std::cerr << "Empty entry found at event " << i << "\n"; + return 1; + } + } + + // Check if there are 10 events, as customarily set in the ini file + // Heavy-ion collisions with hydro and hadronic cascade are very slow to simulate + if (nEvents != 10) + { + std::cerr << "Expected 10 events, got " << nEvents << "\n"; + return 1; + } + + // ---- Neon-Neon parameters ---- + constexpr int kNeonPDG = 1000100200; // Ne-20 ion + constexpr double kEnucleon = 5360.; // GeV per nucleon + constexpr int kA = 20; // Neon mass number + constexpr double kNeonEnergy = kA * kEnucleon / 2.0; // beam energy in GeV + + // Check if each event has two neon ions at expected energy + for (int i = 0; i < nEvents; i++) + { + tree->GetEntry(i); + int count = 0; + + for (int idxMCTrack = 0; idxMCTrack < tracks->size(); ++idxMCTrack) + { + auto track = tracks->at(idxMCTrack); + double energy = track.GetEnergy(); + + // 50 MeV tolerance (floating point safety) + if (std::abs(energy - kNeonEnergy) < 5e-2 && + track.GetPdgCode() == kNeonPDG) + { + count++; + } + } + + if (count < 2) + { + std::cerr << "Event " << i + << " has less than 2 neon ions at " + << kNeonEnergy << " GeV\n"; + return 1; + } + } + + return 0; +} diff --git a/MC/config/common/ini/tests/GeneratorEPOS4OO536TeV.C b/MC/config/common/ini/tests/GeneratorEPOS4OO536TeV.C new file mode 100644 index 000000000..1f26e1962 --- /dev/null +++ b/MC/config/common/ini/tests/GeneratorEPOS4OO536TeV.C @@ -0,0 +1,78 @@ +int External() +{ + std::string path{"o2sim_Kine.root"}; + + // Check that file exists, can be opened and has the correct tree + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) + { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) + { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + // Check if all events are filled + auto nEvents = tree->GetEntries(); + for (Long64_t i = 0; i < nEvents; ++i) + { + tree->GetEntry(i); + if (tracks->empty()) + { + std::cerr << "Empty entry found at event " << i << "\n"; + return 1; + } + } + + // Check if there are 10 events, as customly set in the ini file + // Heavy-ion collisions with hydro and hadronic cascade are very slow to simulate + if (nEvents != 10) + { + std::cerr << "Expected 10 event, got " << nEvents << "\n"; + return 1; + } + + // ---- Oxygen-Oxygen parameters ---- + constexpr int kOxygenPDG = 1000080160; // O-16 ion + constexpr double kEnucleon = 5360.; // GeV per nucleon + constexpr int kA = 16; // Oxygen mass number + constexpr double kOxygenEnergy = kA * kEnucleon / 2.0; // 85760 / 2 GeV + + // Check if each event has two oxygen ions at expected energy + for (int i = 0; i < nEvents; i++) + { + tree->GetEntry(i); + int count = 0; + + for (int idxMCTrack = 0; idxMCTrack < tracks->size(); ++idxMCTrack) + { + auto track = tracks->at(idxMCTrack); + double energy = track.GetEnergy(); + + // 50 MeV tolerance (floating point safety) + if (std::abs(energy - kOxygenEnergy) < 5e-2 && + track.GetPdgCode() == kOxygenPDG) + { + count++; + } + } + + if (count < 2) + { + std::cerr << "Event " << i + << " has less than 2 oxygen ions at " + << kOxygenEnergy << " GeV\n"; + return 1; + } + } + + return 0; +} diff --git a/MC/config/common/ini/tests/GeneratorEPOS4pO962TeV.C b/MC/config/common/ini/tests/GeneratorEPOS4pO962TeV.C new file mode 100644 index 000000000..859bb015e --- /dev/null +++ b/MC/config/common/ini/tests/GeneratorEPOS4pO962TeV.C @@ -0,0 +1,99 @@ +int External() +{ + std::string path{"o2sim_Kine.root"}; + + // Check that file exists, can be opened and has the correct tree + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) + { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) + { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + // Check if all events are filled + auto nEvents = tree->GetEntries(); + for (Long64_t i = 0; i < nEvents; ++i) + { + tree->GetEntry(i); + if (tracks->empty()) + { + std::cerr << "Empty entry found at event " << i << "\n"; + return 1; + } + } + + // Check if there are 10 events, as customly set in the ini file (NEV_TEST) + if (nEvents != 10) + { + std::cerr << "Expected 10 events, got " << nEvents << "\n"; + return 1; + } + + // ---- proton-Oxygen parameters ---- + // The .optns file boosts the output from the symmetric NN CM frame (where + // each nucleon carries ecms/2) to the real asymmetric p/O beam frame via + // "hepmc_rapcms ", so E_p = (ecms/2)*exp(+kRapcms) and + // E_O-nucleon = (ecms/2)*exp(-kRapcms). Keep kRapcms in sync with the value + // set in pO_962TeV_EPOS4.optns / pO_962TeV_EPOS4_hydro.optns. + constexpr int kProtonPDG = 2212; + constexpr int kOxygenPDG = 1000080160; // O-16 ion + constexpr double kEcms = 9620.; // GeV, sqrt(s_NN) as set in the .optns file + constexpr double kRapcms = 0.346225; // rapidity boost set via hepmc_rapcms + constexpr int kA = 16; // Oxygen mass number + const double kProtonEnergy = (kEcms / 2.0) * std::exp(kRapcms); // boosted proton energy + const double kOxygenEnergy = kA * (kEcms / 2.0) * std::exp(-kRapcms); // total energy of the O-16 ion + + // Check if each event has one proton and one oxygen ion at expected energies + for (int i = 0; i < nEvents; i++) + { + tree->GetEntry(i); + int countProton = 0; + int countOxygen = 0; + + for (int idxMCTrack = 0; idxMCTrack < tracks->size(); ++idxMCTrack) + { + auto track = tracks->at(idxMCTrack); + double energy = track.GetEnergy(); + + // 1 GeV tolerance (the rapcms boost is applied in single precision in EPOS4) + if (std::abs(energy - kProtonEnergy) < 1. && + track.GetPdgCode() == kProtonPDG) + { + countProton++; + } + else if (std::abs(energy - kOxygenEnergy) < 1. && + track.GetPdgCode() == kOxygenPDG) + { + countOxygen++; + } + } + + if (countProton < 1) + { + std::cerr << "Event " << i + << " has no proton beam particle at " + << kProtonEnergy << " GeV\n"; + return 1; + } + + if (countOxygen < 1) + { + std::cerr << "Event " << i + << " has no oxygen ion at " + << kOxygenEnergy << " GeV\n"; + return 1; + } + } + + return 0; +} diff --git a/MC/config/common/ini/tests/GeneratorEPOS4pO962TeV_hydro.C b/MC/config/common/ini/tests/GeneratorEPOS4pO962TeV_hydro.C new file mode 100644 index 000000000..475f8d958 --- /dev/null +++ b/MC/config/common/ini/tests/GeneratorEPOS4pO962TeV_hydro.C @@ -0,0 +1,99 @@ +int External() +{ + std::string path{"o2sim_Kine.root"}; + + // Check that file exists, can be opened and has the correct tree + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) + { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) + { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + // Check if all events are filled + auto nEvents = tree->GetEntries(); + for (Long64_t i = 0; i < nEvents; ++i) + { + tree->GetEntry(i); + if (tracks->empty()) + { + std::cerr << "Empty entry found at event " << i << "\n"; + return 1; + } + } + + // Check if there are 2 events, as customly set in the ini file (NEV_TEST) + if (nEvents != 2) + { + std::cerr << "Expected 2 events, got " << nEvents << "\n"; + return 1; + } + + // ---- proton-Oxygen parameters ---- + // The .optns file boosts the output from the symmetric NN CM frame (where + // each nucleon carries ecms/2) to the real asymmetric p/O beam frame via + // "hepmc_rapcms ", so E_p = (ecms/2)*exp(+kRapcms) and + // E_O-nucleon = (ecms/2)*exp(-kRapcms). Keep kRapcms in sync with the value + // set in pO_962TeV_EPOS4.optns / pO_962TeV_EPOS4_hydro.optns. + constexpr int kProtonPDG = 2212; + constexpr int kOxygenPDG = 1000080160; // O-16 ion + constexpr double kEcms = 9620.; // GeV, sqrt(s_NN) as set in the .optns file + constexpr double kRapcms = 0.346225; // rapidity boost set via hepmc_rapcms + constexpr int kA = 16; // Oxygen mass number + const double kProtonEnergy = (kEcms / 2.0) * std::exp(kRapcms); // boosted proton energy + const double kOxygenEnergy = kA * (kEcms / 2.0) * std::exp(-kRapcms); // total energy of the O-16 ion + + // Check if each event has one proton and one oxygen ion at expected energies + for (int i = 0; i < nEvents; i++) + { + tree->GetEntry(i); + int countProton = 0; + int countOxygen = 0; + + for (int idxMCTrack = 0; idxMCTrack < tracks->size(); ++idxMCTrack) + { + auto track = tracks->at(idxMCTrack); + double energy = track.GetEnergy(); + + // 1 GeV tolerance (the rapcms boost is applied in single precision in EPOS4) + if (std::abs(energy - kProtonEnergy) < 1. && + track.GetPdgCode() == kProtonPDG) + { + countProton++; + } + else if (std::abs(energy - kOxygenEnergy) < 1. && + track.GetPdgCode() == kOxygenPDG) + { + countOxygen++; + } + } + + if (countProton < 1) + { + std::cerr << "Event " << i + << " has no proton beam particle at " + << kProtonEnergy << " GeV\n"; + return 1; + } + + if (countOxygen < 1) + { + std::cerr << "Event " << i + << " has no oxygen ion at " + << kOxygenEnergy << " GeV\n"; + return 1; + } + } + + return 0; +} diff --git a/MC/config/common/ini/tests/GeneratorHGPythia8PbPb536TeV.C b/MC/config/common/ini/tests/GeneratorHGPythia8PbPb536TeV.C new file mode 100644 index 000000000..a54dcabba --- /dev/null +++ b/MC/config/common/ini/tests/GeneratorHGPythia8PbPb536TeV.C @@ -0,0 +1,56 @@ +int External() +{ + const int A = 208; // mass number of both nuclei + std::string path{"o2sim_Kine.root"}; + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + auto tree = (TTree*)file.Get("o2sim"); + if (!tree) { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + o2::dataformats::MCEventHeader* header = nullptr; + tree->SetBranchAddress("MCEventHeader.", &header); + + using Key = o2::dataformats::MCInfoKeys; + const auto nEvents = tree->GetEntries(); + if (nEvents == 0) { + std::cerr << "No events found\n"; + return 1; + } + for (Long64_t i = 0; i < nEvents; ++i) { + tree->GetEntry(i); + bool valid = false; + const int nColl = header->getInfo(Key::nColl, valid); + if (!valid || nColl < 1 || nColl > A * A) { + std::cerr << "Missing or invalid Ncoll in event " << i << "\n"; + return 1; + } + const int nPart = header->getInfo(Key::nPart, valid); + if (!valid || nPart < 2 || nPart > 2 * A) { + std::cerr << "Missing or invalid Npart in event " << i << "\n"; + return 1; + } + // each NN collision is a PYTHIA pp event: two beam protons with half of sqrt(s_NN) each + int nBeams = 0; + for (const auto& track : *tracks) { + if (o2::mcgenstatus::getHepMCStatusCode(track.getStatusCode()) == 4) { + if (track.GetPdgCode() != 2212 || std::abs(track.GetEnergy() - 2680.) > 1e-3) { + std::cerr << "Unexpected beam particle " << track.GetPdgCode() << " with energy " << track.GetEnergy() << " in event " << i << "\n"; + return 1; + } + nBeams++; + } + } + if (nBeams != 2 * nColl) { + std::cerr << "Found " << nBeams << " beam protons for " << nColl << " NN collisions in event " << i << "\n"; + return 1; + } + } + return 0; +} diff --git a/MC/config/common/ini/tests/GeneratorPerformance.C b/MC/config/common/ini/tests/GeneratorPerformance.C new file mode 100644 index 000000000..c34ca7c5d --- /dev/null +++ b/MC/config/common/ini/tests/GeneratorPerformance.C @@ -0,0 +1,55 @@ +int Hybrid() +{ + std::string path{"o2sim_Kine.root"}; + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) + { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) + { + std::cerr << "Cannot find tree 'o2sim' in file " << path << "\n"; + return 1; + } + // Get the MCTrack branch + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + // Check if processes with ID 42 are available + const int processID = 42; // Performance test particle custom process ID + int nEvents = tree->GetEntries(); + short int count_perf = 0; + bool flag = false; + for (int i = 0; i < nEvents; i++) + { + tree->GetEntry(i); + int nTracks = tracks->size(); + count_perf = 0; + for (auto &track : *tracks) + { + const auto &process = track.getProcess(); + if (process == processID) + { + flag = true; + // No need to continue checking other tracks in the event + break; + } + } + if (flag == true) + { + count_perf++; + flag = false; + } + } + if (count_perf == 0) + { + std::cerr << "No performance test particles found in the events\n"; + return 1; + } else if (count_perf > nEvents) { + std::cerr << "More performance test flagged events than generated events\n"; + return 1; + } + file.Close(); + return 0; +} \ No newline at end of file diff --git a/MC/config/common/ini/tests/GeneratorPerformanceFix.C b/MC/config/common/ini/tests/GeneratorPerformanceFix.C new file mode 100644 index 000000000..ffda1fb6e --- /dev/null +++ b/MC/config/common/ini/tests/GeneratorPerformanceFix.C @@ -0,0 +1,43 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) { + std::cerr << "Cannot find tree 'o2sim' in file " << path << "\n"; + return 1; + } + // Get the MCTrack branch + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + // Check if processes with ID 42 are available + // And are 100 per event + const int processID = 42; // Performance test particle custom process ID + int nEvents = tree->GetEntries(); + short int count_perf = 0; + unsigned short int expSig = 100; // set by default in the ini file + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + int nTracks = tracks->size(); + count_perf = 0; + for (auto &track : *tracks) + { + const auto& process = track.getProcess(); + if (process == 42) + { + count_perf++; + } + } + if (count_perf != expSig) + { + std::cerr << "Event " << i << ": Expected " << expSig << " performance test particles, found " << count_perf << "\n"; + return 1; + } + } + + file.Close(); + return 0; +} \ No newline at end of file diff --git a/MC/config/common/ini/tests/GeneratorTPCloopers.C b/MC/config/common/ini/tests/GeneratorTPCloopers.C new file mode 100644 index 000000000..7413fd839 --- /dev/null +++ b/MC/config/common/ini/tests/GeneratorTPCloopers.C @@ -0,0 +1,42 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) { + std::cerr << "Cannot find tree 'o2sim' in file " << path << "\n"; + return 1; + } + // Get the MCTrack branch + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + // Check if only pairs are contained in the simulation + int nEvents = tree->GetEntries(); + int count_e = 0; + int count_p = 0; + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + for (auto &track : *tracks) + { + auto pdg = track.GetPdgCode(); + if (pdg == 11) { + count_e++; + } else if (pdg == -11) { + count_p++; + } else { + std::cerr << "Found unexpected PDG code: " << pdg << "\n"; + return 1; + } + } + } + if (count_e < count_p) { + std::cerr << "Less electrons than positrons: " << count_e << " vs " << count_p << "\n"; + return 1; + } + file.Close(); + + return 0; +} \ No newline at end of file diff --git a/MC/config/common/ini/tests/pythia8_NeNe_536.C b/MC/config/common/ini/tests/pythia8_NeNe_536.C new file mode 100644 index 000000000..1c28040e2 --- /dev/null +++ b/MC/config/common/ini/tests/pythia8_NeNe_536.C @@ -0,0 +1,24 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + auto nEvents = tree->GetEntries(); + if (nEvents == 0) { + std::cerr << "No event of interest\n"; + return 1; + } + return 0; +} diff --git a/MC/config/common/ini/tests/pythia8_OO_536.C b/MC/config/common/ini/tests/pythia8_OO_536.C new file mode 100644 index 000000000..1c28040e2 --- /dev/null +++ b/MC/config/common/ini/tests/pythia8_OO_536.C @@ -0,0 +1,24 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + auto nEvents = tree->GetEntries(); + if (nEvents == 0) { + std::cerr << "No event of interest\n"; + return 1; + } + return 0; +} diff --git a/MC/config/common/ini/tests/pythia8_OO_rescattering_536.C b/MC/config/common/ini/tests/pythia8_OO_rescattering_536.C new file mode 100644 index 000000000..1c28040e2 --- /dev/null +++ b/MC/config/common/ini/tests/pythia8_OO_rescattering_536.C @@ -0,0 +1,24 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + auto nEvents = tree->GetEntries(); + if (nEvents == 0) { + std::cerr << "No event of interest\n"; + return 1; + } + return 0; +} diff --git a/MC/config/common/ini/tests/pythia8_PbPb_rescattering_536.C b/MC/config/common/ini/tests/pythia8_PbPb_rescattering_536.C new file mode 100644 index 000000000..1c28040e2 --- /dev/null +++ b/MC/config/common/ini/tests/pythia8_PbPb_rescattering_536.C @@ -0,0 +1,24 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + auto nEvents = tree->GetEntries(); + if (nEvents == 0) { + std::cerr << "No event of interest\n"; + return 1; + } + return 0; +} diff --git a/MC/config/common/ini/tests/pythia8_inel_difftuned.C b/MC/config/common/ini/tests/pythia8_inel_difftuned.C new file mode 100644 index 000000000..7ee7ea397 --- /dev/null +++ b/MC/config/common/ini/tests/pythia8_inel_difftuned.C @@ -0,0 +1,24 @@ +int pythia8() { + std::string path{"o2sim_Kine.root"}; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + auto nEvents = tree->GetEntries(); + if (nEvents == 0) { + std::cerr << "No event of interest\n"; + return 1; + } + return 0; +} diff --git a/MC/config/common/ini/tests/pythia8_pO_961.C b/MC/config/common/ini/tests/pythia8_pO_961.C new file mode 100644 index 000000000..1c28040e2 --- /dev/null +++ b/MC/config/common/ini/tests/pythia8_pO_961.C @@ -0,0 +1,24 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + auto nEvents = tree->GetEntries(); + if (nEvents == 0) { + std::cerr << "No event of interest\n"; + return 1; + } + return 0; +} diff --git a/MC/config/common/pythia8/generator/pythia8_NeNe_536.cfg b/MC/config/common/pythia8/generator/pythia8_NeNe_536.cfg new file mode 100644 index 000000000..6f514e6d6 --- /dev/null +++ b/MC/config/common/pythia8/generator/pythia8_NeNe_536.cfg @@ -0,0 +1,14 @@ +### NeNe beams +Beams:idA = 1000100200 # Neon +Beams:idB = 1000100200 # Neon +Beams:eCM = 5360.0 ### energy + +Beams:frameType = 1 +ParticleDecays:limitTau0 = on +ParticleDecays:tau0Max = 10. ### match alice: 1cm/c = 10.0mm/c + +### To avoid refitting, add the following lines to your configuration file: +HeavyIon:SigFitNGen = 0 +HeavyIon:SigFitDefPar = 2.15,18.42,0.33 + +Random:setSeed = on diff --git a/MC/config/common/pythia8/generator/pythia8_OO_536.cfg b/MC/config/common/pythia8/generator/pythia8_OO_536.cfg new file mode 100644 index 000000000..d587387b9 --- /dev/null +++ b/MC/config/common/pythia8/generator/pythia8_OO_536.cfg @@ -0,0 +1,15 @@ +### OO beams +Beams:idA = 1000080160 +Beams:idB = 1000080160 +Beams:eCM = 5360.0 ### energy + +Beams:frameType = 1 +ParticleDecays:limitTau0 = on +ParticleDecays:tau0Max = 10. ### match alice: 1cm/c = 10.0mm/c + +### Save some CPU at init of jobs +### To avoid refitting, add the following lines to your configuration file: +HeavyIon:SigFitNGen = 0 +HeavyIon:SigFitDefPar = 2.15,18.42,0.33 + +Random:setSeed = on diff --git a/MC/config/common/pythia8/generator/pythia8_OO_rescattering_536.cfg b/MC/config/common/pythia8/generator/pythia8_OO_rescattering_536.cfg new file mode 100644 index 000000000..cd0a11e50 --- /dev/null +++ b/MC/config/common/pythia8/generator/pythia8_OO_rescattering_536.cfg @@ -0,0 +1,22 @@ +### OO beams +Beams:idA = 1000080160 +Beams:idB = 1000080160 +Beams:eCM = 5360.0 ### energy + +Beams:frameType = 1 +ParticleDecays:limitTau0 = on +ParticleDecays:tau0Max = 10. ### match alice: 1cm/c = 10.0mm/c + +### Save some CPU at init of jobs +### To avoid refitting, add the following lines to your configuration file: +HeavyIon:SigFitNGen = 0 +HeavyIon:SigFitDefPar = 2.15,18.42,0.33 + +### enable hadronic rescattering +HadronLevel:Rescatter = on # default = off +Fragmentation:setVertices = on # default = off +PartonVertex:setVertex = on # default = off +Rescattering:nearestNeighbours = off # default = on (but "require a larger retuning effort") +Rescattering:inelastic = on # default = on + +Random:setSeed = on diff --git a/MC/config/common/pythia8/generator/pythia8_PbPb_rescattering_536.cfg b/MC/config/common/pythia8/generator/pythia8_PbPb_rescattering_536.cfg new file mode 100644 index 000000000..d4158bdcf --- /dev/null +++ b/MC/config/common/pythia8/generator/pythia8_PbPb_rescattering_536.cfg @@ -0,0 +1,24 @@ +### OO beams +Beams:idA 1000822080 # Pb +Beams:idB 1000822080 # Pb +Beams:eCM = 5360.0 ### energy + +Beams:frameType = 1 +ParticleDecays:limitTau0 = on +ParticleDecays:tau0Max = 10. ### match alice: 1cm/c = 10.0mm/c + +### Initialize the Angantyr model to fit the total and semi-includive +### cross sections in Pythia within some tolerance. +HeavyIon:SigFitErr = 0.02,0.02,0.1,0.05,0.05,0.0,0.1,0.0 + +### These parameters are typicall suitable for sqrt(S_NN)=5TeV +HeavyIon:SigFitDefPar = 17.24,2.15,0.33,0.0,0.0,0.0,0.0,0.0 + +### enable hadronic rescattering +HadronLevel:Rescatter = on # default = off +Fragmentation:setVertices = on # default = off +PartonVertex:setVertex = on # default = off +Rescattering:nearestNeighbours = off # default = on (but "require a larger retuning effort") +Rescattering:inelastic = on # default = on + +Random:setSeed = on diff --git a/MC/config/common/pythia8/generator/pythia8_hf.cfg b/MC/config/common/pythia8/generator/pythia8_hf.cfg index 273751f09..899e96b31 100644 --- a/MC/config/common/pythia8/generator/pythia8_hf.cfg +++ b/MC/config/common/pythia8/generator/pythia8_hf.cfg @@ -1,7 +1,7 @@ ### beams Beams:idA 2212 # proton Beams:idB 2212 # proton -Beams:eCM 14000. # GeV +Beams:eCM 13600. # GeV ### processes HardQCD:hardccbar on # scatterings g-g / q-qbar -> c-cbar diff --git a/MC/config/common/pythia8/generator/pythia8_inel_536.cfg b/MC/config/common/pythia8/generator/pythia8_inel_536.cfg new file mode 100644 index 000000000..d1fb9b68a --- /dev/null +++ b/MC/config/common/pythia8/generator/pythia8_inel_536.cfg @@ -0,0 +1,11 @@ +### beams +Beams:idA 2212 # proton +Beams:idB 2212 # proton +Beams:eCM 5360.0 # GeV + +### processes +SoftQCD:inelastic on # all inelastic processes + +### decays +ParticleDecays:limitTau0 on +ParticleDecays:tau0Max 10. diff --git a/MC/config/common/pythia8/generator/pythia8_inel_difftuned.cfg b/MC/config/common/pythia8/generator/pythia8_inel_difftuned.cfg new file mode 100644 index 000000000..22dcfbb7e --- /dev/null +++ b/MC/config/common/pythia8/generator/pythia8_inel_difftuned.cfg @@ -0,0 +1,25 @@ +### beams +Beams:idA 2212 # proton +Beams:idB 2212 # proton +Beams:eCM 13600. # GeV + +### processes +SoftQCD:inelastic on # all inelastic processes + +### decays +ParticleDecays:limitTau0 on +ParticleDecays:tau0Max 10. + +### adjust diffraction +### note that PYTHIA calculates the ND sigma via a +### 'remaining from total' formula: no need to put +### that in by hand + +SigmaTotal:mode 0 +SigmaDiffractive:mode 0 +SigmaTotal:sigmaTot 78.6 +SigmaTotal:sigmaEl 0.0 +SigmaTotal:sigmaXB 10.625 +SigmaTotal:sigmaAX 10.625 +SigmaTotal:sigmaXX 9.78 +SigmaTotal:sigmaAXB 0.0 diff --git a/MC/config/common/pythia8/generator/pythia8_pO_961.cfg b/MC/config/common/pythia8/generator/pythia8_pO_961.cfg new file mode 100644 index 000000000..235300c1d --- /dev/null +++ b/MC/config/common/pythia8/generator/pythia8_pO_961.cfg @@ -0,0 +1,15 @@ +### OO beams +Beams:frameType 2 # back-to-back beams of different energies and particles +Beams:idA 2212 # proton +Beams:idB 1000080160 # Oxygen +Beams:eA 6800. # Energy of proton beam in GeV moving in the +z direction +Beams:eB 3400. # Energy in GeV per Oxygen nucleon (6.8 Z TeV) moving in the -z direction + +ParticleDecays:limitTau0 = on +ParticleDecays:tau0Max = 10. ### match alice: 1cm/c = 10.0mm/c + +### To avoid refitting, add the following lines to your configuration file: +HeavyIon:SigFitNGen = 0 +HeavyIon:SigFitDefPar = 2.17,17.56,0.30 + +Random:setSeed = on diff --git a/MC/config/common/pythia8/utils/mkpy8cfg.py b/MC/config/common/pythia8/utils/mkpy8cfg.py index d7530885f..ccea745cb 100755 --- a/MC/config/common/pythia8/utils/mkpy8cfg.py +++ b/MC/config/common/pythia8/utils/mkpy8cfg.py @@ -5,6 +5,20 @@ import argparse + +def derive_process(process_in, beam_type_a, beam_type_b): + """ + Derive the process from the beam types if not already set. + """ + if process_in != "none": + return process_in + if beam_type_a == 2212 and beam_type_b == 2212: + return "inel" + if beam_type_a == 1000822080 and beam_type_b == 1000822080: + return "heavy_ion" + return "none" + + parser = argparse.ArgumentParser(description='Make Pythia8 configuration', formatter_class=argparse.ArgumentDefaultsHelpFormatter) @@ -52,6 +66,9 @@ args = parser.parse_args() +# derive the process +args.process = derive_process(args.process, args.idA, args.idB) + ### open output file fout = open(args.output, 'w') @@ -85,6 +102,9 @@ if args.eCM > 0: fout.write('Beams:eCM = %f \n' % (args.eCM)) elif args.eA > 0 and args.eB > 0: + # Reference frame to be set to 2 if the two back-to-back beams have different energies + if args.eA != args.eB: + fout.write('Beams:frameType = 2 \n') fout.write('Beams:eA = %f \n' % (args.eA)) fout.write('Beams:eB = %f \n' % (args.eB)) else: @@ -119,7 +139,7 @@ fout.write('\n') ### heavy ion settings (valid for Pb-Pb 5520 only) -if args.process == 'heavy_ion': +if args.process == 'heavy_ion' and (args.idA == 1000822080 and args.idB == 1000822080 and args.eCM == 5520.): fout.write('### heavy-ion settings (valid for Pb-Pb 5520 only) \n') fout.write('HeavyIon:SigFitNGen = 0 \n') fout.write('HeavyIon:SigFitDefPar = 13.88,1.84,0.22,0.0,0.0,0.0,0.0,0.0 \n') @@ -146,7 +166,7 @@ fout.write('### <-- generated by mkpy8cfg.py \n') fout.write('\n') - + ### appended files if args.append is not None : for i in args.append : @@ -159,7 +179,7 @@ fout.write('\n') fout.write('### <-- included from %s \n' % (i)) fout.write('\n') - + ### user commands if args.command is not None : fout.write('### --> user commands \n') diff --git a/MC/config/examples/epos4/epos.sh b/MC/config/examples/epos4/epos.sh new file mode 100755 index 000000000..13a30b0d0 --- /dev/null +++ b/MC/config/examples/epos4/epos.sh @@ -0,0 +1,69 @@ +#!/bin/sh +# Script based on CRMC example +# EPOS4 option files must contain ihepmc set to 2 to print HepMC +# data on stdout. -hepmc flag is not needed anymore, but -hepstd is fundamental +# in order not to print useless information on stdout (a z-*optns*.mtr file will be created) + +optns="example" +seed=$(od -An -N2 -tu2 /dev/urandom | tr -d ' ') +EPOS4="" + +if [ -z "$EPO4VSN" ]; then + echo "Error: EPO4VSN environment variable is not set" + exit 1 +fi + +if [ "$EPO4VSN" = "4.0.0" ]; then + EPOS4="$EPOS4_ROOT/epos4/scripts/epos" + export LIBDIR=$EPOS4_ROOT/epos4/bin +else + EPOS4="$EPOS4_ROOT/bin/epos" +fi + +# Check if the environment variable EPO4 is set (mandatory with o2dpg-sim-tests on CI machines) +# If not, set all the EPOS4 related variables, most likely they are unset as well. +if [ -z "${EPO4}" ]; then + export OPT=./ + export HTO=./ + export CHK=./ + if [ "$EPO4VSN" = "4.0.0" ]; then + export EPO4=$EPOS4_ROOT/epos4/ + else + export EPO4=$EPOS4_ROOT/ + fi +fi + +while test $# -gt 0 ; do + case $1 in + -i|--input) optns=$2 ; shift ;; + -s|--seed) seed=$2 ; shift ;; + -h|--help) usage; exit 0 ;; + esac + shift +done + +if [ ! -f $optns.optns ]; then + echo "Error: Options file $optns.optns not found" + exit 2 +fi + +if grep -Fq "set ihq 1" $optns.optns; then + if [ -z "$EPO4HQVSN" ]; then + # Error: EPOS4HQ version not found + exit 3 + else + # Running with EPOS4HQ + if [ -z "${EPO4HQ}" ]; then + export EPO4HQ=$EPOS4HQ_ROOT/ + fi + EPOS4="$EPO4HQ/bin/eposhq" + fi +fi + +if [ $seed -eq 0 ]; then + # Seed can't be 0, random number will be used + seed=$(od -An -N2 -tu2 /dev/urandom | tr -d ' ') +fi + +# OR filters the stdout with only HepMC useful data +$EPOS4 -hepstd -s $seed $optns | sed -n 's/^\(HepMC::\|[EAUWVP] \)/\1/p' \ No newline at end of file diff --git a/MC/config/examples/epos4/generator/NeNe_536TeV_EPOS4.optns b/MC/config/examples/epos4/generator/NeNe_536TeV_EPOS4.optns new file mode 100644 index 000000000..5c84f533d --- /dev/null +++ b/MC/config/examples/epos4/generator/NeNe_536TeV_EPOS4.optns @@ -0,0 +1,33 @@ +!-------------------------------------------------------------------- +! Neon-Neon collisions with hydro and hadronic cascade +!-------------------------------------------------------------------- + +!--------------------------------------- +! Define run +!--------------------------------------- + +application hadron !hadron-hadron, hadron-nucleus, or nucleus-nucleus +set laproj 10 !projectile atomic number +set maproj 20 !projectile mass number +set latarg 10 !target atomic number +set matarg 20 !target mass number +set ecms 5360 !sqrt(s)_pp +set istmax 25 !max status considered for storage + +ftime on !string formation time non-zero +!suppressed decays: +nodecays + 110 20 2130 -2130 2230 -2230 1130 -1130 1330 -1330 2330 -2330 3331 -3331 +end + +set ninicon 1 !number of initial conditions used for hydro evolution +core full !core/corona activated +hydro hlle !hydro activated +eos x3ff !eos activated (epos standard EoS) +hacas full !hadronic cascade activated (UrQMD) +set nfreeze 1 !number of freeze out events per hydro event +set modsho 1 !printout every modsho events +set centrality 0 !0=min bias +set ihepmc 2 !HepMC output enabled on stdout +fillTree4(C1) !C1 sets impact parameter as centrality variable +set nfull 10 diff --git a/MC/config/examples/epos4/generator/NeNe_536TeV_EPOS4HQ.optns b/MC/config/examples/epos4/generator/NeNe_536TeV_EPOS4HQ.optns new file mode 100644 index 000000000..5a5a445d4 --- /dev/null +++ b/MC/config/examples/epos4/generator/NeNe_536TeV_EPOS4HQ.optns @@ -0,0 +1,38 @@ +!-------------------------------------------------------------------- +! Neon-Neon collisions with hydro and hadronic cascade +!-------------------------------------------------------------------- + +!--------------------------------------- +! Define run +!--------------------------------------- + +application hadron !hadron-hadron, hadron-nucleus, or nucleus-nucleus +set laproj 10 !projectile atomic number +set maproj 20 !projectile mass number +set latarg 10 !target atomic number +set matarg 20 !target mass number +set ecms 5360 !sqrt(s)_pp +set istmax 25 !max status considered for storage + +ftime on !string formation time non-zero +!suppressed decays: +nodecays + 110 20 2130 -2130 2230 -2230 1130 -1130 1330 -1330 2330 -2330 3331 -3331 +end + +!---EPOS4HQ specific changes--- +set hqnbcol 1 +set hqnbcolsaveEPOS 1 +!------------------------------- +set ninicon 1 !number of initial conditions used for hydro evolution +core full !core/corona activated +hydro hlle !hydro activated +eos x3ff !eos activated (epos standard EoS) +hacas full !hadronic cascade activated (UrQMD) +set nfreeze 1 !number of freeze out events per hydro event +set modsho 1 !printout every modsho events +set centrality 0 !0=min bias +set ihepmc 2 !HepMC output enabled on stdout +fillTree4(C1) !C1 sets impact parameter as centrality variable +set ihq 1 !Enables HQ +set nfull 10 diff --git a/MC/config/examples/epos4/generator/OO_536TeV_EPOS4.optns b/MC/config/examples/epos4/generator/OO_536TeV_EPOS4.optns new file mode 100644 index 000000000..eeaab8cd9 --- /dev/null +++ b/MC/config/examples/epos4/generator/OO_536TeV_EPOS4.optns @@ -0,0 +1,33 @@ +!-------------------------------------------------------------------- +! Oxygen-Oxygen collisions with hydro and hadronic cascade +!-------------------------------------------------------------------- + +!--------------------------------------- +! Define run +!--------------------------------------- + +application hadron !hadron-hadron, hadron-nucleus, or nucleus-nucleus +set laproj 8 !projectile atomic number +set maproj 16 !projectile mass number +set latarg 8 !target atomic number +set matarg 16 !target mass number +set ecms 5360 !sqrt(s)_pp +set istmax 25 !max status considered for storage + +ftime on !string formation time non-zero +!suppressed decays: +nodecays + 110 20 2130 -2130 2230 -2230 1130 -1130 1330 -1330 2330 -2330 3331 -3331 +end + +set ninicon 1 !number of initial conditions used for hydro evolution +core full !core/corona activated +hydro hlle !hydro activated +eos x3ff !eos activated (epos standard EoS) +hacas full !hadronic cascade activated (UrQMD) +set nfreeze 1 !number of freeze out events per hydro event +set modsho 1 !printout every modsho events +set centrality 0 !0=min bias +set ihepmc 2 !HepMC output enabled on stdout +fillTree4(C1) !C1 sets impact parameter as centrality variable +set nfull 10 diff --git a/MC/config/examples/epos4/generator/OO_536TeV_EPOS4HQ.optns b/MC/config/examples/epos4/generator/OO_536TeV_EPOS4HQ.optns new file mode 100644 index 000000000..4100c362a --- /dev/null +++ b/MC/config/examples/epos4/generator/OO_536TeV_EPOS4HQ.optns @@ -0,0 +1,38 @@ +!-------------------------------------------------------------------- +! Oxygen-Oxygen collisions with hydro and hadronic cascade +!-------------------------------------------------------------------- + +!--------------------------------------- +! Define run +!--------------------------------------- + +application hadron !hadron-hadron, hadron-nucleus, or nucleus-nucleus +set laproj 8 !projectile atomic number +set maproj 16 !projectile mass number +set latarg 8 !target atomic number +set matarg 16 !target mass number +set ecms 5360 !sqrt(s)_pp +set istmax 25 !max status considered for storage + +ftime on !string formation time non-zero +!suppressed decays: +nodecays + 110 20 2130 -2130 2230 -2230 1130 -1130 1330 -1330 2330 -2330 3331 -3331 +end + +!---EPOS4HQ specific changes--- +set hqnbcol 1 +set hqnbcolsaveEPOS 1 +!------------------------------- +set ninicon 1 !number of initial conditions used for hydro evolution +core full !core/corona activated +hydro hlle !hydro activated +eos x3ff !eos activated (epos standard EoS) +hacas full !hadronic cascade activated (UrQMD) +set nfreeze 1 !number of freeze out events per hydro event +set modsho 1 !printout every modsho events +set centrality 0 !0=min bias +set ihepmc 2 !HepMC output enabled on stdout +fillTree4(C1) !C1 sets impact parameter as centrality variable +set ihq 1 !Enables HQ +set nfull 10 diff --git a/MC/config/examples/epos4/generator/example.optns b/MC/config/examples/epos4/generator/example.optns new file mode 100644 index 000000000..015d51a06 --- /dev/null +++ b/MC/config/examples/epos4/generator/example.optns @@ -0,0 +1,32 @@ +!-------------------------------------------------------------------- +! proton-proton collisions with hydro and hadronic cascade +!-------------------------------------------------------------------- + +!--------------------------------------- +! Define run +!--------------------------------------- + +application hadron !hadron-hadron, hadron-nucleus, or nucleus-nucleus +set laproj 1 !projectile atomic number +set maproj 1 !projectile mass number +set latarg 1 !target atomic number +set matarg 1 !target mass number +set ecms 13600 !sqrt(s)_pp +set istmax 25 !max status considered for storage + +ftime on !string formation time non-zero +!suppressed decays: +nodecays + 110 20 2130 -2130 2230 -2230 1130 -1130 1330 -1330 2330 -2330 3331 -3331 +end + +set ninicon 1 !number of initial conditions used for hydro evolution +core full !core/corona activated +hydro hlle !hydro activated +eos x3ff !eos activated (epos standard EoS) +hacas full !hadronic cascade activated (UrQMD) +set nfreeze 1 !number of freeze out events per hydro event +set modsho 1 !printout every modsho events +set centrality 0 !0=min bias +set ihepmc 2 !HepMC output enabled on stdout +set nfull 10 diff --git a/MC/config/examples/epos4/generator/examplehq.optns b/MC/config/examples/epos4/generator/examplehq.optns new file mode 100755 index 000000000..40afb362a --- /dev/null +++ b/MC/config/examples/epos4/generator/examplehq.optns @@ -0,0 +1,33 @@ +!-------------------------------------------------------------------- +! proton-proton collisions with hydro and hadronic cascade +!-------------------------------------------------------------------- + +!--------------------------------------- +! Define run +!--------------------------------------- + +application hadron !hadron-hadron, hadron-nucleus, or nucleus-nucleus +set laproj 1 !projectile atomic number +set maproj 1 !projectile mass number +set latarg 1 !target atomic number +set matarg 1 !target mass number +set ecms 13600 !sqrt(s)_pp +set istmax 25 !max status considered for storage + +ftime on !string formation time non-zero +!suppressed decays: +nodecays + 110 20 2130 -2130 2230 -2230 1130 -1130 1330 -1330 2330 -2330 3331 -3331 +end + +set ninicon 1 !number of initial conditions used for hydro evolution +core full !core/corona activated +hydro hlle !hydro activated +eos x3ff !eos activated (epos standard EoS) +hacas full !hadronic cascade activated (UrQMD) +set nfreeze 1 !number of freeze out events per hydro event +set modsho 1 !printout every modsho events +set centrality 0 !0=min bias +set ihepmc 2 !HepMC output enabled on stdout +set nfull 10 !Total number of events to be generated +set ihq 1 !Enable EPOS4HQ diff --git a/MC/config/examples/epos4/generator/examplepbpb.optns b/MC/config/examples/epos4/generator/examplepbpb.optns new file mode 100644 index 000000000..49a41c350 --- /dev/null +++ b/MC/config/examples/epos4/generator/examplepbpb.optns @@ -0,0 +1,33 @@ +!-------------------------------------------------------------------- +! Lead-Lead collision with hydro and hadronic cascade +!-------------------------------------------------------------------- + +!--------------------------------------- +! Define run +!--------------------------------------- + +application hadron !hadron-hadron, hadron-nucleus, or nucleus-nucleus +set laproj 82 !projectile atomic number +set maproj 208 !projectile mass number +set latarg 82 !target atomic number +set matarg 208 !target mass number +set ecms 5360 !sqrt(s)_pp +set istmax 25 !max status considered for storage + +ftime on !string formation time non-zero +!suppressed decays: +nodecays + 110 20 2130 -2130 2230 -2230 1130 -1130 1330 -1330 2330 -2330 3331 -3331 +end + +set ninicon 1 !number of initial conditions used for hydro evolution +core full !core/corona activated +hydro hlle !hydro activated +eos x3ff !eos activated (epos standard EoS) +hacas full !hadronic cascade activated (UrQMD) +set nfreeze 1 !number of freeze out events per hydro event +set modsho 1 !printout every modsho events +set centrality 0 !0=min bias +set ihepmc 2 !HepMC output enabled on stdout +fillTree4(C1) !C1 sets impact parameter as centrality variable +set nfull 10 \ No newline at end of file diff --git a/MC/config/examples/epos4/generator/pp_136TeV_hydro_cascade.optns b/MC/config/examples/epos4/generator/pp_136TeV_hydro_cascade.optns new file mode 100644 index 000000000..1e4b8ea66 --- /dev/null +++ b/MC/config/examples/epos4/generator/pp_136TeV_hydro_cascade.optns @@ -0,0 +1,32 @@ +!-------------------------------------------------------------------- +! proton-proton collisions at 13.6 TeV with hydro and hadronic cascade +!-------------------------------------------------------------------- + +!--------------------------------------- +! Define run +!--------------------------------------- + +application hadron !hadron-hadron, hadron-nucleus, or nucleus-nucleus +set laproj 1 !projectile atomic number +set maproj 1 !projectile mass number +set latarg 1 !target atomic number +set matarg 1 !target mass number +set ecms 13600 !sqrt(s)_pp +set istmax 25 !max status considered for storage + +ftime on !string formation time non-zero +!suppressed decays: +nodecays + 110 20 2130 -2130 2230 -2230 1130 -1130 1330 -1330 2330 -2330 3331 -3331 +end + +set ninicon 1 !number of initial conditions used for hydro evolution +core full !core/corona activated +hydro hlle !hydro activated +eos x3ff !eos activated (epos standard EoS) +hacas full !hadronic cascade activated (UrQMD) +set nfreeze 10 !number of freeze out events per hydro event +set modsho 1 !printout every modsho events +set centrality 0 !0=min bias +set ihepmc 2 !HepMC output enabled on stdout +set nfull 10 diff --git a/MC/config/examples/epos4/generator/pp_136TeV_hydro_cascade_off.optns b/MC/config/examples/epos4/generator/pp_136TeV_hydro_cascade_off.optns new file mode 100644 index 000000000..e02b08b3f --- /dev/null +++ b/MC/config/examples/epos4/generator/pp_136TeV_hydro_cascade_off.optns @@ -0,0 +1,32 @@ +!-------------------------------------------------------------------- +! proton-proton collisions at 13.6 TeV with hydro and hadronic cascade switched off +!-------------------------------------------------------------------- + +!--------------------------------------- +! Define run +!--------------------------------------- + +application hadron !hadron-hadron, hadron-nucleus, or nucleus-nucleus +set laproj 1 !projectile atomic number +set maproj 1 !projectile mass number +set latarg 1 !target atomic number +set matarg 1 !target mass number +set ecms 13600 !sqrt(s)_pp +set istmax 25 !max status considered for storage + +ftime on !string formation time non-zero +!suppressed decays: +nodecays + 110 20 2130 -2130 2230 -2230 1130 -1130 1330 -1330 2330 -2330 3331 -3331 +end + +set ninicon 1 !number of initial conditions used for hydro evolution +core full !core/corona activated +hydro hlle !hydro activated +eos x3ff !eos activated (epos standard EoS) +hacas off !hadronic cascade activated (UrQMD) +set nfreeze 10 !number of freeze out events per hydro event +set modsho 1 !printout every modsho events +set centrality 0 !0=min bias +set ihepmc 2 !HepMC output enabled on stdout +set nfull 10 diff --git a/MC/config/examples/epos4/generator/pp_13TeV_hydro_cascade.optns b/MC/config/examples/epos4/generator/pp_13TeV_hydro_cascade.optns new file mode 100644 index 000000000..6081a0fa4 --- /dev/null +++ b/MC/config/examples/epos4/generator/pp_13TeV_hydro_cascade.optns @@ -0,0 +1,32 @@ +!-------------------------------------------------------------------- +! proton-proton collisions at 13 TeV with hydro and hadronic cascade +!-------------------------------------------------------------------- + +!--------------------------------------- +! Define run +!--------------------------------------- + +application hadron !hadron-hadron, hadron-nucleus, or nucleus-nucleus +set laproj 1 !projectile atomic number +set maproj 1 !projectile mass number +set latarg 1 !target atomic number +set matarg 1 !target mass number +set ecms 13000 !sqrt(s)_pp +set istmax 25 !max status considered for storage + +ftime on !string formation time non-zero +!suppressed decays: +nodecays + 110 20 2130 -2130 2230 -2230 1130 -1130 1330 -1330 2330 -2330 3331 -3331 +end + +set ninicon 1 !number of initial conditions used for hydro evolution +core full !core/corona activated +hydro hlle !hydro activated +eos x3ff !eos activated (epos standard EoS) +hacas full !hadronic cascade activated (UrQMD) +set nfreeze 1 !number of freeze out events per hydro event +set modsho 1 !printout every modsho events +set centrality 0 !0=min bias +set ihepmc 2 !HepMC output enabled on stdout +set nfull 10 diff --git a/MC/config/examples/epos4/generator_EPOS4.C b/MC/config/examples/epos4/generator_EPOS4.C new file mode 100644 index 000000000..987bed823 --- /dev/null +++ b/MC/config/examples/epos4/generator_EPOS4.C @@ -0,0 +1,101 @@ +#include +#include +#include +#include + +class GeneratorEPOS4 : public o2::eventgen::GeneratorHepMC +{ + public: + GeneratorEPOS4() = default; + ~GeneratorEPOS4() = default; + + bool importParticles() override { + bool status = GeneratorHepMC::importParticles(); + if (!status) { + LOG(error) << "Failed to import particles from HepMC event!"; + return false; + } + // Skip transport of charmonia chi_0c and chi_1c (incompatible with default G4 physics list) + // These are not decayed by EPOS4 (no daughters) + if (!mEnChi) + { + for (int a = 0; a < mParticles.size(); ++a) { + if (mParticles[a].GetPdgCode() == 10441 || mParticles[a].GetPdgCode() == 20443) + { + LOG(debug) << "Setting charmonium state " << mParticles[a].GetPdgCode() << " with index " << a << " not to be transported"; + mParticles[a].SetBit(ParticleStatus::kToBeDone, false); + } + } + } + return true; + } + + void setChiFlag(bool &flag) { + mEnChi = flag; + if (!flag) { + LOG(info) << "Charmonium states chi_0c and chi_1c will not be transported."; + } + } + + private: + bool mEnChi = false; // Switch to enable chi_0c and chi_1c mesons + +}; + +// Next function takes the optns file as argument and edits the maximum number of events to be generated. +// When used as an external generator it is important that the events passed with the -n flag are the same +// or lower of the events set in the optns file, otherwise the generation will crash. That is why the .ini +// example file contains the maximum integer available, assuming less events than that are generated in a real +// life scenario. Unfortunately a larger number cannot be used at the moment since EPOS4 has a signed integer +// type for the nfull parameter. Might be changed in the future. +// When running locally, or on the GRID (not in hyperloop), the default parameters provided in the .ini file of the +// external generation can be overwritten using the confKeyValues option (or similar depending on the tool used). +FairGenerator* generateEPOS4(const std::string &name, const int& nEvents, bool enableChi = false) +{ + // check if the file exists + auto filename = gSystem->ExpandPathName(name.c_str()); + if (!std::filesystem::exists(filename)) + { + LOG(fatal) << "Options file " << filename << " does not exist!"; + exit(1); + } + // cache all the lines of the optns file and replace the number of events + std::ifstream file(filename); + std::string line; + bool found = false; + std::stringstream buffer; + while (std::getline(file, line)) + { + if (line.find("nfull") != std::string::npos){ + // replace the number of events + found = true; + line = "set nfull " + std::to_string(nEvents); + } + buffer << line << "\n"; + } + file.close(); + auto gen = new GeneratorEPOS4(); + // Set the chi flag + gen->setChiFlag(enableChi); + auto ¶m0 = o2::eventgen::GeneratorFileOrCmdParam::Instance(); + auto ¶m = o2::eventgen::GeneratorHepMCParam::Instance(); + auto &conf = o2::conf::SimConfig::Instance(); + // Randomise seed (useful for multiple instances of the generator) + int randomSeed = gRandom->Integer(conf.getStartSeed()); + // Write the updated content back to a file in the current directory + std::string optnsFileName(Form("cfg%d.optns", randomSeed)); + std::ofstream outFile(optnsFileName); + outFile << buffer.str(); + if (!found) + { + outFile << "set nfull " + std::to_string(nEvents); + } + outFile.close(); + optnsFileName = optnsFileName.substr(0, optnsFileName.find_last_of('.')); // remove the .optns extension + // setup the HepMC generator to run with automatic FIFOs + gen->setup(param0, param, conf); + // Replace seed and optns file + gen->setCmd(param0.cmd + " -i " + optnsFileName); + gen->setSeed(randomSeed); + return gen; +} diff --git a/MC/config/examples/external/generator/SimpleCocktail.C b/MC/config/examples/external/generator/SimpleCocktail.C new file mode 100644 index 000000000..e8ce666e2 --- /dev/null +++ b/MC/config/examples/external/generator/SimpleCocktail.C @@ -0,0 +1,80 @@ +// Example of an implementation of an event generator +// that alternates between 2 gun generators. Serves as example +// to construct any meta-generator (such as cocktails) consisting +// or using a pool of underlying o2::eventgen::Generators. + +// Test is using #o2-sim-dpl-eventgen --nEvents 10 --generator external --configKeyValues "GeneratorExternal.fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/examples/external/generator/SimpleCocktail.C;GeneratorExternal.funcName=getSimpleGap()" + +namespace o2 +{ +namespace eventgen +{ + + +/// A very simple gap generator alternating between 2 different particle guns +class SimpleGap : public Generator +{ +public: + SimpleGap() { + // put 2 different generators in the cocktail of generators + gens.push_back(new o2::eventgen::BoxGenerator(22,10,-5,5,0,10,0,360)); + gens.push_back(new o2::eventgen::BoxGenerator(11,10,-5,5,0,10,0,360)); + } + + ~SimpleGap() = default; + + Bool_t Init() override + { + // init all sub-gens + for (auto gen : gens) { + gen->Init(); + } + addSubGenerator(0, "Gun 1"); // name the generators + addSubGenerator(1, "Gun 2"); + return Generator::Init(); + } + + Bool_t generateEvent() override + { + // here we call the individual gun generators in turn + // (but we could easily call all of them to have cocktails) + currentindex++; + currentgen = gens[currentindex % 2]; + currentgen->generateEvent(); + // notify the sub event generator + notifySubGenerator(currentindex % 2); + return true; + } + + // We override this function to import the particles from the + // underlying generators into **this** generator instance + Bool_t importParticles() override + { + mParticles.clear(); // clear container of mother class + currentgen->importParticles(); + std::copy(currentgen->getParticles().begin(), currentgen->getParticles().end(), std::back_insert_iterator(mParticles)); + + // we need to fix particles statuses --> need to enforce this on the importParticles level of individual generators + for (auto& p : mParticles) { + auto st = o2::mcgenstatus::MCGenStatusEncoding(p.GetStatusCode(), p.GetStatusCode()).fullEncoding; + p.SetStatusCode(st); + p.SetBit(ParticleStatus::kToBeDone, true); + } + + return true; + } + +private: + int currentindex = -1; + o2::eventgen::BoxGenerator* currentgen = nullptr; + std::vector gens; +}; + +} // namespace eventgen +} // namespace o2 + +/** generator instance and settings **/ + +FairGenerator* getSimpleGap() { + return new o2::eventgen::SimpleGap(); +} diff --git a/MC/config/examples/herwig/LHC.in b/MC/config/examples/herwig/LHC.in new file mode 100644 index 000000000..cb1ecb4fb --- /dev/null +++ b/MC/config/examples/herwig/LHC.in @@ -0,0 +1,36 @@ +# -*- ThePEG-repository -*- + +################################################################################ +# This file contains our best tune to UE data from ATLAS at 7 TeV. More recent +# tunes and tunes for other centre-of-mass energies as well as more usage +# instructions can be obtained from this Herwig wiki page: +# http://projects.hepforge.org/herwig/trac/wiki/MB_UE_tunes +# The model for soft interactions and diffractions is explained in +# [S. Gieseke, P. Kirchgaesser, F. Loshaj, arXiv:1612.04701] +################################################################################ + +read snippets/PPCollider.in + +################################################## +# Technical parameters for this run +################################################## +cd /Herwig/Generators +################################################## +# LHC physics parameters (override defaults here) +################################################## +set EventGenerator:EventHandler:LuminosityFunction:Energy 13600.0 +# Minimum Bias +read snippets/MB.in + +# Recommended set of parameters for MB/UE runs +set /Herwig/Hadronization/ColourReconnector:ReconnectionProbability 0.5 +set /Herwig/UnderlyingEvent/MPIHandler:pTmin0 3.502683 +set /Herwig/UnderlyingEvent/MPIHandler:InvRadius 1.402055 +set /Herwig/UnderlyingEvent/MPIHandler:Power 0.416852 +set /Herwig/Partons/RemnantDecayer:ladderPower -0.08 +set /Herwig/Partons/RemnantDecayer:ladderNorm 0.95 + +################################################## +# Save run for later usage with 'Herwig run' +################################################## +saverun LHC EventGenerator \ No newline at end of file diff --git a/MC/config/examples/herwig/generator_Herwig.C b/MC/config/examples/herwig/generator_Herwig.C new file mode 100644 index 000000000..1c3da112c --- /dev/null +++ b/MC/config/examples/herwig/generator_Herwig.C @@ -0,0 +1,333 @@ +#define SKIP_HEPMC_CONVERSION 1 +#define HAVE_HEPMC3 1 + +// O2DPG and ROOT includes +#include "FairGenerator.h" +#include "FairPrimaryGenerator.h" +#include "fairlogger/Logger.h" +#include "TParticle.h" +#include "TLorentzVector.h" +#include +#include +#include + +// ThePEG +#include "ThePEG/Repository/EventGenerator.h" +#include "ThePEG/EventRecord/Event.h" +#include "ThePEG/EventRecord/Particle.h" +#include "ThePEG/EventRecord/Step.h" +#include "ThePEG/Config/ThePEG.h" +#include "ThePEG/PDT/ParticleData.h" +#include "ThePEG/Repository/Repository.h" + +// Herwig +#include "Herwig/API/HerwigAPI.h" +#include "Herwig/API/HerwigUI.h" + +// Subclass of HerwigUI to provide minimal implementation of the abstract class +class SimpleHerwigUI : public Herwig::HerwigUI +{ +public: + SimpleHerwigUI(const std::string &inFile, + Herwig::RunMode::Mode mode = Herwig::RunMode::READ, int seed = 0) + : m_inFile(inFile), m_mode(mode), + m_in(inFile), m_out(std::cout), m_err(std::cerr), mSeed(seed) + { + if (!m_in) + { + LOG(fatal) << "Cannot open Herwig input file: " << inFile; + exit(1); + } + std::string hDir = std::getenv("HERWIG_ROOT"); + if (!hDir.empty()) + { + Dirs.push_back(hDir + "/share/Herwig"); + } + } + + Herwig::RunMode::Mode runMode() const override { return m_mode; } + + std::string repository() const override { + std::string rpo = std::getenv("HERWIG_ROOT"); + rpo.append("/share/Herwig/HerwigDefaults.rpo"); + return rpo; + } + std::string inputfile() const override { return m_inFile; } + std::string setupfile() const override { return ""; } + + bool resume() const override { return false; } + bool tics() const override { return false; } + std::string tag() const override { return ""; } + std::string integrationList() const override { return ""; } + + const std::vector &prependReadDirectories() const override + { + return Dirs; + } + const std::vector &appendReadDirectories() const override + { + static std::vector empty; + return empty; + } + + long N() const override { return 1; } // number of events + int seed() const override { return mSeed; } + int jobs() const override { return 1; } + unsigned int jobSize() const override { return 1; } + unsigned int maxJobs() const override { return 1; } + + void quitWithHelp() const override { std::exit(1); } + void quit() const override { std::exit(1); } + + std::ostream &outStream() const override { return m_out; } + std::ostream &errStream() const override { return m_err; } + std::istream &inStream() const override { return m_in; } + +private: + std::string m_inFile; + Herwig::RunMode::Mode m_mode; + mutable std::ifstream m_in; + std::ostream &m_out; + std::ostream &m_err; + std::vector Dirs; + int mSeed = 0; +}; + +namespace o2 +{ +namespace eventgen +{ + +/// HERWIG7 event generator using ThePEG interface +/// Author: Marco Giacalone (marco.giacalone@cern.ch) +/// Based on the O2DPG external generator configurations +class GeneratorHerwig : public Generator +{ +public: + /// Default constructor + GeneratorHerwig(const std::string& configFile = "LHC.in", int seed = -1) + : mConfigFile(configFile) + , mEventGenerator(nullptr) + { + LOG(info) << "HERWIG7 Generator construction"; + LOG(info) << "Config file: " << mConfigFile; + std::string extension = mConfigFile.substr(mConfigFile.find_last_of(".")); + if( extension == ".in" ) { + mIsInputFile = true; + LOG(info) << "Using input file for configuration"; + } else if(extension == ".run") { + mIsInputFile = false; + LOG(info) << "Using run file for configuration"; + } else { + LOG(fatal) << "No file extension found in config file: " << mConfigFile; + exit(1); + } + if (seed < 0) + { + auto &conf = o2::conf::SimConfig::Instance(); + mSeed = gRandom->Integer(conf.getStartSeed()); + } else { + mSeed = seed; + } + LOG(info) << "Using seed: " << mSeed << " for HERWIG simulation"; + } + + /// Destructor + ~GeneratorHerwig() = default; + + /// Initialize the generator + Bool_t Init() override + { + LOG(info) << "Initializing HERWIG7 Generator"; + if (mIsInputFile) { + // Process .in file + return initFromInputFile(); + } else { + // Process .run file directly + return initFromRunFile(); + } + } + + /// Generate event + Bool_t generateEvent() override + { + if (!mEventGenerator) { + LOG(error) << "Event generator not initialized"; + return kFALSE; + } + // Clear previous event particles + hParticles.clear(); + + // Generate the event + mPEGEvent = mEventGenerator->shoot(); + + if (!mPEGEvent) + { + LOG(error) << "Failed to generate event"; + return kFALSE; + } + + // Convert ThePEG event to TParticle format + convertEvent(mPEGEvent); + LOG(debug) << "Herwig7 generated " << hParticles.size() << " particles"; + + return kTRUE; + } + + /// Import particles for transport + Bool_t importParticles() override + { + if (hParticles.empty()) { + LOG(warning) << "No particles to import"; + return kFALSE; + } + + // Add particles to the primary generator + for (const auto& particle : hParticles) { + mParticles.push_back(particle); + } + + return kTRUE; + } + +private: + std::string mConfigFile; ///< HERWIG config file (.in or .run) + Bool_t mIsInputFile = false; ///< True for .in files, false for .run files + ThePEG::EGPtr mEventGenerator; ///< ThePEG event generator + std::vector hParticles; ///< Generated Herwig particles + ThePEG::EventPtr mPEGEvent; ///< Pointer to Current event + int mSeed = 0; ///< Random seed for Herwig + + void printHerwigSearchPaths() + { + const auto &list = ThePEG::Repository::listReadDirs(); + + LOG(info) << "Append directories:\n"; + for (const auto &p : list) + LOG(info) << " " << p << "\n"; + } + + /// Initialize from .in file + Bool_t initFromInputFile() + { + LOG(info) << "Initializing from .in file: " << mConfigFile; + + using namespace ThePEG; + SimpleHerwigUI ui(mConfigFile, Herwig::RunMode::READ, mSeed); + Herwig::API::read(ui); + // For debugging, print the search paths + // printHerwigSearchPaths(); + // Currently the .run filename is set inside the .in file itself with + // the line "saverun LHC EventGenerator" or similar. We assume this is the same as + // the .in file name with .run extension, so change that string accordingly in your .in files. + std::string runFile = mConfigFile; + size_t pos = runFile.find_last_of('.'); + runFile.replace(pos, 4, ".run"); + pos = runFile.find_last_of('/'); + runFile = (pos != std::string::npos) ? runFile.substr(pos + 1) : runFile; + mConfigFile = runFile; + LOG(info) << "Generated run file: " << runFile; + auto res = initFromRunFile(); + if (!res) { + LOG(error) << "Failed to initialize from generated run file"; + return kFALSE; + } + return kTRUE; + } + + /// Initialize from .run file + Bool_t initFromRunFile() + { + LOG(info) << "Initializing from .run file: " << mConfigFile; + + using namespace ThePEG; + + if (!std::ifstream(mConfigFile)) + { + LOG(info) << "Run file does not exist: " << mConfigFile; + return kFALSE; + } + SimpleHerwigUI runui(mConfigFile, Herwig::RunMode::RUN, mSeed); + // Prepare the generator + mEventGenerator = Herwig::API::prepareRun(runui); + if (!mEventGenerator) + { + LOG(fatal) << "Error: prepareRun() returned null."; + return kFALSE; + } + + mEventGenerator->initialize(); + LOG(info) << "Herwig generator initialized successfully."; + return kTRUE; + } + + /// Convert ThePEG event to TParticle format + void convertEvent(ThePEG::EventPtr event) + { + if (!event) return; + + // Get all particles from the event + const ThePEG::tPVector& particles = event->getFinalState(); + + for (size_t i = 0; i < particles.size(); ++i) { + ThePEG::tPPtr particle = particles[i]; + if (!particle) continue; + + // Get particle properties + int pdgCode = particle->id(); + int status = getFinalStateStatus(particle); + + // Get 4-momentum + ThePEG::LorentzMomentum momentum = particle->momentum(); + double px = momentum.x() / ThePEG::GeV; // Convert to GeV + double py = momentum.y() / ThePEG::GeV; + double pz = momentum.z() / ThePEG::GeV; + double e = momentum.e() / ThePEG::GeV; + + // Get production vertex + const ThePEG::LorentzPoint &vertex = particle->vertex(); + double vx = vertex.x() / ThePEG::mm; // Convert to mm + double vy = vertex.y() / ThePEG::mm; + double vz = vertex.z() / ThePEG::mm; + double vt = vertex.t() / ThePEG::mm; // Convert to mm/c + + // Create TParticle + TParticle tparticle( + pdgCode, status, + -1, -1, -1, -1, // mother and daughter indices (to be set properly) + px, py, pz, e, + vx, vy, vz, vt + ); + tparticle.SetStatusCode(o2::mcgenstatus::MCGenStatusEncoding(tparticle.GetStatusCode(), 0).fullEncoding); + tparticle.SetBit(ParticleStatus::kToBeDone, // + o2::mcgenstatus::getHepMCStatusCode(tparticle.GetStatusCode()) == 1); + + hParticles.push_back(tparticle); + } + + LOG(debug) << "Converted " << hParticles.size() << " particles from HERWIG7 event"; + } + + /// Determine final state status for particle + int getFinalStateStatus(ThePEG::tPPtr particle) + { + // In HERWIG/ThePEG, check if particle is stable + if (particle->children().empty()) { + return 1; // Final state particle + } else { + return 2; // Intermediate particle + } + } +}; + +} // namespace eventgen +} // namespace o2 + +/// HERWIG7 generator from .in/.run file. If seed is -1, a random seed is chosen +/// based on the SimConfig starting seed. +FairGenerator* generateHerwig7(const std::string inputFile = "LHC.in", int seed = -1) +{ + auto filePath = gSystem->ExpandPathName(inputFile.c_str()); + auto generator = new o2::eventgen::GeneratorHerwig(filePath, seed); + return generator; +} \ No newline at end of file diff --git a/MC/config/examples/hybrid/example.json b/MC/config/examples/hybrid/example.json new file mode 100644 index 000000000..a43766ab7 --- /dev/null +++ b/MC/config/examples/hybrid/example.json @@ -0,0 +1,30 @@ +{ + "generators": [ + { + "name": "pythia8", + "config": { + "config": "${O2_ROOT}/share/Generators/egconfig/pythia8_inel.cfg", + "hooksFileName": "", + "hooksFuncName": "", + "includePartonEvent": false, + "particleFilter": "", + "verbose": 0 + } + }, + { + "name": "evtpool", + "config": { + "eventPoolPath" : "${O2DPG_MC_CONFIG_ROOT}/MC/config/examples/hybrid/examplemanifest.dat", + "skipNonTrackable" : true, + "roundRobin" : false, + "randomize" : true, + "rngseed" : 0, + "randomphi" : false + } + } + ], + "fractions": [ + 1, + 1 + ] +} diff --git a/MC/config/examples/hybrid/examplehyp8.json b/MC/config/examples/hybrid/examplehyp8.json new file mode 100644 index 000000000..b7d66de30 --- /dev/null +++ b/MC/config/examples/hybrid/examplehyp8.json @@ -0,0 +1,79 @@ +{ + "mode": "parallel", + "generators": [ + { + "name": "external", + "config": { + "fileName": "", + "funcName": "", + "iniFile": "${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/ini/pythia8_pp_136tev.ini" + } + }, + { + "name": "external", + "config": { + "fileName": "", + "funcName": "", + "iniFile": "${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/ini/pythia8_pp_136tev.ini" + } + }, + { + "name": "external", + "config": { + "fileName": "", + "funcName": "", + "iniFile": "${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/ini/pythia8_pp_136tev.ini" + } + }, + { + "name": "external", + "config": { + "fileName": "", + "funcName": "", + "iniFile": "${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/ini/pythia8_pp_136tev.ini" + } + }, + { + "name": "external", + "config": { + "fileName": "", + "funcName": "", + "iniFile": "${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/ini/pythia8_pp_136tev.ini" + } + }, + { + "name": "external", + "config": { + "fileName": "", + "funcName": "", + "iniFile": "${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/ini/pythia8_pp_136tev.ini" + } + }, + { + "name": "external", + "config": { + "fileName": "", + "funcName": "", + "iniFile": "${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/ini/pythia8_pp_136tev.ini" + } + }, + { + "name": "external", + "config": { + "fileName": "", + "funcName": "", + "iniFile": "${O2DPG_MC_CONFIG_ROOT}/MC/config/ALICE3/ini/pythia8_pp_136tev.ini" + } + } + ], + "fractions": [ + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1 + ] +} \ No newline at end of file diff --git a/MC/config/examples/hybrid/examplemanifest.dat b/MC/config/examples/hybrid/examplemanifest.dat new file mode 100755 index 000000000..5cbfda065 --- /dev/null +++ b/MC/config/examples/hybrid/examplemanifest.dat @@ -0,0 +1,10 @@ +alien:///alice/sim/2025/EP25i1_GeneratorHF_D2H_bbbar_Mode2_XiC_NoDecay/1/001/evtpool.root +alien:///alice/sim/2025/EP25i1_GeneratorHF_D2H_bbbar_Mode2_XiC_NoDecay/1/002/evtpool.root +alien:///alice/sim/2025/EP25i1_GeneratorHF_D2H_bbbar_Mode2_XiC_NoDecay/1/003/evtpool.root +alien:///alice/sim/2025/EP25i1_GeneratorHF_D2H_bbbar_Mode2_XiC_NoDecay/1/004/evtpool.root +alien:///alice/sim/2025/EP25i1_GeneratorHF_D2H_bbbar_Mode2_XiC_NoDecay/1/005/evtpool.root +alien:///alice/sim/2025/EP25i1_GeneratorHF_D2H_bbbar_Mode2_XiC_NoDecay/1/006/evtpool.root +alien:///alice/sim/2025/EP25i1_GeneratorHF_D2H_bbbar_Mode2_XiC_NoDecay/1/007/evtpool.root +alien:///alice/sim/2025/EP25i1_GeneratorHF_D2H_bbbar_Mode2_XiC_NoDecay/1/008/evtpool.root +alien:///alice/sim/2025/EP25i1_GeneratorHF_D2H_bbbar_Mode2_XiC_NoDecay/1/009/evtpool.root +alien:///alice/sim/2025/EP25i1_GeneratorHF_D2H_bbbar_Mode2_XiC_NoDecay/1/010/evtpool.root \ No newline at end of file diff --git a/MC/config/examples/hybrid/exampleparallel.json b/MC/config/examples/hybrid/exampleparallel.json new file mode 100644 index 000000000..77416d23d --- /dev/null +++ b/MC/config/examples/hybrid/exampleparallel.json @@ -0,0 +1,47 @@ +{ + "mode": "parallel", + "generators": [ + { + "name": "pythia8pp", + "config": "" + }, + { + "name": "pythia8pp", + "config": "" + }, + { + "name": "pythia8pp", + "config": "" + }, + { + "name": "pythia8pp", + "config": "" + }, + { + "name": "pythia8pp", + "config": "" + }, + { + "name": "pythia8pp", + "config": "" + }, + { + "name": "pythia8pp", + "config": "" + }, + { + "name": "pythia8pp", + "config": "" + } + ], + "fractions": [ + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1 + ] +} \ No newline at end of file diff --git a/MC/config/examples/ini/GeneratorEPOS4.ini b/MC/config/examples/ini/GeneratorEPOS4.ini new file mode 100644 index 000000000..abfbb65dd --- /dev/null +++ b/MC/config/examples/ini/GeneratorEPOS4.ini @@ -0,0 +1,11 @@ +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/examples/epos4/generator_EPOS4.C +funcName=generateEPOS4("${O2DPG_MC_CONFIG_ROOT}/MC/config/examples/epos4/generator/example.optns", 2147483647) + +[GeneratorFileOrCmd] +cmd=${O2DPG_MC_CONFIG_ROOT}/MC/config/examples/epos4/epos.sh +bMaxSwitch=none + +# Set to version 2 if EPOS4.0.0 is used +[HepMC] +version=3 diff --git a/MC/config/examples/ini/GeneratorEPOS4HQ.ini b/MC/config/examples/ini/GeneratorEPOS4HQ.ini new file mode 100644 index 000000000..f5804d584 --- /dev/null +++ b/MC/config/examples/ini/GeneratorEPOS4HQ.ini @@ -0,0 +1,11 @@ +#---> GeneratorEPOS4 +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/examples/epos4/generator_EPOS4.C +funcName=generateEPOS4("${O2DPG_MC_CONFIG_ROOT}/MC/config/examples/epos4/generator/examplehq.optns", 2147483647) + +[GeneratorFileOrCmd] +cmd=${O2DPG_MC_CONFIG_ROOT}/MC/config/examples/epos4/epos.sh +bMaxSwitch=none + +[HepMC] +version=3 \ No newline at end of file diff --git a/MC/config/examples/ini/GeneratorEPOS4HQNeNe536TeV.ini b/MC/config/examples/ini/GeneratorEPOS4HQNeNe536TeV.ini new file mode 100644 index 000000000..b54fff7d5 --- /dev/null +++ b/MC/config/examples/ini/GeneratorEPOS4HQNeNe536TeV.ini @@ -0,0 +1,13 @@ +#NEV_TEST> 10 +#---> GeneratorEPOS4NeNe536TeV +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/examples/epos4/generator_EPOS4.C +funcName=generateEPOS4("${O2DPG_MC_CONFIG_ROOT}/MC/config/examples/epos4/generator/NeNe_536TeV_EPOS4HQ.optns", 2147483647) + +[GeneratorFileOrCmd] +cmd=${O2DPG_MC_CONFIG_ROOT}/MC/config/examples/epos4/epos.sh +bMaxSwitch=none + +# Set to version 2 if EPOS4.0.0 is used +[HepMC] +version=3 diff --git a/MC/config/examples/ini/GeneratorEPOS4HQOO536TeV.ini b/MC/config/examples/ini/GeneratorEPOS4HQOO536TeV.ini new file mode 100644 index 000000000..6f1407888 --- /dev/null +++ b/MC/config/examples/ini/GeneratorEPOS4HQOO536TeV.ini @@ -0,0 +1,13 @@ +#NEV_TEST> 1 +#---> GeneratorEPOS4OO536TeV +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/examples/epos4/generator_EPOS4.C +funcName=generateEPOS4("${O2DPG_MC_CONFIG_ROOT}/MC/config/examples/epos4/generator/OO_536TeV_EPOS4HQ.optns", 2147483647) + +[GeneratorFileOrCmd] +cmd=${O2DPG_MC_CONFIG_ROOT}/MC/config/examples/epos4/epos.sh +bMaxSwitch=none + +# Set to version 2 if EPOS4.0.0 is used +[HepMC] +version=3 diff --git a/MC/config/examples/ini/GeneratorEPOS4NeNe536TeV.ini b/MC/config/examples/ini/GeneratorEPOS4NeNe536TeV.ini new file mode 100644 index 000000000..3dc0901ad --- /dev/null +++ b/MC/config/examples/ini/GeneratorEPOS4NeNe536TeV.ini @@ -0,0 +1,12 @@ +#NEV_TEST> 10 +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/examples/epos4/generator_EPOS4.C +funcName=generateEPOS4("${O2DPG_MC_CONFIG_ROOT}/MC/config/examples/epos4/generator/NeNe_536TeV_EPOS4.optns", 2147483647) + +[GeneratorFileOrCmd] +cmd=${O2DPG_MC_CONFIG_ROOT}/MC/config/examples/epos4/epos.sh +bMaxSwitch=none + +# Set to version 2 if EPOS4.0.0 is used +[HepMC] +version=3 diff --git a/MC/config/examples/ini/GeneratorEPOS4OO536TeV.ini b/MC/config/examples/ini/GeneratorEPOS4OO536TeV.ini new file mode 100644 index 000000000..c05718ff0 --- /dev/null +++ b/MC/config/examples/ini/GeneratorEPOS4OO536TeV.ini @@ -0,0 +1,12 @@ +#NEV_TEST> 1 +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/examples/epos4/generator_EPOS4.C +funcName=generateEPOS4("${O2DPG_MC_CONFIG_ROOT}/MC/config/examples/epos4/generator/OO_536TeV_EPOS4.optns", 2147483647) + +[GeneratorFileOrCmd] +cmd=${O2DPG_MC_CONFIG_ROOT}/MC/config/examples/epos4/epos.sh +bMaxSwitch=none + +# Set to version 2 if EPOS4.0.0 is used +[HepMC] +version=3 diff --git a/MC/config/examples/ini/GeneratorEPOS4PbPb.ini b/MC/config/examples/ini/GeneratorEPOS4PbPb.ini new file mode 100644 index 000000000..6732c459c --- /dev/null +++ b/MC/config/examples/ini/GeneratorEPOS4PbPb.ini @@ -0,0 +1,12 @@ +#NEV_TEST> 1 +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/examples/epos4/generator_EPOS4.C +funcName=generateEPOS4("${O2DPG_MC_CONFIG_ROOT}/MC/config/examples/epos4/generator/examplepbpb.optns", 2147483647) + +[GeneratorFileOrCmd] +cmd=${O2DPG_MC_CONFIG_ROOT}/MC/config/examples/epos4/epos.sh +bMaxSwitch=none + +# Set to version 2 if EPOS4.0.0 is used +[HepMC] +version=3 diff --git a/MC/config/examples/ini/GeneratorEPOS4_pp136TeV.ini b/MC/config/examples/ini/GeneratorEPOS4_pp136TeV.ini new file mode 100644 index 000000000..96bf37247 --- /dev/null +++ b/MC/config/examples/ini/GeneratorEPOS4_pp136TeV.ini @@ -0,0 +1,12 @@ +#---> GeneratorEPOS4 +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/examples/epos4/generator_EPOS4.C +funcName=generateEPOS4("${O2DPG_MC_CONFIG_ROOT}/MC/config/examples/epos4/generator/pp_136TeV_hydro_cascade.optns", 214748364) + +[GeneratorFileOrCmd] +cmd=${O2DPG_MC_CONFIG_ROOT}/MC/config/examples/epos4/epos.sh +bMaxSwitch=none + +# Set to version 2 if EPOS4.0.0 is used +[HepMC] +version=3 diff --git a/MC/config/examples/ini/GeneratorEPOS4_pp136TeV_nohacas.ini b/MC/config/examples/ini/GeneratorEPOS4_pp136TeV_nohacas.ini new file mode 100644 index 000000000..f1efd0687 --- /dev/null +++ b/MC/config/examples/ini/GeneratorEPOS4_pp136TeV_nohacas.ini @@ -0,0 +1,12 @@ +#---> GeneratorEPOS4 +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/examples/epos4/generator_EPOS4.C +funcName=generateEPOS4("${O2DPG_MC_CONFIG_ROOT}/MC/config/examples/epos4/generator/pp_136TeV_hydro_cascade_off.optns", 214748364) + +[GeneratorFileOrCmd] +cmd=${O2DPG_MC_CONFIG_ROOT}/MC/config/examples/epos4/epos.sh +bMaxSwitch=none + +# Set to version 2 if EPOS4.0.0 is used +[HepMC] +version=3 diff --git a/MC/config/examples/ini/GeneratorEPOS4_pp13TeV.ini b/MC/config/examples/ini/GeneratorEPOS4_pp13TeV.ini new file mode 100644 index 000000000..3e5080bf1 --- /dev/null +++ b/MC/config/examples/ini/GeneratorEPOS4_pp13TeV.ini @@ -0,0 +1,11 @@ +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/examples/epos4/generator_EPOS4.C +funcName=generateEPOS4("${O2DPG_MC_CONFIG_ROOT}/MC/config/examples/epos4/generator/pp_13TeV_hydro_cascade.optns", 2147483647) + +[GeneratorFileOrCmd] +cmd=${O2DPG_MC_CONFIG_ROOT}/MC/config/examples/epos4/epos.sh +bMaxSwitch=none + +# Set to version 2 if EPOS4.0.0 is used +[HepMC] +version=3 diff --git a/MC/config/examples/ini/GeneratorExtToHybrid.ini b/MC/config/examples/ini/GeneratorExtToHybrid.ini new file mode 100644 index 000000000..62e0986b7 --- /dev/null +++ b/MC/config/examples/ini/GeneratorExtToHybrid.ini @@ -0,0 +1,3 @@ +[GeneratorHybrid] +configFile = ${O2DPG_MC_CONFIG_ROOT}/MC/config/examples/hybrid/example.json +switchExtToHybrid = true \ No newline at end of file diff --git a/MC/config/examples/ini/GeneratorExtToHybridParallel.ini b/MC/config/examples/ini/GeneratorExtToHybridParallel.ini new file mode 100644 index 000000000..7745d9095 --- /dev/null +++ b/MC/config/examples/ini/GeneratorExtToHybridParallel.ini @@ -0,0 +1,4 @@ +[GeneratorHybrid] +configFile = ${O2DPG_MC_CONFIG_ROOT}/MC/config/examples/hybrid/exampleparallel.json +switchExtToHybrid = true +num_workers = 8 diff --git a/MC/config/examples/ini/GeneratorHERWIG7.ini b/MC/config/examples/ini/GeneratorHERWIG7.ini new file mode 100644 index 000000000..af904d1e8 --- /dev/null +++ b/MC/config/examples/ini/GeneratorHERWIG7.ini @@ -0,0 +1,3 @@ +[GeneratorExternal] +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/examples/herwig/generator_Herwig.C +funcName=generateHerwig7("${O2DPG_MC_CONFIG_ROOT}/MC/config/examples/herwig/LHC.in") diff --git a/MC/config/examples/ini/GeneratorHYPythia8.ini b/MC/config/examples/ini/GeneratorHYPythia8.ini new file mode 100644 index 000000000..da82b263d --- /dev/null +++ b/MC/config/examples/ini/GeneratorHYPythia8.ini @@ -0,0 +1,4 @@ +[GeneratorHybrid] +configFile = ${O2DPG_MC_CONFIG_ROOT}/MC/config/examples/hybrid/examplehyp8.json +switchExtToHybrid = true +num_workers = 8 diff --git a/MC/config/examples/ini/adaptive_pythia8.ini b/MC/config/examples/ini/adaptive_pythia8.ini index 286507c43..dd3281de5 100644 --- a/MC/config/examples/ini/adaptive_pythia8.ini +++ b/MC/config/examples/ini/adaptive_pythia8.ini @@ -4,11 +4,11 @@ ### number of primary particles in the background event [GeneratorExternal] -fileName=${O2DPG_ROOT}/MC/config/examples/external/generator/adaptive_pythia8.C +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/examples/external/generator/adaptive_pythia8.C funcName=adaptive_pythia8("0.002 * x") ### The external generator derives from GeneratorPythia8. ### This part configures the underlying Pythia8 configuration. [GeneratorPythia8] -config = ${O2DPG_ROOT}/MC/config/common/pythia8/generator/pythia8_inel.cfg +config = ${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/generator/pythia8_inel.cfg diff --git a/MC/config/examples/ini/tests/GeneratorEPOS4.C b/MC/config/examples/ini/tests/GeneratorEPOS4.C new file mode 100644 index 000000000..5f735eddf --- /dev/null +++ b/MC/config/examples/ini/tests/GeneratorEPOS4.C @@ -0,0 +1,64 @@ +int External() +{ + std::string path{"o2sim_Kine.root"}; + // Check that file exists, can be opened and has the correct tree + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) + { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) + { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + // Check if all events are filled + auto nEvents = tree->GetEntries(); + for (Long64_t i = 0; i < nEvents; ++i) + { + tree->GetEntry(i); + if (tracks->empty()) + { + std::cerr << "Empty entry found at event " << i << "\n"; + return 1; + } + } + // Check if there are 100 events, as simulated in the o2dpg-test + if (nEvents != 100) + { + std::cerr << "Expected 100 events, got " << nEvents << "\n"; + return 1; + } + // check if each event has two protons with 6800 GeV of energy + // exits if the particle is not a proton + for (int i = 0; i < nEvents; i++) + { + auto check = tree->GetEntry(i); + int count = 0; + for (int idxMCTrack = 0; idxMCTrack < tracks->size(); ++idxMCTrack) + { + auto track = tracks->at(idxMCTrack); + double energy = track.GetEnergy(); + // Check if track energy is approximately equal to 6800 GeV (a tolerance of 65 keV is considered, straight equality does not work due to floating point precision) + if (std::abs(energy - 6800) < 1e-4) + { + if (track.GetPdgCode() != 2212){ + std::cerr << "Found 6800 GeV particle with pdgID " << track.GetPdgCode() << "\n"; + return 1; + } + count++; + } + } + if (count < 2) + { + std::cerr << "Event " << i << " has less than 2 protons at 6800 GeV\n"; + return 1; + } + } + return 0; +} \ No newline at end of file diff --git a/MC/config/examples/ini/tests/GeneratorEPOS4NeNe536TeV.C b/MC/config/examples/ini/tests/GeneratorEPOS4NeNe536TeV.C new file mode 100644 index 000000000..994e64c42 --- /dev/null +++ b/MC/config/examples/ini/tests/GeneratorEPOS4NeNe536TeV.C @@ -0,0 +1,78 @@ +int External() +{ + std::string path{"o2sim_Kine.root"}; + + // Check that file exists, can be opened and has the correct tree + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) + { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree*)file.Get("o2sim"); + if (!tree) + { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + + std::vector* tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + // Check if all events are filled + auto nEvents = tree->GetEntries(); + for (Long64_t i = 0; i < nEvents; ++i) + { + tree->GetEntry(i); + if (tracks->empty()) + { + std::cerr << "Empty entry found at event " << i << "\n"; + return 1; + } + } + + // Check if there is 1 event, as customarily set in the ini file + // Heavy-ion collisions with hydro and hadronic cascade are very slow to simulate + if (nEvents != 10) + { + std::cerr << "Expected 10 events, got " << nEvents << "\n"; + return 1; + } + + // ---- Neon-Neon parameters ---- + constexpr int kNeonPDG = 1000100200; // Ne-20 ion + constexpr double kEnucleon = 5360.; // GeV per nucleon + constexpr int kA = 20; // Neon mass number + constexpr double kNeonEnergy = kA * kEnucleon / 2.0; // beam energy in GeV + + // Check if each event has two neon ions at expected energy + for (int i = 0; i < nEvents; i++) + { + tree->GetEntry(i); + int count = 0; + + for (int idxMCTrack = 0; idxMCTrack < tracks->size(); ++idxMCTrack) + { + auto track = tracks->at(idxMCTrack); + double energy = track.GetEnergy(); + + // 50 MeV tolerance (floating point safety) + if (std::abs(energy - kNeonEnergy) < 5e-2 && + track.GetPdgCode() == kNeonPDG) + { + count++; + } + } + + if (count < 2) + { + std::cerr << "Event " << i + << " has less than 2 neon ions at " + << kNeonEnergy << " GeV\n"; + return 1; + } + } + + return 0; +} diff --git a/MC/config/examples/ini/tests/GeneratorEPOS4OO536TeV.C b/MC/config/examples/ini/tests/GeneratorEPOS4OO536TeV.C new file mode 100644 index 000000000..97798d7e2 --- /dev/null +++ b/MC/config/examples/ini/tests/GeneratorEPOS4OO536TeV.C @@ -0,0 +1,78 @@ +int External() +{ + std::string path{"o2sim_Kine.root"}; + + // Check that file exists, can be opened and has the correct tree + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) + { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) + { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + // Check if all events are filled + auto nEvents = tree->GetEntries(); + for (Long64_t i = 0; i < nEvents; ++i) + { + tree->GetEntry(i); + if (tracks->empty()) + { + std::cerr << "Empty entry found at event " << i << "\n"; + return 1; + } + } + + // Check if there is 1 event, as customly set in the ini file + // Heavy-ion collisions with hydro and hadronic cascade are very slow to simulate + if (nEvents != 1) + { + std::cerr << "Expected 1 event, got " << nEvents << "\n"; + return 1; + } + + // ---- Oxygen-Oxygen parameters ---- + constexpr int kOxygenPDG = 1000080160; // O-16 ion + constexpr double kEnucleon = 5360.; // GeV per nucleon + constexpr int kA = 16; // Oxygen mass number + constexpr double kOxygenEnergy = kA * kEnucleon / 2.0; // 85760 / 2 GeV + + // Check if each event has two oxygen ions at expected energy + for (int i = 0; i < nEvents; i++) + { + tree->GetEntry(i); + int count = 0; + + for (int idxMCTrack = 0; idxMCTrack < tracks->size(); ++idxMCTrack) + { + auto track = tracks->at(idxMCTrack); + double energy = track.GetEnergy(); + + // 50 MeV tolerance (floating point safety) + if (std::abs(energy - kOxygenEnergy) < 5e-2 && + track.GetPdgCode() == kOxygenPDG) + { + count++; + } + } + + if (count < 2) + { + std::cerr << "Event " << i + << " has less than 2 oxygen ions at " + << kOxygenEnergy << " GeV\n"; + return 1; + } + } + + return 0; +} diff --git a/MC/config/examples/ini/tests/GeneratorEPOS4PbPb.C b/MC/config/examples/ini/tests/GeneratorEPOS4PbPb.C new file mode 100644 index 000000000..3cc43d99c --- /dev/null +++ b/MC/config/examples/ini/tests/GeneratorEPOS4PbPb.C @@ -0,0 +1,60 @@ +int External() +{ + std::string path{"o2sim_Kine.root"}; + // Check that file exists, can be opened and has the correct tree + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) + { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) + { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + // Check if all events are filled + auto nEvents = tree->GetEntries(); + for (Long64_t i = 0; i < nEvents; ++i) + { + tree->GetEntry(i); + if (tracks->empty()) + { + std::cerr << "Empty entry found at event " << i << "\n"; + return 1; + } + } + // Check if there is 1 event, as customly set in the ini file + // Lead-Lead collisions with hydro and hadronic cascade are very slow to simulate + if (nEvents != 1) + { + std::cerr << "Expected 1 event, got " << nEvents << "\n"; + return 1; + } + // check if each event has two lead ions with 557440 (208*5360) GeV of energy + for (int i = 0; i < nEvents; i++) + { + auto check = tree->GetEntry(i); + int count = 0; + for (int idxMCTrack = 0; idxMCTrack < tracks->size(); ++idxMCTrack) + { + auto track = tracks->at(idxMCTrack); + double energy = track.GetEnergy(); + // Check if lead ion track energy is approximately equal to 557440 GeV (a tolerance of 50 MeV is considered, straight equality does not work due to floating point precision) + if (std::abs(energy - 557440) < 5e-2 && track.GetPdgCode() == 1000822080) + { + count++; + } + } + if (count < 2) + { + std::cerr << "Event " << i << " has less than 2 lead ions at 557440 GeV\n"; + return 1; + } + } + return 0; +} \ No newline at end of file diff --git a/MC/config/examples/ini/tests/GeneratorEPOS4_pp13TeV.C b/MC/config/examples/ini/tests/GeneratorEPOS4_pp13TeV.C new file mode 100644 index 000000000..eac9c321b --- /dev/null +++ b/MC/config/examples/ini/tests/GeneratorEPOS4_pp13TeV.C @@ -0,0 +1,64 @@ +int External() +{ + std::string path{"o2sim_Kine.root"}; + // Check that file exists, can be opened and has the correct tree + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) + { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) + { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + // Check if all events are filled + auto nEvents = tree->GetEntries(); + for (Long64_t i = 0; i < nEvents; ++i) + { + tree->GetEntry(i); + if (tracks->empty()) + { + std::cerr << "Empty entry found at event " << i << "\n"; + return 1; + } + } + // Check if there are 100 events, as simulated in the o2dpg-test + if (nEvents != 100) + { + std::cerr << "Expected 100 events, got " << nEvents << "\n"; + return 1; + } + // check if each event has two protons with 6500 GeV of energy + // exits if the particle is not a proton + for (int i = 0; i < nEvents; i++) + { + auto check = tree->GetEntry(i); + int count = 0; + for (int idxMCTrack = 0; idxMCTrack < tracks->size(); ++idxMCTrack) + { + auto track = tracks->at(idxMCTrack); + double energy = track.GetEnergy(); + // Check if track energy is approximately equal to 6500 GeV (a tolerance of 65 keV is considered, straight equality does not work due to floating point precision) + if (std::abs(energy - 6500) < 1e-4) + { + if (track.GetPdgCode() != 2212){ + std::cerr << "Found 6500 GeV particle with pdgID " << track.GetPdgCode() << "\n"; + return 1; + } + count++; + } + } + if (count < 2) + { + std::cerr << "Event " << i << " has less than 2 protons at 6500 GeV\n"; + return 1; + } + } + return 0; +} diff --git a/MC/config/examples/ini/tests/GeneratorExtToHybrid.C b/MC/config/examples/ini/tests/GeneratorExtToHybrid.C new file mode 100644 index 000000000..2694d57e4 --- /dev/null +++ b/MC/config/examples/ini/tests/GeneratorExtToHybrid.C @@ -0,0 +1,38 @@ +int Hybrid() +{ + std::string path{"o2sim_Kine.root"}; + // Check that file exists, can be opened and has the correct tree + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) + { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) + { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + // Check if all events are filled + auto nEvents = tree->GetEntries(); + for (Long64_t i = 0; i < nEvents; ++i) + { + tree->GetEntry(i); + if (tracks->empty()) + { + std::cerr << "Empty entry found at event " << i << "\n"; + return 1; + } + } + // Check if there are 100 events, as simulated in the o2dpg-test + if (nEvents != 100) + { + std::cerr << "Expected 100 events, got " << nEvents << "\n"; + return 1; + } + return 0; +} diff --git a/MC/config/examples/ini/tests/GeneratorExtToHybridParallel.C b/MC/config/examples/ini/tests/GeneratorExtToHybridParallel.C new file mode 100644 index 000000000..2694d57e4 --- /dev/null +++ b/MC/config/examples/ini/tests/GeneratorExtToHybridParallel.C @@ -0,0 +1,38 @@ +int Hybrid() +{ + std::string path{"o2sim_Kine.root"}; + // Check that file exists, can be opened and has the correct tree + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) + { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) + { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + // Check if all events are filled + auto nEvents = tree->GetEntries(); + for (Long64_t i = 0; i < nEvents; ++i) + { + tree->GetEntry(i); + if (tracks->empty()) + { + std::cerr << "Empty entry found at event " << i << "\n"; + return 1; + } + } + // Check if there are 100 events, as simulated in the o2dpg-test + if (nEvents != 100) + { + std::cerr << "Expected 100 events, got " << nEvents << "\n"; + return 1; + } + return 0; +} diff --git a/MC/config/examples/ini/tests/GeneratorHERWIG7.C b/MC/config/examples/ini/tests/GeneratorHERWIG7.C new file mode 100644 index 000000000..384dda574 --- /dev/null +++ b/MC/config/examples/ini/tests/GeneratorHERWIG7.C @@ -0,0 +1,38 @@ +int External() +{ + std::string path{"o2sim_Kine.root"}; + // Check that file exists, can be opened and has the correct tree + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) + { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) + { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + // Check if all events are filled + auto nEvents = tree->GetEntries(); + for (Long64_t i = 0; i < nEvents; ++i) + { + tree->GetEntry(i); + if (tracks->empty()) + { + std::cerr << "Empty entry found at event " << i << "\n"; + return 1; + } + } + // Check if there are 100 events, as simulated in the o2dpg-test + if (nEvents != 100) + { + std::cerr << "Expected 100 events, got " << nEvents << "\n"; + return 1; + } + return 0; +} \ No newline at end of file diff --git a/MC/config/examples/ini/tests/GeneratorHYPythia8.C b/MC/config/examples/ini/tests/GeneratorHYPythia8.C new file mode 100644 index 000000000..2694d57e4 --- /dev/null +++ b/MC/config/examples/ini/tests/GeneratorHYPythia8.C @@ -0,0 +1,38 @@ +int Hybrid() +{ + std::string path{"o2sim_Kine.root"}; + // Check that file exists, can be opened and has the correct tree + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) + { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) + { + std::cerr << "Cannot find tree o2sim in file " << path << "\n"; + return 1; + } + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + + // Check if all events are filled + auto nEvents = tree->GetEntries(); + for (Long64_t i = 0; i < nEvents; ++i) + { + tree->GetEntry(i); + if (tracks->empty()) + { + std::cerr << "Empty entry found at event " << i << "\n"; + return 1; + } + } + // Check if there are 100 events, as simulated in the o2dpg-test + if (nEvents != 100) + { + std::cerr << "Expected 100 events, got " << nEvents << "\n"; + return 1; + } + return 0; +} diff --git a/MC/config/examples/ini/trigger_mpi.ini b/MC/config/examples/ini/trigger_mpi.ini index cee2378b5..24bd0aa2a 100644 --- a/MC/config/examples/ini/trigger_mpi.ini +++ b/MC/config/examples/ini/trigger_mpi.ini @@ -3,10 +3,10 @@ ### according to the specified function call [TriggerExternal] -fileName=${O2DPG_ROOT}/MC/config/common/external/trigger/trigger_mpi.C +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/external/trigger/trigger_mpi.C funcName=trigger_mpi(10) ### This part configures Pythia8 [GeneratorPythia8] -config = ${O2DPG_ROOT}/MC/config/common/pythia8/generator/pythia8_inel.cfg +config = ${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/generator/pythia8_inel.cfg diff --git a/MC/config/examples/ini/trigger_multiplicity.ini b/MC/config/examples/ini/trigger_multiplicity.ini index 36137bb4d..6ad6e12e3 100644 --- a/MC/config/examples/ini/trigger_multiplicity.ini +++ b/MC/config/examples/ini/trigger_multiplicity.ini @@ -3,10 +3,10 @@ ### according to the specified function call [TriggerExternal] -fileName=${O2DPG_ROOT}/MC/config/common/external/trigger/trigger_multiplicity.C +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/external/trigger/trigger_multiplicity.C funcName=trigger_multiplicity(-0.8, 0.8, 100) ### This part configures Pythia8 [GeneratorPythia8] -config = ${O2DPG_ROOT}/MC/config/common/pythia8/generator/pythia8_inel.cfg +config = ${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/generator/pythia8_inel.cfg diff --git a/MC/config/examples/ini/trigger_multiplicity_stableparticles_inFIT.ini b/MC/config/examples/ini/trigger_multiplicity_stableparticles_inFIT.ini index e0711c43f..bc1a0e991 100644 --- a/MC/config/examples/ini/trigger_multiplicity_stableparticles_inFIT.ini +++ b/MC/config/examples/ini/trigger_multiplicity_stableparticles_inFIT.ini @@ -3,10 +3,10 @@ ### according to the specified function call [TriggerExternal] -fileName=${O2DPG_ROOT}/MC/config/common/external/trigger/multiplicity_stableparticles_inFIT.C +fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/external/trigger/multiplicity_stableparticles_inFIT.C funcName=multiplicity_stableparticles_inFIT(5350) ### This part configures Pythia8 [GeneratorPythia8] -config = ${O2DPG_ROOT}/MC/config/common/pythia8/generator/pythia8_hi.cfg +config = ${O2DPG_MC_CONFIG_ROOT}/MC/config/common/pythia8/generator/pythia8_hi.cfg diff --git a/MC/config/examples/trigger/trigger_impactb.macro b/MC/config/examples/trigger/trigger_impactb.macro new file mode 100644 index 000000000..076af058c --- /dev/null +++ b/MC/config/examples/trigger/trigger_impactb.macro @@ -0,0 +1,45 @@ +#include "Generators/Trigger.h" +#include "TParticle.h" +#include + +// a very simple trigger example, examining generated particles +o2::eventgen::Trigger trigger() +{ + // + return [](const std::vector& particles) -> bool { + std::cout << "Running trigger on event with size " << particles.size() << "\n"; + if (particles.size() > 10000) { + return true; + } + return false; + }; +} + +#include "Pythia8/Pythia.h" +#include "Pythia8/HIInfo.h" +#include +// a deep trigger example, looking into the internal generator state +o2::eventgen::DeepTrigger + trigger_impactb_pythia8(double bmin = 5., double bmax = 10.) +{ + return [bmin, bmax](void* interface, std::string name) -> bool { + if (!name.compare("pythia8")) { + auto py8 = reinterpret_cast(interface); +#if PYTHIA_VERSION_INTEGER < 8300 + auto hiinfo = py8->info.hiinfo; +#else + auto hiinfo = py8->info.hiInfo; +#endif + if (!hiinfo) { + LOG(fatal) << "Cannot define impact parameter: is \'pythia8\' running in heavy-ion mode?"; + } + auto b = hiinfo->b(); + auto selected = (b > bmin && b < bmax); + LOG(info) << "Impact parameter = " << b << " fm: " << (selected ? "selected" : "rejected"); + return selected; + } else { + LOG(fatal) << "Cannot define impact parameter for generator interface \'" << name << "\'"; + } + return false; + }; +} diff --git a/MC/prodinfo/README.md b/MC/prodinfo/README.md new file mode 100644 index 000000000..af99241b5 --- /dev/null +++ b/MC/prodinfo/README.md @@ -0,0 +1,9 @@ +This directory contains scripts and function to collect, define and upload +CCDB meta data objects for (official) MC productions. + +This meta data can be queried in other stages, such as analysis, for the purpose of further data processing. + +TODO: + +- include cycle number in data +- include software versions (2tag or not) \ No newline at end of file diff --git a/MC/prodinfo/bctvx_evselqa_harvester.py b/MC/prodinfo/bctvx_evselqa_harvester.py new file mode 100755 index 000000000..387abd293 --- /dev/null +++ b/MC/prodinfo/bctvx_evselqa_harvester.py @@ -0,0 +1,312 @@ +#!/usr/bin/env python3 +""" +PyROOT pipeline to: + - parse file paths like ./LHC25as/cpass0/568664/AnalysisResults.root + - choose highest-priority pass per run + - extract histogram event-selection-qa-task/hBcTVX + - hash histogram to prevent duplicates + - write upload ROOT file + - upload using o2-ccdb-upload (or optionally call o2::CcdbApi from C++) + + +Usage: + python3 upload_pipeline.py --file-list files.txt + OR + python3 upload_pipeline.py ./LHC25as/*/*/*/AnalysisResults.root +""" + +import os +import sys +import json +import argparse +import hashlib +import tempfile +import subprocess +from collections import defaultdict + +# PyROOT import +import ROOT +from ROOT import o2 # for O2 access + +# -------- user config ---------- +# priority: earlier in list -> higher priority +PASS_PRIORITY = ["apass6", "apass5", "apass4", "apass3", "apass2", "apass1", "cpass0"] + +# path inside AnalysisResults.root to histogram +HIST_PATH = "event-selection-qa-task/hBcTVX" + +# Local JSON file storing processed histogram hashes to avoid duplicates +PROCESSED_HASH_DB = "processed_hashes.json" + +def getRunInformation(runnumber): + runInfo = o2.parameters.AggregatedRunInfo.buildAggregatedRunInfo(o2.ccdb.BasicCCDBManager.instance(), runnumber) + return {"SOR" : runInfo.sor, + "EOR" : runInfo.eor} + + +def make_ccdb_upload_command(localfile, passname, runnumber, sor, eor, key="ccdb_object"): + l = [ + "o2-ccdb-upload", + "--host", "http://ccdb-test.cern.ch:8080", # <-- adapt to your CCDB server + "--path", "GLO/CALIB/EVSELQA/HBCTVX", # will be filled per-run + "--file", f"{localfile}", # will be replaced with filename + "-k", f"{key}", + "-m", f"run_number={runnumber};pass={passname}", # no extra quotes here (only needed on shell) + "--starttimestamp", f"{sor}", + "--endtimestamp", f"{eor}", + ] + return l # " ".join(l) + +# ------------------------------- +def load_processed_db(path): + if os.path.exists(path): + with open(path, "r") as f: + return json.load(f) + else: + return {"hashes": []} + + +def save_processed_db(path, db): + with open(path, "w") as f: + json.dump(db, f, indent=2) + + +def parse_path_meta(filepath): + """ + Find a pattern *////AnalysisResults.root anywhere in the path. + Returns {period, pass, run}. + + Example accepted paths: + ./LHC25as/cpass0/568664/AnalysisResults.root + /tmp/foo/2023/LHC23zzh/cpass0/544095/AnalysisResults.root + """ + p = os.path.normpath(filepath) + parts = p.split(os.sep) + + # Find the index of AnalysisResults.root + try: + idx = parts.index("AnalysisResults.root") + except ValueError: + # maybe something like analysisresults.root? Lowercase? + # Try case-insensitive fallback + idx = None + for i, comp in enumerate(parts): + if comp.lower() == "analysisresults.root": + idx = i + break + if idx is None: + raise ValueError(f"File does not contain AnalysisResults.root: {filepath}") + + # Need at least 3 dirs before it: period, pass, run + if idx < 3: + raise ValueError(f"Cannot extract period/pass/run from short path: {filepath}") + + run = parts[idx-1] + passname = parts[idx-2] + period = parts[idx-3] + + # Optional sanity checks + if not run.isdigit(): + raise ValueError(f"Run number is not numeric: '{run}' in path {filepath}") + + return {"period": period, "pass": passname, "run": run} + + +def pass_priority_rank(pass_name): + try: + return PASS_PRIORITY.index(pass_name) + except ValueError: + # unknown pass name -> low priority (append at end) + return len(PASS_PRIORITY) + + +def pick_best_pass_file(files_for_run): + """ + files_for_run: list of dicts with keys {pass, path, period} + returns the dict for the chosen file (highest priority) + """ + # sort by priority (lower index -> higher preference) + files_sorted = sorted(files_for_run, key=lambda x: pass_priority_rank(x["pass"])) + return files_sorted[0] if files_sorted else None + + +def histogram_hash(hist): + """ + Deterministic hash of a TH1* content: + - axis nbins, xmin, xmax + - bin contents + bin errors + Returns hex sha256 string. + """ + h = hist + nbins = h.GetNbinsX() + xmin = h.GetXaxis().GetXmin() + xmax = h.GetXaxis().GetXmax() + # collect values + m = hashlib.sha256() + m.update(f"{nbins}|{xmin}|{xmax}|{h.GetName()}|{h.GetTitle()}".encode("utf-8")) + for b in range(0, nbins + 2): # include under/overflow + c = float(h.GetBinContent(b)) + e = float(h.GetBinError(b)) + m.update(f"{b}:{c:.17g}:{e:.17g};".encode("utf-8")) + return m.hexdigest() + + +def extract_histogram_from_file(root_path, hist_path): + """ + Returns a clone of the TH1 found at hist_path or raises on error. + """ + f = ROOT.TFile.Open(root_path, "READ") + if not f or f.IsZombie(): + raise IOError(f"Cannot open file {root_path}") + obj = f.Get(hist_path) + if not obj: + f.Close() + raise KeyError(f"Histogram {hist_path} not found in {root_path}") + if not isinstance(obj, ROOT.TH1): + f.Close() + raise TypeError(f"Object at {hist_path} is not a TH1 (found {type(obj)}) in {root_path}") + # clone to decouple from file and then close file + clone = obj.Clone(obj.GetName()) + clone.SetDirectory(0) + f.Close() + return clone + + +def write_upload_root(hist, meta, outpath): + """ + Writes histogram and metadata (as a TObjString) into a new ROOT file for uploading. + meta: dict of metadata (period, pass, run, runinfo, hash) + """ + f = ROOT.TFile(outpath, "RECREATE") + f.cd() + # set name to include run for clarity + hist_copy = hist.Clone(hist.GetName()) + hist_copy.SetDirectory(f) + hist_copy.Write() + # write metadata as JSON inside TObjString + json_meta = json.dumps(meta) + sobj = ROOT.TObjString(json_meta) + sobj.Write("metadata") + f.Close() + + +def upload_ccdb_via_cli(upload_file, ccdb_path, passname, runnumber, sor, eor): + """ + Call o2-ccdb-upload CLI with CCDB_UPLOAD_CMD template. + Adjust template above for your environment if needed. + """ + cmd = make_ccdb_upload_command(upload_file, passname, runnumber, sor, eor, key="hBcTVX") + print("Running upload command:", " ".join(cmd)) + res = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) + if res.returncode != 0: + # raise RuntimeError(f"o2-ccdb-upload failed: {res.returncode}\nstdout:{res.stdout}\nstderr:{res.stderr}") + print (f"o2-ccdb-upload failed: {res.returncode}\nstdout:{res.stdout}\nstderr:{res.stderr}") + return False + + print (f"o2-ccdb-upload succeeded: {res.returncode}\nstdout:{res.stdout}\nstderr:{res.stderr}") + return True + + +def main(argv): + parser = argparse.ArgumentParser(description="Extract histogram from AnalysisResults.root and upload to CCDB") + parser.add_argument("--file-list", help="Text file with one file path per line (or '-')", default=None) + parser.add_argument("paths", nargs="*", help="globs or paths to AnalysisResults.root files") + parser.add_argument("--skip-upload", action="store_true", help="Only create upload ROOT files, do not call o2-ccdb-upload") + parser.add_argument("--out-dir", default="ccdb_uploads", help="Where to put temporary upload ROOT files") + parser.add_argument("--processed-db", default=PROCESSED_HASH_DB, help="JSON file to keep processed-hashes") + parser.add_argument("--ccdb-base-path", default="/calibration/hBcTVX", help="Base path inside CCDB where to upload") + args = parser.parse_args(argv) + + # collect files + file_paths = [] + if args.file_list: + if args.file_list == "-": + lines = sys.stdin.read().splitlines() + else: + with open(args.file_list, "r") as f: + lines = [ln.strip() for ln in f if ln.strip()] + file_paths.extend(lines) + if args.paths: + # expand globs + import glob + for p in args.paths: + file_paths.extend(sorted(glob.glob(p))) + if not file_paths: + print("No files provided. Exiting.") + return 1 + + # build per-run grouping + runs = defaultdict(list) + for p in file_paths: + try: + meta = parse_path_meta(p) + except Exception as e: + print(f"Skipping {p}: cannot parse path: {e}") + continue + runs[meta["run"]].append({"path": p, "pass": meta["pass"], "period": meta["period"]}) + + # load processed DB + db = load_processed_db(args.processed_db) + processed_hashes = set(db.get("hashes", [])) + + os.makedirs(args.out_dir, exist_ok=True) + + for run, filelist in runs.items(): + selected = pick_best_pass_file(filelist) + if not selected: + print(f"No candidate for run {run}, skipping.") + continue + path = selected["path"] + period = selected["period"] + pass_name = selected["pass"] + print(f"Selected for run {run}: {path} (period={period}, pass={pass_name})") + + try: + hist = extract_histogram_from_file(path, HIST_PATH) + except Exception as e: + print(f"Failed to extract histogram from {path}: {e}") + continue + + # compute hash + hsh = histogram_hash(hist) + if hsh in processed_hashes: + print(f"Histogram hash {hsh} for run {run} already processed -> skipping upload.") + continue + + # get run information + runinfo = getRunInformation(int(run)) + + # prepare metadata + meta = { + "period": period, + "pass": pass_name, + "run": run, + "runinfo": runinfo, + "hist_name": hist.GetName(), + "hist_title": hist.GetTitle(), + "hash": hsh + } + + # write temporary upload file + out_fname = os.path.join(args.out_dir, f"upload_{period}_{pass_name}_{run}.root") + write_upload_root(hist, meta, out_fname) + print(f"Wrote upload file: {out_fname}") + + # perform upload + if not args.skip_upload: + # build ccdb path (customize to your conventions) + ccdb_path = os.path.join(args.ccdb_base_path, period, pass_name, run) + upload_ccdb_via_cli(out_fname, ccdb_path, pass_name, run, runinfo["SOR"], runinfo["EOR"]) + + # mark as processed (only after successful upload or skip-upload) + processed_hashes.add(hsh) + db["hashes"] = list(processed_hashes) + save_processed_db(args.processed_db, db) + print(f"Marked hash {hsh} as processed.") + + print("Done.") + return 0 + + +if __name__ == "__main__": + sys.exit(main(sys.argv[1:])) diff --git a/MC/prodinfo/mcprodinfo_ccdb_upload.py b/MC/prodinfo/mcprodinfo_ccdb_upload.py new file mode 100644 index 000000000..2de6cfd81 --- /dev/null +++ b/MC/prodinfo/mcprodinfo_ccdb_upload.py @@ -0,0 +1,160 @@ +import json +import os +import requests +import subprocess + +import dataclasses # to define the MCProdInfo data layout and convert it to dict +from dataclasses import dataclass, field, asdict, fields +from typing import Optional +import hashlib + +@dataclass(frozen=True) +class MCProdInfo: + """ + struct for MonteCarlo production info + """ + LPMProductionTag: str + Col: int + IntRate: float # only indicative of some interaction rate (could vary within the run) + RunNumber: int + OrbitsPerTF: int + # max_events_per_tf: Optional[int] = -1 + Comment: Optional[str] = None + McTag: Optional[str] = None # main software tag used + RecoTag: Optional[str] = None # RecoTag (if any) + Hash: Optional[str] = field(default=None) + + def __post_init__(self): + if self.Hash == None: + # Hash only the meaningful fields + data_to_hash = { + k: v for k, v in asdict(self).items() + if k != 'Hash' + } + hash_str = hashlib.sha256( + json.dumps(data_to_hash, sort_keys=True).encode() + ).hexdigest() + object.__setattr__(self, 'Hash', hash_str) + + +import re + +def extract_metadata_blocks_from_CCDB(text: str): + blocks = [] + # Split on 'Metadata:\n' and iterate over each block + sections = text.split('Metadata:\n') + for section in sections[1:]: # skip the first chunk (before any Metadata:) + metadata = {} + for line in section.splitlines(): + if not line.strip(): # stop at first blank line + break + match = re.match(r'\s*(\w+)\s*=\s*(.+)', line) + if match: + key, val = match.groups() + # Type conversion + if val == "None": + val = None + elif val.isdigit() or (val.startswith('-') and val[1:].isdigit()): + val = int(val) + else: + try: + val = float(val) + except ValueError: + val = val.strip() + metadata[key] = val + if metadata: + blocks.append(metadata) + return blocks + + + +def query_mcprodinfo(base_url, user, run_number, lpm_prod_tag, cert_dir="/tmp"): + """ + Queries MCProdInfo from CCDB. Returns object or None + """ + # check if the tokenfiles are there + key_path = os.environ.get("JALIEN_TOKEN_KEY") + cert_path = os.environ.get("JALIEN_TOKEN_CERT") + if key_path == None and cert_path == None: + uid = os.getuid() + cert_path = os.path.join(cert_dir, f"tokencert_{uid}.pem") + key_path = os.path.join(cert_dir, f"tokenkey_{uid}.pem") + + # Build full URL + user_path = 'Users/' + user[0] + '/' + user + start = run_number + stop = run_number + 1 + url = f"{base_url}/browse/{user_path}/MCProdInfo/{lpm_prod_tag}/{start}/{stop}" + + response = requests.get(url, cert=(cert_path, key_path), verify=False) + if response.status_code != 404: + meta = extract_metadata_blocks_from_CCDB(response.content.decode('utf-8')) + if (len(meta) > 0): + def filter_known_fields(cls, data: dict) -> dict: + valid_keys = {f.name for f in fields(cls)} + return {k: v for k, v in data.items() if k in valid_keys} + + clean_meta = filter_known_fields(MCProdInfo, meta[0]) + return MCProdInfo(**clean_meta) + + return None + + +def upload_mcprodinfo_meta(base_url, user, run_number, lpm_prod_tag, keys, cert_dir="/tmp"): + """ + Uploads an empty .dat file using client certificates. + + Parameters: + - base_url (str): The base HTTPS URL, e.g., "https://URL" + - user (str): The uploader --> Determines location "Users/f/foo_bar/MCProdInfo/..." + - keys (dict): Dictionary with meta information to upload, e.g., {"key1": "var1", "key2": "var2"} + - cert_dir (str): Directory where the .pem files are located (default: /tmp) + + Returns: + - Response object from the POST request + """ + # Create an empty file + empty_file = "empty.dat" + with open(empty_file, "w") as f: + f.write("0") + + # Construct user ID-specific cert and key paths + key_path = os.environ.get("JALIEN_TOKEN_KEY") + cert_path = os.environ.get("JALIEN_TOKEN_CERT") + if key_path == None and cert_path == None: + uid = os.getuid() + cert_path = os.path.join(cert_dir, f"tokencert_{uid}.pem") + key_path = os.path.join(cert_dir, f"tokenkey_{uid}.pem") + + # Build full URL + query = "/".join(f"{k}={v}" for k, v in keys.items()) + user_path = 'Users/' + user[0] + '/' + user + start = run_number + stop = run_number + 1 + url = f"{base_url}/{user_path}/MCProdInfo/{lpm_prod_tag}/{start}/{stop}/{query}" + + print (f"Full {url}") + + # Prepare request + with open(empty_file, 'rb') as f: + files = {'blob': f} + response = requests.post(url, files=files, cert=(cert_path, key_path), verify=False) + + # Optional: remove the temporary file + os.remove(empty_file) + + return response + +def publish_MCProdInfo(mc_prod_info, ccdb_url = "https://alice-ccdb.cern.ch", username = "aliprod", force_overwrite=False, include_meta_into_aod=False): + print("Publishing MCProdInfo") + + if mc_prod_info.LPMProductionTag == None or len(mc_prod_info.LPMProductionTag) == 0: + print ("No LPM production tag found; Not publishing") + return + + # see if this already has meta-data uploaded, otherwise do nothing + mc_prod_info_q = query_mcprodinfo(ccdb_url, username, mc_prod_info.RunNumber, mc_prod_info.LPMProductionTag) + if mc_prod_info_q == None or force_overwrite == True: + # could make this depend on hash values in future + upload_mcprodinfo_meta(ccdb_url, username, mc_prod_info.RunNumber, mc_prod_info.LPMProductionTag, dataclasses.asdict(mc_prod_info)) + diff --git a/MC/prodinfo/mcprodinfo_harvester.py b/MC/prodinfo/mcprodinfo_harvester.py new file mode 100755 index 000000000..426bdb767 --- /dev/null +++ b/MC/prodinfo/mcprodinfo_harvester.py @@ -0,0 +1,205 @@ +#!/usr/bin/env python3 + +# A python tool, that fills CCDB MCProdInfo by +# harvesting the data from production log files from the GRID. +# This is useful, when the information was not directly filled +# by the MC job itself. + +import json +import subprocess +import sys +from collections import defaultdict +from zipfile import ZipFile +import re +import os +import argparse + +from mcprodinfo_ccdb_upload import MCProdInfo, publish_MCProdInfo + + +def alien_find(path, pattern="*"): + cmd = ["alien.py", "find", path, pattern] + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return [line.strip() for line in result.stdout.splitlines() if line.strip()] + + +def alien_cp(alien_path, local_path): + cmd = ["alien.py", "cp", f"alien://{alien_path}", f"file://{local_path}"] + subprocess.run(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=True) + + +def parse_workflow_path(path, prod_tag): + parts = path.strip("/").split("/") + try: + idx = parts.index(prod_tag) + except ValueError: + return None + + after = parts[idx + 1 :] + if len(after) < 2: + return None + + if after[0].isdigit() and len(after[0]) == 1: + cycle = int(after[0]) + run_number = int(after[1]) + split = after[2] + else: + cycle = None + run_number = int(after[0]) + split = after[1] + + return cycle, run_number, split + + +def extract_from_zip(local_zip_path): + """Extract workflow.json and stdout from log_archive.zip.""" + wf_data = None + env_vars = {} + try: + with ZipFile(local_zip_path, "r") as zf: + # workflow.json + if "workflow.json" in zf.namelist(): + with zf.open("workflow.json") as wf_file: + wf_data = json.load(wf_file) + + # stdout (could be named stdout or stdout.log) + candidates = [n for n in zf.namelist() if n.startswith("stdout")] + if candidates: + with zf.open(candidates[0]) as so: + text = so.read().decode(errors="ignore") + for key in [ + "ALIEN_JDL_PACKAGES", + "ALIEN_JDL_O2DPG_ASYNC_RECO_TAG", + "ALIEN_MASTERJOB", + ]: + m = re.search(rf"{key}=(.*)", text) + if m: + env_vars[key] = m.group(1).strip() + except Exception as e: + print(f"⚠️ Failed to extract from {local_zip_path}: {e}") + return wf_data, env_vars + + +def build_info(prod_tag, run_number, wf_data, env_vars): + meta=wf_data.get("meta") + if meta != None: + int_rate = meta.get("interactionRate") + col_system = meta.get("col") + orbits_per_tf = meta.get("orbitsPerTF") + + return MCProdInfo( + LPMProductionTag=prod_tag, + Col=col_system, + IntRate=int_rate, + RunNumber=run_number, + OrbitsPerTF=orbits_per_tf, + McTag=env_vars.get("ALIEN_JDL_PACKAGES"), + RecoTag=env_vars.get("ALIEN_JDL_O2DPG_ASYNC_RECO_TAG") + ) + + +def pick_split(prod_tag, run_number, candidates, ascending=True): + """Pick the first valid split (min if ascending, max if not).""" + def split_key(entry): + _, split, _ = entry + try: + return int(split) + except ValueError: + return float("inf") + + candidates_sorted = sorted(candidates, key=split_key, reverse=not ascending) + + for cycle, split, zip_path in candidates_sorted: + print (f"Trying to analyse {zip_path}") + local_zip = f"/tmp/log_archive_{run_number}_{cycle or 0}_{split}.zip" + try: + alien_cp(zip_path, local_zip) + except subprocess.CalledProcessError: + continue + + wf_data, env_vars = extract_from_zip(local_zip) + + try: + os.remove(local_zip) # cleanup downloaded file + except OSError: + pass + + if wf_data: + info = build_info(prod_tag, run_number, wf_data, env_vars) + return info, cycle, split, zip_path + print (f"Failed") + + return None, None, None, None + + +def process_prod_tag(prod_tag, year="2025", ccdb_url=None, username=None, overwrite=False): + base_path = f"/alice/sim/{year}/{prod_tag}" + + workflow_files = alien_find(base_path, "workflow.json") + # exclude some unnecessary paths + workflow_files = [ + zf for zf in workflow_files + if "/AOD/" not in zf and "/QC/" not in zf and "/TimeseriesTPCmerging/" not in zf and "/Stage" not in zf + ] + # directories containing workflow.json + workflow_dirs = {os.path.dirname(wf) for wf in workflow_files} + print (f"Found {len(workflow_dirs)} workflow dirs") + + # Step 1: find all log_archive.zip files + print (f"Querying AliEn for all directories with zip files") + zip_files = alien_find(base_path, "log_archive.zip") + + # exclude some unnecessary paths + zip_files = [ + zf for zf in zip_files + if "/AOD/" not in zf and "/QC/" not in zf and "/TimeseriesTPCmerging/" not in zf and "/Stage" not in zf + ] + zip_files_dirs = {os.path.dirname(zf) for zf in zip_files} + print (f"Found {len(zip_files_dirs)} zip dirs") + + # keep only zips in dirs where workflow.json also exists + relevant_zips = [zf for zf in zip_files if os.path.dirname(zf) in workflow_dirs] + + # Step 2: group by run_number + runs = defaultdict(list) + for zf in relevant_zips: + parsed = parse_workflow_path(zf, prod_tag) + if parsed is None: + continue + cycle, run_number, split = parsed + runs[run_number].append((cycle, split, zf)) + + print(f"Found {len(runs)} run numbers") + + # Step 3: for each run_number, handle smallest and largest valid split + for run_number, candidates in sorted(runs.items()): + print (f"Analysing run {run_number}") + info_min, cycle_min, split_min, _ = pick_split(prod_tag, run_number, candidates, ascending=True) + info_max, cycle_max, split_max, _ = pick_split(prod_tag, run_number, candidates, ascending=False) + + # some consistency checks + if info_min and info_max: + if info_min.Col != info_max.Col: + print(f"❌ ColSystem mismatch for run {run_number}") + if info_min.OrbitsPerTF != info_max.OrbitsPerTF: + print(f"❌ OrbitsPerTF mismatch for run {run_number}") + + publish_MCProdInfo(info_min, username=username, ccdb_url=ccdb_url, force_overwrite=overwrite) + print (info_min) + + +def main(): + parser = argparse.ArgumentParser( + description="Harvest MC production metadata from AlienGRID and publish to CCDB" + ) + parser.add_argument("--prod_tag", required=True, help="Production tag (e.g. prod2025a)") + parser.add_argument("--ccdb", required=False, default="https://alice-ccdb.cern.ch", help="CCDB server URL") + parser.add_argument("--username", required=False, help="GRID username (needs appropriate AliEn token initialized)") + parser.add_argument("--year", default="2025", help="Production year (default: 2025)") + parser.add_argument("--overwrite", action="store_true", help="Overwrite existing entries") + args = parser.parse_args() + + process_prod_tag(args.prod_tag, year=args.year, ccdb_url=args.ccdb, username=args.username, overwrite=args.overwrite) + +if __name__ == "__main__": + main() diff --git a/MC/run/ANCHOR/2021/OCT/pass4/anchorMC.sh b/MC/run/ANCHOR/2021/OCT/pass4/anchorMC.sh index 7a5ca1e4e..db58f773a 100755 --- a/MC/run/ANCHOR/2021/OCT/pass4/anchorMC.sh +++ b/MC/run/ANCHOR/2021/OCT/pass4/anchorMC.sh @@ -99,13 +99,13 @@ NSIGEVENTS=${NSIGEVENTS:-22} # THIS NEEDS TO COME FROM OUTSIDE # echo "$" | awk -F' -- ' '{print $1, $3}' -baseargs="-tf ${NTIMEFRAMES} --split-id ${ALIEN_JDL_SPLITID:-0} --prod-split ${ALIEN_JDL_PRODSPLIT:-100} --run-number ${RUNNUMBER} -eCM 900 -col pp" +baseargs="-tf ${NTIMEFRAMES} --split-id ${ALIEN_JDL_SPLITID:-1} --prod-split ${ALIEN_JDL_PRODSPLIT:-100} --run-number ${RUNNUMBER} -eCM 900 -col pp" # THIS NEEDS TO COME FROM OUTSIDE remainingargs="-gen pythia8 -proc cdiff -ns ${NSIGEVENTS} \ -interactionRate ${INTERACTIONRATE} \ -confKey \"Diamond.width[2]=6.0;Diamond.width[0]=0.01;Diamond.width[1]=0.01;Diamond.position[0]=0.0;Diamond.position[1]=-0.035;Diamond.position[2]=0.41\" \ - --include-local-qc --include-analysis --mft-reco-full" + --include-local-qc --include-analysis" remainingargs="${remainingargs} -e ${SIMENGINE} -j ${NWORKERS}" remainingargs="${remainingargs} -productionTag ${ALIEN_JDL_LPMPRODUCTIONTAG:-alibi_anchorTest_tmp}" @@ -126,7 +126,7 @@ echo "TIMESTAMP IS ${TIMESTAMP}" export ALICEO2_CCDB_LOCALCACHE=$PWD/.ccdb [ ! -d .ccdb ] && mkdir .ccdb -CCDBOBJECTS="/CTP/Calib/OrbitReset /GLO/Config/GRPMagField/ /GLO/Config/GRPLHCIF /ITS/Calib/DeadMap /ITS/Calib/NoiseMap /ITS/Calib/ClusterDictionary /TPC/Calib/PadGainFull /TPC/Calib/TopologyGain /TPC/Calib/TimeGain /TPC/Calib/PadGainResidual /TPC/Config/FEEPad /TOF/Calib/Diagnostic /TOF/Calib/LHCphase /TOF/Calib/FEELIGHT /TOF/Calib/ChannelCalib /MFT/Calib/DeadMap /MFT/Calib/NoiseMap /MFT/Calib/ClusterDictionary /FT0/Calibration/ChannelTimeOffset /FV0/Calibration/ChannelTimeOffset" +CCDBOBJECTS="/CTP/Calib/OrbitReset /GLO/Config/GRPMagField/ /GLO/Config/GRPLHCIF /ITS/Calib/DeadMap /ITS/Calib/NoiseMap /ITS/Calib/ClusterDictionary /TPC/Calib/PadGainFull /TPC/Calib/TopologyGain /TPC/Calib/TimeGain /TPC/Calib/PadGainResidual /TPC/Config/FEEPad /TOF/Calib/Diagnostic /TOF/Calib/LHCphase /TOF/Calib/FEELIGHT /TOF/Calib/ChannelCalib /MFT/Calib/DeadMap /MFT/Calib/NoiseMap /MFT/Calib/ClusterDictionary /MFT/Calib/Align /FT0/Calibration/ChannelTimeOffset /FV0/Calibration/ChannelTimeOffset" ${O2_ROOT}/bin/o2-ccdb-downloadccdbfile --host http://alice-ccdb.cern.ch/ -p ${CCDBOBJECTS} -d .ccdb --timestamp ${TIMESTAMP} if [ ! "$?" == "0" ]; then @@ -134,8 +134,8 @@ if [ ! "$?" == "0" ]; then exit 1 fi -# -- Create aligned geometry using ITS and MFT ideal alignments to avoid overlaps in geant -CCDBOBJECTS_IDEAL_MC="ITS/Calib/Align MFT/Calib/Align" +# -- Create aligned geometry using ITS ideal alignment to avoid overlaps in geant +CCDBOBJECTS_IDEAL_MC="ITS/Calib/Align" TIMESTAMP_IDEAL_MC=1 ${O2_ROOT}/bin/o2-ccdb-downloadccdbfile --host http://alice-ccdb.cern.ch/ -p ${CCDBOBJECTS_IDEAL_MC} -d .ccdb --timestamp ${TIMESTAMP_IDEAL_MC} if [ ! "$?" == "0" ]; then diff --git a/MC/run/ANCHOR/2022/JUN/pass1/anchorMC.sh b/MC/run/ANCHOR/2022/JUN/pass1/anchorMC.sh index 814b832c2..c75b8f581 100755 --- a/MC/run/ANCHOR/2022/JUN/pass1/anchorMC.sh +++ b/MC/run/ANCHOR/2022/JUN/pass1/anchorMC.sh @@ -89,13 +89,13 @@ NSIGEVENTS=${NSIGEVENTS:-22} # THIS NEEDS TO COME FROM OUTSIDE # echo "$" | awk -F' -- ' '{print $1, $3}' -baseargs="-tf ${NTIMEFRAMES} --split-id ${ALIEN_JDL_SPLITID:-0} --prod-split ${ALIEN_JDL_PRODSPLIT:-100} --run-number ${RUNNUMBER} -eCM 900 -col pp" +baseargs="-tf ${NTIMEFRAMES} --split-id ${ALIEN_JDL_SPLITID:-1} --prod-split ${ALIEN_JDL_PRODSPLIT:-100} --run-number ${RUNNUMBER} -eCM 900 -col pp" # THIS NEEDS TO COME FROM OUTSIDE remainingargs="-gen pythia8 -proc cdiff -ns ${NSIGEVENTS} \ -interactionRate ${INTERACTIONRATE} \ -confKey \"Diamond.width[2]=6.0;Diamond.width[0]=0.01;Diamond.width[1]=0.01;Diamond.position[0]=0.0;Diamond.position[1]=-0.035;Diamond.position[2]=0.41\" \ - --include-local-qc --include-analysis --mft-reco-full" + --include-local-qc --include-analysis" remainingargs="${remainingargs} -e ${SIMENGINE} -j ${NWORKERS}" remainingargs="${remainingargs} -productionTag ${ALIEN_JDL_LPMPRODUCTIONTAG:-alibi_anchorTest_tmp}" @@ -117,7 +117,7 @@ echo "TIMESTAMP IS ${TIMESTAMP}" export ALICEO2_CCDB_LOCALCACHE=$PWD/.ccdb [ ! -d .ccdb ] && mkdir .ccdb -CCDBOBJECTS="/CTP/Calib/OrbitReset /GLO/Config/GRPMagField/ /GLO/Config/GRPLHCIF /ITS/Calib/DeadMap /ITS/Calib/NoiseMap /ITS/Calib/ClusterDictionary /TPC/Calib/PadGainFull /TPC/Calib/TopologyGain /TPC/Calib/TimeGain /TPC/Calib/PadGainResidual /TPC/Config/FEEPad /TOF/Calib/Diagnostic /TOF/Calib/LHCphase /TOF/Calib/FEELIGHT /TOF/Calib/ChannelCalib /MFT/Calib/DeadMap /MFT/Calib/NoiseMap /MFT/Calib/ClusterDictionary /FT0/Calibration/ChannelTimeOffset /FV0/Calibration/ChannelTimeOffset /GLO/GRP/BunchFilling" +CCDBOBJECTS="/CTP/Calib/OrbitReset /GLO/Config/GRPMagField/ /GLO/Config/GRPLHCIF /ITS/Calib/DeadMap /ITS/Calib/NoiseMap /ITS/Calib/ClusterDictionary /TPC/Calib/PadGainFull /TPC/Calib/TopologyGain /TPC/Calib/TimeGain /TPC/Calib/PadGainResidual /TPC/Config/FEEPad /TOF/Calib/Diagnostic /TOF/Calib/LHCphase /TOF/Calib/FEELIGHT /TOF/Calib/ChannelCalib /MFT/Calib/DeadMap /MFT/Calib/NoiseMap /MFT/Calib/ClusterDictionary /MFT/Calib/Align /FT0/Calibration/ChannelTimeOffset /FV0/Calibration/ChannelTimeOffset /GLO/GRP/BunchFilling" ${O2_ROOT}/bin/o2-ccdb-downloadccdbfile --host http://alice-ccdb.cern.ch/ -p ${CCDBOBJECTS} -d .ccdb --timestamp ${TIMESTAMP} if [ ! "$?" == "0" ]; then @@ -125,8 +125,8 @@ if [ ! "$?" == "0" ]; then exit 1 fi -# -- Create aligned geometry using ITS and MFT ideal alignments to avoid overlaps in geant -CCDBOBJECTS_IDEAL_MC="ITS/Calib/Align MFT/Calib/Align" +# -- Create aligned geometry using ITS ideal alignment to avoid overlaps in geant +CCDBOBJECTS_IDEAL_MC="ITS/Calib/Align" TIMESTAMP_IDEAL_MC=1 ${O2_ROOT}/bin/o2-ccdb-downloadccdbfile --host http://alice-ccdb.cern.ch/ -p ${CCDBOBJECTS_IDEAL_MC} -d .ccdb --timestamp ${TIMESTAMP_IDEAL_MC} if [ ! "$?" == "0" ]; then diff --git a/MC/run/ANCHOR/anchor-dpl-options-blacklist.json b/MC/run/ANCHOR/anchor-dpl-options-blacklist.json new file mode 100644 index 000000000..f224be858 --- /dev/null +++ b/MC/run/ANCHOR/anchor-dpl-options-blacklist.json @@ -0,0 +1,46 @@ +{ + "__comment__" : "Options (per dpl workflow) *not* to be transfered from async-reco to MC workflows.", + "global": [ + "--pipeline", + "--disable-mc", + "--loop" + ], + "o2-aod-producer-workflow": [ + "--reco-pass", + "--aod-writer-maxfilesize" + ], + "o2-its-reco-workflow": [ + "--digits-from-upstream" + ], + "o2-tof-reco-workflow": [ + "--input-type", + "--output-type", + "--local-cmp" + ], + "o2-tof-matcher-workflow": [ + "--use-fit", + "--tof-lanes", + "--output-type", + "--enable-dia" + ], + "o2-mft-reco-workflow": [ + "--digits-from-upstream", + "--nThreads" + ], + "o2-emcal-cell-recalibrator-workflow": [ + "--redirect-led" + ], + "o2-phos-reco-workflow": [ + "--input-type", + "--output-type" + ], + "o2-trd-global-tracking" : [ + "--enable-ph" + ], + "o2-trd-tracklet-transformer" : [ + "--disable-irframe-reader" + ], + "o2-tpcits-match-workflow" : [ + "--nthreads" + ] +} \ No newline at end of file diff --git a/MC/run/ANCHOR/anchorMC.sh b/MC/run/ANCHOR/anchorMC.sh new file mode 100755 index 000000000..00615a253 --- /dev/null +++ b/MC/run/ANCHOR/anchorMC.sh @@ -0,0 +1,443 @@ +#!/bin/bash + +# add distortion maps +# https://alice.its.cern.ch/jira/browse/O2-3346?focusedCommentId=300982&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-300982 +# +# export O2DPG_ENABLE_TPC_DISTORTIONS=ON +# SCFile=$PWD/distortions_5kG_lowIR.root # file needs to be downloaded +# export O2DPG_TPC_DIGIT_EXTRA=" --distortionType 2 --readSpaceCharge ${SCFile} " + +# +# procedure setting up and executing an anchored MC +# + +######################## +# helper functionality # +######################## + +echo_info() +{ + echo "INFO [anchorMC]: ${*}" +} + +echo_error() +{ + echo "ERROR [anchorMC]: ${*}" +} + +print_help() +{ + echo "Usage: ./anchorMC.sh" + echo + echo "This needs O2 and O2DPG loaded from alienv." + echo + echo "Make sure the following env variables are set:" + echo "ALIEN_JDL_LPMANCHORPASSNAME or ANCHORPASSNAME," + echo "ALIEN_JDL_MCANCHOR or MCANCHOR," + echo "ALIEN_JDL_LPMPASSNAME or PASSNAME," + echo "ALIEN_JDL_LPMRUNNUMBER or RUNNUMBER," + echo "ALIEN_JDL_LPMPRODUCTIONTYPE or PRODUCTIONTYPE," + echo "ALIEN_JDL_LPMINTERACTIONTYPE or INTERACTIONTYPE," + echo "ALIEN_JDL_LPMPRODUCTIONTAG or PRODUCTIONTAG," + echo "ALIEN_JDL_LPMANCHORRUN or ANCHORRUN," + echo "ALIEN_JDL_LPMANCHORPRODUCTION or ANCHORPRODUCTION," + echo "ALIEN_JDL_LPMANCHORYEAR or ANCHORYEAR," + echo + echo "as well as:" + echo "NTIMEFRAMES," + echo "SPLITID," + echo "PRODSPLIT." + echo + echo "Optional are:" + echo "ALIEN_JDL_CPULIMIT or CPULIMIT, set the CPU limit of the workflow runner, default: 8," + echo "NWORKERS, set the number of workers during detector transport, default: 8," + echo "ALIEN_JDL_SIMENGINE or SIMENGINE, choose the transport engine, default: TGeant4," + echo "ALIEN_JDL_WORKFLOWDETECTORS, set detectors to be taken into account, default: not-used (take the ones from async-reco)" + echo "ALIEN_JDL_ANCHOR_SIM_OPTIONS, additional options that are passed to the workflow creation, default: -gen pythia8," + echo "ALIEN_JDL_ADDTIMESERIESINMC, run TPC time series. Default: 1, switch off by setting to 0," + echo "ALIEN_JDL_MC_ORBITS_PER_TF=N, enforce some orbits per timeframe, instead of determining from CCDB" + echo "ALIEN_JDL_RUN_TIME_SPAN_FILE=FILE, use a run-time-span file to exclude bad data-taking periods" + echo "ALIEN_JDL_INVERT_IRFRAME_SELECTION, invertes the choice of ALIEN_JDL_RUN_TIME_SPAN_FILE" + echo "ALIEN_JDL_CCDB_CONDITION_NOT_AFTER, sets the condition_not_after timestamp for CCDB queries" + echo "ALIEN_O2DPG_FILEGRAPH=FILE, delete intermediate files early, using a FileIOGraph report" + echo "ALIEN_O2DPG_RESOURCES=FILE, use resource estimates learned by a pilot run instead of sampling them" + echo "DISABLE_QC, set this to disable QC, e.g. to 1" + echo "CYCLE, to set a cycle number different than 0" + echo "NSIGEVENTS, to enforce a specific upper limit of events in a timeframe (not counting orbit-early) events" +} + +# Prevent the script from being soured to omit unexpected surprises when exit is used +SCRIPT_NAME="$(basename "$(test -L "$0" && readlink "$0" || echo "$0")")" +if [ "${SCRIPT_NAME}" != "$(basename ${BASH_SOURCE[0]})" ] ; then + echo_error "This script cannot not be sourced" >&2 + return 1 +fi + +while [ "$1" != "" ] ; do + case $1 in + --help|-h ) shift + print_help + exit 0 + ;; + * ) echo "Unknown argument ${1}" + exit 1 + ;; + esac +done + +# make sure O2DPG + O2 is loaded +[ ! "${O2DPG_ROOT}" ] && echo_error "This needs O2DPG loaded" && exit 1 +[ ! "${O2_ROOT}" ] && echo_error "This needs O2 loaded" && exit 1 + +# check if jq is there +which jq >/dev/null 2>&1 +[ "${?}" != "0" ] && { echo_error "jq is not found. Install or load via alienv." ; exit 1 ; } + +alien-token-info >/dev/null 2>&1 +[ "${?}" != "0" ] && { echo_error "No GRID token found, required to run." ; exit 1 ; } + +################################################################# +# Set all required variables to identify an anchored production # +################################################################# + +# Allow for both "ALIEN_JDL_LPM" as well as "KEY" + +# the only four where there is a real default for +export ALIEN_JDL_CPULIMIT=${ALIEN_JDL_CPULIMIT:-${CPULIMIT:-8}} +export ALIEN_JDL_SIMENGINE=${ALIEN_JDL_SIMENGINE:-${SIMENGINE:-TGeant4}} +# can be passed to contain additional options that will be passed to o2dpg_sim_workflow_anchored.py and eventually to o2dpg_sim_workflow.py +export ALIEN_JDL_ANCHOR_SIM_OPTIONS=${ALIEN_JDL_ANCHOR_SIM_OPTIONS:--gen pythia8} +# all others MUST be set by the user/on the outside +export ALIEN_JDL_LPMANCHORPASSNAME=${ALIEN_JDL_LPMANCHORPASSNAME:-${ANCHORPASSNAME}} +# LPMPASSNAME is used in O2 and O2DPG scripts, however on the other hand, ALIEN_JDL_LPMANCHORPASSNAME is the one that is set in JDL templates; so use ALIEN_JDL_LPMANCHORPASSNAME and set ALIEN_JDL_LPMPASSNAME +export ALIEN_JDL_LPMPASSNAME=${ALIEN_JDL_LPMANCHORPASSNAME} +export ALIEN_JDL_LPMRUNNUMBER=${ALIEN_JDL_LPMRUNNUMBER:-${RUNNUMBER}} +export ALIEN_JDL_LPMPRODUCTIONTYPE=${ALIEN_JDL_LPMPRODUCTIONTYPE:-${PRODUCTIONTYPE}} +export ALIEN_JDL_LPMINTERACTIONTYPE=${ALIEN_JDL_LPMINTERACTIONTYPE:-${INTERACTIONTYPE}} +export ALIEN_JDL_LPMPRODUCTIONTAG=${ALIEN_JDL_LPMPRODUCTIONTAG:-${PRODUCTIONTAG}} +export ALIEN_JDL_LPMANCHORRUN=${ALIEN_JDL_LPMANCHORRUN:-${ANCHORRUN}} +export ALIEN_JDL_LPMANCHORPRODUCTION=${ALIEN_JDL_LPMANCHORPRODUCTION:-${ANCHORPRODUCTION}} +export ALIEN_JDL_LPMANCHORYEAR=${ALIEN_JDL_LPMANCHORYEAR:-${ANCHORYEAR}} +# decide whether to run TPC time series; on by default, switched off by setting to 0 +export ALIEN_JDL_ADDTIMESERIESINMC=${ALIEN_JDL_ADDTIMESERIESINMC:-1} + +# check for presence of essential variables that need to be set +[ -z "${ALIEN_JDL_LPMANCHORPASSNAME}" ] && { echo_error "Set ALIEN_JDL_LPMANCHORPASSNAME or ANCHORPASSNAME" ; exit 1 ; } +[ -z "${ALIEN_JDL_LPMRUNNUMBER}" ] && { echo_error "Set ALIEN_JDL_LPMRUNNUMBER or RUNNUMBER" ; exit 1 ; } +[ -z "${ALIEN_JDL_LPMPRODUCTIONTYPE}" ] && { echo_error "Set ALIEN_JDL_LPMPRODUCTIONTYPE or PRODUCTIONTYPE" ; exit 1 ; } +[ -z "${ALIEN_JDL_LPMINTERACTIONTYPE}" ] && { echo_error "Set ALIEN_JDL_LPMINTERACTIONTYPE or INTERACTIONTYPE" ; exit 1 ; } +[ -z "${ALIEN_JDL_LPMPRODUCTIONTAG}" ] && { echo_error "Set ALIEN_JDL_LPMPRODUCTIONTAG or PRODUCTIONTAG" ; exit 1 ; } +[ -z "${ALIEN_JDL_LPMANCHORRUN}" ] && { echo_error "Set ALIEN_JDL_LPMANCHORRUN or ANCHORRUN" ; exit 1 ; } +[ -z "${ALIEN_JDL_LPMANCHORPRODUCTION}" ] && { echo_error "Set ALIEN_JDL_LPMANCHORPRODUCTION or ANCHORPRODUCTION" ; exit 1 ; } +[ -z "${ALIEN_JDL_LPMANCHORYEAR}" ] && { echo_error "Set ALIEN_JDL_LPMANCHORYEAR or ANCHORYEAR" ; exit 1 ; } + +[ -z "${NTIMEFRAMES}" ] && { echo_error "Set NTIMEFRAMES" ; exit 1 ; } +[ -z "${SPLITID}" ] && { echo_error "Set SPLITID" ; exit 1 ; } +[ -z "${PRODSPLIT}" ] && { echo_error "Set PRODSPLIT" ; exit 1 ; } + + +# cache the production tag, will be set to a special anchor tag; reset later in fact +ALIEN_JDL_LPMPRODUCTIONTAG_KEEP=$ALIEN_JDL_LPMPRODUCTIONTAG +echo_info "Substituting ALIEN_JDL_LPMPRODUCTIONTAG=$ALIEN_JDL_LPMPRODUCTIONTAG with ALIEN_JDL_LPMANCHORPRODUCTION=$ALIEN_JDL_LPMANCHORPRODUCTION for simulating reco pass..." +ALIEN_JDL_LPMPRODUCTIONTAG=$ALIEN_JDL_LPMANCHORPRODUCTION + +if [[ $ALIEN_JDL_ANCHOR_SIM_OPTIONS == *"--tpc-distortion-type 2"* ]]; then + export O2DPG_ENABLE_TPC_DISTORTIONS=ON + # set the SCALING SOURCE to CTP for MC unless explicitely given from outside + export ALIEN_JDL_TPCSCALINGSOURCE=${ALIEN_JDL_TPCSCALINGSOURCE:-"CTP"} +fi + + +# The number of signal events can be given, but should be useful only in +# certain expert modes. In the default case, the final event number is determined by the timeframe length. +if [ -z "${NSIGEVENTS}" ]; then + NSIGEVENTS=10000 # this is just some big number; In the simulation the event number is the minimum of this number and what fits into a single timeframe + # based on the interaction rate. The number is a reasonable upper limit related to ~5696 collisions that fit into 32 LHC orbits at 2MHz interaction rate. +fi + +if [ -z "${CYCLE}" ]; then + echo_info "No CYCLE number given ... defaulting to 0" + CYCLE=0 +fi + +# this generates an exact reproducer script for this job +# that can be used locally for debugging etc. +if [[ -n "${ALIEN_PROC_ID}" && -n "${JALIEN_WSPORT}" ]]; then + ${O2DPG_ROOT}/GRID/utils/getReproducerScript.sh ${ALIEN_PROC_ID} +fi + +# also for this keep a real default +NWORKERS=${NWORKERS:-8} +# set a default seed if not given +SEED=${ALIEN_PROC_ID:-${SEED:-1}} + +ONCVMFS=0 + +if [ "${ALIEN_JDL_O2DPG_OVERWRITE}" ]; then + echo "Setting O2DPG_ROOT to overwritten path ${ALIEN_JDL_O2DPG_OVERWRITE}" + export O2DPG_ROOT=${ALIEN_JDL_O2DPG_OVERWRITE} +fi + +export > env_base.env + +# The 2-tag / alternative-reco setup below uses 'module purge' to swap the software +# environment inside a subshell. If the shell provides no 'module' function, build +# one on a 'modulecmd' binary. Its location varies by version, so resolve it flexibly +# (was hardwired to /usr/bin/modulecmd) and verify that it runs. Fail +# hard -- but only when an alternative-reco tag is set, since single-tag jobs never call +# 'module' -- instead of silently no-op'ing every 'module' call (cf. O2-7070). +# The candidate list also resolves symlinks ('readlink -f') and probes the canonical +# environment-modules install (/usr/share/Modules): on some HPC/container setups +# /usr/bin/modulecmd is a *dangling* symlink (-> /etc/alternatives -> the real .tcl), +# which exec's as "No such file or directory"; this recovers when the real binary is +# still reachable, and otherwise still fails hard (a genuinely missing Modules tree in +# the job's mount namespace is a site-side problem). +if ! declare -F module > /dev/null; then + MODULECMD_BIN="" + for _cand in "${MODULECMD}" "/usr/bin/modulecmd" "$(command -v modulecmd 2>/dev/null)" \ + "${MODULESHOME:+${MODULESHOME}/libexec/modulecmd}" \ + "$(readlink -f /usr/bin/modulecmd 2>/dev/null)" \ + /usr/share/Modules/libexec/modulecmd* /usr/share/Modules/*/libexec/modulecmd*; do + if [ -n "${_cand}" ] && [ -x "${_cand}" ] && "${_cand}" bash list > /dev/null 2>&1; then + MODULECMD_BIN="${_cand}" + break + fi + done + if [ -n "${MODULECMD_BIN}" ]; then + echo_info "Using modulecmd at ${MODULECMD_BIN}" + elif [ "${ALIEN_JDL_O2DPG_ASYNC_RECO_TAG}" ]; then + echo_error "Alternative-reco (2-tag) requested (ALIEN_JDL_O2DPG_ASYNC_RECO_TAG=${ALIEN_JDL_O2DPG_ASYNC_RECO_TAG})" + echo_error "but no working 'modulecmd' (needed for 'module purge') was found." + echo_error " Looked at: \$MODULECMD='${MODULECMD}', /usr/bin/modulecmd (+readlink -f), PATH," + echo_error " \$MODULESHOME/libexec/modulecmd, /usr/share/Modules/{,*/}libexec/modulecmd*." + echo_error " Diagnostics: MODULESHOME='${MODULESHOME}' /usr/bin/modulecmd -> '$(readlink -f /usr/bin/modulecmd 2>/dev/null)' PATH='${PATH}'" + exit 1 + else + echo_info "No 'modulecmd' found, but no alternative-reco tag is set, so 'module' is not needed; continuing." + fi + export MODULECMD_BIN + module() { + if [ -z "${MODULECMD_BIN}" ]; then + echo_error "'module ${*}' invoked but no usable 'modulecmd' was found (see earlier diagnostics)." + return 127 + fi + eval "$("${MODULECMD_BIN}" bash "$@")"; + } + export -f module +fi + +[[ "${BASEDIR}" == /cvmfs/* ]] && ONCVMFS=1 +if [ ! "${MODULEPATH}" ]; then + export MODULEPATH=${BASEDIR}/../Modules/modulefiles + if [ "${ONCVMFS}" == "1" ]; then + PLATFORM=$(echo "${BASEDIR}" | sed -E 's|.*/([^/]+)/Packages|\1|') + export MODULEPATH=${MODULEPATH}:${BASEDIR}/../../etc/toolchain/modulefiles/${PLATFORM} + fi + echo "Determined Modulepath to be ${MODULEPATH}" +fi + +#<----- START OF part that should run under a clean alternative software environment if this was given ------ +# It runs in a subshell, so the MC software environment of this shell stays untouched +# and does not need to be restored afterwards. +( + if [ "${ALIEN_JDL_O2DPG_ASYNC_RECO_TAG}" ]; then + if [ "${LOADEDMODULES}" ]; then + echo "Unloading initial modules" + module purge --no-pager + fi + echo_info "Using tag ${ALIEN_JDL_O2DPG_ASYNC_RECO_TAG} to setup anchored MC" + /cvmfs/alice.cern.ch/bin/alienv printenv "${ALIEN_JDL_O2DPG_ASYNC_RECO_TAG}" > async_environment.env 2> async_environment.err + # alienv printenv returns 0 also for an unknown tag, so check its output instead + if [ ! -s async_environment.env ] || grep -q "ERROR" async_environment.err; then + echo_error "Could not set up the software environment for ${ALIEN_JDL_O2DPG_ASYNC_RECO_TAG}:" + cat async_environment.err + exit 1 + fi + source async_environment.env + export > env_async.env + fi + + # default async_pass.sh script + DPGRECO=$O2DPG_ROOT/DATA/production/configurations/asyncReco/async_pass.sh + # default destenv_extra.sh script + DPGSETENV=$O2DPG_ROOT/DATA/production/configurations/asyncReco/setenv_extra.sh + + # a specific async_pass.sh script is in the current directory, assume that one should be used + if [[ -f async_pass.sh ]]; then + # the default is executable, however, this may not be, so make it so + chmod +x async_pass.sh + DPGRECO=./async_pass.sh + else + cp -v $DPGRECO . + fi + + # if there is no setenv_extra.sh in this directory (so no special version is "shipped" with this rpodcution), copy the default one + if [[ ! -f setenv_extra.sh ]] ; then + cp ${DPGSETENV} . + echo_info "Use default setenv_extra.sh from ${DPGSETENV}." + else + echo_info "setenv_extra.sh was found in the current working directory, use it." + fi + + chmod u+x setenv_extra.sh + + echo_info "Setting up DPGRECO to ${DPGRECO}" + + # take out line running the workflow (if we don't have data input) + [ ${CTF_TEST_FILE} ] || sed -i '/WORKFLOWMODE=run/d' async_pass.sh + + # create workflow ---> creates the file that can be parsed + export IGNORE_EXISTING_SHMFILES=1 + touch list.list + + # run the async_pass.sh and store output to log file for later inspection and extraction of information + ./async_pass.sh ${CTF_TEST_FILE:-""} &> async_pass_log.log + RECO_RC=$? + + echo_info "async_pass.sh finished with ${RECO_RC}" + exit ${RECO_RC} +) +RECO_RC=$? + +if [[ "${RECO_RC}" != "0" ]] ; then + exit ${RECO_RC} +fi + +# check that workflowconfig.log was created correctly +if [[ ! -f workflowconfig.log ]]; then + echo "Workflowconfig.log file not found" + exit 1 +fi + +export ALIEN_JDL_LPMPRODUCTIONTAG=$ALIEN_JDL_LPMPRODUCTIONTAG_KEEP +echo_info "Setting back ALIEN_JDL_LPMPRODUCTIONTAG to $ALIEN_JDL_LPMPRODUCTIONTAG" +#<----- END OF part that should run under a clean alternative software environment if this was given ------ + +# now create the local MC config file --> config-json.json +# we create the new config output with blacklist functionality +ASYNC_CONFIG_BLACKLIST=${ASYNC_CONFIG_BLACKLIST:-${O2DPG_ROOT}/MC/run/ANCHOR/anchor-dpl-options-blacklist.json} +${O2DPG_ROOT}/MC/bin/o2dpg_dpl_config_tools.py workflowconfig.log ${ASYNC_CONFIG_BLACKLIST} config-json.json +ASYNC_WF_RC=${?} + +# check if config reasonably created +if [[ "${ASYNC_WF_RC}" != "0" || `grep "ConfigParams" config-json.json 2> /dev/null | wc -l` == "0" ]]; then + echo_error "Problem in anchor config creation. Exiting." + exit 1 +fi + +# -- CREATE THE MC JOB DESCRIPTION ANCHORED TO RUN -- + + +# publish MCPRODINFO for first few jobs of a production +# if external script exported PUBLISH_MCPRODINFO, it will be published anyways +if [ -z "$PUBLISH_MCPRODINFO" ] && [ "$SPLITID" -lt 20 ]; then + PUBLISH_MCPRODINFO_OPTION="--publish-mcprodinfo" + echo "Will publish MCProdInfo" +else + echo "Will not publish MCProdInfo" +fi + +# these arguments will be digested by o2dpg_sim_workflow_anchored.py +baseargs="-tf ${NTIMEFRAMES} --split-id ${SPLITID} --prod-split ${PRODSPLIT} --cycle ${CYCLE} --run-number ${ALIEN_JDL_LPMRUNNUMBER} \ + ${ALIEN_JDL_RUN_TIME_SPAN_FILE:+--run-time-span-file ${ALIEN_JDL_RUN_TIME_SPAN_FILE} ${ALIEN_JDL_INVERT_IRFRAME_SELECTION:+--invert-irframe-selection}} \ + ${ALIEN_JDL_MC_ORBITS_PER_TF:+--orbitsPerTF ${ALIEN_JDL_MC_ORBITS_PER_TF}} ${PUBLISH_MCPRODINFO_OPTION}" + +# these arguments will be passed as well but only eventually be digested by o2dpg_sim_workflow.py which is called from o2dpg_sim_workflow_anchored.py +remainingargs="-seed ${SEED} -ns ${NSIGEVENTS} --include-local-qc --pregenCollContext" +remainingargs="${remainingargs} -e ${ALIEN_JDL_SIMENGINE} -j ${NWORKERS}" +remainingargs="${remainingargs} -productionTag ${ALIEN_JDL_LPMPRODUCTIONTAG:-alibi_anchorTest_tmp}" +# prepend(!) ALIEN_JDL_ANCHOR_SIM_OPTIONS +# since the last passed argument wins, e.g. -productionTag cannot be overwritten by the user +remainingargs="${ALIEN_JDL_ANCHOR_SIM_OPTIONS} ${remainingargs} --anchor-config config-json.json" +# apply software tagging choice +# remainingargs="${remainingargs} ${ALIEN_JDL_O2DPG_ASYNC_RECO_TAG:+--alternative-reco-software ${ALIEN_JDL_O2DPG_ASYNC_RECO_TAG}}" +ALIEN_JDL_O2DPG_ASYNC_RECO_FROMSTAGE=${ALIEN_JDL_O2DPG_ASYNC_RECO_FROMSTAGE:-RECO} +remainingargs="${remainingargs} ${ALIEN_JDL_O2DPG_ASYNC_RECO_TAG:+--alternative-reco-software ${PWD}/env_async.env@${ALIEN_JDL_O2DPG_ASYNC_RECO_FROMSTAGE}}" +# potentially add CCDB timemachine timestamp +remainingargs="${remainingargs} ${ALIEN_JDL_CCDB_CONDITION_NOT_AFTER:+--condition-not-after ${ALIEN_JDL_CCDB_CONDITION_NOT_AFTER}}" + +echo_info "baseargs passed to o2dpg_sim_workflow_anchored.py: ${baseargs}" +echo_info "remainingargs forwarded to o2dpg_sim_workflow.py: ${remainingargs}" + +anchoringLogFile=timestampsampling_${ALIEN_JDL_LPMRUNNUMBER}.log +# query CCDB has changed, w/o "_" +${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow_anchored.py ${baseargs} -- ${remainingargs} &> ${anchoringLogFile} +WF_RC="${?}" +if [ "${WF_RC}" != "0" ] ; then + echo_error "Problem during anchor timestamp sampling and workflow creation. Exiting." + exit ${WF_RC} +fi + +TIMESTAMP=`grep "Determined timestamp to be" ${anchoringLogFile} | awk '//{print $6}'` +echo_info "TIMESTAMP IS ${TIMESTAMP}" + +if [ "${ONLY_WORKFLOW_CREATION}" ]; then + exit 0 +fi + +# check if this job is exluded because it falls inside a bad data-taking period +ISEXCLUDED=$(grep "TIMESTAMP IS EXCLUDED IN RUN" ${anchoringLogFile}) +if [ "${ISEXCLUDED}" ]; then + # we can quit here; there is nothing to do + # (apart from maybe creating a fake empty AO2D.root file or the like) + echo "Timestamp is excluded from run. Nothing to do here" + exit 0 +fi + +# -- RUN THE MC WORKLOAD TO PRODUCE TARGETS -- + +export FAIRMQ_IPC_PREFIX=./ + +echo_info "Ready to start main workflow" + +# Let us construct the workflow targets +targetString="" +if [ "${ALIEN_JDL_O2DPGWORKFLOWTARGET}" ]; then + # The user gave ${ALIEN_JDL_O2DPGWORKFLOWTARGET}. This is an expert mode not used in production. + # In this case, we will build just that. No QC, no TPC timeseries, ... + targetString=${ALIEN_JDL_O2DPGWORKFLOWTARGET} +else + targetString="'aodmerge.*'" + # Now add more targets depending on options + # -) The TPC timeseries targets + if [[ "${ALIEN_JDL_ADDTIMESERIESINMC}" == "1" ]]; then + targetString="${targetString} 'tpctimes.*'" + fi + # -) TPC residual calibration + if [ "${ALIEN_JDL_DOTPCRESIDUALEXTRACTION}" ]; then + targetString="${targetString} 'tpcresidmerge.*'" + fi + # -) QC tasks + if [[ -z "${DISABLE_QC}" && "${remainingargs}" == *"--include-local-qc"* ]]; then + targetString="${targetString} '^.*QC.*'" # QC tasks should have QC in the name + fi +fi +echo_info "Workflow will run with target specification ${targetString}" + +# resource estimates learned by a pilot run (o2dpg_sim_metrics.py json-stat) +# replace the runtime sampling; without them we sample dynamically as before +RESOURCE_ARGS="--dynamic-resources" +if [ -n "${ALIEN_O2DPG_RESOURCES}" ]; then + RESOURCE_ARGS="--update-resources ${ALIEN_O2DPG_RESOURCES}" +fi + +${O2DPG_ROOT}/MC/bin/o2_dpg_workflow_runner.py -f workflow.json -tt ${targetString} \ + --cpu-limit ${ALIEN_JDL_CPULIMIT:-8} ${RESOURCE_ARGS} \ + ${ALIEN_O2DPG_FILEGRAPH:+--remove-files-early ${ALIEN_O2DPG_FILEGRAPH}} \ + ${ALIEN_O2DPG_ADDITIONAL_WORKFLOW_RUNNER_ARGS} + +MCRC=$? # <--- we'll report back this code + +# +# full logs tar-ed for output, regardless the error code or validation - to catch also QC logs... +# +if [[ -n "$ALIEN_PROC_ID" ]]; then + find ./ \( -name "*.log*" -o -name "*mergerlog*" -o -name "*serverlog*" -o -name "*workerlog*" -o -name "pythia8.cfg" -o -name "reproducer*.sh" \) | tar -czvf debug_log_archive.tgz -T - + if [[ "$ALIEN_JDL_CREATE_TAR_IN_MC" == "1" ]]; then + find ./ \( -name "*.log*" -o -name "*mergerlog*" -o -name "*serverlog*" -o -name "*workerlog*" -o -name "*.root" \) | tar -czvf debug_full_archive.tgz -T - + fi +fi + +unset FAIRMQ_IPC_PREFIX + +exit ${MCRC} diff --git a/MC/run/ANCHOR/anchorMC_DataEmbedding.sh b/MC/run/ANCHOR/anchorMC_DataEmbedding.sh new file mode 100755 index 000000000..8ceee6799 --- /dev/null +++ b/MC/run/ANCHOR/anchorMC_DataEmbedding.sh @@ -0,0 +1,498 @@ +#!/bin/bash + +# add distortion maps +# https://alice.its.cern.ch/jira/browse/O2-3346?focusedCommentId=300982&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-300982 +# +# export O2DPG_ENABLE_TPC_DISTORTIONS=ON +# SCFile=$PWD/distortions_5kG_lowIR.root # file needs to be downloaded +# export O2DPG_TPC_DIGIT_EXTRA=" --distortionType 2 --readSpaceCharge ${SCFile} " + +# +# procedure setting up and executing an anchored MC +# + +######################## +# helper functionality # +######################## + +echo_info() +{ + echo "INFO [anchorMC]: ${*}" +} + +echo_error() +{ + echo "ERROR [anchorMC]: ${*}" +} + +print_help() +{ + echo "Usage: ./anchorMC.sh" + echo + echo "This needs O2 and O2DPG loaded from alienv." + echo + echo "Make sure the following env variables are set:" + echo "ALIEN_JDL_LPMANCHORPASSNAME or ANCHORPASSNAME," + echo "ALIEN_JDL_MCANCHOR or MCANCHOR," + echo "ALIEN_JDL_LPMPASSNAME or PASSNAME," + echo "ALIEN_JDL_LPMRUNNUMBER or RUNNUMBER," + echo "ALIEN_JDL_LPMPRODUCTIONTYPE or PRODUCTIONTYPE," + echo "ALIEN_JDL_LPMINTERACTIONTYPE or INTERACTIONTYPE," + echo "ALIEN_JDL_LPMPRODUCTIONTAG or PRODUCTIONTAG," + echo "ALIEN_JDL_LPMANCHORRUN or ANCHORRUN," + echo "ALIEN_JDL_LPMANCHORPRODUCTION or ANCHORPRODUCTION," + echo "ALIEN_JDL_LPMANCHORYEAR or ANCHORYEAR," + echo + echo "as well as:" + echo "NTIMEFRAMES," + echo "SPLITID," + echo "PRODSPLIT." + echo + echo "Optional are:" + echo "ALIEN_JDL_CPULIMIT or CPULIMIT, set the CPU limit of the workflow runner, default: 8," + echo "NWORKERS, set the number of workers during detector transport, default: 8," + echo "ALIEN_JDL_SIMENGINE or SIMENGINE, choose the transport engine, default: TGeant4," + echo "ALIEN_JDL_WORKFLOWDETECTORS, set detectors to be taken into account, default: not-used (take the ones from async-reco)" + echo "ALIEN_JDL_ANCHOR_SIM_OPTIONS, additional options that are passed to the workflow creation, default: -gen pythia8," + echo "ALIEN_JDL_ADDTIMESERIESINMC, run TPC time series. Default: 1, switch off by setting to 0," + echo "ALIEN_JDL_MC_ORBITS_PER_TF=N, enforce some orbits per timeframe, instead of determining from CCDB" + echo "ALIEN_JDL_RUN_TIME_SPAN_FILE=FILE, use a run-time-span file to exclude bad data-taking periods" + echo "ALIEN_JDL_INVERT_IRFRAME_SELECTION, invertes the choice of ALIEN_JDL_RUN_TIME_SPAN_FILE" + echo "ALIEN_JDL_CCDB_CONDITION_NOT_AFTER, sets the condition_not_after timestamp for CCDB queries" + echo "DISABLE_QC, set this to disable QC, e.g. to 1" + echo "CYCLE, to set a cycle number different than 0" + echo "NSIGEVENTS, to enforce a specific upper limit of events in a timeframe (not counting orbit-early) events" +} + +# Prevent the script from being soured to omit unexpected surprises when exit is used +SCRIPT_NAME="$(basename "$(test -L "$0" && readlink "$0" || echo "$0")")" +if [ "${SCRIPT_NAME}" != "$(basename ${BASH_SOURCE[0]})" ] ; then + echo_error "This script cannot not be sourced" >&2 + return 1 +fi + +while [ "$1" != "" ] ; do + case $1 in + --help|-h ) shift + print_help + exit 0 + ;; + * ) echo "Unknown argument ${1}" + exit 1 + ;; + esac +done + +# make sure O2DPG + O2 is loaded +[ ! "${O2DPG_ROOT}" ] && echo_error "This needs O2DPG loaded" && exit 1 +[ ! "${O2_ROOT}" ] && echo_error "This needs O2 loaded" && exit 1 + +# check if jq is there +which jq >/dev/null 2>&1 +[ "${?}" != "0" ] && { echo_error "jq is not found. Install or load via alienv." ; exit 1 ; } + +alien-token-info >/dev/null 2>&1 +[ "${?}" != "0" ] && { echo_error "No GRID token found, required to run." ; exit 1 ; } + +################################################################# +# Set all required variables to identify an anchored production # +################################################################# + +# Allow for both "ALIEN_JDL_LPM" as well as "KEY" + +# the only four where there is a real default for +export ALIEN_JDL_CPULIMIT=${ALIEN_JDL_CPULIMIT:-${CPULIMIT:-8}} +export ALIEN_JDL_SIMENGINE=${ALIEN_JDL_SIMENGINE:-${SIMENGINE:-TGeant4}} +# can be passed to contain additional options that will be passed to o2dpg_sim_workflow_anchored.py and eventually to o2dpg_sim_workflow.py +export ALIEN_JDL_ANCHOR_SIM_OPTIONS=${ALIEN_JDL_ANCHOR_SIM_OPTIONS:--gen pythia8} +# all others MUST be set by the user/on the outside +export ALIEN_JDL_LPMANCHORPASSNAME=${ALIEN_JDL_LPMANCHORPASSNAME:-${ANCHORPASSNAME}} +# LPMPASSNAME is used in O2 and O2DPG scripts, however on the other hand, ALIEN_JDL_LPMANCHORPASSNAME is the one that is set in JDL templates; so use ALIEN_JDL_LPMANCHORPASSNAME and set ALIEN_JDL_LPMPASSNAME +export ALIEN_JDL_LPMPASSNAME=${ALIEN_JDL_LPMANCHORPASSNAME} +export ALIEN_JDL_LPMRUNNUMBER=${ALIEN_JDL_LPMRUNNUMBER:-${RUNNUMBER}} +export ALIEN_JDL_LPMPRODUCTIONTYPE=${ALIEN_JDL_LPMPRODUCTIONTYPE:-${PRODUCTIONTYPE}} +export ALIEN_JDL_LPMINTERACTIONTYPE=${ALIEN_JDL_LPMINTERACTIONTYPE:-${INTERACTIONTYPE}} +export ALIEN_JDL_LPMPRODUCTIONTAG=${ALIEN_JDL_LPMPRODUCTIONTAG:-${PRODUCTIONTAG}} +export ALIEN_JDL_LPMANCHORRUN=${ALIEN_JDL_LPMANCHORRUN:-${ANCHORRUN}} +export ALIEN_JDL_LPMANCHORPRODUCTION=${ALIEN_JDL_LPMANCHORPRODUCTION:-${ANCHORPRODUCTION}} +export ALIEN_JDL_LPMANCHORYEAR=${ALIEN_JDL_LPMANCHORYEAR:-${ANCHORYEAR}} +# decide whether to run TPC time series; on by default, switched off by setting to 0 +export ALIEN_JDL_ADDTIMESERIESINMC=${ALIEN_JDL_ADDTIMESERIESINMC:-1} + +# check for presence of essential variables that need to be set +[ -z "${ALIEN_JDL_LPMANCHORPASSNAME}" ] && { echo_error "Set ALIEN_JDL_LPMANCHORPASSNAME or ANCHORPASSNAME" ; exit 1 ; } +[ -z "${ALIEN_JDL_LPMRUNNUMBER}" ] && { echo_error "Set ALIEN_JDL_LPMRUNNUMBER or RUNNUMBER" ; exit 1 ; } +[ -z "${ALIEN_JDL_LPMPRODUCTIONTYPE}" ] && { echo_error "Set ALIEN_JDL_LPMPRODUCTIONTYPE or PRODUCTIONTYPE" ; exit 1 ; } +[ -z "${ALIEN_JDL_LPMINTERACTIONTYPE}" ] && { echo_error "Set ALIEN_JDL_LPMINTERACTIONTYPE or INTERACTIONTYPE" ; exit 1 ; } +[ -z "${ALIEN_JDL_LPMPRODUCTIONTAG}" ] && { echo_error "Set ALIEN_JDL_LPMPRODUCTIONTAG or PRODUCTIONTAG" ; exit 1 ; } +[ -z "${ALIEN_JDL_LPMANCHORRUN}" ] && { echo_error "Set ALIEN_JDL_LPMANCHORRUN or ANCHORRUN" ; exit 1 ; } +[ -z "${ALIEN_JDL_LPMANCHORPRODUCTION}" ] && { echo_error "Set ALIEN_JDL_LPMANCHORPRODUCTION or ANCHORPRODUCTION" ; exit 1 ; } +[ -z "${ALIEN_JDL_LPMANCHORYEAR}" ] && { echo_error "Set ALIEN_JDL_LPMANCHORYEAR or ANCHORYEAR" ; exit 1 ; } + +[ -z "${NTIMEFRAMES}" ] && { echo_error "Set NTIMEFRAMES" ; exit 1 ; } +[ -z "${SPLITID}" ] && { echo_error "Set SPLITID" ; exit 1 ; } +[ -z "${PRODSPLIT}" ] && { echo_error "Set PRODSPLIT" ; exit 1 ; } + + +# cache the production tag, will be set to a special anchor tag; reset later in fact +ALIEN_JDL_LPMPRODUCTIONTAG_KEEP=$ALIEN_JDL_LPMPRODUCTIONTAG +echo_info "Substituting ALIEN_JDL_LPMPRODUCTIONTAG=$ALIEN_JDL_LPMPRODUCTIONTAG with ALIEN_JDL_LPMANCHORPRODUCTION=$ALIEN_JDL_LPMANCHORPRODUCTION for simulating reco pass..." +ALIEN_JDL_LPMPRODUCTIONTAG=$ALIEN_JDL_LPMANCHORPRODUCTION + +if [[ $ALIEN_JDL_ANCHOR_SIM_OPTIONS == *"--tpc-distortion-type 2"* ]]; then + export O2DPG_ENABLE_TPC_DISTORTIONS=ON + # set the SCALING SOURCE to CTP for MC unless explicitely given from outside + export ALIEN_JDL_TPCSCALINGSOURCE=${ALIEN_JDL_TPCSCALINGSOURCE:-"CTP"} +fi + + +# The number of signal events can be given, but should be useful only in +# certain expert modes. In the default case, the final event number is determined by the timeframe length. +if [ -z "${NSIGEVENTS}" ]; then + NSIGEVENTS=10000 # this is just some big number; In the simulation the event number is the minimum of this number and what fits into a single timeframe + # based on the interaction rate. The number is a reasonable upper limit related to ~5696 collisions that fit into 32 LHC orbits at 2MHz interaction rate. +fi + +if [ -z "${CYCLE}" ]; then + echo_info "No CYCLE number given ... defaulting to 0" + CYCLE=0 +fi + +# this generates an exact reproducer script for this job +# that can be used locally for debugging etc. +if [[ -n "${ALIEN_PROC_ID}" && -n "${JALIEN_WSPORT}" ]]; then + ${O2DPG_ROOT}/GRID/utils/getReproducerScript.sh ${ALIEN_PROC_ID} +fi + +# also for this keep a real default +NWORKERS=${NWORKERS:-8} +# set a default seed if not given +SEED=${ALIEN_PROC_ID:-${SEED:-1}} + +ONCVMFS=0 + +if [ "${ALIEN_JDL_O2DPG_OVERWRITE}" ]; then + echo "Setting O2DPG_ROOT to overwritten path ${ALIEN_JDL_O2DPG_OVERWRITE}" + export O2DPG_ROOT=${ALIEN_JDL_O2DPG_OVERWRITE} +fi + +export > env_base.env + +# The 2-tag / alternative-reco setup below uses 'module purge' to swap the software +# environment inside a subshell. If the shell provides no 'module' function, build +# one on a 'modulecmd' binary. Its location varies by version, so resolve it flexibly +# (was hardwired to /usr/bin/modulecmd) and verify that it runs. Fail +# hard -- but only when an alternative-reco tag is set, since single-tag jobs never call +# 'module' -- instead of silently no-op'ing every 'module' call (cf. O2-7070). +# The candidate list also resolves symlinks ('readlink -f') and probes the canonical +# environment-modules install (/usr/share/Modules): on some HPC/container setups +# /usr/bin/modulecmd is a *dangling* symlink (-> /etc/alternatives -> the real .tcl), +# which exec's as "No such file or directory"; this recovers when the real binary is +# still reachable, and otherwise still fails hard (a genuinely missing Modules tree in +# the job's mount namespace is a site-side problem). +if ! declare -F module > /dev/null; then + MODULECMD_BIN="" + for _cand in "${MODULECMD}" "/usr/bin/modulecmd" "$(command -v modulecmd 2>/dev/null)" \ + "${MODULESHOME:+${MODULESHOME}/libexec/modulecmd}" \ + "$(readlink -f /usr/bin/modulecmd 2>/dev/null)" \ + /usr/share/Modules/libexec/modulecmd* /usr/share/Modules/*/libexec/modulecmd*; do + if [ -n "${_cand}" ] && [ -x "${_cand}" ] && "${_cand}" bash list > /dev/null 2>&1; then + MODULECMD_BIN="${_cand}" + break + fi + done + if [ -n "${MODULECMD_BIN}" ]; then + echo_info "Using modulecmd at ${MODULECMD_BIN}" + elif [ "${ALIEN_JDL_O2DPG_ASYNC_RECO_TAG}" ]; then + echo_error "Alternative-reco (2-tag) requested (ALIEN_JDL_O2DPG_ASYNC_RECO_TAG=${ALIEN_JDL_O2DPG_ASYNC_RECO_TAG})" + echo_error "but no working 'modulecmd' (needed for 'module purge') was found." + echo_error " Looked at: \$MODULECMD='${MODULECMD}', /usr/bin/modulecmd (+readlink -f), PATH," + echo_error " \$MODULESHOME/libexec/modulecmd, /usr/share/Modules/{,*/}libexec/modulecmd*." + echo_error " Diagnostics: MODULESHOME='${MODULESHOME}' /usr/bin/modulecmd -> '$(readlink -f /usr/bin/modulecmd 2>/dev/null)' PATH='${PATH}'" + exit 1 + else + echo_info "No 'modulecmd' found, but no alternative-reco tag is set, so 'module' is not needed; continuing." + fi + export MODULECMD_BIN + module() { + if [ -z "${MODULECMD_BIN}" ]; then + echo_error "'module ${*}' invoked but no usable 'modulecmd' was found (see earlier diagnostics)." + return 127 + fi + eval "$("${MODULECMD_BIN}" bash "$@")"; + } + export -f module +fi + +[[ "${BASEDIR}" == /cvmfs/* ]] && ONCVMFS=1 +if [ ! "${MODULEPATH}" ]; then + export MODULEPATH=${BASEDIR}/../Modules/modulefiles + if [ "${ONCVMFS}" == "1" ]; then + PLATFORM=$(echo "${BASEDIR}" | sed -E 's|.*/([^/]+)/Packages|\1|') + export MODULEPATH=${MODULEPATH}:${BASEDIR}/../../etc/toolchain/modulefiles/${PLATFORM} + fi + echo "Determined Modulepath to be ${MODULEPATH}" +fi + +#<----- START OF part that should run under a clean alternative software environment if this was given ------ +# It runs in a subshell, so the MC software environment of this shell stays untouched +# and does not need to be restored afterwards. +( + if [ "${ALIEN_JDL_O2DPG_ASYNC_RECO_TAG}" ]; then + if [ "${LOADEDMODULES}" ]; then + echo "Unloading initial modules" + module purge --no-pager + fi + echo_info "Using tag ${ALIEN_JDL_O2DPG_ASYNC_RECO_TAG} to setup anchored MC" + /cvmfs/alice.cern.ch/bin/alienv printenv "${ALIEN_JDL_O2DPG_ASYNC_RECO_TAG}" > async_environment.env 2> async_environment.err + # alienv printenv returns 0 also for an unknown tag, so check its output instead + if [ ! -s async_environment.env ] || grep -q "ERROR" async_environment.err; then + echo_error "Could not set up the software environment for ${ALIEN_JDL_O2DPG_ASYNC_RECO_TAG}:" + cat async_environment.err + exit 1 + fi + source async_environment.env + export > env_async.env + fi + + # default async_pass.sh script + DPGRECO=$O2DPG_ROOT/DATA/production/configurations/asyncReco/async_pass.sh + # default destenv_extra.sh script + DPGSETENV=$O2DPG_ROOT/DATA/production/configurations/asyncReco/setenv_extra.sh + + # a specific async_pass.sh script is in the current directory, assume that one should be used + if [[ -f async_pass.sh ]]; then + # the default is executable, however, this may not be, so make it so + chmod +x async_pass.sh + DPGRECO=./async_pass.sh + else + cp -v $DPGRECO . + fi + + # if there is no setenv_extra.sh in this directory (so no special version is "shipped" with this rpodcution), copy the default one + if [[ ! -f setenv_extra.sh ]] ; then + cp ${DPGSETENV} . + echo_info "Use default setenv_extra.sh from ${DPGSETENV}." + else + echo_info "setenv_extra.sh was found in the current working directory, use it." + fi + + chmod u+x setenv_extra.sh + + echo_info "Setting up DPGRECO to ${DPGRECO}" + + # take out line running the workflow (if we don't have data input) + [ ${CTF_TEST_FILE} ] || sed -i '/WORKFLOWMODE=run/d' async_pass.sh + + # create workflow ---> creates the file that can be parsed + export IGNORE_EXISTING_SHMFILES=1 + touch list.list + + # run the async_pass.sh and store output to log file for later inspection and extraction of information + ./async_pass.sh ${CTF_TEST_FILE:-""} &> async_pass_log.log + RECO_RC=$? + + echo_info "async_pass.sh finished with ${RECO_RC}" + exit ${RECO_RC} +) +RECO_RC=$? + +if [[ "${RECO_RC}" != "0" ]] ; then + exit ${RECO_RC} +fi + +# check that workflowconfig.log was created correctly +if [[ ! -f workflowconfig.log ]]; then + echo "Workflowconfig.log file not found" + exit 1 +fi + +export ALIEN_JDL_LPMPRODUCTIONTAG=$ALIEN_JDL_LPMPRODUCTIONTAG_KEEP +echo_info "Setting back ALIEN_JDL_LPMPRODUCTIONTAG to $ALIEN_JDL_LPMPRODUCTIONTAG" +#<----- END OF part that should run under a clean alternative software environment if this was given ------ + +# the async software environment, referenced from within the per-timeframe workspaces below +ASYNC_ENV_FILE=${PWD}/env_async.env + +# now create the local MC config file --> config-json.json +# we create the new config output with blacklist functionality +ASYNC_CONFIG_BLACKLIST=${ASYNC_CONFIG_BLACKLIST:-${O2DPG_ROOT}/MC/run/ANCHOR/anchor-dpl-options-blacklist.json} +${O2DPG_ROOT}/MC/bin/o2dpg_dpl_config_tools.py workflowconfig.log ${ASYNC_CONFIG_BLACKLIST} config-json.json +ASYNC_WF_RC=${?} + +# check if config reasonably created +if [[ "${ASYNC_WF_RC}" != "0" || `grep "ConfigParams" config-json.json 2> /dev/null | wc -l` == "0" ]]; then + echo_error "Problem in anchor config creation. Exiting." + exit 1 +fi + +# -- CREATE THE MC JOB DESCRIPTION ANCHORED TO RUN -- + + +# publish MCPRODINFO for first few jobs of a production +# if external script exported PUBLISH_MCPRODINFO, it will be published anyways +if [ -z "$PUBLISH_MCPRODINFO" ] && [ "$SPLITID" -lt 20 ]; then + PUBLISH_MCPRODINFO_OPTION="--publish-mcprodinfo" + echo "Will publish MCProdInfo" +else + echo "Will not publish MCProdInfo" +fi +PUBLISH_MCPRODINFO_OPTION="" + +# let's take the input data AO2D from a JDL variable +AOD_DATA_FILE=${ALIEN_JDL_MC_DATA_EMBEDDING_AO2D} +if [ "${ALIEN_JDL_MC_DATA_EMBEDDING_AO2D}" ]; then + NTIMEFRAMES=1 + NWORKERS=1 # these embedding jobs process only light pp type signals ... the parallelism will come via parallel workflows +fi + +# call the python script to extract all collision contexts +python3 ${O2DPG_ROOT}/MC/utils/o2dpg_data_embedding_utils.py --aod-file ${AOD_DATA_FILE} --run-number ${ALIEN_JDL_LPMRUNNUMBER} --limit ${DATA_EMBEDDING_LIMIT:-8} + +parallel_job_count=0 +failed_count=0 +for external_context in collission_context_*.root; do + echo "Embedding into ${external_context}" + # extract timeframe from name + anchoring_tf="${external_context#collission_context_}" # remove prefix 'collision_context_' + anchoring_tf="${anchoring_tf%.root}" # remove suffix '.root' + # now we have a string with DF_FOLDERNAME:TIMEFRAMEID + + df_folder="${anchoring_tf%%:*}" + anchoring_tf="${anchoring_tf##*:}" + + echo "Treating timeframe ${anchoring_tf} coming from folder ${df_folder}" + + # we do it in a separate workspace + workspace="TF_${anchoring_tf}" + mkdir "${workspace}"; cd "${workspace}" + # fetch the apass reco anchoring config + cp ../*.json . + + # we need to adjust the SEED for each job + JOBSEED=$SEED + [ "$JOBSEED" != "-1" ] && let JOBSEED=JOBSEED+anchoring_tf + echo "TF ${anchoring_tf} got seed ${JOBSEED}" + + # these arguments will be digested by o2dpg_sim_workflow_anchored.py + anchoringArgs="--split-id ${SPLITID} --prod-split ${PRODSPLIT} --cycle ${CYCLE}" + if [ "${ALIEN_JDL_MC_DATA_EMBEDDING_AO2D}" ]; then + anchoringArgs="--timeframeID ${anchoring_tf}" + fi + baseargs="-tf ${NTIMEFRAMES} ${anchoringArgs} --run-number ${ALIEN_JDL_LPMRUNNUMBER} \ + ${ALIEN_JDL_RUN_TIME_SPAN_FILE:+--run-time-span-file ${ALIEN_JDL_RUN_TIME_SPAN_FILE} ${ALIEN_JDL_INVERT_IRFRAME_SELECTION:+--invert-irframe-selection}} \ + ${ALIEN_JDL_MC_ORBITS_PER_TF:+--orbitsPerTF ${ALIEN_JDL_MC_ORBITS_PER_TF}} ${PUBLISH_MCPRODINFO_OPTION}" + + # these arguments will be passed as well but only eventually be digested by o2dpg_sim_workflow.py which is called from o2dpg_sim_workflow_anchored.py + remainingargs="-seed ${JOBSEED} -ns ${NSIGEVENTS} --include-local-qc --pregenCollContext" + remainingargs="${remainingargs} -e ${ALIEN_JDL_SIMENGINE} -j ${NWORKERS}" + remainingargs="${remainingargs} -productionTag ${ALIEN_JDL_LPMPRODUCTIONTAG:-alibi_anchorTest_tmp}" + # prepend(!) ALIEN_JDL_ANCHOR_SIM_OPTIONS + # since the last passed argument wins, e.g. -productionTag cannot be overwritten by the user + remainingargs="${ALIEN_JDL_ANCHOR_SIM_OPTIONS} ${remainingargs} --anchor-config config-json.json" + # apply software tagging choice + # remainingargs="${remainingargs} ${ALIEN_JDL_O2DPG_ASYNC_RECO_TAG:+--alternative-reco-software ${ALIEN_JDL_O2DPG_ASYNC_RECO_TAG}}" + ALIEN_JDL_O2DPG_ASYNC_RECO_FROMSTAGE=${ALIEN_JDL_O2DPG_ASYNC_RECO_FROMSTAGE:-RECO} + remainingargs="${remainingargs} ${ALIEN_JDL_O2DPG_ASYNC_RECO_TAG:+--alternative-reco-software ${ASYNC_ENV_FILE}@${ALIEN_JDL_O2DPG_ASYNC_RECO_FROMSTAGE}}" + # potentially add CCDB timemachine timestamp + remainingargs="${remainingargs} ${ALIEN_JDL_CCDB_CONDITION_NOT_AFTER:+--condition-not-after ${ALIEN_JDL_CCDB_CONDITION_NOT_AFTER}}" + # add external collision context injection + if [ "${ALIEN_JDL_MC_DATA_EMBEDDING_AO2D}" ]; then + remainingargs="${remainingargs} --data-anchoring ${PWD}/../${external_context} --aod-output-folder ${df_folder##*_} --aod-parent-file ${ALIEN_JDL_MC_DATA_EMBEDDING_AO2D}" + fi + + echo_info "baseargs passed to o2dpg_sim_workflow_anchored.py: ${baseargs}" + echo_info "remainingargs forwarded to o2dpg_sim_workflow.py: ${remainingargs}" + + anchoringLogFile=timestampsampling_${ALIEN_JDL_LPMRUNNUMBER}.log + # query CCDB has changed, w/o "_" + ${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow_anchored.py ${baseargs} -- ${remainingargs} &> ${anchoringLogFile} + WF_RC="${?}" + if [ "${WF_RC}" != "0" ] ; then + echo_error "Problem during anchor timestamp sampling and workflow creation. Exiting." + exit ${WF_RC} + fi + + TIMESTAMP=`grep "Determined timestamp to be" ${anchoringLogFile} | awk '//{print $6}'` + echo_info "TIMESTAMP IS ${TIMESTAMP}" + + if [ "${ONLY_WORKFLOW_CREATION}" ]; then + continue # or exit + fi + + # check if this job is exluded because it falls inside a bad data-taking period + ISEXCLUDED=$(grep "TIMESTAMP IS EXCLUDED IN RUN" ${anchoringLogFile}) + if [ "${ISEXCLUDED}" ]; then + # we can quit here; there is nothing to do + # (apart from maybe creating a fake empty AO2D.root file or the like) + echo "Timestamp is excluded from run. Nothing to do here" + continue # or exit 0 + fi + + # -- RUN THE MC WORKLOAD TO PRODUCE TARGETS -- + + export FAIRMQ_IPC_PREFIX=./ + echo_info "Ready to start main workflow" + + # Let us construct the workflow targets + targetString="" + if [ "${ALIEN_JDL_O2DPGWORKFLOWTARGET}" ]; then + # The user gave ${ALIEN_JDL_O2DPGWORKFLOWTARGET}. This is an expert mode not used in production. + # In this case, we will build just that. No QC, no TPC timeseries, ... + targetString=${ALIEN_JDL_O2DPGWORKFLOWTARGET} + else + targetString="'aodmerge.*'" + # Now add more targets depending on options + # -) The TPC timeseries targets + if [[ "${ALIEN_JDL_ADDTIMESERIESINMC}" == "1" ]]; then + targetString="${targetString} 'tpctimes.*'" + fi + # -) TPC residual calibration + if [ "${ALIEN_JDL_DOTPCRESIDUALSEXTRACTION}" ]; then + targetString="${targetString} 'tpcresidmerge.*'" + fi + # -) QC tasks + if [[ -z "${DISABLE_QC}" && "${remainingargs}" == *"--include-local-qc"* ]]; then + targetString="${targetString} '^.*QC.*'" # QC tasks should have QC in the name + fi + fi + echo_info "Workflow will run with target specification ${targetString}" + + ${O2DPG_ROOT}/MC/bin/o2_dpg_workflow_runner.py -f workflow.json -tt ${targetString} -j 1 \ + --cpu-limit ${ALIEN_JDL_CPULIMIT:-8} --dynamic-resources \ + ${ALIEN_O2DPG_FILEGRAPH:+--remove-files-early ${ALIEN_O2DPG_FILEGRAPH}} \ + ${ALIEN_O2DPG_ADDITIONAL_WORKFLOW_RUNNER_ARGS} &> pipeline_log & + + ((parallel_job_count++)) + # If limit reached, wait for one job to finish + if ((parallel_job_count >= 8)); then + if ! wait -n; then + ((failed_count++)) + fi + ((parallel_job_count--)) + fi + + cd .. +done # done outer loop +while ((parallel_job_count > 0)); do + if ! wait -n; then + ((failed_count++)) + fi + ((parallel_job_count--)) +done + +if [ "${ALIEN_JDL_MC_DATA_EMBEDDING_AO2D}" ]; then + # produce the final merged AO2D + find ./ -maxdepth 2 -mindepth 2 -name "AO2D.root" > aod_inputs.txt + o2-aod-merger --input aod_inputs.txt --output AO2D.root --merge-by-name # --merge-by-name restricts merging to folders of same name +fi + +# +# full logs tar-ed for output, regardless the error code or validation - to catch also QC logs... +# +if [[ -n "$ALIEN_PROC_ID" ]]; then + find ./ \( -name "*.log*" -o -name "*mergerlog*" -o -name "*serverlog*" -o -name "*workerlog*" -o -name "pythia8.cfg" -o -name "reproducer*.sh" \) | tar -czvf debug_log_archive.tgz -T - + if [[ "$ALIEN_JDL_CREATE_TAR_IN_MC" == "1" ]]; then + find ./ \( -name "*.log*" -o -name "*mergerlog*" -o -name "*serverlog*" -o -name "*workerlog*" -o -name "*.root" \) | tar -czvf debug_full_archive.tgz -T - + fi +fi diff --git a/MC/run/ANCHOR/tests/ML_clusterizer/run.sh b/MC/run/ANCHOR/tests/ML_clusterizer/run.sh new file mode 100755 index 000000000..9f800d868 --- /dev/null +++ b/MC/run/ANCHOR/tests/ML_clusterizer/run.sh @@ -0,0 +1,68 @@ +#!/bin/bash + +#JDL_OUTPUT=*.txt@disk=1,AO2D*.root@disk=2,*.log@disk=1,*stat*@disk=1,*.json@disk=1,debug*tgz@disk=2,tf*/coll*.root +#JDL_ERROROUTPUT=*.txt@disk=1,AO2D*.root@disk=2,*.log@disk=1,*.json@disk=1,debug*tgz@disk=2 +#JDL_PACKAGE=O2PDPSuite::daily-20251215-0000-1 + +# +# An **EXAMPLE** showing injection of GPU_proc_nn config_keys into the workflow creation, so +# that TPC clusterizer runs with ML kernel. +# + +# example anchoring +export ALIEN_JDL_LPMANCHORPASSNAME=apass4 +export ALIEN_JDL_MCANCHOR=apass4 +export ALIEN_JDL_CPULIMIT=8 +export ALIEN_JDL_LPMRUNNUMBER=544124 +export ALIEN_JDL_LPMPRODUCTIONTYPE=MC +export ALIEN_JDL_LPMINTERACTIONTYPE=PbPb +export ALIEN_JDL_LPMPRODUCTIONTAG=MLClusterTest +export ALIEN_JDL_LPMANCHORRUN=544124 +export ALIEN_JDL_LPMANCHORPRODUCTION=LHC23zz +export ALIEN_JDL_LPMANCHORYEAR=2023 + +export NTIMEFRAMES=1 +export PRODSPLIT=${ALIEN_O2DPG_GRIDSUBMIT_PRODSPLIT:-100} +export SPLITID=${ALIEN_O2DPG_GRIDSUBMIT_SUBJOBID:-50} +export CYCLE=0 + +# this file modifies few config key values for GPU_proc_nn for application in TPC clusterization +LOCAL_CONFIG="customize_ml_clusterizing.json" +cat > ${LOCAL_CONFIG} < ${INPUT_FILE_STRIPPED} + +# Read the header line and convert it into variable names +IFS=',' read -r -a headers < "$INPUT_FILE_STRIPPED" + +# Replace placeholders in the header (e.g., %{VAR} → VAR) +for i in "${!headers[@]}"; do + headers[$i]=$(echo "${headers[$i]}" | sed -E 's/#?%\{//;s/\}//g') +done + +TOPWORKDIR="" + +# Read and process each subsequent line +{ + read # Skip the header line + + count=1 # Counter for output files + datestring=$(date +"%Y%m%d_%H%M%S") + while IFS=',' read -r -a values; do + # Assign each value to its corresponding variable + for i in "${!headers[@]}"; do + declare "${headers[$i]}"="${values[$i]}" + done + + PRODUCTION_TAG="2tagtest_${datestring}_${count}" + # Example: Print assigned variables + echo "SOFTWARETAG_SIM: $SOFTWARETAG_SIM" + echo "SOFTWARETAG_ASYNC: $SOFTWARETAG_ASYNC" + echo "PASSNAME: $PASSNAME" + echo "COL_SYSTEM: $COL_SYSTEM" + echo "RUN_NUMBER: $RUN_NUMBER" + echo "INTERACTIONTYPE: $INTERACTIONTYPE" + echo "PRODUCTION_TAG: $PRODUCTION_TAG" + echo "ANCHOR_PRODUCTION: $ANCHOR_PRODUCTION" + echo "ANCHORYEAR: $ANCHORYEAR" + echo "SIM_OPTIONS: $SIM_OPTIONS" + echo "--------------------------------" + + if [ "${DAILYTAGTOTEST}" ]; then + SOFTWARETAG_SIM=${DAILYTAGTOTEST} + fi + + OUTPUT_FILE_FINAL="${WORKING_DIR}/${OUTPUT_FILE}_case${count}.sh" + + # create final test script with these values + cp "$TEMPLATE_FILE" "${OUTPUT_FILE_FINAL}" + for var in "${headers[@]}"; do + sed -i "s|%{$var}|${!var}|g" "$OUTPUT_FILE_FINAL" + done + # put the require spec + sed -i "s/%{JDL_REQUIREMENT}/${REQUIRE_STRING}/g" "$OUTPUT_FILE_FINAL" + + # inject custom repo if available + if [ "${O2DPG_CUSTOM_REPO}" ]; then + sed -i "s|%{O2DPG_CUSTOM_REPO}|${O2DPG_CUSTOM_REPO}|g" "$OUTPUT_FILE_FINAL" + else + sed -i "/%{O2DPG_CUSTOM_REPO}/d" "$OUTPUT_FILE_FINAL" + fi + + TOPWORKDIR=2tag_release_testing_${BUILD_TAG:-${SOFTWARETAG_SIM}} + + # we submit the test to the GRID (with some multiplicity) + # ${WORKING_DIR}/submit_case${count}_${SOFTWARETAG_ASYNC//::/-} + echo "${O2DPG_ROOT}/GRID/utils/grid_submit.sh --prodsplit ${PRODSPLIT:-4} --singularity --ttl ${TTL:-3600} --script ${OUTPUT_FILE_FINAL} \ + --jobname "anchorTest_${count}" --wait-any --topworkdir ${TOPWORKDIR}" > ${WORKING_DIR}/submit_case${count}.sh + # TODO: optional local execution with --local option + + ((count++)) # Increment counter for next row + done +} < "${INPUT_FILE_STRIPPED}" #Redirect file input here to avoid subshell issues + +cd ${WORKING_DIR} + +# now we submit all the jobs in the background and wait for them to return +declare -A logfiles +declare -A urls +for s in submit*.sh; do + echo "submitting ${s}" + export GRID_SUBMIT_WORKDIR="${WORKING_DIR}/${s}_workdir" + ( + bash ${s} &> log_${s} + echo "Job ${s} returned" + ) & + logfiles["$s"]="log_${s}" +done + +# Next stage is to wait until all jobs are actually running on +# AliEn +waitcounter=0 +maxwait=100 +while (( ${#logfiles[@]} > 0 && waitcounter < maxwait )); do + for script in "${!logfiles[@]}"; do + logfile=${logfiles["$script"]} + if grep -q "https://alimonitor.cern.ch/agent/jobs/details.jsp?pid=" "$logfile" 2>/dev/null; then + # Extract URL: strip ANSI codes, find URL, take first match + url=$(sed 's/\x1B\[[0-9;]*[a-zA-Z]//g' "$logfile" \ + | grep -o 'https://alimonitor.cern.ch/agent/jobs/details.jsp?pid=[0-9]*' \ + | head -n1) + + echo "Job ${script} has AliEn job URL: ${url}" + urls["$script"]=${url} + unset logfiles["$script"] + fi + done + sleep 1 + ((waitcounter++)) +done + +# wait for all (GRID) jobs to return +echo "Waiting for jobs to return/finish" +wait + +# verify / validate the output produced from these jobs +# The test is successfull if at least one subjob from each test +# produced the AO2D output. +echo "-- Jobs done ... validating --" + +FINAL_SUCCESS=0 +for s in submit*.sh; do + # find output path + TEST_OUTPUT_PATH="${WORKING_DIR}/${s}_workdir" # $(grep "Local working directory is" log_${s} | awk '//{print $5}') + + # get the Output path on JAlien from the JDL + ALIEN_OUTPUT_FOLDER=$(grep 'OutputDir' ${TEST_OUTPUT_PATH}/*.jdl | cut -d'"' -f2 | sed 's|/[^/]*#.*#.*$||') + + # see if there is an AO2D.root and a workflow.json in one of the jobs in that folder + AODS_FOUND=$(alien.py find ${ALIEN_OUTPUT_FOLDER} AO2D.root) + WORKFLOWS_FOUND=$(alien.py find ${ALIEN_OUTPUT_FOLDER} workflow.json) + + if [[ -z ${WORKFLOWS_FOUND} || -z ${AODS_FOUND} ]]; then + echo "❌ Missing files for case $s: Check here for logs ${urls[${s}]}" + FINAL_SUCCESS=1 # mark as failure + # also upload log file to AliEn for later inspection + alien.py cp file:./log_${s} alien:~/${TOPWORKDIR} + else + echo "✅ Files found in $s" + fi +done + +if [[ ${FINAL_SUCCESS} -eq 0 ]]; then + echo "✅ All submissions have required files." +else + echo "❌ Some submissions are missing required files." +fi + +#TODO: echo "-- Cleaning up ... " +cd .. + +exit ${FINAL_SUCCESS} diff --git a/MC/run/HEPscore/hep-score.sh b/MC/run/HEPscore/hep-score.sh index f7671c94f..f25df2c4f 100755 --- a/MC/run/HEPscore/hep-score.sh +++ b/MC/run/HEPscore/hep-score.sh @@ -8,8 +8,6 @@ [ ! "${O2DPG_ROOT}" ] && echo "Error: This needs O2DPG loaded" && exit 1 [ ! "${O2_ROOT}" ] && echo "Error: This needs O2 loaded" && exit 1 -# ----------- LOAD UTILITY FUNCTIONS -------------------------- -. ${O2_ROOT}/share/scripts/jobutils.sh # ----------- START ACTUAL JOB ----------------------------- NSIGEVENTS=${NSIGEVENTS:-5} diff --git a/MC/run/PWGDQ/runBeautyToJpsi_fwdy_PbPb.sh b/MC/run/PWGDQ/runBeautyToJpsi_fwdy_PbPb.sh index b2b0edde4..e15940a53 100644 --- a/MC/run/PWGDQ/runBeautyToJpsi_fwdy_PbPb.sh +++ b/MC/run/PWGDQ/runBeautyToJpsi_fwdy_PbPb.sh @@ -5,8 +5,6 @@ [ ! "${O2_ROOT}" ] && echo "Error: This needs O2 loaded" && exit 1 -# ----------- LOAD UTILITY FUNCTIONS -------------------------- -. ${O2_ROOT}/share/scripts/jobutils.sh RNDSEED=${RNDSEED:-0} NSIGEVENTS=${NSIGEVENTS:-1} @@ -14,9 +12,9 @@ NBKGEVENTS=${NBKGEVENTS:-1} NWORKERS=${NWORKERS:-8} NTIMEFRAMES=${NTIMEFRAMES:-1} -${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM 5020 -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -e TGeant4 -mod "--skipModules ZDC" \ - -trigger "external" -ini $O2DPG_ROOT/MC/config/PWGDQ/ini/GeneratorHF_bbbar_fwdy.ini \ - -genBkg pythia8 -procBkg "heavy_ion" -colBkg PbPb --embedding -nb ${NBKGEVENTS} --mft-reco-full --mft-assessment-full --fwdmatching-assessment-full --fwdmatching-save-trainingdata +${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM 5020 -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -e TGeant4 \ + -trigger "external" -ini $O2DPG_ROOT/MC/config/PWGDQ/ini/GeneratorHF_bbbar_fwdy.ini -interactionRate 500000 \ + -genBkg pythia8 -procBkg "heavy_ion" -colBkg PbPb --embedding -nb ${NBKGEVENTS} --mft-assessment-full --fwdmatching-assessment-full --fwdmatching-save-trainingdata # run workflow ${O2DPG_ROOT}/MC/bin/o2_dpg_workflow_runner.py -f workflow.json diff --git a/MC/run/PWGDQ/runBeautyToJpsi_fwdy_pp.sh b/MC/run/PWGDQ/runBeautyToJpsi_fwdy_pp.sh index 8d6a8dca7..2becd0cc5 100755 --- a/MC/run/PWGDQ/runBeautyToJpsi_fwdy_pp.sh +++ b/MC/run/PWGDQ/runBeautyToJpsi_fwdy_pp.sh @@ -5,8 +5,6 @@ [ ! "${O2_ROOT}" ] && echo "Error: This needs O2 loaded" && exit 1 -# ----------- LOAD UTILITY FUNCTIONS -------------------------- -. ${O2_ROOT}/share/scripts/jobutils.sh RNDSEED=${RNDSEED:-0} NSIGEVENTS=${NSIGEVENTS:-1} @@ -14,9 +12,9 @@ NBKGEVENTS=${NBKGEVENTS:-1} NWORKERS=${NWORKERS:-8} NTIMEFRAMES=${NTIMEFRAMES:-1} -${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM 13600 -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -e TGeant4 -mod "--skipModules ZDC" \ - -trigger "external" -ini $O2DPG_ROOT/MC/config/PWGDQ/ini/GeneratorHF_bbbar_fwdy.ini \ - -genBkg pythia8 -procBkg inel -colBkg pp --embedding -nb ${NBKGEVENTS} --mft-reco-full --fwdmatching-4-param --fwdmatching-cut-4-param +${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM 13600 -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -e TGeant4 \ + -trigger "external" -ini $O2DPG_ROOT/MC/config/PWGDQ/ini/GeneratorHF_bbbar_fwdy.ini -interactionRate 500000 \ + -genBkg pythia8 -procBkg inel -colBkg pp --embedding -nb ${NBKGEVENTS} --fwdmatching-4-param --fwdmatching-cut-4-param # run workflow ${O2DPG_ROOT}/MC/bin/o2_dpg_workflow_runner.py -f workflow.json diff --git a/MC/run/PWGDQ/runBeautyToJpsi_midy_pp.sh b/MC/run/PWGDQ/runBeautyToJpsi_midy_pp.sh index ffc534a4d..4d44a6853 100755 --- a/MC/run/PWGDQ/runBeautyToJpsi_midy_pp.sh +++ b/MC/run/PWGDQ/runBeautyToJpsi_midy_pp.sh @@ -5,8 +5,6 @@ [ ! "${O2_ROOT}" ] && echo "Error: This needs O2 loaded" && exit 1 -# ----------- LOAD UTILITY FUNCTIONS -------------------------- -. ${O2_ROOT}/share/scripts/jobutils.sh RNDSEED=${RNDSEED:-0} NSIGEVENTS=${NSIGEVENTS:-1} @@ -14,9 +12,9 @@ NBKGEVENTS=${NBKGEVENTS:-1} NWORKERS=${NWORKERS:-8} NTIMEFRAMES=${NTIMEFRAMES:-1} -${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM 900 -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -e TGeant4 -mod "--skipModules ZDC" \ - -trigger "external" -ini $O2DPG_ROOT/MC/config/PWGDQ/ini/GeneratorHF_bbbar_midy.ini \ - -genBkg pythia8 -procBkg inel -colBkg pp --embedding -nb ${NBKGEVENTS} --mft-reco-full +${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM 900 -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -e TGeant4 \ + -trigger "external" -ini $O2DPG_ROOT/MC/config/PWGDQ/ini/GeneratorHF_bbbar_midy.ini -interactionRate 500000 \ + -genBkg pythia8 -procBkg inel -colBkg pp --embedding -nb ${NBKGEVENTS} # run workflow ${O2DPG_ROOT}/MC/bin/o2_dpg_workflow_runner.py -f workflow.json diff --git a/MC/run/PWGDQ/runBeautyToMuons_fwd_pp.sh b/MC/run/PWGDQ/runBeautyToMuons_fwd_pp.sh index 92118177f..cd5209ab2 100644 --- a/MC/run/PWGDQ/runBeautyToMuons_fwd_pp.sh +++ b/MC/run/PWGDQ/runBeautyToMuons_fwd_pp.sh @@ -8,8 +8,6 @@ export ALICEO2_CCDB_LOCALCACHE=$PWD/.ccdb -# ----------- LOAD UTILITY FUNCTIONS -------------------------- -. ${O2_ROOT}/share/scripts/jobutils.sh RNDSEED=${RNDSEED:-0} NSIGEVENTS=${NSIGEVENTS:-1} @@ -18,9 +16,9 @@ NWORKERS=${NWORKERS:-8} NTIMEFRAMES=${NTIMEFRAMES:-1} -${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM 13600 -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -e TGeant4 -mod "MCH MFT MID ITS" \ +${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM 13600 -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -e TGeant4 \ -trigger "external" -ini $O2DPG_ROOT/MC/config/PWGDQ/ini/GeneratorHF_bbbarToMuonsSemileptonic_fwdy.ini \ - -genBkg pythia8 -procBkg cdiff -colBkg pp --embedding -nb ${NBKGEVENTS} --mft-reco-full \ + -genBkg pythia8 -procBkg cdiff -colBkg pp --embedding -nb ${NBKGEVENTS} \ -confKeyBkg "Diamond.width[2]=6" -interactionRate 2000 --mft-assessment-full --fwdmatching-assessment-full # run workflow diff --git a/MC/run/PWGDQ/runBeautyToMuons_noForce_fwd_pp.sh b/MC/run/PWGDQ/runBeautyToMuons_noForce_fwd_pp.sh new file mode 100644 index 000000000..69f363b23 --- /dev/null +++ b/MC/run/PWGDQ/runBeautyToMuons_noForce_fwd_pp.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash + +# make sure O2DPG + O2 is loaded +[ ! "${O2DPG_ROOT}" ] && echo "Error: This needs O2DPG loaded" && exit 1 +[ ! "${O2_ROOT}" ] && echo "Error: This needs O2 loaded" && exit 1 + +# ----------- SETUP LOCAL CCDB CACHE -------------------------- +export ALICEO2_CCDB_LOCALCACHE=$PWD/.ccdb + + + +RNDSEED=${RNDSEED:-0} +NSIGEVENTS=${NSIGEVENTS:-1} +NBKGEVENTS=${NBKGEVENTS:-1} +NWORKERS=${NWORKERS:-8} +NTIMEFRAMES=${NTIMEFRAMES:-1} + + +${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM 13600 -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -e TGeant4 \ + -trigger "external" -ini $O2DPG_ROOT/MC/config/PWGDQ/ini/GeneratorHF_bbbarToDDbarToMuons_fwdy.ini \ + -genBkg pythia8 -procBkg cdiff -colBkg pp --embedding -nb ${NBKGEVENTS} \ + -confKeyBkg "Diamond.width[2]=6" -interactionRate 2000 --mft-assessment-full --fwdmatching-assessment-full + +# run workflow +${O2DPG_ROOT}/MC/bin/o2_dpg_workflow_runner.py -f workflow.json -f workflow.json -tt aod -jmax 1 diff --git a/MC/run/PWGDQ/runBeautyToPsiAndJpsi_fwdy_pp_triggerGap.sh b/MC/run/PWGDQ/runBeautyToPsiAndJpsi_fwdy_pp_triggerGap.sh index 90f4fac9a..a1d2f9f57 100755 --- a/MC/run/PWGDQ/runBeautyToPsiAndJpsi_fwdy_pp_triggerGap.sh +++ b/MC/run/PWGDQ/runBeautyToPsiAndJpsi_fwdy_pp_triggerGap.sh @@ -5,8 +5,6 @@ [ ! "${O2_ROOT}" ] && echo "Error: This needs O2 loaded" && exit 1 -# ----------- LOAD UTILITY FUNCTIONS -------------------------- -. ${O2_ROOT}/share/scripts/jobutils.sh RNDSEED=${RNDSEED:-0} NSIGEVENTS=${NSIGEVENTS:-1} @@ -14,8 +12,8 @@ NBKGEVENTS=${NBKGEVENTS:-1} NWORKERS=${NWORKERS:-8} NTIMEFRAMES=${NTIMEFRAMES:-1} -${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM 13600 -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -e TGeant4 -mod "--skipModules ZDC" \ - -ini $O2DPG_ROOT/MC/config/PWGDQ/ini/GeneratorHF_bbbar_PsiAndJpsi_fwdy_triggerGap.ini --mft-reco-full --mft-assessment-full --fwdmatching-assessment-full +${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM 13600 -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -e TGeant4 \ + -ini $O2DPG_ROOT/MC/config/PWGDQ/ini/GeneratorHF_bbbar_PsiAndJpsi_fwdy_triggerGap.ini --mft-assessment-full --fwdmatching-assessment-full -interactionRate 500000 # run workflow ${O2DPG_ROOT}/MC/bin/o2_dpg_workflow_runner.py -f workflow.json diff --git a/MC/run/PWGDQ/runBeautyToPsiAndJpsi_midy_pp_triggerGap.sh b/MC/run/PWGDQ/runBeautyToPsiAndJpsi_midy_pp_triggerGap.sh index 4b0f86376..0dad5b4a5 100755 --- a/MC/run/PWGDQ/runBeautyToPsiAndJpsi_midy_pp_triggerGap.sh +++ b/MC/run/PWGDQ/runBeautyToPsiAndJpsi_midy_pp_triggerGap.sh @@ -5,8 +5,6 @@ [ ! "${O2_ROOT}" ] && echo "Error: This needs O2 loaded" && exit 1 -# ----------- LOAD UTILITY FUNCTIONS -------------------------- -. ${O2_ROOT}/share/scripts/jobutils.sh RNDSEED=${RNDSEED:-0} NSIGEVENTS=${NSIGEVENTS:-1} @@ -14,8 +12,8 @@ NBKGEVENTS=${NBKGEVENTS:-1} NWORKERS=${NWORKERS:-8} NTIMEFRAMES=${NTIMEFRAMES:-1} -${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM 13600 -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -e TGeant4 -mod "--skipModules ZDC" \ - -ini $O2DPG_ROOT/MC/config/PWGDQ/ini/GeneratorHF_bbbar_PsiAndJpsi_midy_triggerGap.ini --mft-reco-full +${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM 13600 -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -e TGeant4 \ + -ini $O2DPG_ROOT/MC/config/PWGDQ/ini/GeneratorHF_bbbar_PsiAndJpsi_midy_triggerGap.ini -interactionRate 500000 # run workflow ${O2DPG_ROOT}/MC/bin/o2_dpg_workflow_runner.py -f workflow.json diff --git a/MC/run/PWGDQ/runBeautyToPsi_fwdy_PbPb.sh b/MC/run/PWGDQ/runBeautyToPsi_fwdy_PbPb.sh index 3ef8fda82..38dd22686 100755 --- a/MC/run/PWGDQ/runBeautyToPsi_fwdy_PbPb.sh +++ b/MC/run/PWGDQ/runBeautyToPsi_fwdy_PbPb.sh @@ -5,8 +5,6 @@ [ ! "${O2_ROOT}" ] && echo "Error: This needs O2 loaded" && exit 1 -# ----------- LOAD UTILITY FUNCTIONS -------------------------- -. ${O2_ROOT}/share/scripts/jobutils.sh RNDSEED=${RNDSEED:-0} NSIGEVENTS=${NSIGEVENTS:-2} @@ -14,9 +12,9 @@ NBKGEVENTS=${NBKGEVENTS:-2} NWORKERS=${NWORKERS:-4} NTIMEFRAMES=${NTIMEFRAMES:-1} -${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM 5020 -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -e TGeant4 -mod "--skipModules ZDC" \ - -trigger "external" -ini $O2DPG_ROOT/MC/config/PWGDQ/ini/GeneratorHF_bbbar_Psi2S_fwdy.ini \ - -genBkg pythia8 -procBkg "heavy_ion" -colBkg PbPb --embedding -nb ${NBKGEVENTS} --mft-reco-full --mft-assessment-full --fwdmatching-assessment-full +${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM 5020 -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -e TGeant4 \ + -trigger "external" -ini $O2DPG_ROOT/MC/config/PWGDQ/ini/GeneratorHF_bbbar_Psi2S_fwdy.ini -interactionRate 500000 \ + -genBkg pythia8 -procBkg "heavy_ion" -colBkg PbPb --embedding -nb ${NBKGEVENTS} --mft-assessment-full --fwdmatching-assessment-full # run workflow ${O2DPG_ROOT}/MC/bin/o2_dpg_workflow_runner.py -f workflow.json diff --git a/MC/run/PWGDQ/runBeautyToPsi_fwdy_pp.sh b/MC/run/PWGDQ/runBeautyToPsi_fwdy_pp.sh index c55192b0e..9bd9b125a 100755 --- a/MC/run/PWGDQ/runBeautyToPsi_fwdy_pp.sh +++ b/MC/run/PWGDQ/runBeautyToPsi_fwdy_pp.sh @@ -5,8 +5,6 @@ [ ! "${O2_ROOT}" ] && echo "Error: This needs O2 loaded" && exit 1 -# ----------- LOAD UTILITY FUNCTIONS -------------------------- -. ${O2_ROOT}/share/scripts/jobutils.sh RNDSEED=${RNDSEED:-0} NSIGEVENTS=${NSIGEVENTS:-1} @@ -14,9 +12,9 @@ NBKGEVENTS=${NBKGEVENTS:-1} NWORKERS=${NWORKERS:-8} NTIMEFRAMES=${NTIMEFRAMES:-1} -${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM 900 -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -e TGeant4 -mod "--skipModules ZDC" \ - -trigger "external" -ini $O2DPG_ROOT/MC/config/PWGDQ/ini/GeneratorHF_bbbar_Psi2S_fwdy.ini \ - -genBkg pythia8 -procBkg inel -colBkg pp --embedding -nb ${NBKGEVENTS} --mft-reco-full +${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM 900 -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -e TGeant4 \ + -trigger "external" -ini $O2DPG_ROOT/MC/config/PWGDQ/ini/GeneratorHF_bbbar_Psi2S_fwdy.ini -interactionRate 500000 \ + -genBkg pythia8 -procBkg inel -colBkg pp --embedding -nb ${NBKGEVENTS} # run workflow ${O2DPG_ROOT}/MC/bin/o2_dpg_workflow_runner.py -f workflow.json diff --git a/MC/run/PWGDQ/runBeautyToPsi_midy_pp.sh b/MC/run/PWGDQ/runBeautyToPsi_midy_pp.sh index 01bba73f4..58e9cb74e 100755 --- a/MC/run/PWGDQ/runBeautyToPsi_midy_pp.sh +++ b/MC/run/PWGDQ/runBeautyToPsi_midy_pp.sh @@ -5,8 +5,6 @@ [ ! "${O2_ROOT}" ] && echo "Error: This needs O2 loaded" && exit 1 -# ----------- LOAD UTILITY FUNCTIONS -------------------------- -. ${O2_ROOT}/share/scripts/jobutils.sh RNDSEED=${RNDSEED:-0} NSIGEVENTS=${NSIGEVENTS:-1} @@ -14,9 +12,9 @@ NBKGEVENTS=${NBKGEVENTS:-1} NWORKERS=${NWORKERS:-8} NTIMEFRAMES=${NTIMEFRAMES:-1} -${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM 900 -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -e TGeant4 -mod "--skipModules ZDC" \ +${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM 900 -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -e TGeant4 \ -trigger "external" -ini $O2DPG_ROOT/MC/config/PWGDQ/ini/GeneratorHF_bbbar_Psi2S_midy.ini \ - -genBkg pythia8 -procBkg inel -colBkg pp --embedding -nb ${NBKGEVENTS} --mft-reco-full + -genBkg pythia8 -procBkg inel -colBkg pp --embedding -nb ${NBKGEVENTS} -interactionRate 500000 # run workflow ${O2DPG_ROOT}/MC/bin/o2_dpg_workflow_runner.py -f workflow.json diff --git a/MC/run/PWGDQ/runBplusToJpsi_midy_pp.sh b/MC/run/PWGDQ/runBplusToJpsi_midy_pp.sh index 509cb814a..5c43fda51 100755 --- a/MC/run/PWGDQ/runBplusToJpsi_midy_pp.sh +++ b/MC/run/PWGDQ/runBplusToJpsi_midy_pp.sh @@ -5,8 +5,6 @@ [ ! "${O2_ROOT}" ] && echo "Error: This needs O2 loaded" && exit 1 -# ----------- LOAD UTILITY FUNCTIONS -------------------------- -. ${O2_ROOT}/share/scripts/jobutils.sh RNDSEED=${RNDSEED:-0} NSIGEVENTS=${NSIGEVENTS:-1} @@ -14,8 +12,8 @@ NBKGEVENTS=${NBKGEVENTS:-1} NWORKERS=${NWORKERS:-8} NTIMEFRAMES=${NTIMEFRAMES:-1} -${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM 13600 -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -e TGeant4 -mod "--skipModules ZDC" \ - -trigger "external" -ini $O2DPG_ROOT/MC/config/PWGDQ/ini/GeneratorHF_bbbarToBplus_midy.ini \ +${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM 13600 -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -e TGeant4 \ + -trigger "external" -ini $O2DPG_ROOT/MC/config/PWGDQ/ini/GeneratorHF_bbbarToBplus_midy.ini -interactionRate 500000 \ -genBkg pythia8 -procBkg inel -colBkg pp --embedding -nb ${NBKGEVENTS} # run workflow diff --git a/MC/run/PWGDQ/runBplusToJpsi_midy_pp_triggerGap.sh b/MC/run/PWGDQ/runBplusToJpsi_midy_pp_triggerGap.sh index 0e74920d8..46aed6068 100755 --- a/MC/run/PWGDQ/runBplusToJpsi_midy_pp_triggerGap.sh +++ b/MC/run/PWGDQ/runBplusToJpsi_midy_pp_triggerGap.sh @@ -5,8 +5,6 @@ [ ! "${O2_ROOT}" ] && echo "Error: This needs O2 loaded" && exit 1 -# ----------- LOAD UTILITY FUNCTIONS -------------------------- -. ${O2_ROOT}/share/scripts/jobutils.sh RNDSEED=${RNDSEED:-0} NSIGEVENTS=${NSIGEVENTS:-1} @@ -14,8 +12,8 @@ NBKGEVENTS=${NBKGEVENTS:-1} NWORKERS=${NWORKERS:-8} NTIMEFRAMES=${NTIMEFRAMES:-1} -${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM 13600 -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -e TGeant4 -mod "--skipModules ZDC" \ - -ini $O2DPG_ROOT/MC/config/PWGDQ/ini/GeneratorHF_bbbarToBplus_midy_triggerGap.ini --mft-reco-full +${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM 13600 -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -e TGeant4 \ + -ini $O2DPG_ROOT/MC/config/PWGDQ/ini/GeneratorHF_bbbarToBplus_midy_triggerGap.ini -interactionRate 500000 # run workflow ${O2DPG_ROOT}/MC/bin/o2_dpg_workflow_runner.py -f workflow.json diff --git a/MC/run/PWGDQ/runCharmToMuons_fwd_pp.sh b/MC/run/PWGDQ/runCharmToMuons_fwd_pp.sh index 327298964..58cf706e3 100644 --- a/MC/run/PWGDQ/runCharmToMuons_fwd_pp.sh +++ b/MC/run/PWGDQ/runCharmToMuons_fwd_pp.sh @@ -8,8 +8,6 @@ export ALICEO2_CCDB_LOCALCACHE=$PWD/.ccdb -# ----------- LOAD UTILITY FUNCTIONS -------------------------- -. ${O2_ROOT}/share/scripts/jobutils.sh RNDSEED=${RNDSEED:-0} NSIGEVENTS=${NSIGEVENTS:-1} @@ -18,9 +16,9 @@ NWORKERS=${NWORKERS:-8} NTIMEFRAMES=${NTIMEFRAMES:-1} -${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM 13600 -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -e TGeant4 -mod "MCH MFT MID ITS" \ +${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM 13600 -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -e TGeant4 \ -trigger "external" -ini $O2DPG_ROOT/MC/config/PWGDQ/ini/GeneratorHF_ccbarToMuonsSemileptonic_fwdy.ini \ - -genBkg pythia8 -procBkg cdiff -colBkg pp --embedding -nb ${NBKGEVENTS} --mft-reco-full \ + -genBkg pythia8 -procBkg cdiff -colBkg pp --embedding -nb ${NBKGEVENTS} \ -confKeyBkg "Diamond.width[2]=6" -interactionRate 2000 --mft-assessment-full --fwdmatching-assessment-full # run workflow diff --git a/MC/run/PWGDQ/runFwdMuBoxGen_PbPb.sh b/MC/run/PWGDQ/runFwdMuBoxGen_PbPb.sh index a5e34d1e9..3163b3770 100755 --- a/MC/run/PWGDQ/runFwdMuBoxGen_PbPb.sh +++ b/MC/run/PWGDQ/runFwdMuBoxGen_PbPb.sh @@ -6,8 +6,6 @@ [ ! "${O2_ROOT}" ] && echo "Error: This needs O2 loaded" && exit 1 -# ----------- LOAD UTILITY FUNCTIONS -------------------------- -. ${O2_ROOT}/share/scripts/jobutils.sh RNDSEED=${RNDSEED:-0} NSIGEVENTS=${NSIGEVENTS:-1} @@ -17,8 +15,8 @@ NTIMEFRAMES=${NTIMEFRAMES:-1} NBOXMUONS=${NBOXMUONS:-2} ${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM 5020 -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -e TGeant4 \ - -confKey "GeneratorExternal.fileName=${O2DPG_ROOT}/MC/config/PWGDQ/external/generator/GeneratorBoxFwd.C;GeneratorExternal.funcName=fwdMuBoxGen()" \ - -genBkg pythia8 -procBkg "heavy_ion" -colBkg PbPb --embedding -nb ${NBKGEVENTS} --mft-reco-full --mft-assessment-full --fwdmatching-assessment-full --fwdmatching-save-trainingdata + -confKey "GeneratorExternal.fileName=${O2DPG_ROOT}/MC/config/PWGDQ/external/generator/GeneratorBoxFwd.C;GeneratorExternal.funcName=fwdMuBoxGen()" -interactionRate 500000 \ + -genBkg pythia8 -procBkg "heavy_ion" -colBkg PbPb --embedding -nb ${NBKGEVENTS} --mft-assessment-full --fwdmatching-assessment-full --fwdmatching-save-trainingdata # run workflow (MFT-related tasks) NMUONS=$NBOXMUONS ${O2DPG_ROOT}/MC/bin/o2_dpg_workflow_runner.py -f workflow.json -tt "(mft.*)" diff --git a/MC/run/PWGDQ/runFwdMuBoxGen_pp.sh b/MC/run/PWGDQ/runFwdMuBoxGen_pp.sh index 1d54c8e6e..396530969 100755 --- a/MC/run/PWGDQ/runFwdMuBoxGen_pp.sh +++ b/MC/run/PWGDQ/runFwdMuBoxGen_pp.sh @@ -6,8 +6,6 @@ [ ! "${O2_ROOT}" ] && echo "Error: This needs O2 loaded" && exit 1 -# ----------- LOAD UTILITY FUNCTIONS -------------------------- -. ${O2_ROOT}/share/scripts/jobutils.sh RNDSEED=${RNDSEED:-0} NSIGEVENTS=${NSIGEVENTS:-1} @@ -16,9 +14,9 @@ NWORKERS=${NWORKERS:-8} NTIMEFRAMES=${NTIMEFRAMES:-1} NBOXMUONS=${NBOXMUONS:-2} -${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM 13600 -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -e TGeant4 -mod "--skipModules ZDC" \ - -confKey "GeneratorExternal.fileName=${O2DPG_ROOT}/MC/config/PWGDQ/external/generator/GeneratorBoxFwd.C;GeneratorExternal.funcName=fwdMuBoxGen()" \ - -genBkg pythia8 -procBkg inel -colBkg pp --embedding -nb ${NBKGEVENTS} --mft-reco-full --mft-assessment-full --fwdmatching-assessment-full --fwdmatching-save-trainingdata +${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM 13600 -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -e TGeant4 \ + -confKey "GeneratorExternal.fileName=${O2DPG_ROOT}/MC/config/PWGDQ/external/generator/GeneratorBoxFwd.C;GeneratorExternal.funcName=fwdMuBoxGen()" -interactionRate 500000 \ + -genBkg pythia8 -procBkg inel -colBkg pp --embedding -nb ${NBKGEVENTS} --mft-assessment-full --fwdmatching-assessment-full --fwdmatching-save-trainingdata # run workflow (MFT-related tasks) NMUONS=$NBOXMUONS ${O2DPG_ROOT}/MC/bin/o2_dpg_workflow_runner.py -f workflow.json -tt "(mft.*)" diff --git a/MC/run/PWGDQ/runPhotoproducedCharmonia_midy_PbPb5TeV.sh b/MC/run/PWGDQ/runPhotoproducedCharmonia_midy_PbPb5TeV.sh new file mode 100644 index 000000000..4347822d7 --- /dev/null +++ b/MC/run/PWGDQ/runPhotoproducedCharmonia_midy_PbPb5TeV.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash + +RNDSEED=${RNDSEED:-0} +NWORKERS=${NWORKERS:-8} + + +export ALIEN_JDL_LPMPRODUCTIONTYPE=MC +export ALIEN_JDL_CPULIMIT=20 +export ALIEN_JDL_LPMANCHORPASSNAME=apass4 +export ALIEN_JDL_MCANCHOR=apass4 +export ALIEN_JDL_COLLISIONSYSTEM=PbPb +export ALIEN_JDL_LPMPASSNAME=apass4 +export ALIEN_JDL_LPMRUNNUMBER=544474 +export ALIEN_JDL_LPMANCHORRUN=544474 + +export ALIEN_JDL_LPMINTERACTIONTYPE=PbPb +export ALIEN_JDL_LPMPRODUCTIONTAG="TestProd" +export ALIEN_JDL_LPMANCHORPRODUCTION="TestProd" +export ALIEN_JDL_LPMANCHORYEAR=2023 + +export ALIEN_JDL_O2DPGWORKFLOWTARGET="aod" + +export PRODSPLIT=${ALIEN_O2DPG_GRIDSUBMIT_PRODSPLIT:-10} +export SPLITID=${ALIEN_O2DPG_GRIDSUBMIT_SUBJOBID:-10} +export CYCLE=0 +export NTIMEFRAMES=8 +export NSIGEVENTS=20 +export NBKGEVENTS=10 + +CONFIGNAME=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGDQ/ini/Generator_InjectedPhotoproucedCharmoniaMidy_PbPb5TeV.ini + +export ALIEN_JDL_ANCHOR_SIM_OPTIONS="-eCM 5360 -gen external -j ${NWORKERS} -tf ${NTIMEFRAMES} -e TGeant4 -seed 0 -ini ${CONFIGNAME} -genBkg pythia8 -procBkg \"heavy_ion\" -colBkg PbPb --embedding -nb $NBKGEVENTS --embeddPattern @0:e2" + +${O2DPG_ROOT}/MC/run/ANCHOR/anchorMC.sh \ No newline at end of file diff --git a/MC/run/PWGDQ/runPromptCharmonia_fwdy_PbPb.sh b/MC/run/PWGDQ/runPromptCharmonia_fwdy_PbPb.sh index 725cbaa6b..e8d2462d3 100755 --- a/MC/run/PWGDQ/runPromptCharmonia_fwdy_PbPb.sh +++ b/MC/run/PWGDQ/runPromptCharmonia_fwdy_PbPb.sh @@ -5,8 +5,6 @@ [ ! "${O2_ROOT}" ] && echo "Error: This needs O2 loaded" && exit 1 -# ----------- LOAD UTILITY FUNCTIONS -------------------------- -. ${O2_ROOT}/share/scripts/jobutils.sh RNDSEED=${RNDSEED:-0} NSIGEVENTS=${NSIGEVENTS:-1} @@ -14,9 +12,9 @@ NBKGEVENTS=${NBKGEVENTS:-1} NWORKERS=${NWORKERS:-8} NTIMEFRAMES=${NTIMEFRAMES:-1} -${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM 5020 -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -e TGeant4 -mod "--skipModules ZDC" \ +${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM 5020 -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -e TGeant4 \ -confKey "GeneratorExternal.fileName=${O2DPG_ROOT}/MC/config/PWGDQ/external/generator/GeneratorCocktailPromptCharmoniaToMuonEvtGen_PbPb5TeV.C;GeneratorExternal.funcName=GeneratorCocktailPromptCharmoniaToMuonEvtGen_PbPb5TeV()" \ - -genBkg pythia8 -procBkg "heavy_ion" -colBkg PbPb --embedding -nb ${NBKGEVENTS} --mft-reco-full --mft-assessment-full --fwdmatching-assessment-full --fwdmatching-save-trainingdata + -genBkg pythia8 -procBkg "heavy_ion" -colBkg PbPb --embedding -nb ${NBKGEVENTS} --mft-assessment-full --fwdmatching-assessment-full --fwdmatching-save-trainingdata -interactionRate 500000 # run workflow ${O2DPG_ROOT}/MC/bin/o2_dpg_workflow_runner.py -f workflow.json diff --git a/MC/run/PWGDQ/runPromptCharmonia_fwdy_pp.sh b/MC/run/PWGDQ/runPromptCharmonia_fwdy_pp.sh index 0b4465e43..7dbe40c4c 100755 --- a/MC/run/PWGDQ/runPromptCharmonia_fwdy_pp.sh +++ b/MC/run/PWGDQ/runPromptCharmonia_fwdy_pp.sh @@ -5,8 +5,6 @@ [ ! "${O2_ROOT}" ] && echo "Error: This needs O2 loaded" && exit 1 -# ----------- LOAD UTILITY FUNCTIONS -------------------------- -. ${O2_ROOT}/share/scripts/jobutils.sh RNDSEED=${RNDSEED:-0} NSIGEVENTS=${NSIGEVENTS:-1} @@ -15,9 +13,9 @@ NWORKERS=${NWORKERS:-8} NTIMEFRAMES=${NTIMEFRAMES:-1} TARGETTASK=${TARGETTASK:+-tt ${TARGETTASK}} -${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM 13600 -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -e TGeant4 -mod "--skipModules ZDC" \ +${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM 13600 -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -e TGeant4 \ -confKey "GeneratorExternal.fileName=${O2DPG_ROOT}/MC/config/PWGDQ/external/generator/GeneratorParamPromptJpsiToMuonEvtGen_pp13TeV.C;GeneratorExternal.funcName=GeneratorParamPromptJpsiToMuonEvtGen_pp13TeV()" \ - -genBkg pythia8 -procBkg inel -colBkg pp --embedding -nb ${NBKGEVENTS} --mft-reco-full --fwdmatching-4-param --fwdmatching-cut-4-param + -genBkg pythia8 -procBkg inel -colBkg pp --embedding -nb ${NBKGEVENTS} --fwdmatching-4-param --fwdmatching-cut-4-param -interactionRate 500000 # run workflow diff --git a/MC/run/PWGDQ/runPromptCharmonia_fwdy_pp_assessment.sh b/MC/run/PWGDQ/runPromptCharmonia_fwdy_pp_assessment.sh index ec9cf2ccb..7b6d2f294 100755 --- a/MC/run/PWGDQ/runPromptCharmonia_fwdy_pp_assessment.sh +++ b/MC/run/PWGDQ/runPromptCharmonia_fwdy_pp_assessment.sh @@ -5,8 +5,6 @@ [ ! "${O2_ROOT}" ] && echo "Error: This needs O2 loaded" && exit 1 -# ----------- LOAD UTILITY FUNCTIONS -------------------------- -. ${O2_ROOT}/share/scripts/jobutils.sh RNDSEED=${RNDSEED:-0} NSIGEVENTS=${NSIGEVENTS:-1} @@ -14,9 +12,9 @@ NBKGEVENTS=${NBKGEVENTS:-1} NWORKERS=${NWORKERS:-8} NTIMEFRAMES=${NTIMEFRAMES:-1} -${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM 900 -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -e TGeant4 -mod "--skipModules ZDC" \ +${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM 900 -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -e TGeant4 \ -confKey "GeneratorExternal.fileName=${O2DPG_ROOT}/MC/config/PWGDQ/external/generator/GeneratorCocktailPromptCharmoniaToMuonEvtGen_pp13TeV.C;GeneratorExternal.funcName=GeneratorCocktailPromptCharmoniaToMuonEvtGen_pp13TeV()" \ - -genBkg pythia8 -procBkg inel -colBkg pp --embedding -nb ${NBKGEVENTS} --mft-reco-full --mft-assessment-full --fwdmatching-assessment-full + -genBkg pythia8 -procBkg inel -colBkg pp --embedding -nb ${NBKGEVENTS} --mft-assessment-full --fwdmatching-assessment-full -interactionRate 500000 # run workflow diff --git a/MC/run/PWGDQ/runPromptCharmonia_fwdy_pp_assessment_triggerGap.sh b/MC/run/PWGDQ/runPromptCharmonia_fwdy_pp_assessment_triggerGap.sh index fc637c33d..fb1a8ca90 100755 --- a/MC/run/PWGDQ/runPromptCharmonia_fwdy_pp_assessment_triggerGap.sh +++ b/MC/run/PWGDQ/runPromptCharmonia_fwdy_pp_assessment_triggerGap.sh @@ -5,8 +5,6 @@ [ ! "${O2_ROOT}" ] && echo "Error: This needs O2 loaded" && exit 1 -# ----------- LOAD UTILITY FUNCTIONS -------------------------- -. ${O2_ROOT}/share/scripts/jobutils.sh RNDSEED=${RNDSEED:-0} NSIGEVENTS=${NSIGEVENTS:-1} @@ -14,8 +12,8 @@ NBKGEVENTS=${NBKGEVENTS:-1} NWORKERS=${NWORKERS:-8} NTIMEFRAMES=${NTIMEFRAMES:-1} -${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM 13600 -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -e TGeant4 -mod "--skipModules ZDC" \ - -ini $O2DPG_ROOT/MC/config/PWGDQ/ini/Generator_InjectedPromptCharmoniaFwdy_TriggerGap.ini --mft-reco-full --mft-assessment-full --fwdmatching-assessment-full +${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM 13600 -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -e TGeant4 \ + -ini $O2DPG_ROOT/MC/config/PWGDQ/ini/Generator_InjectedPromptCharmoniaFwdy_TriggerGap.ini --mft-assessment-full --fwdmatching-assessment-full -interactionRate 500000 # run workflow ${O2DPG_ROOT}/MC/bin/o2_dpg_workflow_runner.py -f workflow.json diff --git a/MC/run/PWGDQ/runPromptCharmonia_midy_pp.sh b/MC/run/PWGDQ/runPromptCharmonia_midy_pp.sh index 4538eed9a..e2e9ec057 100755 --- a/MC/run/PWGDQ/runPromptCharmonia_midy_pp.sh +++ b/MC/run/PWGDQ/runPromptCharmonia_midy_pp.sh @@ -5,8 +5,6 @@ [ ! "${O2_ROOT}" ] && echo "Error: This needs O2 loaded" && exit 1 -# ----------- LOAD UTILITY FUNCTIONS -------------------------- -. ${O2_ROOT}/share/scripts/jobutils.sh RNDSEED=${RNDSEED:-0} NSIGEVENTS=${NSIGEVENTS:-1} @@ -14,9 +12,9 @@ NBKGEVENTS=${NBKGEVENTS:-1} NWORKERS=${NWORKERS:-8} NTIMEFRAMES=${NTIMEFRAMES:-1} -${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM 900 -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -e TGeant4 -mod "--skipModules ZDC" \ +${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM 900 -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -e TGeant4 \ -confKey "GeneratorExternal.fileName=${O2DPG_ROOT}/MC/config/PWGDQ/external/generator/GeneratorCocktailPromptCharmoniaToElectronEvtGen_pp13TeV.C;GeneratorExternal.funcName=GeneratorCocktailPromptCharmoniaToElectronEvtGen_pp13TeV()" \ - -genBkg pythia8 -procBkg inel -colBkg pp --embedding -nb ${NBKGEVENTS} --mft-reco-full + -genBkg pythia8 -procBkg inel -colBkg pp --embedding -nb ${NBKGEVENTS} -interactionRate 500000 # run workflow diff --git a/MC/run/PWGDQ/runPromptCharmonia_midy_pp_triggerGap.sh b/MC/run/PWGDQ/runPromptCharmonia_midy_pp_triggerGap.sh index 0636e7eb6..ed549861a 100755 --- a/MC/run/PWGDQ/runPromptCharmonia_midy_pp_triggerGap.sh +++ b/MC/run/PWGDQ/runPromptCharmonia_midy_pp_triggerGap.sh @@ -5,8 +5,6 @@ [ ! "${O2_ROOT}" ] && echo "Error: This needs O2 loaded" && exit 1 -# ----------- LOAD UTILITY FUNCTIONS -------------------------- -. ${O2_ROOT}/share/scripts/jobutils.sh RNDSEED=${RNDSEED:-0} NSIGEVENTS=${NSIGEVENTS:-1} @@ -14,8 +12,8 @@ NBKGEVENTS=${NBKGEVENTS:-1} NWORKERS=${NWORKERS:-8} NTIMEFRAMES=${NTIMEFRAMES:-1} -${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM 13600 -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -e TGeant4 -mod "--skipModules ZDC" \ - -ini $O2DPG_ROOT/MC/config/PWGDQ/ini/Generator_InjectedPromptCharmoniaMidy_TriggerGap.ini --mft-reco-full +${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM 13600 -interactionRate 500000 -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -e TGeant4 \ + -ini $O2DPG_ROOT/MC/config/PWGDQ/ini/Generator_InjectedPromptCharmoniaMidy_TriggerGap.ini # run workflow ${O2DPG_ROOT}/MC/bin/o2_dpg_workflow_runner.py -f workflow.json diff --git a/MC/run/PWGDQ/runPromptJpsi_fwdy_pp_assessment_triggerGap.sh b/MC/run/PWGDQ/runPromptJpsi_fwdy_pp_assessment_triggerGap.sh index d1f03343f..3634d30d2 100755 --- a/MC/run/PWGDQ/runPromptJpsi_fwdy_pp_assessment_triggerGap.sh +++ b/MC/run/PWGDQ/runPromptJpsi_fwdy_pp_assessment_triggerGap.sh @@ -5,8 +5,6 @@ [ ! "${O2_ROOT}" ] && echo "Error: This needs O2 loaded" && exit 1 -# ----------- LOAD UTILITY FUNCTIONS -------------------------- -. ${O2_ROOT}/share/scripts/jobutils.sh RNDSEED=${RNDSEED:-0} NSIGEVENTS=${NSIGEVENTS:-1} @@ -14,8 +12,8 @@ NBKGEVENTS=${NBKGEVENTS:-1} NWORKERS=${NWORKERS:-8} NTIMEFRAMES=${NTIMEFRAMES:-1} -${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM 13600 -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -e TGeant4 -mod "--skipModules ZDC" \ - -ini $O2DPG_ROOT/MC/config/PWGDQ/ini/Generator_InjectedPromptJpsiFwdy_TriggerGap.ini --mft-reco-full --mft-assessment-full --fwdmatching-assessment-full +${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM 13600 -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -e TGeant4 \ + -ini $O2DPG_ROOT/MC/config/PWGDQ/ini/Generator_InjectedPromptJpsiFwdy_TriggerGap.ini --mft-assessment-full --fwdmatching-assessment-full -interactionRate 500000 # run workflow ${O2DPG_ROOT}/MC/bin/o2_dpg_workflow_runner.py -f workflow.json diff --git a/MC/run/PWGDQ/runPromptJpsi_midy_pp.sh b/MC/run/PWGDQ/runPromptJpsi_midy_pp.sh index 311d87c70..203aa4048 100755 --- a/MC/run/PWGDQ/runPromptJpsi_midy_pp.sh +++ b/MC/run/PWGDQ/runPromptJpsi_midy_pp.sh @@ -5,8 +5,6 @@ [ ! "${O2_ROOT}" ] && echo "Error: This needs O2 loaded" && exit 1 -# ----------- LOAD UTILITY FUNCTIONS -------------------------- -. ${O2_ROOT}/share/scripts/jobutils.sh RNDSEED=${RNDSEED:-0} NSIGEVENTS=${NSIGEVENTS:-1} @@ -14,9 +12,9 @@ NBKGEVENTS=${NBKGEVENTS:-1} NWORKERS=${NWORKERS:-8} NTIMEFRAMES=${NTIMEFRAMES:-1} -${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM 900 -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -e TGeant4 -mod "--skipModules ZDC" \ +${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM 900 -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -e TGeant4 \ -confKey "GeneratorExternal.fileName=${O2DPG_ROOT}/MC/config/PWGDQ/external/generator/GeneratorParamPromptJpsiToElectronEvtGen_pp13TeV.C;GeneratorExternal.funcName=GeneratorParamPromptJpsiToElectronEvtGen_pp13TeV()" \ - -genBkg pythia8 -procBkg inel -colBkg pp --embedding -nb ${NBKGEVENTS} --mft-reco-full + -genBkg pythia8 -procBkg inel -colBkg pp --embedding -nb ${NBKGEVENTS} -interactionRate 500000 # run workflow ${O2DPG_ROOT}/MC/bin/o2_dpg_workflow_runner.py -f workflow.json diff --git a/MC/run/PWGDQ/runPromptPsi2S_fwdy_pp_assessment_triggerGap.sh b/MC/run/PWGDQ/runPromptPsi2S_fwdy_pp_assessment_triggerGap.sh index 406152d24..caf085726 100755 --- a/MC/run/PWGDQ/runPromptPsi2S_fwdy_pp_assessment_triggerGap.sh +++ b/MC/run/PWGDQ/runPromptPsi2S_fwdy_pp_assessment_triggerGap.sh @@ -5,8 +5,6 @@ [ ! "${O2_ROOT}" ] && echo "Error: This needs O2 loaded" && exit 1 -# ----------- LOAD UTILITY FUNCTIONS -------------------------- -. ${O2_ROOT}/share/scripts/jobutils.sh RNDSEED=${RNDSEED:-0} NSIGEVENTS=${NSIGEVENTS:-1} @@ -14,8 +12,8 @@ NBKGEVENTS=${NBKGEVENTS:-1} NWORKERS=${NWORKERS:-8} NTIMEFRAMES=${NTIMEFRAMES:-1} -${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM 13600 -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -e TGeant4 -mod "--skipModules ZDC" \ - -ini $O2DPG_ROOT/MC/config/PWGDQ/ini/Generator_InjectedPromptPsi2SFwdy_TriggerGap.ini --mft-reco-full --mft-assessment-full --fwdmatching-assessment-full +${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM 13600 -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -e TGeant4 \ + -ini $O2DPG_ROOT/MC/config/PWGDQ/ini/Generator_InjectedPromptPsi2SFwdy_TriggerGap.ini --mft-assessment-full --fwdmatching-assessment-full -interactionRate 500000 # run workflow ${O2DPG_ROOT}/MC/bin/o2_dpg_workflow_runner.py -f workflow.json diff --git a/MC/run/PWGDQ/run_pp_JpsiHFcorr_ccbar_gaptrigger.sh b/MC/run/PWGDQ/run_pp_JpsiHFcorr_ccbar_gaptrigger.sh index 215a49e12..b18ba13cd 100755 --- a/MC/run/PWGDQ/run_pp_JpsiHFcorr_ccbar_gaptrigger.sh +++ b/MC/run/PWGDQ/run_pp_JpsiHFcorr_ccbar_gaptrigger.sh @@ -8,8 +8,6 @@ [ ! "${O2DPG_ROOT}" ] && echo "Error: This needs O2DPG loaded" && exit 1 [ ! "${O2_ROOT}" ] && echo "Error: This needs O2 loaded" && exit 1 -# ----------- LOAD UTILITY FUNCTIONS -------------------------- -. ${O2_ROOT}/share/scripts/jobutils.sh # ----------- START ACTUAL JOB ----------------------------- @@ -22,7 +20,7 @@ NTIMEFRAMES=${NTIMEFRAMES:-1} # create workflow #ccbar filter -${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM 13600 -col pp -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -interactionRate 500000 -confKey "Diamond.width[2]=6." -e ${SIMENGINE} ${SEED} -mod "--skipModules ZDC" \ +${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM 13600 -col pp -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -interactionRate 500000 -confKey "Diamond.width[2]=6." -e ${SIMENGINE} ${SEED} \ -ini $O2DPG_ROOT/MC/config/PWGDQ/ini/GeneratorJPsiHFCorr_ccbar.ini \ # run workflow diff --git a/MC/run/PWGEM/runAnchoredDYee_OO_5360_Gap2.sh b/MC/run/PWGEM/runAnchoredDYee_OO_5360_Gap2.sh new file mode 100644 index 000000000..9b36bcf51 --- /dev/null +++ b/MC/run/PWGEM/runAnchoredDYee_OO_5360_Gap2.sh @@ -0,0 +1,52 @@ +#!/bin/bash + +# +# Steering script for HF enhanced dielectron MC anchored to LHC25ae +# + +# example anchoring + +export ALIEN_JDL_LPMANCHORPASSNAME=apass2 +export ALIEN_JDL_MCANCHOR=apass2 +export ALIEN_JDL_CPULIMIT=8 +export ALIEN_JDL_LPMRUNNUMBER=564356 +export ALIEN_JDL_LPMPRODUCTIONTYPE=MC +export ALIEN_JDL_LPMINTERACTIONTYPE=pp +export ALIEN_JDL_LPMPRODUCTIONTAG=LHC26a2 +export ALIEN_JDL_LPMANCHORRUN=564356 +export ALIEN_JDL_LPMANCHORPRODUCTION=LHC25ae +export ALIEN_JDL_LPMANCHORYEAR=2025 +export ALIEN_JDL_OUTPUT=*.dat@disk=1,*.txt@disk=1,*.root@disk=2 + +export NTIMEFRAMES=1 +export NSIGEVENTS=20 +export SPLITID=100 +export PRODSPLIT=153 +export CYCLE=0 + +# on the GRID, this is set and used as seed; when set, it takes precedence over SEED +#export ALIEN_PROC_ID=2963436952 +export SEED=0 + +# for pp and 50 events per TF, we launch only 4 workers. +export NWORKERS=2 + +# define the generator via ini file +# use 30/70 sampling for different generators +# No forced beauty decays as we have observed biases + +# generate random number +RNDSIG=$(($RANDOM % 100)) + +CONFIGNAME="GeneratorDYee_GapTriggered_Gap2_OO5360GeV.ini" + +export ALIEN_JDL_ANCHOR_SIM_OPTIONS="-gen external -ini $O2DPG_ROOT/MC/config/PWGEM/ini/$CONFIGNAME" + +# run the central anchor steering script; this includes +# * derive timestamp +# * derive interaction rate +# * extract and prepare configurations (which detectors are contained in the run etc.) +# * run the simulation (and QC) +# To disable QC, uncomment the following line +#export DISABLE_QC=1 +${O2DPG_ROOT}/MC/run/ANCHOR/anchorMC.sh diff --git a/MC/run/PWGEM/runAnchoredDYee_OO_5360_Gap5.sh b/MC/run/PWGEM/runAnchoredDYee_OO_5360_Gap5.sh new file mode 100644 index 000000000..8767cae2d --- /dev/null +++ b/MC/run/PWGEM/runAnchoredDYee_OO_5360_Gap5.sh @@ -0,0 +1,52 @@ +#!/bin/bash + +# +# Steering script for HF enhanced dielectron MC anchored to LHC25ae +# + +# example anchoring + +export ALIEN_JDL_LPMANCHORPASSNAME=apass2 +export ALIEN_JDL_MCANCHOR=apass2 +export ALIEN_JDL_CPULIMIT=8 +export ALIEN_JDL_LPMRUNNUMBER=564356 +export ALIEN_JDL_LPMPRODUCTIONTYPE=MC +export ALIEN_JDL_LPMINTERACTIONTYPE=pp +export ALIEN_JDL_LPMPRODUCTIONTAG=LHC26a2 +export ALIEN_JDL_LPMANCHORRUN=564356 +export ALIEN_JDL_LPMANCHORPRODUCTION=LHC25ae +export ALIEN_JDL_LPMANCHORYEAR=2025 +export ALIEN_JDL_OUTPUT=*.dat@disk=1,*.txt@disk=1,*.root@disk=2 + +export NTIMEFRAMES=1 +export NSIGEVENTS=20 +export SPLITID=100 +export PRODSPLIT=153 +export CYCLE=0 + +# on the GRID, this is set and used as seed; when set, it takes precedence over SEED +#export ALIEN_PROC_ID=2963436952 +export SEED=0 + +# for pp and 50 events per TF, we launch only 4 workers. +export NWORKERS=2 + +# define the generator via ini file +# use 30/70 sampling for different generators +# No forced beauty decays as we have observed biases + +# generate random number +RNDSIG=$(($RANDOM % 100)) + +CONFIGNAME="GeneratorDYee_GapTriggered_Gap5_OO5360GeV.ini" + +export ALIEN_JDL_ANCHOR_SIM_OPTIONS="-gen external -ini $O2DPG_ROOT/MC/config/PWGEM/ini/$CONFIGNAME" + +# run the central anchor steering script; this includes +# * derive timestamp +# * derive interaction rate +# * extract and prepare configurations (which detectors are contained in the run etc.) +# * run the simulation (and QC) +# To disable QC, uncomment the following line +#export DISABLE_QC=1 +${O2DPG_ROOT}/MC/run/ANCHOR/anchorMC.sh diff --git a/MC/run/PWGEM/runAnchoredDYee_pp_13600_Gap5.sh b/MC/run/PWGEM/runAnchoredDYee_pp_13600_Gap5.sh new file mode 100644 index 000000000..77450c320 --- /dev/null +++ b/MC/run/PWGEM/runAnchoredDYee_pp_13600_Gap5.sh @@ -0,0 +1,50 @@ +#!/bin/bash + +# +# Steering script for HF enhanced dielectron MC anchored to LHC24ap,aq apass1 +# + +# example anchoring +# taken from https://its.cern.ch/jira/browse/O2-5670 +export ALIEN_JDL_LPMANCHORPASSNAME=apass1 +export ALIEN_JDL_MCANCHOR=apass1 +export ALIEN_JDL_CPULIMIT=8 +export ALIEN_JDL_LPMRUNNUMBER=559348 +export ALIEN_JDL_LPMPRODUCTIONTYPE=MC +export ALIEN_JDL_LPMINTERACTIONTYPE=pp +export ALIEN_JDL_LPMPRODUCTIONTAG=LHC24a6 +export ALIEN_JDL_LPMANCHORRUN=559348 +export ALIEN_JDL_LPMANCHORPRODUCTION=LHC26ac +export ALIEN_JDL_LPMANCHORYEAR=2026 +export ALIEN_JDL_OUTPUT=*.dat@disk=1,*.txt@disk=1,*.root@disk=2 + +export NTIMEFRAMES=1 +export NSIGEVENTS=20 +export SPLITID=100 +export PRODSPLIT=153 +export CYCLE=0 + +# on the GRID, this is set and used as seed; when set, it takes precedence over SEED +#export ALIEN_PROC_ID=2963436952 +export SEED=0 + +# for pp and 50 events per TF, we launch only 4 workers. +export NWORKERS=2 + +# define the generator via ini file +# use 20/40/40 sampling for different generators +# generate random number +#RNDSIG=$(($RANDOM % 100)) + +CONFIGNAME="GeneratorDYee_GapTriggered_Gap5_pp13600GeV.ini" + +export ALIEN_JDL_ANCHOR_SIM_OPTIONS="-gen external -ini $O2DPG_ROOT/MC/config/PWGEM/ini/$CONFIGNAME" + +# run the central anchor steering script; this includes +# * derive timestamp +# * derive interaction rate +# * extract and prepare configurations (which detectors are contained in the run etc.) +# * run the simulation (and QC) +# To disable QC, uncomment the following line +#export DISABLE_QC=1 +${O2DPG_ROOT}/MC/run/ANCHOR/anchorMC.sh diff --git a/MC/run/PWGEM/runAnchoredDYee_pp_5360_Gap5.sh b/MC/run/PWGEM/runAnchoredDYee_pp_5360_Gap5.sh new file mode 100644 index 000000000..6d703d179 --- /dev/null +++ b/MC/run/PWGEM/runAnchoredDYee_pp_5360_Gap5.sh @@ -0,0 +1,50 @@ +#!/bin/bash + +# +# Steering script for HF enhanced dielectron MC anchored to LHC24ap,aq apass1 +# + +# example anchoring +# taken from https://its.cern.ch/jira/browse/O2-5670 +export ALIEN_JDL_LPMANCHORPASSNAME=apass1 +export ALIEN_JDL_MCANCHOR=apass1 +export ALIEN_JDL_CPULIMIT=8 +export ALIEN_JDL_LPMRUNNUMBER=559348 +export ALIEN_JDL_LPMPRODUCTIONTYPE=MC +export ALIEN_JDL_LPMINTERACTIONTYPE=pp +export ALIEN_JDL_LPMPRODUCTIONTAG=LHC24a6 +export ALIEN_JDL_LPMANCHORRUN=559348 +export ALIEN_JDL_LPMANCHORPRODUCTION=LHC24ap +export ALIEN_JDL_LPMANCHORYEAR=2024 +export ALIEN_JDL_OUTPUT=*.dat@disk=1,*.txt@disk=1,*.root@disk=2 + +export NTIMEFRAMES=1 +export NSIGEVENTS=20 +export SPLITID=100 +export PRODSPLIT=153 +export CYCLE=0 + +# on the GRID, this is set and used as seed; when set, it takes precedence over SEED +#export ALIEN_PROC_ID=2963436952 +export SEED=0 + +# for pp and 50 events per TF, we launch only 4 workers. +export NWORKERS=2 + +# define the generator via ini file +# use 20/40/40 sampling for different generators +# generate random number +#RNDSIG=$(($RANDOM % 100)) + +CONFIGNAME="GeneratorDYee_GapTriggered_Gap5_pp5360GeV.ini" + +export ALIEN_JDL_ANCHOR_SIM_OPTIONS="-gen external -ini $O2DPG_ROOT/MC/config/PWGEM/ini/$CONFIGNAME" + +# run the central anchor steering script; this includes +# * derive timestamp +# * derive interaction rate +# * extract and prepare configurations (which detectors are contained in the run etc.) +# * run the simulation (and QC) +# To disable QC, uncomment the following line +#export DISABLE_QC=1 +${O2DPG_ROOT}/MC/run/ANCHOR/anchorMC.sh diff --git a/MC/run/PWGEM/runAnchoredDYmumu_pp_13600_Gap1.sh b/MC/run/PWGEM/runAnchoredDYmumu_pp_13600_Gap1.sh new file mode 100644 index 000000000..86e411252 --- /dev/null +++ b/MC/run/PWGEM/runAnchoredDYmumu_pp_13600_Gap1.sh @@ -0,0 +1,50 @@ +#!/bin/bash + +# +# Steering script for DY->dimuon MC anchored to LHC26 pp apass1 +# + +# example anchoring +# taken from https://its.cern.ch/jira/browse/O2-5670 +export ALIEN_JDL_LPMANCHORPASSNAME=apass1 +export ALIEN_JDL_MCANCHOR=apass1 +export ALIEN_JDL_CPULIMIT=8 +export ALIEN_JDL_LPMRUNNUMBER=559348 +export ALIEN_JDL_LPMPRODUCTIONTYPE=MC +export ALIEN_JDL_LPMINTERACTIONTYPE=pp +export ALIEN_JDL_LPMPRODUCTIONTAG=LHC24a6 +export ALIEN_JDL_LPMANCHORRUN=559348 +export ALIEN_JDL_LPMANCHORPRODUCTION=LHC26ac +export ALIEN_JDL_LPMANCHORYEAR=2026 +export ALIEN_JDL_OUTPUT=*.dat@disk=1,*.txt@disk=1,*.root@disk=2 + +export NTIMEFRAMES=1 +export NSIGEVENTS=20 +export SPLITID=100 +export PRODSPLIT=153 +export CYCLE=0 + +# on the GRID, this is set and used as seed; when set, it takes precedence over SEED +#export ALIEN_PROC_ID=2963436952 +export SEED=0 + +# for pp and 50 events per TF, we launch only 4 workers. +export NWORKERS=2 + +# define the generator via ini file +# use 20/40/40 sampling for different generators +# generate random number +#RNDSIG=$(($RANDOM % 100)) + +CONFIGNAME="GeneratorDYmumu_GapTriggered_Gap1_pp13600GeV.ini" + +export ALIEN_JDL_ANCHOR_SIM_OPTIONS="-gen external -ini $O2DPG_ROOT/MC/config/PWGEM/ini/$CONFIGNAME" + +# run the central anchor steering script; this includes +# * derive timestamp +# * derive interaction rate +# * extract and prepare configurations (which detectors are contained in the run etc.) +# * run the simulation (and QC) +# To disable QC, uncomment the following line +#export DISABLE_QC=1 +${O2DPG_ROOT}/MC/run/ANCHOR/anchorMC.sh diff --git a/MC/run/PWGEM/runAnchoredHFGapToDielectrons_pp_5360_Gap5.sh b/MC/run/PWGEM/runAnchoredHFGapToDielectrons_pp_5360_Gap5.sh new file mode 100644 index 000000000..019a53415 --- /dev/null +++ b/MC/run/PWGEM/runAnchoredHFGapToDielectrons_pp_5360_Gap5.sh @@ -0,0 +1,57 @@ +#!/bin/bash + +# +# Steering script for HF enhanced dielectron MC anchored to LHC24ap,aq apass1 +# + +# example anchoring +# taken from https://its.cern.ch/jira/browse/O2-5670 +export ALIEN_JDL_LPMANCHORPASSNAME=apass1 +export ALIEN_JDL_MCANCHOR=apass1 +export ALIEN_JDL_CPULIMIT=8 +export ALIEN_JDL_LPMRUNNUMBER=559348 +export ALIEN_JDL_LPMPRODUCTIONTYPE=MC +export ALIEN_JDL_LPMINTERACTIONTYPE=pp +export ALIEN_JDL_LPMPRODUCTIONTAG=LHC24a6 +export ALIEN_JDL_LPMANCHORRUN=559348 +export ALIEN_JDL_LPMANCHORPRODUCTION=LHC24ap +export ALIEN_JDL_LPMANCHORYEAR=2024 +export ALIEN_JDL_OUTPUT=*.dat@disk=1,*.txt@disk=1,*.root@disk=2 + +export NTIMEFRAMES=1 +export NSIGEVENTS=20 +export SPLITID=100 +export PRODSPLIT=153 +export CYCLE=0 + +# on the GRID, this is set and used as seed; when set, it takes precedence over SEED +#export ALIEN_PROC_ID=2963436952 +export SEED=0 + +# for pp and 50 events per TF, we launch only 4 workers. +export NWORKERS=2 + +# define the generator via ini file +# use 20/40/40 sampling for different generators +# generate random number +RNDSIG=$(($RANDOM % 100)) + + +if [[ $RNDSIG -ge 0 && $RNDSIG -lt 20 ]]; +then + CONFIGNAME="GeneratorHFGapTriggered_Charm_Gap5_pp5360GeV.ini" +elif [[ $RNDSIG -ge 20 && $RNDSIG -lt 100 ]]; +then + CONFIGNAME="GeneratorHFGapTriggered_BeautyNoForcedDecay_Gap5_pp5360GeV.ini" +fi + +export ALIEN_JDL_ANCHOR_SIM_OPTIONS="-gen external -ini $O2DPG_ROOT/MC/config/PWGEM/ini/$CONFIGNAME" + +# run the central anchor steering script; this includes +# * derive timestamp +# * derive interaction rate +# * extract and prepare configurations (which detectors are contained in the run etc.) +# * run the simulation (and QC) +# To disable QC, uncomment the following line +#export DISABLE_QC=1 +${O2DPG_ROOT}/MC/run/ANCHOR/anchorMC.sh diff --git a/MC/run/PWGEM/runAnchoredHFGapToDielectrons_pp_Gap3.sh b/MC/run/PWGEM/runAnchoredHFGapToDielectrons_pp_Gap3.sh new file mode 100644 index 000000000..cf52198ea --- /dev/null +++ b/MC/run/PWGEM/runAnchoredHFGapToDielectrons_pp_Gap3.sh @@ -0,0 +1,60 @@ +#!/bin/bash + +# +# Steering script for HF enhanced dielectron MC anchored to LHC22o apass6 +# + +# example anchoring +# taken from https://its.cern.ch/jira/browse/O2-4586 +export ALIEN_JDL_LPMANCHORPASSNAME=apass6 +export ALIEN_JDL_MCANCHOR=apass6 +export ALIEN_JDL_CPULIMIT=8 +export ALIEN_JDL_LPMRUNNUMBER=526641 +export ALIEN_JDL_LPMPRODUCTIONTYPE=MC +export ALIEN_JDL_LPMINTERACTIONTYPE=pp +export ALIEN_JDL_LPMPRODUCTIONTAG=LHC24a2 +export ALIEN_JDL_LPMANCHORRUN=526641 +export ALIEN_JDL_LPMANCHORPRODUCTION=LHC22o +export ALIEN_JDL_LPMANCHORYEAR=2022 +export ALIEN_JDL_OUTPUT=*.dat@disk=1,*.txt@disk=1,*.root@disk=2 + +export NTIMEFRAMES=1 +export NSIGEVENTS=20 +export SPLITID=100 +export PRODSPLIT=153 +export CYCLE=0 + +# on the GRID, this is set and used as seed; when set, it takes precedence over SEED +#export ALIEN_PROC_ID=2963436952 +export SEED=0 + +# for pp and 50 events per TF, we launch only 4 workers. +export NWORKERS=2 + +# define the generator via ini file +# use 20/40/40 sampling for different generators +# generate random number +RNDSIG=$(($RANDOM % 100)) + + +if [[ $RNDSIG -ge 0 && $RNDSIG -lt 20 ]]; +then + CONFIGNAME="GeneratorHFGapTriggered_Charm_Gap3.ini" +elif [[ $RNDSIG -ge 20 && $RNDSIG -lt 40 ]]; +then + CONFIGNAME="GeneratorHFGapTriggered_BeautyForcedDecay_Gap3.ini" +elif [[ $RNDSIG -ge 40 && $RNDSIG -lt 100 ]]; +then + CONFIGNAME="GeneratorHFGapTriggered_BeautyNoForcedDecay_Gap3.ini" +fi + +export ALIEN_JDL_ANCHOR_SIM_OPTIONS="-gen external -ini $O2DPG_ROOT/MC/config/PWGEM/ini/$CONFIGNAME" + +# run the central anchor steering script; this includes +# * derive timestamp +# * derive interaction rate +# * extract and prepare configurations (which detectors are contained in the run etc.) +# * run the simulation (and QC) +# To disable QC, uncomment the following line +#export DISABLE_QC=1 +${O2DPG_ROOT}/MC/run/ANCHOR/anchorMC.sh diff --git a/MC/run/PWGEM/runAnchoredHFGapToDielectrons_pp_Gap4.sh b/MC/run/PWGEM/runAnchoredHFGapToDielectrons_pp_Gap4.sh new file mode 100644 index 000000000..a6d99eb2f --- /dev/null +++ b/MC/run/PWGEM/runAnchoredHFGapToDielectrons_pp_Gap4.sh @@ -0,0 +1,60 @@ +#!/bin/bash + +# +# Steering script for HF enhanced dielectron MC anchored to LHC22o apass6 +# + +# example anchoring +# taken from https://its.cern.ch/jira/browse/O2-4586 +export ALIEN_JDL_LPMANCHORPASSNAME=apass6 +export ALIEN_JDL_MCANCHOR=apass6 +export ALIEN_JDL_CPULIMIT=8 +export ALIEN_JDL_LPMRUNNUMBER=526641 +export ALIEN_JDL_LPMPRODUCTIONTYPE=MC +export ALIEN_JDL_LPMINTERACTIONTYPE=pp +export ALIEN_JDL_LPMPRODUCTIONTAG=LHC24a2 +export ALIEN_JDL_LPMANCHORRUN=526641 +export ALIEN_JDL_LPMANCHORPRODUCTION=LHC22o +export ALIEN_JDL_LPMANCHORYEAR=2022 +export ALIEN_JDL_OUTPUT=*.dat@disk=1,*.txt@disk=1,*.root@disk=2 + +export NTIMEFRAMES=1 +export NSIGEVENTS=20 +export SPLITID=100 +export PRODSPLIT=153 +export CYCLE=0 + +# on the GRID, this is set and used as seed; when set, it takes precedence over SEED +#export ALIEN_PROC_ID=2963436952 +export SEED=0 + +# for pp and 50 events per TF, we launch only 4 workers. +export NWORKERS=2 + +# define the generator via ini file +# use 20/40/40 sampling for different generators +# generate random number +RNDSIG=$(($RANDOM % 100)) + + +if [[ $RNDSIG -ge 0 && $RNDSIG -lt 20 ]]; +then + CONFIGNAME="GeneratorHFGapTriggered_Charm_Gap4.ini" +elif [[ $RNDSIG -ge 20 && $RNDSIG -lt 40 ]]; +then + CONFIGNAME="GeneratorHFGapTriggered_BeautyForcedDecay_Gap4.ini" +elif [[ $RNDSIG -ge 40 && $RNDSIG -lt 100 ]]; +then + CONFIGNAME="GeneratorHFGapTriggered_BeautyNoForcedDecay_Gap4.ini" +fi + +export ALIEN_JDL_ANCHOR_SIM_OPTIONS="-gen external -ini $O2DPG_ROOT/MC/config/PWGEM/ini/$CONFIGNAME" + +# run the central anchor steering script; this includes +# * derive timestamp +# * derive interaction rate +# * extract and prepare configurations (which detectors are contained in the run etc.) +# * run the simulation (and QC) +# To disable QC, uncomment the following line +#export DISABLE_QC=1 +${O2DPG_ROOT}/MC/run/ANCHOR/anchorMC.sh diff --git a/MC/run/PWGEM/runAnchoredHFGapToDielectrons_pp_Gap5.sh b/MC/run/PWGEM/runAnchoredHFGapToDielectrons_pp_Gap5.sh new file mode 100644 index 000000000..a3726aab3 --- /dev/null +++ b/MC/run/PWGEM/runAnchoredHFGapToDielectrons_pp_Gap5.sh @@ -0,0 +1,57 @@ +#!/bin/bash + +# +# Steering script for HF enhanced dielectron MC anchored to LHC22o apass6 +# + +# example anchoring +# taken from https://its.cern.ch/jira/browse/O2-4586 +export ALIEN_JDL_LPMANCHORPASSNAME=apass6 +export ALIEN_JDL_MCANCHOR=apass6 +export ALIEN_JDL_CPULIMIT=8 +export ALIEN_JDL_LPMRUNNUMBER=526641 +export ALIEN_JDL_LPMPRODUCTIONTYPE=MC +export ALIEN_JDL_LPMINTERACTIONTYPE=pp +export ALIEN_JDL_LPMPRODUCTIONTAG=LHC24a2 +export ALIEN_JDL_LPMANCHORRUN=526641 +export ALIEN_JDL_LPMANCHORPRODUCTION=LHC22o +export ALIEN_JDL_LPMANCHORYEAR=2022 +export ALIEN_JDL_OUTPUT=*.dat@disk=1,*.txt@disk=1,*.root@disk=2 + +export NTIMEFRAMES=1 +export NSIGEVENTS=20 +export SPLITID=100 +export PRODSPLIT=153 +export CYCLE=0 + +# on the GRID, this is set and used as seed; when set, it takes precedence over SEED +#export ALIEN_PROC_ID=2963436952 +export SEED=0 + +# for pp and 50 events per TF, we launch only 4 workers. +export NWORKERS=2 + +# define the generator via ini file +# use 20/40/40 sampling for different generators +# generate random number +RNDSIG=$(($RANDOM % 100)) + + +if [[ $RNDSIG -ge 0 && $RNDSIG -lt 20 ]]; +then + CONFIGNAME="GeneratorHFGapTriggered_Charm_Gap5.ini" +elif [[ $RNDSIG -ge 20 && $RNDSIG -lt 100 ]]; +then + CONFIGNAME="GeneratorHFGapTriggered_BeautyNoForcedDecay_Gap5.ini" +fi + +export ALIEN_JDL_ANCHOR_SIM_OPTIONS="-gen external -ini $O2DPG_ROOT/MC/config/PWGEM/ini/$CONFIGNAME" + +# run the central anchor steering script; this includes +# * derive timestamp +# * derive interaction rate +# * extract and prepare configurations (which detectors are contained in the run etc.) +# * run the simulation (and QC) +# To disable QC, uncomment the following line +#export DISABLE_QC=1 +${O2DPG_ROOT}/MC/run/ANCHOR/anchorMC.sh diff --git a/MC/run/PWGEM/runAnchoredHFGapToDielectrons_pp_Gap6.sh b/MC/run/PWGEM/runAnchoredHFGapToDielectrons_pp_Gap6.sh new file mode 100644 index 000000000..47fc1a37b --- /dev/null +++ b/MC/run/PWGEM/runAnchoredHFGapToDielectrons_pp_Gap6.sh @@ -0,0 +1,60 @@ +#!/bin/bash + +# +# Steering script for HF enhanced dielectron MC anchored to LHC22o apass6 +# + +# example anchoring +# taken from https://its.cern.ch/jira/browse/O2-4586 +export ALIEN_JDL_LPMANCHORPASSNAME=apass6 +export ALIEN_JDL_MCANCHOR=apass6 +export ALIEN_JDL_CPULIMIT=8 +export ALIEN_JDL_LPMRUNNUMBER=526641 +export ALIEN_JDL_LPMPRODUCTIONTYPE=MC +export ALIEN_JDL_LPMINTERACTIONTYPE=pp +export ALIEN_JDL_LPMPRODUCTIONTAG=LHC24a2 +export ALIEN_JDL_LPMANCHORRUN=526641 +export ALIEN_JDL_LPMANCHORPRODUCTION=LHC22o +export ALIEN_JDL_LPMANCHORYEAR=2022 +export ALIEN_JDL_OUTPUT=*.dat@disk=1,*.txt@disk=1,*.root@disk=2 + +export NTIMEFRAMES=1 +export NSIGEVENTS=20 +export SPLITID=100 +export PRODSPLIT=153 +export CYCLE=0 + +# on the GRID, this is set and used as seed; when set, it takes precedence over SEED +#export ALIEN_PROC_ID=2963436952 +export SEED=0 + +# for pp and 50 events per TF, we launch only 4 workers. +export NWORKERS=2 + +# define the generator via ini file +# use 20/40/40 sampling for different generators +# generate random number +RNDSIG=$(($RANDOM % 100)) + + +if [[ $RNDSIG -ge 0 && $RNDSIG -lt 20 ]]; +then + CONFIGNAME="GeneratorHFGapTriggered_Charm_Gap6.ini" +elif [[ $RNDSIG -ge 20 && $RNDSIG -lt 40 ]]; +then + CONFIGNAME="GeneratorHFGapTriggered_BeautyForcedDecay_Gap6.ini" +elif [[ $RNDSIG -ge 40 && $RNDSIG -lt 100 ]]; +then + CONFIGNAME="GeneratorHFGapTriggered_BeautyNoForcedDecay_Gap6.ini" +fi + +export ALIEN_JDL_ANCHOR_SIM_OPTIONS="-gen external -ini $O2DPG_ROOT/MC/config/PWGEM/ini/$CONFIGNAME" + +# run the central anchor steering script; this includes +# * derive timestamp +# * derive interaction rate +# * extract and prepare configurations (which detectors are contained in the run etc.) +# * run the simulation (and QC) +# To disable QC, uncomment the following line +#export DISABLE_QC=1 +${O2DPG_ROOT}/MC/run/ANCHOR/anchorMC.sh diff --git a/MC/run/PWGEM/runAnchoredHFGapToDielectrons_pp_Gap7.sh b/MC/run/PWGEM/runAnchoredHFGapToDielectrons_pp_Gap7.sh new file mode 100644 index 000000000..27907ac30 --- /dev/null +++ b/MC/run/PWGEM/runAnchoredHFGapToDielectrons_pp_Gap7.sh @@ -0,0 +1,60 @@ +#!/bin/bash + +# +# Steering script for HF enhanced dielectron MC anchored to LHC22o apass6 +# + +# example anchoring +# taken from https://its.cern.ch/jira/browse/O2-4586 +export ALIEN_JDL_LPMANCHORPASSNAME=apass6 +export ALIEN_JDL_MCANCHOR=apass6 +export ALIEN_JDL_CPULIMIT=8 +export ALIEN_JDL_LPMRUNNUMBER=526641 +export ALIEN_JDL_LPMPRODUCTIONTYPE=MC +export ALIEN_JDL_LPMINTERACTIONTYPE=pp +export ALIEN_JDL_LPMPRODUCTIONTAG=LHC24a2 +export ALIEN_JDL_LPMANCHORRUN=526641 +export ALIEN_JDL_LPMANCHORPRODUCTION=LHC22o +export ALIEN_JDL_LPMANCHORYEAR=2022 +export ALIEN_JDL_OUTPUT=*.dat@disk=1,*.txt@disk=1,*.root@disk=2 + +export NTIMEFRAMES=1 +export NSIGEVENTS=20 +export SPLITID=100 +export PRODSPLIT=153 +export CYCLE=0 + +# on the GRID, this is set and used as seed; when set, it takes precedence over SEED +#export ALIEN_PROC_ID=2963436952 +export SEED=0 + +# for pp and 50 events per TF, we launch only 4 workers. +export NWORKERS=2 + +# define the generator via ini file +# use 20/40/40 sampling for different generators +# generate random number +RNDSIG=$(($RANDOM % 100)) + + +if [[ $RNDSIG -ge 0 && $RNDSIG -lt 20 ]]; +then + CONFIGNAME="GeneratorHFGapTriggered_Charm_Gap7.ini" +elif [[ $RNDSIG -ge 20 && $RNDSIG -lt 40 ]]; +then + CONFIGNAME="GeneratorHFGapTriggered_BeautyForcedDecay_Gap7.ini" +elif [[ $RNDSIG -ge 40 && $RNDSIG -lt 100 ]]; +then + CONFIGNAME="GeneratorHFGapTriggered_BeautyNoForcedDecay_Gap7.ini" +fi + +export ALIEN_JDL_ANCHOR_SIM_OPTIONS="-gen external -ini $O2DPG_ROOT/MC/config/PWGEM/ini/$CONFIGNAME" + +# run the central anchor steering script; this includes +# * derive timestamp +# * derive interaction rate +# * extract and prepare configurations (which detectors are contained in the run etc.) +# * run the simulation (and QC) +# To disable QC, uncomment the following line +#export DISABLE_QC=1 +${O2DPG_ROOT}/MC/run/ANCHOR/anchorMC.sh diff --git a/MC/run/PWGEM/runAnchoredHFmumu_pp_13600_Gap1.sh b/MC/run/PWGEM/runAnchoredHFmumu_pp_13600_Gap1.sh new file mode 100644 index 000000000..6e60d1815 --- /dev/null +++ b/MC/run/PWGEM/runAnchoredHFmumu_pp_13600_Gap1.sh @@ -0,0 +1,57 @@ +#!/bin/bash + +# +# Steering script for HF enhanced dielectron MC anchored to LHC26 pp apass1 +# + +# example anchoring +# taken from https://its.cern.ch/jira/browse/O2-5670 +export ALIEN_JDL_LPMANCHORPASSNAME=apass1 +export ALIEN_JDL_MCANCHOR=apass1 +export ALIEN_JDL_CPULIMIT=8 +export ALIEN_JDL_LPMRUNNUMBER=559348 +export ALIEN_JDL_LPMPRODUCTIONTYPE=MC +export ALIEN_JDL_LPMINTERACTIONTYPE=pp +export ALIEN_JDL_LPMPRODUCTIONTAG=LHC26e9 +export ALIEN_JDL_LPMANCHORRUN=559348 +export ALIEN_JDL_LPMANCHORPRODUCTION=LHC26ac +export ALIEN_JDL_LPMANCHORYEAR=2024 +export ALIEN_JDL_OUTPUT=*.dat@disk=1,*.txt@disk=1,*.root@disk=2 + +export NTIMEFRAMES=1 +export NSIGEVENTS=20 +export SPLITID=100 +export PRODSPLIT=153 +export CYCLE=0 + +# on the GRID, this is set and used as seed; when set, it takes precedence over SEED +#export ALIEN_PROC_ID=2963436952 +export SEED=0 + +# for pp and 50 events per TF, we launch only 4 workers. +export NWORKERS=2 + +# define the generator via ini file +# use 20/40/40 sampling for different generators +# generate random number +RNDSIG=$(($RANDOM % 100)) + + +if [[ $RNDSIG -ge 0 && $RNDSIG -lt 20 ]]; +then + CONFIGNAME="GeneratorHFGapTriggered_Charm_Muon_pp13600GeV_Gap1.ini" +elif [[ $RNDSIG -ge 20 && $RNDSIG -lt 100 ]]; +then + CONFIGNAME="GeneratorHFGapTriggered_BeautyNoForcedDecay_Muon_pp13600GeV_Gap1.ini" +fi + +export ALIEN_JDL_ANCHOR_SIM_OPTIONS="-gen external -ini $O2DPG_ROOT/MC/config/PWGEM/ini/$CONFIGNAME" + +# run the central anchor steering script; this includes +# * derive timestamp +# * derive interaction rate +# * extract and prepare configurations (which detectors are contained in the run etc.) +# * run the simulation (and QC) +# To disable QC, uncomment the following line +#export DISABLE_QC=1 +${O2DPG_ROOT}/MC/run/ANCHOR/anchorMC.sh diff --git a/MC/run/PWGEM/runAnchoredPythia8GapTriggeredLFgamma_pp_np1_gap2.sh b/MC/run/PWGEM/runAnchoredPythia8GapTriggeredLFgamma_pp_np1_gap2.sh new file mode 100644 index 000000000..dc599d8f2 --- /dev/null +++ b/MC/run/PWGEM/runAnchoredPythia8GapTriggeredLFgamma_pp_np1_gap2.sh @@ -0,0 +1,50 @@ +#!/bin/bash + +# +# Steering script for LF->ee enhanced dielectron MC anchored to LHC22o apass6 +# + +# example anchoring +# taken from https://its.cern.ch/jira/browse/O2-4586 +export ALIEN_JDL_LPMANCHORPASSNAME=apass6 +export ALIEN_JDL_MCANCHOR=apass6 +export ALIEN_JDL_CPULIMIT=8 +export ALIEN_JDL_LPMRUNNUMBER=526641 +export ALIEN_JDL_LPMPRODUCTIONTYPE=MC +export ALIEN_JDL_LPMINTERACTIONTYPE=pp +export ALIEN_JDL_LPMPRODUCTIONTAG=LHC24b1b +export ALIEN_JDL_LPMANCHORRUN=526641 +export ALIEN_JDL_LPMANCHORPRODUCTION=LHC22o +export ALIEN_JDL_LPMANCHORYEAR=2022 +export ALIEN_JDL_OUTPUT=*.dat@disk=1,*.txt@disk=1,*.root@disk=2 + +export NTIMEFRAMES=1 +export NSIGEVENTS=20 +export SPLITID=100 +export PRODSPLIT=153 +export CYCLE=0 + +# on the GRID, this is set and used as seed; when set, it takes precedence over SEED +#export ALIEN_PROC_ID=2963436952 +export SEED=0 + +# for pp and 50 events per TF, we launch only 4 workers. +export NWORKERS=4 + +# define the generator via ini file +# use 20/40/40 sampling for different generators +# generate random number +RNDSIG=$(($RANDOM % 100)) + +CONFIGNAME="Generator_GapTriggered_LFgamma_np1_gap2.ini" + +export ALIEN_JDL_ANCHOR_SIM_OPTIONS="-gen external -ini $O2DPG_ROOT/MC/config/PWGEM/ini/$CONFIGNAME" + +# run the central anchor steering script; this includes +# * derive timestamp +# * derive interaction rate +# * extract and prepare configurations (which detectors are contained in the run etc.) +# * run the simulation (and QC) +# To disable QC, uncomment the following line +#export DISABLE_QC=1 +${O2DPG_ROOT}/MC/run/ANCHOR/anchorMC.sh diff --git a/MC/run/PWGEM/runAnchoredPythia8GapTriggeredLFgamma_pp_np1_gap4.sh b/MC/run/PWGEM/runAnchoredPythia8GapTriggeredLFgamma_pp_np1_gap4.sh new file mode 100644 index 000000000..f43d5d78d --- /dev/null +++ b/MC/run/PWGEM/runAnchoredPythia8GapTriggeredLFgamma_pp_np1_gap4.sh @@ -0,0 +1,50 @@ +#!/bin/bash + +# +# Steering script for LF->ee enhanced dielectron MC anchored to LHC22o apass6 +# + +# example anchoring +# taken from https://its.cern.ch/jira/browse/O2-4586 +export ALIEN_JDL_LPMANCHORPASSNAME=apass6 +export ALIEN_JDL_MCANCHOR=apass6 +export ALIEN_JDL_CPULIMIT=8 +export ALIEN_JDL_LPMRUNNUMBER=526641 +export ALIEN_JDL_LPMPRODUCTIONTYPE=MC +export ALIEN_JDL_LPMINTERACTIONTYPE=pp +export ALIEN_JDL_LPMPRODUCTIONTAG=LHC24b1b +export ALIEN_JDL_LPMANCHORRUN=526641 +export ALIEN_JDL_LPMANCHORPRODUCTION=LHC22o +export ALIEN_JDL_LPMANCHORYEAR=2022 +export ALIEN_JDL_OUTPUT=*.dat@disk=1,*.txt@disk=1,*.root@disk=2 + +export NTIMEFRAMES=1 +export NSIGEVENTS=20 +export SPLITID=100 +export PRODSPLIT=153 +export CYCLE=0 + +# on the GRID, this is set and used as seed; when set, it takes precedence over SEED +#export ALIEN_PROC_ID=2963436952 +export SEED=0 + +# for pp and 50 events per TF, we launch only 4 workers. +export NWORKERS=4 + +# define the generator via ini file +# use 20/40/40 sampling for different generators +# generate random number +RNDSIG=$(($RANDOM % 100)) + +CONFIGNAME="Generator_GapTriggered_LFgamma_np1_gap4.ini" + +export ALIEN_JDL_ANCHOR_SIM_OPTIONS="-gen external -ini $O2DPG_ROOT/MC/config/PWGEM/ini/$CONFIGNAME" + +# run the central anchor steering script; this includes +# * derive timestamp +# * derive interaction rate +# * extract and prepare configurations (which detectors are contained in the run etc.) +# * run the simulation (and QC) +# To disable QC, uncomment the following line +#export DISABLE_QC=1 +${O2DPG_ROOT}/MC/run/ANCHOR/anchorMC.sh diff --git a/MC/run/PWGEM/runAnchoredPythia8GapTriggeredLFgamma_pp_np1_gap6.sh b/MC/run/PWGEM/runAnchoredPythia8GapTriggeredLFgamma_pp_np1_gap6.sh new file mode 100644 index 000000000..98eccaf22 --- /dev/null +++ b/MC/run/PWGEM/runAnchoredPythia8GapTriggeredLFgamma_pp_np1_gap6.sh @@ -0,0 +1,50 @@ +#!/bin/bash + +# +# Steering script for LF->ee enhanced dielectron MC anchored to LHC22o apass6 +# + +# example anchoring +# taken from https://its.cern.ch/jira/browse/O2-4586 +export ALIEN_JDL_LPMANCHORPASSNAME=apass6 +export ALIEN_JDL_MCANCHOR=apass6 +export ALIEN_JDL_CPULIMIT=8 +export ALIEN_JDL_LPMRUNNUMBER=526641 +export ALIEN_JDL_LPMPRODUCTIONTYPE=MC +export ALIEN_JDL_LPMINTERACTIONTYPE=pp +export ALIEN_JDL_LPMPRODUCTIONTAG=LHC24b1b +export ALIEN_JDL_LPMANCHORRUN=526641 +export ALIEN_JDL_LPMANCHORPRODUCTION=LHC22o +export ALIEN_JDL_LPMANCHORYEAR=2022 +export ALIEN_JDL_OUTPUT=*.dat@disk=1,*.txt@disk=1,*.root@disk=2 + +export NTIMEFRAMES=1 +export NSIGEVENTS=20 +export SPLITID=100 +export PRODSPLIT=153 +export CYCLE=0 + +# on the GRID, this is set and used as seed; when set, it takes precedence over SEED +#export ALIEN_PROC_ID=2963436952 +export SEED=0 + +# for pp and 50 events per TF, we launch only 4 workers. +export NWORKERS=4 + +# define the generator via ini file +# use 20/40/40 sampling for different generators +# generate random number +RNDSIG=$(($RANDOM % 100)) + +CONFIGNAME="Generator_GapTriggered_LFgamma_np1_gap6.ini" + +export ALIEN_JDL_ANCHOR_SIM_OPTIONS="-gen external -ini $O2DPG_ROOT/MC/config/PWGEM/ini/$CONFIGNAME" + +# run the central anchor steering script; this includes +# * derive timestamp +# * derive interaction rate +# * extract and prepare configurations (which detectors are contained in the run etc.) +# * run the simulation (and QC) +# To disable QC, uncomment the following line +#export DISABLE_QC=1 +${O2DPG_ROOT}/MC/run/ANCHOR/anchorMC.sh diff --git a/MC/run/PWGEM/runAnchoredPythia8GapTriggeredLFgamma_pp_np3_gap5.sh b/MC/run/PWGEM/runAnchoredPythia8GapTriggeredLFgamma_pp_np3_gap5.sh new file mode 100644 index 000000000..0b8e3c83f --- /dev/null +++ b/MC/run/PWGEM/runAnchoredPythia8GapTriggeredLFgamma_pp_np3_gap5.sh @@ -0,0 +1,50 @@ +#!/bin/bash + +# +# Steering script for LF->ee enhanced dielectron MC anchored to LHC22o apass6 +# + +# example anchoring +# taken from https://its.cern.ch/jira/browse/O2-4586 +export ALIEN_JDL_LPMANCHORPASSNAME=apass6 +export ALIEN_JDL_MCANCHOR=apass6 +export ALIEN_JDL_CPULIMIT=8 +export ALIEN_JDL_LPMRUNNUMBER=526641 +export ALIEN_JDL_LPMPRODUCTIONTYPE=MC +export ALIEN_JDL_LPMINTERACTIONTYPE=pp +export ALIEN_JDL_LPMPRODUCTIONTAG=LHC24b1b +export ALIEN_JDL_LPMANCHORRUN=526641 +export ALIEN_JDL_LPMANCHORPRODUCTION=LHC22o +export ALIEN_JDL_LPMANCHORYEAR=2022 +export ALIEN_JDL_OUTPUT=*.dat@disk=1,*.txt@disk=1,*.root@disk=2 + +export NTIMEFRAMES=1 +export NSIGEVENTS=20 +export SPLITID=100 +export PRODSPLIT=153 +export CYCLE=0 + +# on the GRID, this is set and used as seed; when set, it takes precedence over SEED +#export ALIEN_PROC_ID=2963436952 +export SEED=0 + +# for pp and 50 events per TF, we launch only 4 workers. +export NWORKERS=4 + +# define the generator via ini file +# use 20/40/40 sampling for different generators +# generate random number +RNDSIG=$(($RANDOM % 100)) + +CONFIGNAME="Generator_GapTriggered_LFgamma_np3_gap5.ini" + +export ALIEN_JDL_ANCHOR_SIM_OPTIONS="-gen external -ini $O2DPG_ROOT/MC/config/PWGEM/ini/$CONFIGNAME" + +# run the central anchor steering script; this includes +# * derive timestamp +# * derive interaction rate +# * extract and prepare configurations (which detectors are contained in the run etc.) +# * run the simulation (and QC) +# To disable QC, uncomment the following line +#export DISABLE_QC=1 +${O2DPG_ROOT}/MC/run/ANCHOR/anchorMC.sh diff --git a/MC/run/PWGEM/runAnchoredPythia8GapTriggeredLFgamma_pp_np5_gap5.sh b/MC/run/PWGEM/runAnchoredPythia8GapTriggeredLFgamma_pp_np5_gap5.sh new file mode 100644 index 000000000..ab63b0738 --- /dev/null +++ b/MC/run/PWGEM/runAnchoredPythia8GapTriggeredLFgamma_pp_np5_gap5.sh @@ -0,0 +1,50 @@ +#!/bin/bash + +# +# Steering script for LF->ee enhanced dielectron MC anchored to LHC22o apass6 +# + +# example anchoring +# taken from https://its.cern.ch/jira/browse/O2-4586 +export ALIEN_JDL_LPMANCHORPASSNAME=apass6 +export ALIEN_JDL_MCANCHOR=apass6 +export ALIEN_JDL_CPULIMIT=8 +export ALIEN_JDL_LPMRUNNUMBER=526641 +export ALIEN_JDL_LPMPRODUCTIONTYPE=MC +export ALIEN_JDL_LPMINTERACTIONTYPE=pp +export ALIEN_JDL_LPMPRODUCTIONTAG=LHC24b1b +export ALIEN_JDL_LPMANCHORRUN=526641 +export ALIEN_JDL_LPMANCHORPRODUCTION=LHC22o +export ALIEN_JDL_LPMANCHORYEAR=2022 +export ALIEN_JDL_OUTPUT=*.dat@disk=1,*.txt@disk=1,*.root@disk=2 + +export NTIMEFRAMES=1 +export NSIGEVENTS=20 +export SPLITID=100 +export PRODSPLIT=153 +export CYCLE=0 + +# on the GRID, this is set and used as seed; when set, it takes precedence over SEED +#export ALIEN_PROC_ID=2963436952 +export SEED=0 + +# for pp and 50 events per TF, we launch only 4 workers. +export NWORKERS=4 + +# define the generator via ini file +# use 20/40/40 sampling for different generators +# generate random number +RNDSIG=$(($RANDOM % 100)) + +CONFIGNAME="Generator_GapTriggered_LFgamma_np5_gap5.ini" + +export ALIEN_JDL_ANCHOR_SIM_OPTIONS="-gen external -ini $O2DPG_ROOT/MC/config/PWGEM/ini/$CONFIGNAME" + +# run the central anchor steering script; this includes +# * derive timestamp +# * derive interaction rate +# * extract and prepare configurations (which detectors are contained in the run etc.) +# * run the simulation (and QC) +# To disable QC, uncomment the following line +#export DISABLE_QC=1 +${O2DPG_ROOT}/MC/run/ANCHOR/anchorMC.sh diff --git a/MC/run/PWGEM/runHFFullToDielectrons_pp.sh b/MC/run/PWGEM/runHFFullToDielectrons_pp.sh index 6393f9791..e17ba6516 100644 --- a/MC/run/PWGEM/runHFFullToDielectrons_pp.sh +++ b/MC/run/PWGEM/runHFFullToDielectrons_pp.sh @@ -8,8 +8,6 @@ export ALICEO2_CCDB_LOCALCACHE=$PWD/.ccdb -# ----------- LOAD UTILITY FUNCTIONS -------------------------- -. ${O2_ROOT}/share/scripts/jobutils.sh RNDSEED=${RNDSEED:-0} NSIGEVENTS=${NSIGEVENTS:-1} @@ -36,7 +34,7 @@ fi -${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM 13600 -col pp -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -e TGeant4 -mod "--skipModules ZDC" \ +${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM 13600 -col pp -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -e TGeant4 \ ${SEED} \ -trigger "external" -ini $O2DPG_ROOT/MC/config/PWGEM/ini/$CONFIGNAME \ -confKeyBkg "Diamond.width[2]=6" -interactionRate ${INTRATE} diff --git a/MC/run/PWGEM/runHFGapToDielectrons_pp.sh b/MC/run/PWGEM/runHFGapToDielectrons_pp.sh index b83c839e3..9cf1b5556 100644 --- a/MC/run/PWGEM/runHFGapToDielectrons_pp.sh +++ b/MC/run/PWGEM/runHFGapToDielectrons_pp.sh @@ -8,14 +8,13 @@ export ALICEO2_CCDB_LOCALCACHE=$PWD/.ccdb -# ----------- LOAD UTILITY FUNCTIONS -------------------------- -. ${O2_ROOT}/share/scripts/jobutils.sh RNDSEED=${RNDSEED:-0} NSIGEVENTS=${NSIGEVENTS:-1} NWORKERS=${NWORKERS:-8} NTIMEFRAMES=${NTIMEFRAMES:-1} INTRATE=${INTRATE:-500000} +GAPRATIO=${GAPRATIO:-3} [[ ${SPLITID} != "" ]] && SEED="-seed ${SPLITID}" || SEED="" #generate random number @@ -24,19 +23,19 @@ echo $RNDSIG if [[ $RNDSIG -ge 0 && $RNDSIG -lt 20 ]]; then - CONFIGNAME="GeneratorHFGapTriggered_Charm.ini" + CONFIGNAME="GeneratorHFGapTriggered_Charm_Gap$GAPRATIO.ini" elif [[ $RNDSIG -ge 20 && $RNDSIG -lt 40 ]]; then - CONFIGNAME="GeneratorHFGapTriggered_BeautyForcedDecay.ini" + CONFIGNAME="GeneratorHFGapTriggered_BeautyForcedDecay_Gap$GAPRATIO.ini" elif [[ $RNDSIG -ge 40 && $RNDSIG -lt 100 ]]; then - CONFIGNAME="GeneratorHFGapTriggered_BeautyNoForcedDecay.ini" + CONFIGNAME="GeneratorHFGapTriggered_BeautyNoForcedDecay_Gap$GAPRATIO.ini" fi -${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM 13600 -col pp -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -e TGeant4 -mod "--skipModules ZDC" \ +${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM 13600 -col pp -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -e TGeant4 \ ${SEED} \ -ini $O2DPG_ROOT/MC/config/PWGEM/ini/$CONFIGNAME \ -confKeyBkg "Diamond.width[2]=6" -interactionRate ${INTRATE} diff --git a/MC/run/PWGEM/runHFToDielectrons_PbPb.sh b/MC/run/PWGEM/runHFToDielectrons_PbPb.sh new file mode 100644 index 000000000..4193f6751 --- /dev/null +++ b/MC/run/PWGEM/runHFToDielectrons_PbPb.sh @@ -0,0 +1,51 @@ +#!/usr/bin/env bash + +RNDSEED=${RNDSEED:-0} +NWORKERS=${NWORKERS:-8} + + +export ALIEN_JDL_LPMPRODUCTIONTYPE=MC +export ALIEN_JDL_CPULIMIT=20 +export ALIEN_JDL_LPMANCHORPASSNAME=apass4 +export ALIEN_JDL_MCANCHOR=apass4 +export ALIEN_JDL_COLLISIONSYSTEM=PbPb +export ALIEN_JDL_LPMPASSNAME=apass4 +export ALIEN_JDL_LPMRUNNUMBER=544474 +export ALIEN_JDL_LPMANCHORRUN=544474 + +export ALIEN_JDL_LPMINTERACTIONTYPE=PbPb +export ALIEN_JDL_LPMPRODUCTIONTAG="TestProd17032026" +export ALIEN_JDL_LPMANCHORPRODUCTION="LHC26ac" +export ALIEN_JDL_LPMANCHORYEAR=2023 + +export ALIEN_JDL_O2DPGWORKFLOWTARGET="aod" + +export PRODSPLIT=${ALIEN_O2DPG_GRIDSUBMIT_PRODSPLIT:-10} +export SPLITID=${ALIEN_O2DPG_GRIDSUBMIT_SUBJOBID:-10} +export CYCLE=0 +export NTIMEFRAMES=8 +export NSIGEVENTS=20 +export NBKGEVENTS=10 + +# define the generator via ini file +# use 20/80 sampling for different generators +# generate random number +RNDSIG=$(($RANDOM % 100)) + + +if [[ $RNDSIG -ge 0 && $RNDSIG -lt 20 ]]; +then + CONFIGNAME="GeneratorHF_Charm_PbPb_electron.ini" +elif [[ $RNDSIG -ge 20 && $RNDSIG -lt 100 ]]; +then + CONFIGNAME="GeneratorHF_BeautyNoForcedDecay_PbPb_electron.ini" +fi + + +# generator and other sim configuration; parallel world for ITS +SIM_OPTIONS="-eCM 5360 -gen external -j ${NWORKERS} -tf ${NTIMEFRAMES} -e TGeant4 -seed 0 -ini ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGEM/ini/$CONFIGNAME -genBkg pythia8 -procBkg \"heavy_ion\" -colBkg PbPb --embedding -nb $NBKGEVENTS" + +export ALIEN_JDL_ANCHOR_SIM_OPTIONS="${SIM_OPTIONS}" + +${O2DPG_ROOT}/MC/run/ANCHOR/anchorMC.sh + diff --git a/MC/run/PWGEM/runHFToDielectrons_pp.sh b/MC/run/PWGEM/runHFToDielectrons_pp.sh index 7f2ac8a38..ab41fe0ac 100644 --- a/MC/run/PWGEM/runHFToDielectrons_pp.sh +++ b/MC/run/PWGEM/runHFToDielectrons_pp.sh @@ -8,8 +8,6 @@ export ALICEO2_CCDB_LOCALCACHE=$PWD/.ccdb -# ----------- LOAD UTILITY FUNCTIONS -------------------------- -. ${O2_ROOT}/share/scripts/jobutils.sh RNDSEED=${RNDSEED:-0} NSIGEVENTS=${NSIGEVENTS:-1} @@ -35,7 +33,7 @@ fi -${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM 13600 -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -e TGeant4 -mod "--skipModules ZDC" \ +${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM 13600 -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -e TGeant4 \ -trigger "external" -ini $O2DPG_ROOT/MC/config/PWGEM/ini/$CONFIGNAME \ -genBkg pythia8 -procBkg cdiff -colBkg pp --embedding -nb ${NBKGEVENTS} \ -confKeyBkg "Diamond.width[2]=6" -interactionRate 500000 diff --git a/MC/run/PWGEM/runHfToDielectrons_OO_Gap2.sh b/MC/run/PWGEM/runHfToDielectrons_OO_Gap2.sh new file mode 100644 index 000000000..8026630ff --- /dev/null +++ b/MC/run/PWGEM/runHfToDielectrons_OO_Gap2.sh @@ -0,0 +1,59 @@ +#!/bin/bash + +# +# Steering script for HF enhanced dielectron MC anchored to LHC25ae +# + +# example anchoring + +export ALIEN_JDL_LPMANCHORPASSNAME=apass2 +export ALIEN_JDL_MCANCHOR=apass2 +export ALIEN_JDL_CPULIMIT=8 +export ALIEN_JDL_LPMRUNNUMBER=564356 +export ALIEN_JDL_LPMPRODUCTIONTYPE=MC +export ALIEN_JDL_LPMINTERACTIONTYPE=pp +export ALIEN_JDL_LPMPRODUCTIONTAG=LHC26a2 +export ALIEN_JDL_LPMANCHORRUN=564356 +export ALIEN_JDL_LPMANCHORPRODUCTION=LHC25ae +export ALIEN_JDL_LPMANCHORYEAR=2025 +export ALIEN_JDL_OUTPUT=*.dat@disk=1,*.txt@disk=1,*.root@disk=2 + +export NTIMEFRAMES=1 +export NSIGEVENTS=20 +export SPLITID=100 +export PRODSPLIT=153 +export CYCLE=0 + +# on the GRID, this is set and used as seed; when set, it takes precedence over SEED +#export ALIEN_PROC_ID=2963436952 +export SEED=0 + +# for pp and 50 events per TF, we launch only 4 workers. +export NWORKERS=2 + +# define the generator via ini file +# use 30/70 sampling for different generators +# No forced beauty decays as we have observed biases + +# generate random number +RNDSIG=$(($RANDOM % 100)) + + +if [[ $RNDSIG -ge 0 && $RNDSIG -lt 30 ]]; +then + CONFIGNAME="GeneratorHFGapTriggered_Charm_Gap2_OO_electron.ini" +elif [[ $RNDSIG -ge 30 && $RNDSIG -lt 100 ]]; +then + CONFIGNAME="GeneratorHFGapTriggered_BeautyNoForcedDecay_Gap2_OO_electron.ini" +fi + +export ALIEN_JDL_ANCHOR_SIM_OPTIONS="-gen external -ini $O2DPG_ROOT/MC/config/PWGEM/ini/$CONFIGNAME" + +# run the central anchor steering script; this includes +# * derive timestamp +# * derive interaction rate +# * extract and prepare configurations (which detectors are contained in the run etc.) +# * run the simulation (and QC) +# To disable QC, uncomment the following line +#export DISABLE_QC=1 +${O2DPG_ROOT}/MC/run/ANCHOR/anchorMC.sh diff --git a/MC/run/PWGEM/runPythia8GapTriggeredLFgamma_pp.sh b/MC/run/PWGEM/runPythia8GapTriggeredLFgamma_pp.sh new file mode 100644 index 000000000..76a456f2f --- /dev/null +++ b/MC/run/PWGEM/runPythia8GapTriggeredLFgamma_pp.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash + +# make sure O2DPG + O2 is loaded +[ ! "${O2DPG_ROOT}" ] && echo "Error: This needs O2DPG loaded" && exit 1 +[ ! "${O2_ROOT}" ] && echo "Error: This needs O2 loaded" && exit 1 + +# ----------- SETUP LOCAL CCDB CACHE -------------------------- +export ALICEO2_CCDB_LOCALCACHE=$PWD/.ccdb + + + +NSIGEVENTS=${NSIGEVENTS:-10} +NWORKERS=${NWORKERS:-8} +NTIMEFRAMES=${NTIMEFRAMES:-1} +INTRATE=${INTRATE:-500000} +GAP=${GAP:-5} +NP=${NP:-1} + +CONFIGNAME="Generator_GapTriggered_LFgamma_np${NP}_gap${GAP}.ini" + +${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM 13600 -col pp -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -e TGeant4 \ + -ini $O2DPG_ROOT/MC/config/PWGEM/ini/$CONFIGNAME \ + -confKeyBkg "Diamond.width[2]=6" -interactionRate ${INTRATE} + +# run workflow +${O2DPG_ROOT}/MC/bin/o2_dpg_workflow_runner.py -f workflow.json -tt aod --cpu-limit 32 diff --git a/MC/run/PWGEM/runPythia8GapTriggeredLFmumu_pp.sh b/MC/run/PWGEM/runPythia8GapTriggeredLFmumu_pp.sh new file mode 100644 index 000000000..85642a50c --- /dev/null +++ b/MC/run/PWGEM/runPythia8GapTriggeredLFmumu_pp.sh @@ -0,0 +1,49 @@ +#!/usr/bin/env bash + +# make sure O2DPG + O2 is loaded +#[ ! "${O2DPG_ROOT}" ] && echo "Error: This needs O2DPG loaded" && exit 1 +#[ ! "${O2_ROOT}" ] && echo "Error: This needs O2 loaded" && exit 1 + +# ----------- SETUP LOCAL CCDB CACHE -------------------------- +#export ALICEO2_CCDB_LOCALCACHE=$PWD/.ccdb + + + +#NSIGEVENTS=${NSIGEVENTS:-10} +#NWORKERS=${NWORKERS:-8} +#NTIMEFRAMES=${NTIMEFRAMES:-1} +#INTRATE=${INTRATE:-500000} + +#CONFIGNAME="Generator_GapTriggered_LFmumu_np1_gap5.ini" + +#${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM 13600 -col pp -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -e TGeant4 \ + #-ini $O2DPG_ROOT/MC/config/PWGEM/ini/$CONFIGNAME \ + #-confKeyBkg "Diamond.width[2]=6" -interactionRate ${INTRATE} + +# run workflow +#${O2DPG_ROOT}/MC/bin/o2_dpg_workflow_runner.py -f workflow.json -tt aod --cpu-limit 32 + +#!/usr/bin/env bash + +# make sure O2DPG + O2 is loaded +[ ! "${O2DPG_ROOT}" ] && echo "Error: This needs O2DPG loaded" && exit 1 +[ ! "${O2_ROOT}" ] && echo "Error: This needs O2 loaded" && exit 1 + +# ----------- SETUP LOCAL CCDB CACHE -------------------------- +export ALICEO2_CCDB_LOCALCACHE=$PWD/.ccdb + + + +NSIGEVENTS=${NSIGEVENTS:-10} +NWORKERS=${NWORKERS:-8} +NTIMEFRAMES=${NTIMEFRAMES:-1} +INTRATE=${INTRATE:-500000} + +CONFIGNAME="Generator_GapTriggered_LFmumu.ini" + +${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM 13600 -col pp -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -e TGeant4 \ + -ini $O2DPG_ROOT/MC/config/PWGEM/ini/$CONFIGNAME \ + -confKeyBkg "Diamond.width[2]=6" -interactionRate ${INTRATE} + +# run workflow +${O2DPG_ROOT}/MC/bin/o2_dpg_workflow_runner.py -f workflow.json -tt aod --cpu-limit 32 diff --git a/MC/run/PWGGAJE/run_decaygammajets.sh b/MC/run/PWGGAJE/run_decaygammajets.sh index 81283e219..039b6ac5b 100755 --- a/MC/run/PWGGAJE/run_decaygammajets.sh +++ b/MC/run/PWGGAJE/run_decaygammajets.sh @@ -9,8 +9,6 @@ #set -x -# ----------- LOAD UTILITY FUNCTIONS -------------------------- -. ${O2_ROOT}/share/scripts/jobutils.sh # ----------- START ACTUAL JOB ----------------------------- @@ -79,7 +77,7 @@ echo 'Detector acceptance option ' $PARTICLE_ACCEPTANCE ${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM ${CONFIG_ENERGY} -col pp -gen pythia8 -proc "jets" \ -ptHatMin ${PTHATMIN} -ptHatMax ${PTHATMAX} \ -tf ${NTIMEFRAMES} -ns ${NSIGEVENTS} -e ${SIMENGINE} \ - -j ${NWORKERS} -mod "--skipModules ZDC" \ + -j ${NWORKERS} \ -weightPow ${WEIGHTPOW} \ -trigger "external" -ini "\$O2DPG_ROOT/MC/config/PWGGAJE/ini/trigger_decay_gamma.ini" diff --git a/MC/run/PWGGAJE/run_dirgamma.sh b/MC/run/PWGGAJE/run_dirgamma.sh index 30403128c..daca947f2 100755 --- a/MC/run/PWGGAJE/run_dirgamma.sh +++ b/MC/run/PWGGAJE/run_dirgamma.sh @@ -9,8 +9,6 @@ #set -x -# ----------- LOAD UTILITY FUNCTIONS -------------------------- -. ${O2_ROOT}/share/scripts/jobutils.sh # ----------- START ACTUAL JOB ----------------------------- @@ -63,7 +61,7 @@ echo 'Parton PDG option ' $CONFIG_OUTPARTON_PDG ${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM ${CONFIG_ENERGY} -col pp -gen pythia8 -proc "dirgamma" \ -ptHatMin ${PTHATMIN} -ptHatMax ${PTHATMAX} \ -tf ${NTIMEFRAMES} -ns ${NSIGEVENTS} -e ${SIMENGINE} \ - -j ${NWORKERS} -mod "--skipModules ZDC" \ + -j ${NWORKERS} \ -weightPow ${WEIGHTPOW} \ -trigger "external" -ini "\$O2DPG_ROOT/MC/config/PWGGAJE/ini/trigger_prompt_gamma.ini" diff --git a/MC/run/PWGGAJE/run_dirgamma_embedding.sh b/MC/run/PWGGAJE/run_dirgamma_embedding.sh index 938bbe979..c867a3759 100755 --- a/MC/run/PWGGAJE/run_dirgamma_embedding.sh +++ b/MC/run/PWGGAJE/run_dirgamma_embedding.sh @@ -6,8 +6,6 @@ #set -x -# ----------- LOAD UTILITY FUNCTIONS -------------------------- -. ${O2_ROOT}/share/scripts/jobutils.sh # ----------- START ACTUAL JOB ----------------------------- @@ -61,7 +59,7 @@ ${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM ${CONFIG_ENERGY} -col pp -gen pythia8 -proc "dirgamma" \ -ptHatMin ${PTHATMIN} -ptHatMax ${PTHATMAX} \ -tf ${NTIMEFRAMES} -ns ${NSIGEVENTS} -e ${SIMENGINE} \ - -j ${NWORKERS} -mod "--skipModules ZDC" \ + -j ${NWORKERS} \ -weightPow ${WEIGHTPOW} \ -trigger "external" -ini "\$O2DPG_ROOT/MC/config/PWGGAJE/ini/trigger_prompt_gamma.ini" diff --git a/MC/run/PWGGAJE/run_dirgamma_hook.sh b/MC/run/PWGGAJE/run_dirgamma_hook.sh index af2d98b13..8b108f4d7 100755 --- a/MC/run/PWGGAJE/run_dirgamma_hook.sh +++ b/MC/run/PWGGAJE/run_dirgamma_hook.sh @@ -9,8 +9,6 @@ #set -x -# ----------- LOAD UTILITY FUNCTIONS -------------------------- -. ${O2_ROOT}/share/scripts/jobutils.sh # ----------- START ACTUAL JOB ----------------------------- @@ -63,7 +61,7 @@ echo 'Parton PDG option ' $CONFIG_OUTPARTON_PDG ${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM ${CONFIG_ENERGY} -col pp -gen pythia8 -proc "dirgamma" \ -ptHatMin ${PTHATMIN} -ptHatMax ${PTHATMAX} \ -tf ${NTIMEFRAMES} -ns ${NSIGEVENTS} -e ${SIMENGINE} \ - -j ${NWORKERS} -mod "--skipModules ZDC" \ + -j ${NWORKERS} \ -weightPow ${WEIGHTPOW} \ -ini "\$O2DPG_ROOT/MC/config/PWGGAJE/ini/hook_prompt_gamma.ini" diff --git a/MC/run/PWGGAJE/run_dirgamma_hook_embedding.sh b/MC/run/PWGGAJE/run_dirgamma_hook_embedding.sh index 60e8d5371..8d6a37b83 100755 --- a/MC/run/PWGGAJE/run_dirgamma_hook_embedding.sh +++ b/MC/run/PWGGAJE/run_dirgamma_hook_embedding.sh @@ -10,8 +10,6 @@ #set -x -# ----------- LOAD UTILITY FUNCTIONS -------------------------- -. ${O2_ROOT}/share/scripts/jobutils.sh # ----------- START ACTUAL JOB ----------------------------- @@ -67,7 +65,7 @@ ${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM ${CONFIG_ENERGY} -col pp -gen pythia8 -proc "dirgamma" \ -ptHatMin ${PTHATMIN} -ptHatMax ${PTHATMAX} \ -tf ${NTIMEFRAMES} -ns ${NSIGEVENTS} -e ${SIMENGINE} \ - -j ${NWORKERS} -mod "--skipModules ZDC" \ + -j ${NWORKERS} \ -weightPow ${WEIGHTPOW} \ -ini "\$O2DPG_ROOT/MC/config/PWGGAJE/ini/hook_prompt_gamma.ini" diff --git a/MC/run/PWGGAJE/run_jets.sh b/MC/run/PWGGAJE/run_jets.sh index 314450710..9e31cbcd0 100755 --- a/MC/run/PWGGAJE/run_jets.sh +++ b/MC/run/PWGGAJE/run_jets.sh @@ -5,8 +5,6 @@ #set -x -# ----------- LOAD UTILITY FUNCTIONS -------------------------- -. ${O2_ROOT}/share/scripts/jobutils.sh # ----------- START ACTUAL JOB ----------------------------- @@ -42,8 +40,8 @@ fi ${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM ${CONFIG_ENERGY} -col pp -gen pythia8 -proc "jets" \ -ptHatMin ${PTHATMIN} -ptHatMax ${PTHATMAX} \ -tf ${NTIMEFRAMES} -ns ${NSIGEVENTS} -e ${SIMENGINE} \ - -j ${NWORKERS} -mod "--skipModules ZDC" \ - -weightPow ${WEIGHTPOW} + -j ${NWORKERS} \ + -weightPow ${WEIGHTPOW} -interactionRate 500000 # run workflow ${O2DPG_ROOT}/MC/bin/o2_dpg_workflow_runner.py -f workflow.json diff --git a/MC/run/PWGGAJE/run_jets_HF_bbbar.sh b/MC/run/PWGGAJE/run_jets_HF_bbbar.sh index be00d7fe9..d9749d300 100644 --- a/MC/run/PWGGAJE/run_jets_HF_bbbar.sh +++ b/MC/run/PWGGAJE/run_jets_HF_bbbar.sh @@ -7,8 +7,6 @@ [ ! "${O2DPG_ROOT}" ] && echo "Error: This needs O2DPG loaded" && exit 1 [ ! "${O2_ROOT}" ] && echo "Error: This needs O2 loaded" && exit 1 -# ----------- LOAD UTILITY FUNCTIONS -------------------------- -. ${O2_ROOT}/share/scripts/jobutils.sh # ----------- START ACTUAL JOB ----------------------------- @@ -47,7 +45,7 @@ fi ${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM ${CONFIG_ENERGY} -col pp -gen external -proc "jets" \ -ptHatMin ${PTHATMIN} -ptHatMax ${PTHATMAX} \ -tf ${NTIMEFRAMES} -ns ${NSIGEVENTS} -e ${SIMENGINE} \ - -j ${NWORKERS} -mod "--skipModules ZDC" \ + -j ${NWORKERS} \ -interactionRate 500000 -confKey "Diamond.width[2]=6." ${SEED} \ -ini $O2DPG_ROOT/MC/config/PWGHF/ini/GeneratorHFTrigger_bbbar.ini \ -weightPow ${WEIGHTPOW} diff --git a/MC/run/PWGGAJE/run_jets_HF_ccbar.sh b/MC/run/PWGGAJE/run_jets_HF_ccbar.sh index 3149f5092..22aece831 100644 --- a/MC/run/PWGGAJE/run_jets_HF_ccbar.sh +++ b/MC/run/PWGGAJE/run_jets_HF_ccbar.sh @@ -7,50 +7,27 @@ [ ! "${O2DPG_ROOT}" ] && echo "Error: This needs O2DPG loaded" && exit 1 [ ! "${O2_ROOT}" ] && echo "Error: This needs O2 loaded" && exit 1 -# ----------- LOAD UTILITY FUNCTIONS -------------------------- -. ${O2_ROOT}/share/scripts/jobutils.sh # ----------- START ACTUAL JOB ----------------------------- RNDSEED=${RNDSEED:-0} # [default = 0] time-based random seed -NSIGEVENTS=${NSIGEVENTS:-10} +NSIGEVENTS=${NSIGEVENTS:-5} NTIMEFRAMES=${NTIMEFRAMES:-1} NWORKERS=${NWORKERS:-8} MODULES="--skipModules ZDC" #"PIPE ITS TPC EMCAL" CONFIG_ENERGY=${CONFIG_ENERGY:-13600.0} SIMENGINE=${SIMENGINE:-TGeant4} -WEIGHTPOW=${WEIGHTPOW:-6.0} [[ ${SPLITID} != "" ]] && SEED="-seed ${SPLITID}" || SEED="" -# Default for weighted productions -PTHATMIN=${PTHATMIN:-5.0} -PTHATMAX=${PTHATMAX:-300.0} - -# Define the pt hat bin arrays -pthatbin_loweredges=(0 5 7 9 12 16 21 28 36 45 57 70 85 99 115 132 150 169 190 212 235) -pthatbin_higheredges=( 5 7 9 12 16 21 28 36 45 57 70 85 99 115 132 150 169 190 212 235 -1) - -# Recover environmental vars for pt binning -#PTHATBIN=${PTHATBIN:-1} - -if [ -z "$PTHATBIN" ]; then - echo "Open Pt-hat range set" -else - PTHATMIN=${pthatbin_loweredges[$PTHATBIN]} - PTHATMAX=${pthatbin_higheredges[$PTHATBIN]} -fi - - -#ccbar filter -${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM ${CONFIG_ENERGY} -col pp -gen external -proc "jets" \ - -ptHatMin ${PTHATMIN} -ptHatMax ${PTHATMAX} \ - -tf ${NTIMEFRAMES} -ns ${NSIGEVENTS} -e ${SIMENGINE} \ - -j ${NWORKERS} -mod "--skipModules ZDC" \ - -interactionRate 500000 -confKey "Diamond.width[2]=6." ${SEED} \ - -ini $O2DPG_ROOT/MC/config/PWGHF/ini/GeneratorHFTrigger_ccbar.ini \ - -weightPow ${WEIGHTPOW} +#ccbar filter and bias2SelectionPow and PtHat settings are in the ini file given below +${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM ${CONFIG_ENERGY} -col pp -gen external -proc "jets" \ + -tf ${NTIMEFRAMES} -ns ${NSIGEVENTS} -e ${SIMENGINE} \ + -j ${NWORKERS} \ + -interactionRate 500000 -confKey "Diamond.width[2]=6." ${SEED} \ + -ini $O2DPG_ROOT/MC/config/PWGGAJE/ini/GeneratorHFJETrigger_ccbar.ini + # run workflow # allow increased timeframe parallelism with --cpu-limit 32 diff --git a/MC/run/PWGGAJE/run_jets_embedding.sh b/MC/run/PWGGAJE/run_jets_embedding.sh index 88d69fd32..d58ed2aca 100755 --- a/MC/run/PWGGAJE/run_jets_embedding.sh +++ b/MC/run/PWGGAJE/run_jets_embedding.sh @@ -7,8 +7,6 @@ #set -x -# ----------- LOAD UTILITY FUNCTIONS -------------------------- -. ${O2_ROOT}/share/scripts/jobutils.sh # ----------- START ACTUAL JOB ----------------------------- @@ -47,7 +45,7 @@ ${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM ${CONFIG_ENERGY} \ -col pp -gen pythia8 -proc "jets" \ -ptHatMin ${PTHATMIN} -ptHatMax ${PTHATMAX} \ -tf ${NTIMEFRAMES} -ns ${NSIGEVENTS} -e ${SIMENGINE} \ - -j ${NWORKERS} -mod "--skipModules ZDC" \ - -weightPow ${WEIGHTPOW} + -j ${NWORKERS} \ + -weightPow ${WEIGHTPOW} -interactionRate 500000 # run workflow ${O2DPG_ROOT}/MC/bin/o2_dpg_workflow_runner.py -f workflow.json diff --git a/MC/run/PWGGAJE/run_jets_hook.sh b/MC/run/PWGGAJE/run_jets_hook.sh index 45fc8eab2..66a557813 100755 --- a/MC/run/PWGGAJE/run_jets_hook.sh +++ b/MC/run/PWGGAJE/run_jets_hook.sh @@ -5,8 +5,6 @@ #set -x -# ----------- LOAD UTILITY FUNCTIONS -------------------------- -. ${O2_ROOT}/share/scripts/jobutils.sh # ----------- START ACTUAL JOB ----------------------------- @@ -59,7 +57,7 @@ echo 'Parton PDG option ' $CONFIG_OUTPARTON_PDG ${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM ${CONFIG_ENERGY} -col pp -gen pythia8 -proc "jets" \ -ptHatMin ${PTHATMIN} -ptHatMax ${PTHATMAX} \ -tf ${NTIMEFRAMES} -ns ${NSIGEVENTS} -e ${SIMENGINE} \ - -j ${NWORKERS} -mod "--skipModules ZDC" \ + -j ${NWORKERS} \ -ini "\$O2DPG_ROOT/MC/config/PWGGAJE/ini/hook_jets.ini" \ -weightPow ${WEIGHTPOW} # run workflow diff --git a/MC/run/PWGHF/analysis_benchmark.sh b/MC/run/PWGHF/analysis_benchmark.sh index 5d26d3d35..172a85f3d 100755 --- a/MC/run/PWGHF/analysis_benchmark.sh +++ b/MC/run/PWGHF/analysis_benchmark.sh @@ -10,8 +10,6 @@ [ ! "${O2DPG_ROOT}" ] && echo "Error: This needs O2DPG loaded" && exit 1 [ ! "${O2_ROOT}" ] && echo "Error: This needs O2 loaded" && exit 1 -# ----------- LOAD UTILITY FUNCTIONS -------------------------- -. ${O2_ROOT}/share/scripts/jobutils.sh # ----------- START ACTUAL JOB ----------------------------- @@ -25,16 +23,13 @@ PYPROCESS=${PYPROCESS:-ccbar} #ccbar, bbar, ... # create simulation workflow ${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM 5020 -col pp -gen pythia8 -proc ${PYPROCESS} -tf ${NTIMEFRAMES} -nb ${NBKGEVENTS} \ - -ns ${NSIGEVENTS} -e ${SIMENGINE} \ - -j ${NWORKERS} --embedding -interactionRate 50000 + -ns ${NSIGEVENTS} -e ${SIMENGINE} -interactionRate 500000 \ + -j ${NWORKERS} -genBkg pythia8 --embedding # Simulating a user who extends this workflow by an analysis task -${O2DPG_ROOT}/MC/bin/o2dpg-workflow-tools.py create workflow_ana --workflow --add-task mchist -needs="" -for i in $(seq 1 $NTIMEFRAMES) -do - needs+="aodmerge_$i " -done +${O2DPG_ROOT}/MC/bin/o2dpg-workflow-tools.py create workflow_ana --add-task mchist +needs="aodmerge" + # Comments: # 1. The output AOD name is the one created by the simulation workflow # 2. base name of AOD merge tasks is known as well to be aodmerge diff --git a/MC/run/PWGHF/create_embedding_workflow.py b/MC/run/PWGHF/create_embedding_workflow.py deleted file mode 100755 index efc2dbc6f..000000000 --- a/MC/run/PWGHF/create_embedding_workflow.py +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env python3 - -# -# A script producing a consistent MC->RECO->AOD workflow -# with optional embedding with parameters for PWGHF. -# - -import os -import sys - -# we simply delegate to main script with some PWGHF settings -command='${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM 13000 -col pp -proc ccbar --embedding -interactionRate 50000 ' - -# and add given user options -for i in range(1, len(sys.argv)): - command += sys.argv[i] - command += ' ' - -os.system(command) diff --git a/MC/run/PWGHF/embedding_benchmark.sh b/MC/run/PWGHF/embedding_benchmark.sh deleted file mode 100755 index b7aa0c6ac..000000000 --- a/MC/run/PWGHF/embedding_benchmark.sh +++ /dev/null @@ -1,36 +0,0 @@ -#!/bin/bash - -# -# A example workflow MC->RECO->AOD doing signal-background embedding, meant -# to study embedding speedups. -# Background events are reused across timeframes. -# - -# make sure O2DPG + O2 is loaded -[ ! "${O2DPG_ROOT}" ] && echo "Error: This needs O2DPG loaded" && exit 1 -[ ! "${O2_ROOT}" ] && echo "Error: This needs O2 loaded" && exit 1 - -# ----------- LOAD UTILITY FUNCTIONS -------------------------- -. ${O2_ROOT}/share/scripts/jobutils.sh - -# ----------- START ACTUAL JOB ----------------------------- - -NSIGEVENTS=${NSIGEVENTS:-20} -NTIMEFRAMES=${NTIMEFRAMES:-5} -NWORKERS=${NWORKERS:-8} -NBKGEVENTS=${NBKGEVENTS:-20} -MODULES="--skipModules ZDC" -SIMENGINE=${SIMENGINE:-TGeant4} -PYPROCESS=${PYPROCESS:-ccbar} #ccbar, bbar, ... -SEED=${SEED:+-seed $SEED} - -# create workflow -${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM 5020 -col pp -gen pythia8 -proc ${PYPROCESS} \ - -colBkg PbPb -genBkg pythia8 -procBkg "heavy_ion" \ - -tf ${NTIMEFRAMES} -nb ${NBKGEVENTS} \ - -ns ${NSIGEVENTS} -e ${SIMENGINE} \ - -j ${NWORKERS} --embedding -interactionRate 50000 \ - --include-analysis -run 310000 ${SEED} - -# run workflow -${O2DPG_ROOT}/MC/bin/o2_dpg_workflow_runner.py -f workflow.json --cpu-limit ${CPULIMIT:-8} -tt aod diff --git a/MC/run/PWGHF/run_OmegaCInjected.sh b/MC/run/PWGHF/run_OmegaCInjected.sh index 29c354952..924f4b3e7 100755 --- a/MC/run/PWGHF/run_OmegaCInjected.sh +++ b/MC/run/PWGHF/run_OmegaCInjected.sh @@ -8,8 +8,6 @@ [ ! "${O2DPG_ROOT}" ] && echo "Error: This needs O2DPG loaded" && exit 1 [ ! "${O2_ROOT}" ] && echo "Error: This needs O2 loaded" && exit 1 -# ----------- LOAD UTILITY FUNCTIONS -------------------------- -. ${O2_ROOT}/share/scripts/jobutils.sh # ----------- START ACTUAL JOB ----------------------------- @@ -27,7 +25,7 @@ SYSTEM=${SYSTEM:-pp} ENERGY=${ENERGY:-13600} [[ ${SPLITID} != "" ]] && SEED="-seed ${SPLITID}" || SEED="" -${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -e ${SIMENGINE} ${SEED} -eCM 13600 -col pp -colBkg pp -gen external -genBkg pythia8 -procBkg inel -j ${NWORKERS} -ns ${NSIGEVENTS} -nb ${NBKGEVENTS} -tf ${NTIMEFRAMES} -interactionRate 500000 -confKey "Diamond.width[2]=6." -mod "--skipModules ZDC" \ +${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -e ${SIMENGINE} ${SEED} -eCM 13600 -col pp -colBkg pp -gen external -genBkg pythia8 -procBkg inel -j ${NWORKERS} -ns ${NSIGEVENTS} -nb ${NBKGEVENTS} -tf ${NTIMEFRAMES} -interactionRate 500000 -confKey "Diamond.width[2]=6." \ --embedding -ini $O2DPG_ROOT/MC/config/PWGHF/ini/GeneratorHFOmegaCEmb.ini diff --git a/MC/run/PWGHF/run_OmegaCToXiPiInjected.sh b/MC/run/PWGHF/run_OmegaCToXiPiInjected.sh index 14a73e330..0584ebe13 100755 --- a/MC/run/PWGHF/run_OmegaCToXiPiInjected.sh +++ b/MC/run/PWGHF/run_OmegaCToXiPiInjected.sh @@ -8,8 +8,6 @@ [ ! "${O2DPG_ROOT}" ] && echo "Error: This needs O2DPG loaded" && exit 1 [ ! "${O2_ROOT}" ] && echo "Error: This needs O2 loaded" && exit 1 -# ----------- LOAD UTILITY FUNCTIONS -------------------------- -. ${O2_ROOT}/share/scripts/jobutils.sh # ----------- START ACTUAL JOB ----------------------------- @@ -27,7 +25,7 @@ SYSTEM=${SYSTEM:-pp} ENERGY=${ENERGY:-13600} [[ ${SPLITID} != "" ]] && SEED="-seed ${SPLITID}" || SEED="" -${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -e ${SIMENGINE} ${SEED} -eCM 13600 -col pp -colBkg pp -gen external -genBkg pythia8 -procBkg inel -j ${NWORKERS} -ns ${NSIGEVENTS} -nb ${NBKGEVENTS} -tf ${NTIMEFRAMES} -interactionRate 500000 -confKey "Diamond.width[2]=6." -mod "--skipModules ZDC" \ +${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -e ${SIMENGINE} ${SEED} -eCM 13600 -col pp -colBkg pp -gen external -genBkg pythia8 -procBkg inel -j ${NWORKERS} -ns ${NSIGEVENTS} -nb ${NBKGEVENTS} -tf ${NTIMEFRAMES} -interactionRate 500000 -confKey "Diamond.width[2]=6." \ --embedding -ini $O2DPG_ROOT/MC/config/PWGHF/ini/GeneratorHFOmegaCToXiPiEmb.ini diff --git a/MC/run/PWGHF/run_XiCToXiPiInjected.sh b/MC/run/PWGHF/run_XiCToXiPiInjected.sh index 34b2aad89..112eb49a6 100755 --- a/MC/run/PWGHF/run_XiCToXiPiInjected.sh +++ b/MC/run/PWGHF/run_XiCToXiPiInjected.sh @@ -8,8 +8,6 @@ [ ! "${O2DPG_ROOT}" ] && echo "Error: This needs O2DPG loaded" && exit 1 [ ! "${O2_ROOT}" ] && echo "Error: This needs O2 loaded" && exit 1 -# ----------- LOAD UTILITY FUNCTIONS -------------------------- -. ${O2_ROOT}/share/scripts/jobutils.sh # ----------- START ACTUAL JOB ----------------------------- @@ -27,7 +25,7 @@ SYSTEM=${SYSTEM:-pp} ENERGY=${ENERGY:-13600} [[ ${SPLITID} != "" ]] && SEED="-seed ${SPLITID}" || SEED="" -${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -e ${SIMENGINE} ${SEED} -eCM 13600 -col pp -colBkg pp -gen external -genBkg pythia8 -procBkg inel -j ${NWORKERS} -ns ${NSIGEVENTS} -nb ${NBKGEVENTS} -tf ${NTIMEFRAMES} -interactionRate 500000 -confKey "Diamond.width[2]=6." -mod "--skipModules ZDC" \ +${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -e ${SIMENGINE} ${SEED} -eCM 13600 -col pp -colBkg pp -gen external -genBkg pythia8 -procBkg inel -j ${NWORKERS} -ns ${NSIGEVENTS} -nb ${NBKGEVENTS} -tf ${NTIMEFRAMES} -interactionRate 500000 -confKey "Diamond.width[2]=6." \ --embedding -ini $O2DPG_ROOT/MC/config/PWGHF/ini/GeneratorHFXiCToXiPiEmb.ini diff --git a/MC/run/PWGHF/run_anchor_ini_from_cvmfs_example.sh b/MC/run/PWGHF/run_anchor_ini_from_cvmfs_example.sh new file mode 100755 index 000000000..b19028d6a --- /dev/null +++ b/MC/run/PWGHF/run_anchor_ini_from_cvmfs_example.sh @@ -0,0 +1,42 @@ +#!/bin/bash + +export ALIEN_JDL_LPMANCHORPASSNAME="apass4" +export ALIEN_JDL_MCANCHOR="apass4" +export ALIEN_JDL_COLLISIONSYSTEM="pp" +export ALIEN_JDL_LPMPASSNAME="apass4" +export ALIEN_JDL_LPMRUNNUMBER="539071" +export ALIEN_JDL_LPMPRODUCTIONTYPE="MC" +export ALIEN_JDL_LPMINTERACTIONTYPE="pp" +export ALIEN_JDL_LPMPRODUCTIONTAG="LHC24_2023zg_apass4_MC_test" +export ALIEN_JDL_LPMANCHORRUN="539071" +export ALIEN_JDL_LPMANCHORPRODUCTION="LHC23zg" +export ALIEN_JDL_LPMANCHORYEAR="2023" + +# added export +export NTIMEFRAMES=8 +export NSIGEVENTS=100 +export SPLITID=100 +export PRODSPLIT=153 +export CYCLE=0 +export ALIEN_PROC_ID=2963436952 + +# disable the QC +export DISABLE_QC=1 + +# modify ini file, to have external generator and/or config from a specific tag different from the one used for anchoring +ORIGINALINI=${O2DPG_ROOT}/MC/config/PWGHF/ini/GeneratorHF_D2H_bbbar_Bforced_gap5_Mode2.ini # original .ini file to be modified +MODIFIEDINI=GeneratorHF_D2H_bbbar_Bforced_gap5_Mode2_fromCVMFS.ini # output name for the modified .ini file + +CFGTOREPLACE="\${O2DPG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_beautyhadronic_with_decays_Mode2.cfg" # original config file name to be modified +CFGFROMCVMFS="/cvmfs/alice.cern.ch/el9-x86_64/Packages/O2DPG/daily-20240912-0200-1/MC/config/PWGHF/pythia8/generator/pythia8_beautyhadronic_with_decays_Mode2.cfg" # new config file name to use + +GENTOREPLACE="\${O2DPG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C" # original external generator file name to be modified +GENFROMCVMFS="/cvmfs/alice.cern.ch/el9-x86_64/Packages/O2DPG/daily-20240912-0200-1/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C" # new external generator file name to use + +if [ ! -f $MODIFIEDINI ]; then + sed -e "s|$CFGTOREPLACE|$CFGFROMCVMFS|g" -e "s|$GENTOREPLACE|$GENFROMCVMFS|g" $ORIGINALINI > $MODIFIEDINI +fi +MODIFIEDINI_ABSPATH=$(readlink -f $MODIFIEDINI) +export ALIEN_JDL_ANCHOR_SIM_OPTIONS="-gen external -ini $MODIFIEDINI_ABSPATH" + +${O2DPG_ROOT}/MC/run/ANCHOR/anchorMC.sh diff --git a/MC/run/PWGHF/run_embedding.sh b/MC/run/PWGHF/run_embedding.sh deleted file mode 100755 index a7c373457..000000000 --- a/MC/run/PWGHF/run_embedding.sh +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env bash - -set -x - -MODULES="PIPE ITS TPC" -BKGEVENTS=5 -SIGEVENTS=20 -NWORKERS=8 - -# generate background - -o2-sim -j ${NWORKERS} -n ${BKGEVENTS} -g pythia8hi -m ${MODULES} -o bkg \ - --configFile ${O2DPG_ROOT}/MC/config/common/ini/basic.ini \ - > logbkg 2>&1 - -# generate Pythia8 configuration - -RNDSEED=0 # [default = 0] time-based random seed -PTHATMIN=0. # [default = 0] -PTHATMAX=-1. # [default = -1] - -${O2DPG_ROOT}/MC/config/common/pythia8/utils/mkpy8cfg.py \ - --output=pythia8.cfg \ - --seed=${RNDSEED} \ - --idA=2212 \ - --idB=2212 \ - --eCM=13000. \ - --process=ccbar \ - --ptHatMin=${PTHATMIN} \ - --ptHatMax=${PTHATMAX} - -# embed signal into background - -o2-sim -j ${NWORKERS} -n ${SIGEVENTS} -g extgen -m ${MODULES} -o sgn \ - --configFile ${O2DPG_ROOT}/MC/config/PWGHF/ini/GeneratorHF.ini \ - --configKeyValues "GeneratorPythia8.config=pythia8.cfg" \ - --embedIntoFile bkg_Kine.root \ - > logsgn 2>&1 diff --git a/MC/run/PWGHF/run_pp_HFtriggers_Bforced_gaptrigger.sh b/MC/run/PWGHF/run_pp_HFtriggers_Bforced_gaptrigger.sh index 517c79352..ccec3bbb9 100755 --- a/MC/run/PWGHF/run_pp_HFtriggers_Bforced_gaptrigger.sh +++ b/MC/run/PWGHF/run_pp_HFtriggers_Bforced_gaptrigger.sh @@ -8,8 +8,6 @@ [ ! "${O2DPG_ROOT}" ] && echo "Error: This needs O2DPG loaded" && exit 1 [ ! "${O2_ROOT}" ] && echo "Error: This needs O2 loaded" && exit 1 -# ----------- LOAD UTILITY FUNCTIONS -------------------------- -. ${O2_ROOT}/share/scripts/jobutils.sh # ----------- START ACTUAL JOB ----------------------------- @@ -23,7 +21,7 @@ NTIMEFRAMES=${NTIMEFRAMES:-1} # create workflow #ccbar filter -${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM 13600 -col pp -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -interactionRate 500000 -confKey "Diamond.width[2]=6.;" -e ${SIMENGINE} ${SEED} -mod "--skipModules ZDC" \ +${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM 13600 -col pp -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -interactionRate 500000 -confKey "Diamond.width[2]=6.;" -e ${SIMENGINE} ${SEED} \ -ini $O2DPG_ROOT/MC/config/PWGHF/ini/GeneratorHFTrigger_Bforced.ini \ # run workflow diff --git a/MC/run/PWGHF/run_pp_HFtriggers_bbbar_gaptrigger.sh b/MC/run/PWGHF/run_pp_HFtriggers_bbbar_gaptrigger.sh index b85a5376e..d5c2973d4 100755 --- a/MC/run/PWGHF/run_pp_HFtriggers_bbbar_gaptrigger.sh +++ b/MC/run/PWGHF/run_pp_HFtriggers_bbbar_gaptrigger.sh @@ -8,8 +8,6 @@ [ ! "${O2DPG_ROOT}" ] && echo "Error: This needs O2DPG loaded" && exit 1 [ ! "${O2_ROOT}" ] && echo "Error: This needs O2 loaded" && exit 1 -# ----------- LOAD UTILITY FUNCTIONS -------------------------- -. ${O2_ROOT}/share/scripts/jobutils.sh # ----------- START ACTUAL JOB ----------------------------- @@ -22,7 +20,7 @@ NTIMEFRAMES=${NTIMEFRAMES:-1} # create workflow #ccbar filter -${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM 13600 -col pp -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -interactionRate 500000 -confKey "Diamond.width[2]=6.;" -e ${SIMENGINE} ${SEED} -mod "--skipModules ZDC" \ +${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM 13600 -col pp -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -interactionRate 500000 -confKey "Diamond.width[2]=6.;" -e ${SIMENGINE} ${SEED} \ -ini $O2DPG_ROOT/MC/config/PWGHF/ini/GeneratorHFTrigger_bbbar.ini \ # run workflow diff --git a/MC/run/PWGHF/run_pp_HFtriggers_ccbar_gaptrigger.sh b/MC/run/PWGHF/run_pp_HFtriggers_ccbar_gaptrigger.sh index 77446c9a1..c84fa121d 100755 --- a/MC/run/PWGHF/run_pp_HFtriggers_ccbar_gaptrigger.sh +++ b/MC/run/PWGHF/run_pp_HFtriggers_ccbar_gaptrigger.sh @@ -8,8 +8,6 @@ [ ! "${O2DPG_ROOT}" ] && echo "Error: This needs O2DPG loaded" && exit 1 [ ! "${O2_ROOT}" ] && echo "Error: This needs O2 loaded" && exit 1 -# ----------- LOAD UTILITY FUNCTIONS -------------------------- -. ${O2_ROOT}/share/scripts/jobutils.sh # ----------- START ACTUAL JOB ----------------------------- @@ -22,7 +20,7 @@ NTIMEFRAMES=${NTIMEFRAMES:-1} # create workflow #ccbar filter -${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM 13600 -col pp -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -interactionRate 500000 -confKey "Diamond.width[2]=6." -e ${SIMENGINE} ${SEED} -mod "--skipModules ZDC" \ +${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM 13600 -col pp -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -interactionRate 500000 -confKey "Diamond.width[2]=6." -e ${SIMENGINE} ${SEED} \ -ini $O2DPG_ROOT/MC/config/PWGHF/ini/GeneratorHFTrigger_ccbar.ini \ # run workflow diff --git a/MC/run/PWGHF/run_pp_HFtriggers_ccbar_userhook_replaceBkg.sh b/MC/run/PWGHF/run_pp_HFtriggers_ccbar_userhook_replaceBkg.sh index d03bd8845..9a2e71c96 100755 --- a/MC/run/PWGHF/run_pp_HFtriggers_ccbar_userhook_replaceBkg.sh +++ b/MC/run/PWGHF/run_pp_HFtriggers_ccbar_userhook_replaceBkg.sh @@ -8,8 +8,6 @@ [ ! "${O2DPG_ROOT}" ] && echo "Error: This needs O2DPG loaded" && exit 1 [ ! "${O2_ROOT}" ] && echo "Error: This needs O2 loaded" && exit 1 -# ----------- LOAD UTILITY FUNCTIONS -------------------------- -. ${O2_ROOT}/share/scripts/jobutils.sh # ----------- START ACTUAL JOB ----------------------------- @@ -24,7 +22,7 @@ NBKGEVENTS=$(($NSIGEVENTS * $NBKGEVENTSPERSIGNALEVENT)) # create workflow #ccbar filter -${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -e ${SIMENGINE} ${SEED} -eCM 13600 -col pp -colBkg pp -gen pythia8 -genBkg pythia8 -procBkg "" -j ${NWORKERS} -ns ${NSIGEVENTS} -nb ${NBKGEVENTS} -tf ${NTIMEFRAMES} -interactionRate 500000 -confKey "Diamond.width[2]=6." -mod "--skipModules ZDC" \ +${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -e ${SIMENGINE} ${SEED} -eCM 13600 -col pp -colBkg pp -gen pythia8 -genBkg pythia8 -procBkg "" -j ${NWORKERS} -ns ${NSIGEVENTS} -nb ${NBKGEVENTS} -tf ${NTIMEFRAMES} -interactionRate 500000 -confKey "Diamond.width[2]=6." \ --embedding --embeddPattern r0:e${NBKGEVENTSPERSIGNALEVENT} -ini $O2DPG_ROOT/MC/config/PWGHF/ini/GeneratorHFTrigger_ccbar.ini -iniBkg $O2DPG_ROOT/MC/config/PWGHF/ini/GeneratorHFTrigger_bkg.ini \ # run workflow diff --git a/MC/run/PWGHF/run_pp_HFtriggers_omegactoxipi_gaptrigger.sh b/MC/run/PWGHF/run_pp_HFtriggers_omegactoxipi_gaptrigger.sh index 95db5efc7..44c50816f 100644 --- a/MC/run/PWGHF/run_pp_HFtriggers_omegactoxipi_gaptrigger.sh +++ b/MC/run/PWGHF/run_pp_HFtriggers_omegactoxipi_gaptrigger.sh @@ -8,8 +8,6 @@ [ ! "${O2DPG_ROOT}" ] && echo "Error: This needs O2DPG loaded" && exit 1 [ ! "${O2_ROOT}" ] && echo "Error: This needs O2 loaded" && exit 1 -# ----------- LOAD UTILITY FUNCTIONS -------------------------- -. ${O2_ROOT}/share/scripts/jobutils.sh # ----------- START ACTUAL JOB ----------------------------- @@ -23,7 +21,7 @@ NTIMEFRAMES=${NTIMEFRAMES:-1} # create workflow #ccbar filter -${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM 13600 -col pp -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -interactionRate 500000 -confKey "Diamond.width[2]=6." -e ${SIMENGINE} ${SEED} -mod "--skipModules ZDC" \ +${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM 13600 -col pp -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -interactionRate 500000 -confKey "Diamond.width[2]=6." -e ${SIMENGINE} ${SEED} \ -ini $O2DPG_ROOT/MC/config/PWGHF/ini/GeneratorHFTrigger_OmegaCToXiPi.ini \ # run workflow diff --git a/MC/run/PWGHF/run_pp_HFtriggers_xi_omega_c_gaptrigger.sh b/MC/run/PWGHF/run_pp_HFtriggers_xi_omega_c_gaptrigger.sh new file mode 100755 index 000000000..71cee05dd --- /dev/null +++ b/MC/run/PWGHF/run_pp_HFtriggers_xi_omega_c_gaptrigger.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +# +# A example workflow MC->RECO->AOD for a simple pp min bias +# production, targetting test beam conditions. + +# make sure O2DPG + O2 is loaded +[ ! "${O2DPG_ROOT}" ] && echo "Error: This needs O2DPG loaded" && exit 1 +[ ! "${O2_ROOT}" ] && echo "Error: This needs O2 loaded" && exit 1 + + +# ----------- START ACTUAL JOB ----------------------------- + +NWORKERS=${NWORKERS:-8} +CPU_LIMIT=${CPU_LIMIT:-32} +MODULES="--skipModules ZDC" +SIMENGINE=${SIMENGINE:-TGeant4} +NSIGEVENTS=${NSIGEVENTS:-30} +NTIMEFRAMES=${NTIMEFRAMES:-1} +[[ ${SPLITID} != "" ]] && SEED="-seed ${SPLITID}" || SEED="" +# create workflow + +#ccbar filter +${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM 13600 -col pp -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -interactionRate 500000 -confKey "Diamond.width[2]=6." -e ${SIMENGINE} ${SEED} \ + -ini $O2DPG_ROOT/MC/config/PWGHF/ini/GeneratorHFTrigger_Xi_Omega_C.ini \ + +# run workflow +# allow increased timeframe parallelism with --cpu-limit 32 +${O2DPG_ROOT}/MC/bin/o2_dpg_workflow_runner.py -f workflow.json -tt aod --cpu-limit ${CPU_LIMIT} \ No newline at end of file diff --git a/MC/run/PWGHF/run_pp_HFtriggers_xictoxipi_gaptrigger.sh b/MC/run/PWGHF/run_pp_HFtriggers_xictoxipi_gaptrigger.sh index e3558c441..b51b22859 100644 --- a/MC/run/PWGHF/run_pp_HFtriggers_xictoxipi_gaptrigger.sh +++ b/MC/run/PWGHF/run_pp_HFtriggers_xictoxipi_gaptrigger.sh @@ -8,8 +8,6 @@ [ ! "${O2DPG_ROOT}" ] && echo "Error: This needs O2DPG loaded" && exit 1 [ ! "${O2_ROOT}" ] && echo "Error: This needs O2 loaded" && exit 1 -# ----------- LOAD UTILITY FUNCTIONS -------------------------- -. ${O2_ROOT}/share/scripts/jobutils.sh # ----------- START ACTUAL JOB ----------------------------- @@ -23,7 +21,7 @@ NTIMEFRAMES=${NTIMEFRAMES:-1} # create workflow #ccbar filter -${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM 13600 -col pp -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -interactionRate 500000 -confKey "Diamond.width[2]=6." -e ${SIMENGINE} ${SEED} -mod "--skipModules ZDC" \ +${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM 13600 -col pp -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -interactionRate 500000 -confKey "Diamond.width[2]=6." -e ${SIMENGINE} ${SEED} \ -ini $O2DPG_ROOT/MC/config/PWGHF/ini/GeneratorHFTrigger_XiCToXiPi.ini \ # run workflow diff --git a/MC/run/PWGHF/run_pp_PWGHF_testbeam_ccbar_trigger.sh b/MC/run/PWGHF/run_pp_PWGHF_testbeam_ccbar_trigger.sh index 160b8f2c6..c6f01aad3 100644 --- a/MC/run/PWGHF/run_pp_PWGHF_testbeam_ccbar_trigger.sh +++ b/MC/run/PWGHF/run_pp_PWGHF_testbeam_ccbar_trigger.sh @@ -8,8 +8,6 @@ [ ! "${O2DPG_ROOT}" ] && echo "Error: This needs O2DPG loaded" && exit 1 [ ! "${O2_ROOT}" ] && echo "Error: This needs O2 loaded" && exit 1 -# ----------- LOAD UTILITY FUNCTIONS -------------------------- -. ${O2_ROOT}/share/scripts/jobutils.sh # ----------- START ACTUAL JOB ----------------------------- diff --git a/MC/run/PWGHF/run_pp_anchor2022_D2H_ccbar_and_bbbar_gap5.sh b/MC/run/PWGHF/run_pp_anchor2022_D2H_ccbar_and_bbbar_gap5.sh new file mode 100755 index 000000000..3dd5f2902 --- /dev/null +++ b/MC/run/PWGHF/run_pp_anchor2022_D2H_ccbar_and_bbbar_gap5.sh @@ -0,0 +1,180 @@ +#!/bin/bash + +# add distortion maps +# https://alice.its.cern.ch/jira/browse/O2-3346?focusedCommentId=300982&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-300982 +# +# export O2DPG_ENABLE_TPC_DISTORTIONS=ON +# SCFile=$PWD/distortions_5kG_lowIR.root # file needs to be downloaded +# export O2DPG_TPC_DIGIT_EXTRA=" --distortionType 2 --readSpaceCharge ${SCFile} " + +# +# procedure setting up and executing an anchored MC +# + +# make sure O2DPG + O2 is loaded +[ ! "${O2DPG_ROOT}" ] && echo "Error: This needs O2DPG loaded" && exit 1 +[ ! "${O2_ROOT}" ] && echo "Error: This needs O2 loaded" && exit 1 + +# ------ CREATE AN MC CONFIG STARTING FROM RECO SCRIPT -------- +# - this part should not be done on the GRID, where we should rather +# point to an existing config (O2DPG repo or local disc or whatever) +export ALIEN_JDL_LPMANCHORYEAR=${ALIEN_JDL_LPMANCHORYEAR:-2022} +RUNNUMBER=${ALIEN_JDL_LPMRUNNUMBER:-517616} +#INTERACTIONRATE=${INTERACTIONRATE:-2000} + +# get the async script (we need to modify it) +# the script location can be configured with a JDL option + +ALIEN_JDL_LPMPRODUCTIONTAG_KEEP=$ALIEN_JDL_LPMPRODUCTIONTAG +echo "Substituting ALIEN_JDL_LPMPRODUCTIONTAG=$ALIEN_JDL_LPMPRODUCTIONTAG with ALIEN_JDL_LPMANCHORPRODUCTION=$ALIEN_JDL_LPMANCHORPRODUCTION for simulating reco pass..." +ALIEN_JDL_LPMPRODUCTIONTAG=$ALIEN_JDL_LPMANCHORPRODUCTION + +# ZDC causes issues for sim +#export ALIEN_JDL_WORKFLOWDETECTORS=ITS,TPC,TOF,FV0,FT0,FDD,MID,MFT,MCH,TRD,EMC,PHS,CPV,HMP,ZDC,CTP +export ALIEN_JDL_WORKFLOWDETECTORS=ITS,TPC,TOF,FV0,FT0,FDD,MID,MFT,MCH,TRD,EMC,PHS,CPV,HMP,CTP + +### async_pass.sh +DPGRECO=$O2DPG_ROOT/DATA/production/configurations/asyncReco/async_pass.sh + +if [[ -f async_pass.sh ]]; then + chmod +x async_pass.sh + DPGRECO=./async_pass.sh +else + cp -v $DPGRECO . +fi + +if [[ ! -f setenv_extra.sh ]]; then + cp -v ${DPGRECO%/*}/setenv_extra.sh . +fi + +echo "[INFO alien_async_pass.sh] Setting up DPGRECO to ${DPGRECO}" + +#settings that are MC-specific +sed -i 's/GPU_global.dEdxUseFullGainMap=1;GPU_global.dEdxDisableResidualGainMap=1/GPU_global.dEdxSplineTopologyCorrFile=splines_for_dedx_V1_MC_iter0_PP.root;GPU_global.dEdxDisableTopologyPol=1;GPU_global.dEdxDisableGainMap=1;GPU_global.dEdxDisableResidualGainMap=1;GPU_global.dEdxDisableResidualGain=1/' setenv_extra.sh +### ??? + +chmod +x async_pass.sh + +# take out line running the workflow (if we don't have data input) +[ ${CTF_TEST_FILE} ] || sed -i '/WORKFLOWMODE=run/d' async_pass.sh + +# create workflow ---> creates the file that can be parsed +export IGNORE_EXISTING_SHMFILES=1 +touch list.list + +./async_pass.sh ${CTF_TEST_FILE:-""} 2&> async_pass_log.log +RECO_RC=$? + +echo "RECO finished with ${RECO_RC}" +if [ "${NO_MC}" ]; then + return ${RECO_RC} 2>/dev/null || exit ${RECO_RC} # optionally quit here and don't do MC (useful for testing) +fi + +ALIEN_JDL_LPMPRODUCTIONTAG=$ALIEN_JDL_LPMPRODUCTIONTAG_KEEP +echo "Setting back ALIEN_JDL_LPMPRODUCTIONTAG to $ALIEN_JDL_LPMPRODUCTIONTAG" + +# now create the local MC config file --> config-config.json +${O2DPG_ROOT}/UTILS/parse-async-WorkflowConfig.py + +# check if config reasonably created +if [[ `grep "o2-ctf-reader-workflow-options" config-json.json 2> /dev/null | wc -l` == "0" ]]; then + echo "Problem in anchor config creation. Stopping." + exit 1 +fi + +# -- CREATE THE MC JOB DESCRIPTION ANCHORED TO RUN -- + +NWORKERS=${NWORKERS:-8} +MODULES="--skipModules ZDC" +SIMENGINE=${SIMENGINE:-TGeant4} +SIMENGINE=${ALIEN_JDL_SIMENGINE:-${SIMENGINE}} +NTIMEFRAMES=${NTIMEFRAMES:-50} +NSIGEVENTS=${NSIGEVENTS:-22} + +SPLITID=${SPLITID:-0} +PRODSPLIT=${PRODSPLIT:-100} +CYCLE=${CYCLE:-0} +# let SEED=$SPLITID+$CYCLE*$PRODSPLIT +SEED=${ALIEN_PROC_ID} + +# create workflow +# THIS NEEDS TO COME FROM OUTSIDE +# echo "$" | awk -F' -- ' '{print $1, $3}' + +# baseargs="-col pp -eCM 13600 -tf ${NTIMEFRAMES} --split-id ${SPLITID} --prod-split ${PRODSPLIT} --cycle ${CYCLE} --run-number ${RUNNUMBER}" +baseargs="-tf ${NTIMEFRAMES} --split-id ${SPLITID} --prod-split ${PRODSPLIT} --cycle ${CYCLE} --run-number ${RUNNUMBER}" + +# THIS NEEDS TO COME FROM OUTSIDE +remainingargs="-gen external -ini $O2DPG_ROOT/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_and_bbbar_gap5.ini -seed ${SEED} -ns ${NSIGEVENTS} --include-local-qc" + +remainingargs="${remainingargs} -e ${SIMENGINE} -j ${NWORKERS}" +remainingargs="${remainingargs} -productionTag ${ALIEN_JDL_LPMPRODUCTIONTAG:-alibi_anchorTest_tmp}" +remainingargs="${remainingargs} --anchor-config config-json.json" + +echo "baseargs: ${baseargs}" +echo "remainingargs: ${remainingargs}" + +# query CCDB has changed, w/o "_" +${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow_anchored.py ${baseargs} -- ${remainingargs} &> timestampsampling_${RUNNUMBER}.log +[ "$?" != "0" ] && echo "Problem during anchor timestamp sampling " && exit 1 + +TIMESTAMP=`grep "Determined timestamp to be" timestampsampling_${RUNNUMBER}.log | awk '//{print $6}'` +echo "TIMESTAMP IS ${TIMESTAMP}" + +# -- PREFETCH CCDB OBJECTS TO DISC -- +# (make sure the right objects at the right timestamp are fetched +# until https://alice.its.cern.ch/jira/browse/O2-2852 is fixed) +export ALICEO2_CCDB_LOCALCACHE=$PWD/.ccdb +[ ! -d .ccdb ] && mkdir .ccdb + +CCDBOBJECTS="/CTP/Calib/OrbitReset /GLO/Config/GRPMagField/ /GLO/Config/GRPLHCIF /ITS/Calib/DeadMap /ITS/Calib/NoiseMap /ITS/Calib/ClusterDictionary /TPC/Calib/PadGainFull /TPC/Calib/TopologyGain /TPC/Calib/TimeGain /TPC/Calib/PadGainResidual /TPC/Config/FEEPad /TOF/Calib/Diagnostic /TOF/Calib/LHCphase /TOF/Calib/FEELIGHT /TOF/Calib/ChannelCalib /MFT/Calib/DeadMap /MFT/Calib/NoiseMap /MFT/Calib/ClusterDictionary /FT0/Calibration/ChannelTimeOffset /FV0/Calibration/ChannelTimeOffset /GLO/GRP/BunchFilling" + +${O2_ROOT}/bin/o2-ccdb-downloadccdbfile --host http://alice-ccdb.cern.ch/ -p ${CCDBOBJECTS} -d .ccdb --timestamp ${TIMESTAMP} +if [ ! "$?" == "0" ]; then + echo "Problem during CCDB prefetching of ${CCDBOBJECTS}. Exiting." + exit 1 +fi + +# -- Create aligned geometry using ITS and MFT ideal alignments to avoid overlaps in geant +CCDBOBJECTS_IDEAL_MC="ITS/Calib/Align MFT/Calib/Align" +TIMESTAMP_IDEAL_MC=1 +${O2_ROOT}/bin/o2-ccdb-downloadccdbfile --host http://alice-ccdb.cern.ch/ -p ${CCDBOBJECTS_IDEAL_MC} -d .ccdb --timestamp ${TIMESTAMP_IDEAL_MC} +if [ ! "$?" == "0" ]; then + echo "Problem during CCDB prefetching of ${CCDBOBJECTS_IDEAL_MC}. Exiting." + exit 1 +fi + +# ${O2_ROOT}/bin/o2-create-aligned-geometry-workflow --configKeyValues "HBFUtils.startTime=${TIMESTAMP}" --condition-remap=file://${ALICEO2_CCDB_LOCALCACHE}=ITS/Calib/Align,MFT/Calib/Align -b +echo "run with echo in pipe" | ${O2_ROOT}/bin/o2-create-aligned-geometry-workflow --configKeyValues "HBFUtils.startTime=${TIMESTAMP}" --condition-remap=file://${ALICEO2_CCDB_LOCALCACHE}=ITS/Calib/Align,MFT/Calib/Align -b +mkdir -p $ALICEO2_CCDB_LOCALCACHE/GLO/Config/GeometryAligned +ln -s -f $PWD/o2sim_geometry-aligned.root $ALICEO2_CCDB_LOCALCACHE/GLO/Config/GeometryAligned/snapshot.root + +# -- RUN THE MC WORKLOAD TO PRODUCE AOD -- + +export FAIRMQ_IPC_PREFIX=./ + +echo "Ready to start main workflow" + +${O2DPG_ROOT}/MC/bin/o2_dpg_workflow_runner.py -f workflow.json -tt ${ALIEN_JDL_O2DPGWORKFLOWTARGET:-aod} --cpu-limit ${ALIEN_JDL_CPULIMIT:-8} +MCRC=$? # <--- we'll report back this code +#exit 0 + +if [[ "${MCRC}" = "0" && "${remainingargs}" == *"--include-local-qc"* ]] ; then + # do QC tasks + echo "Doing QC" + ${O2DPG_ROOT}/MC/bin/o2_dpg_workflow_runner.py -f workflow.json --target-labels QC --cpu-limit ${ALIEN_JDL_CPULIMIT:-8} -k + RC=$? +fi + +# +# full logs tar-ed for output, regardless the error code or validation - to catch also QC logs... +# +if [[ -n "$ALIEN_PROC_ID" ]]; then + find ./ \( -name "*.log*" -o -name "*mergerlog*" -o -name "*serverlog*" -o -name "*workerlog*" -o -name "pythia8.cfg" \) | tar -czvf debug_log_archive.tgz -T - + find ./ \( -name "*.log*" -o -name "*mergerlog*" -o -name "*serverlog*" -o -name "*workerlog*" -o -name "*.root" \) | tar -czvf debug_full_archive.tgz -T - +fi + +unset FAIRMQ_IPC_PREFIX + +return ${MCRC} 2>/dev/null || exit ${MCRC} + \ No newline at end of file diff --git a/MC/run/PWGHF/run_pp_testbeam_LcTrigger.sh b/MC/run/PWGHF/run_pp_testbeam_LcTrigger.sh index 19a818173..77a506c72 100644 --- a/MC/run/PWGHF/run_pp_testbeam_LcTrigger.sh +++ b/MC/run/PWGHF/run_pp_testbeam_LcTrigger.sh @@ -8,8 +8,6 @@ [ ! "${O2DPG_ROOT}" ] && echo "Error: This needs O2DPG loaded" && exit 1 [ ! "${O2_ROOT}" ] && echo "Error: This needs O2 loaded" && exit 1 -# ----------- LOAD UTILITY FUNCTIONS -------------------------- -#. ${O2_ROOT}/share/scripts/jobutils.sh # ----------- START ACTUAL JOB ----------------------------- diff --git a/MC/run/PWGHF/run_pp_testbeam_ccbarfilter.sh b/MC/run/PWGHF/run_pp_testbeam_ccbarfilter.sh index be6e0272a..1267296bb 100644 --- a/MC/run/PWGHF/run_pp_testbeam_ccbarfilter.sh +++ b/MC/run/PWGHF/run_pp_testbeam_ccbarfilter.sh @@ -8,8 +8,6 @@ [ ! "${O2DPG_ROOT}" ] && echo "Error: This needs O2DPG loaded" && exit 1 [ ! "${O2_ROOT}" ] && echo "Error: This needs O2 loaded" && exit 1 -# ----------- LOAD UTILITY FUNCTIONS -------------------------- -#. ${O2_ROOT}/share/scripts/jobutils.sh # ----------- START ACTUAL JOB ----------------------------- @@ -23,7 +21,7 @@ NTIMEFRAMES=${NTIMEFRAMES:-1} # create workflow #ccbar filter -${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM 900 -col pp -gen pythia8 -proc "inel" -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -interactionRate 10000 -confKey "Diamond.width[2]=6." -e TGeant4 -mod "--skipModules ZDC" \ +${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM 900 -col pp -gen pythia8 -proc "inel" -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -interactionRate 10000 -confKey "Diamond.width[2]=6." -e TGeant4 \ -ini $O2DPG_ROOT/MC/config/PWGHF/ini/GeneratorHF_ccbar.ini \ # run workflow diff --git a/MC/run/PWGLF/runLFInjector.sh b/MC/run/PWGLF/runLFInjector.sh index db7ace628..16a62602e 100755 --- a/MC/run/PWGLF/runLFInjector.sh +++ b/MC/run/PWGLF/runLFInjector.sh @@ -6,7 +6,6 @@ # The following variables can be set from the outside: # - NWORKERS: number of workers to use (default 8) -# - MODULES: modules to be run (default "--skipModules ZDC") # - SIMENGINE: simulation engine (default TGeant4) # - NSIGEVENTS: number of signal events (default 1) # - NBKGEVENTS: number of background events (default 1) @@ -36,13 +35,10 @@ echo "CFGINIFILE = $1" export IGNORE_VALIDITYCHECK_OF_CCDB_LOCALCACHE=1 #export ALICEO2_CCDB_LOCALCACHE=.ccdb -# ----------- LOAD UTILITY FUNCTIONS -------------------------- -. ${O2_ROOT}/share/scripts/jobutils.sh # ----------- START ACTUAL JOB ------------------------------- NWORKERS=${NWORKERS:-8} -MODULES=${MODULES:---skipModules ZDC} SIMENGINE=${SIMENGINE:-TGeant4} NSIGEVENTS=${NSIGEVENTS:-1} NBKGEVENTS=${NBKGEVENTS:-1} @@ -65,9 +61,9 @@ echo "ENERGY = $ENERGY" echo "CFGINIFILE = $CFGINIFILE" # create workflow -${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM ${ENERGY} -col ${SYSTEM} -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -interactionRate ${INTRATE} -confKey "Diamond.width[2]=6." -e ${SIMENGINE} ${SEED} -mod "${MODULES}" \ +${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM ${ENERGY} -col ${SYSTEM} -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -interactionRate ${INTRATE} -confKey "Diamond.width[2]=6." -e ${SIMENGINE} ${SEED} \ -ini ${CFGINIFILE} # run workflow O2_SIM_WORKFLOW_RUNNER=${O2_SIM_WORKFLOW_RUNNER:-"${O2DPG_ROOT}/MC/bin/o2_dpg_workflow_runner.py"} -$O2_SIM_WORKFLOW_RUNNER -f workflow.json -tt aod --cpu-limit $NWORKERS \ No newline at end of file +$O2_SIM_WORKFLOW_RUNNER -f workflow.json -tt aod --cpu-limit $NWORKERS diff --git a/MC/run/PWGLF/run_Coalescence_pp.sh b/MC/run/PWGLF/run_Coalescence_pp.sh new file mode 100755 index 000000000..c01ac6ac6 --- /dev/null +++ b/MC/run/PWGLF/run_Coalescence_pp.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +# +# A example workflow MC->RECO->AOD for a simple pp min bias +# production, targetting test beam conditions. + +# make sure O2DPG + O2 is loaded +[ ! "${O2DPG_ROOT}" ] && echo "Error: This needs O2DPG loaded" && exit 1 +[ ! "${O2_ROOT}" ] && echo "Error: This needs O2 loaded" && exit 1 + + +# ----------- START ACTUAL JOB ----------------------------- + +NWORKERS=${NWORKERS:-8} +MODULES="--skipModules ZDC" +SIMENGINE=${SIMENGINE:-TGeant4} +NSIGEVENTS=${NSIGEVENTS:-1} +NTIMEFRAMES=${NTIMEFRAMES:-1} +INTRATE=${INTRATE:-500000} +SYSTEM=${SYSTEM:-pp} +ENERGY=${ENERGY:-13600} +[[ ${SPLITID} != "" ]] && SEED="-seed ${SPLITID}" || SEED="" + +# create workflow +${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM ${ENERGY} -col ${SYSTEM} -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -interactionRate ${INTRATE} -confKey "Diamond.width[0]=0.005;Diamond.width[1]=0.005;Diamond.width[2]=6." -e ${SIMENGINE} ${SEED} \ + -ini $O2DPG_ROOT/MC/config/PWGLF/ini/GeneratorLF_Coalescence.ini + +# run workflow +${O2DPG_ROOT}/MC/bin/o2_dpg_workflow_runner.py -f workflow.json -tt aod --cpu-limit 32 diff --git a/MC/run/PWGLF/run_DeTrHeInjected.sh b/MC/run/PWGLF/run_DeTrHeInjected.sh index 439b505bc..409c84808 100755 --- a/MC/run/PWGLF/run_DeTrHeInjected.sh +++ b/MC/run/PWGLF/run_DeTrHeInjected.sh @@ -12,8 +12,6 @@ export IGNORE_VALIDITYCHECK_OF_CCDB_LOCALCACHE=1 #export ALICEO2_CCDB_LOCALCACHE=.ccdb -# ----------- LOAD UTILITY FUNCTIONS -------------------------- -. ${O2_ROOT}/share/scripts/jobutils.sh # ----------- START ACTUAL JOB ----------------------------- diff --git a/MC/run/PWGLF/run_DeTrHeInjected_PbPb.sh b/MC/run/PWGLF/run_DeTrHeInjected_PbPb.sh index cb93c5d78..06bacbc23 100755 --- a/MC/run/PWGLF/run_DeTrHeInjected_PbPb.sh +++ b/MC/run/PWGLF/run_DeTrHeInjected_PbPb.sh @@ -12,8 +12,6 @@ export IGNORE_VALIDITYCHECK_OF_CCDB_LOCALCACHE=1 #export ALICEO2_CCDB_LOCALCACHE=.ccdb -# ----------- LOAD UTILITY FUNCTIONS -------------------------- -. ${O2_ROOT}/share/scripts/jobutils.sh # ----------- START ACTUAL JOB ----------------------------- diff --git a/MC/run/PWGLF/run_DoubleLambdaTriggered.sh b/MC/run/PWGLF/run_DoubleLambdaTriggered.sh new file mode 100755 index 000000000..c7e2ae418 --- /dev/null +++ b/MC/run/PWGLF/run_DoubleLambdaTriggered.sh @@ -0,0 +1,41 @@ +#!/bin/bash + +# make sure O2DPG + O2 is loaded +[ ! "${O2DPG_ROOT}" ] && echo "Error: This needs O2DPG loaded" && exit 1 +[ ! "${O2_ROOT}" ] && echo "Error: This needs O2 loaded" && exit 1 + +# ----------- CONFIGURE -------------------------- +export IGNORE_VALIDITYCHECK_OF_CCDB_LOCALCACHE=1 +#export ALICEO2_CCDB_LOCALCACHE=.ccdb + + +# ----------- START ACTUAL JOB ----------------------------- + +NWORKERS=${NWORKERS:-8} +SIMENGINE=${SIMENGINE:-TGeant4} +NSIGEVENTS=${NSIGEVENTS:-100} +NTIMEFRAMES=${NTIMEFRAMES:-1} +INTRATE=${INTRATE:-50000} +SYSTEM=${SYSTEM:-pp} +ENERGY=${ENERGY:-13600} +CFGINIFILE=${CFGINIFILE:-"${O2DPG_ROOT}/MC/config/PWGLF/ini/GeneratorDoubleLambdaTriggered.ini"} +SEED="-seed 1995" +#[[ ${SPLITID} != "" ]] && SEED="-seed ${SPLITID}" || SEED="" + +echo "NWORKERS = $NWORKERS" + +# create workflow +O2_SIM_WORKFLOW=${O2_SIM_WORKFLOW:-"${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py"} +CFGDECAY="${O2DPG_ROOT}/MC/config/PWGLF/pythia8/decayer/force_lambda_charged_decay.cfg" +G4CFG="${O2DPG_ROOT}/MC/config/PWGLF/pythia8/decayer/g4_ext_decayer_lambda.in" +$O2_SIM_WORKFLOW -eCM ${ENERGY} -col ${SYSTEM} -gen external \ + -j ${NWORKERS} \ + -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -interactionRate ${INTRATE} \ + -confKey "Diamond.width[0]=0.1;Diamond.width[1]=0.1;Diamond.width[2]=6.;;DecayerPythia8.config[1]=${CFGDECAY};DecayerPythia8.showChanged=1;G4.configMacroFile=${G4CFG}" \ + ${SEED} \ + -e ${SIMENGINE} \ + -ini $CFGINIFILE + +# run workflow +O2_SIM_WORKFLOW_RUNNER=${O2_SIM_WORKFLOW_RUNNER:-"${O2DPG_ROOT}/MC/bin/o2_dpg_workflow_runner.py"} +$O2_SIM_WORKFLOW_RUNNER -f workflow.json -tt aod --cpu-limit $NWORKERS diff --git a/MC/run/PWGLF/run_GeneratorLF_antid_and_highpt.sh b/MC/run/PWGLF/run_GeneratorLF_antid_and_highpt.sh new file mode 100644 index 000000000..49f7ca958 --- /dev/null +++ b/MC/run/PWGLF/run_GeneratorLF_antid_and_highpt.sh @@ -0,0 +1,26 @@ +#!/bin/bash + + +# make sure O2DPG + O2 is loaded +[ ! "${O2DPG_ROOT}" ] && echo "Error: This needs O2DPG loaded" && exit 1 +[ ! "${O2_ROOT}" ] && echo "Error: This needs O2 loaded" && exit 1 + + +# ----------- START ACTUAL JOB ----------------------------- + +NWORKERS=${NWORKERS:-8} +MODULES="--skipModules ZDC" +SIMENGINE=${SIMENGINE:-TGeant4} +NSIGEVENTS=${NSIGEVENTS:-1} +NTIMEFRAMES=${NTIMEFRAMES:-1} +INTRATE=${INTRATE:-500000} +SYSTEM=${SYSTEM:-pp} +ENERGY=${ENERGY:-13600} +[[ ${SPLITID} != "" ]] && SEED="-seed ${SPLITID}" || SEED="" + +# create workflow +${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM ${ENERGY} -col ${SYSTEM} -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -interactionRate ${INTRATE} -confKey "Diamond.width[0]=0.005;Diamond.width[1]=0.005;Diamond.width[2]=6." -e ${SIMENGINE} ${SEED} \ + -ini $O2DPG_ROOT/MC/config/PWGLF/ini/GeneratorLF_antid_and_highpt.ini + +# run workflow +${O2DPG_ROOT}/MC/bin/o2_dpg_workflow_runner.py -f workflow.json -tt aod --cpu-limit 32 diff --git a/MC/run/PWGLF/run_GeneratorLF_highpt.sh b/MC/run/PWGLF/run_GeneratorLF_highpt.sh new file mode 100644 index 000000000..817cd3303 --- /dev/null +++ b/MC/run/PWGLF/run_GeneratorLF_highpt.sh @@ -0,0 +1,26 @@ +#!/bin/bash + + +# make sure O2DPG + O2 is loaded +[ ! "${O2DPG_ROOT}" ] && echo "Error: This needs O2DPG loaded" && exit 1 +[ ! "${O2_ROOT}" ] && echo "Error: This needs O2 loaded" && exit 1 + + +# ----------- START ACTUAL JOB ----------------------------- + +NWORKERS=${NWORKERS:-8} +MODULES="--skipModules ZDC" +SIMENGINE=${SIMENGINE:-TGeant4} +NSIGEVENTS=${NSIGEVENTS:-1} +NTIMEFRAMES=${NTIMEFRAMES:-1} +INTRATE=${INTRATE:-500000} +SYSTEM=${SYSTEM:-pp} +ENERGY=${ENERGY:-13600} +[[ ${SPLITID} != "" ]] && SEED="-seed ${SPLITID}" || SEED="" + +# create workflow +${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM ${ENERGY} -col ${SYSTEM} -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -interactionRate ${INTRATE} -confKey "Diamond.width[0]=0.005;Diamond.width[1]=0.005;Diamond.width[2]=6." -e ${SIMENGINE} ${SEED} \ + -ini $O2DPG_ROOT/MC/config/PWGLF/ini/GeneratorLF_HighPt.ini + +# run workflow +${O2DPG_ROOT}/MC/bin/o2_dpg_workflow_runner.py -f workflow.json -tt aod --cpu-limit 32 diff --git a/MC/run/PWGLF/run_GeneratorLF_highpt_strangeness.sh b/MC/run/PWGLF/run_GeneratorLF_highpt_strangeness.sh new file mode 100644 index 000000000..ebf598e24 --- /dev/null +++ b/MC/run/PWGLF/run_GeneratorLF_highpt_strangeness.sh @@ -0,0 +1,26 @@ +#!/bin/bash + + +# make sure O2DPG + O2 is loaded +[ ! "${O2DPG_ROOT}" ] && echo "Error: This needs O2DPG loaded" && exit 1 +[ ! "${O2_ROOT}" ] && echo "Error: This needs O2 loaded" && exit 1 + + +# ----------- START ACTUAL JOB ----------------------------- + +NWORKERS=${NWORKERS:-8} +MODULES="--skipModules ZDC" +SIMENGINE=${SIMENGINE:-TGeant4} +NSIGEVENTS=${NSIGEVENTS:-1} +NTIMEFRAMES=${NTIMEFRAMES:-1} +INTRATE=${INTRATE:-500000} +SYSTEM=${SYSTEM:-pp} +ENERGY=${ENERGY:-13600} +[[ ${SPLITID} != "" ]] && SEED="-seed ${SPLITID}" || SEED="" + +# create workflow +${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM ${ENERGY} -col ${SYSTEM} -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -interactionRate ${INTRATE} -confKey "Diamond.width[0]=0.005;Diamond.width[1]=0.005;Diamond.width[2]=6." -e ${SIMENGINE} ${SEED} \ + -ini $O2DPG_ROOT/MC/config/PWGLF/ini/GeneratorLF_highpt_strangeness.ini + +# run workflow +${O2DPG_ROOT}/MC/bin/o2_dpg_workflow_runner.py -f workflow.json -tt aod --cpu-limit 32 diff --git a/MC/run/PWGLF/run_HyperInjectedGap.sh b/MC/run/PWGLF/run_HyperInjectedGap.sh index a973ac443..2b408891a 100644 --- a/MC/run/PWGLF/run_HyperInjectedGap.sh +++ b/MC/run/PWGLF/run_HyperInjectedGap.sh @@ -8,8 +8,6 @@ [ ! "${O2DPG_ROOT}" ] && echo "Error: This needs O2DPG loaded" && exit 1 [ ! "${O2_ROOT}" ] && echo "Error: This needs O2 loaded" && exit 1 -# ----------- LOAD UTILITY FUNCTIONS -------------------------- -. ${O2_ROOT}/share/scripts/jobutils.sh # ----------- START ACTUAL JOB ----------------------------- @@ -25,7 +23,7 @@ ENERGY=${ENERGY:-13600} [[ ${SPLITID} != "" ]] && SEED="-seed ${SPLITID}" || SEED="" # create workflow -${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM ${ENERGY} -col ${SYSTEM} -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -interactionRate ${INTRATE} -confKey "Diamond.width[0]=0.1;Diamond.width[1]=0.1;Diamond.width[2]=6." -e ${SIMENGINE} ${SEED} -mod "--skipModules ZDC" \ +${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM ${ENERGY} -col ${SYSTEM} -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -interactionRate ${INTRATE} -confKey "Diamond.width[0]=0.1;Diamond.width[1]=0.1;Diamond.width[2]=6." -e ${SIMENGINE} ${SEED} \ -ini ${O2DPG_ROOT}/MC/config/PWGLF/ini/GeneratorLFHyper${SYSTEM}Gap.ini # run workflow diff --git a/MC/run/PWGLF/run_HyperNucleiInjectedGap.sh b/MC/run/PWGLF/run_HyperNucleiInjectedGap.sh index 027f6d0a0..d53659f9a 100644 --- a/MC/run/PWGLF/run_HyperNucleiInjectedGap.sh +++ b/MC/run/PWGLF/run_HyperNucleiInjectedGap.sh @@ -8,14 +8,12 @@ [ ! "${O2DPG_ROOT}" ] && echo "Error: This needs O2DPG loaded" && exit 1 [ ! "${O2_ROOT}" ] && echo "Error: This needs O2 loaded" && exit 1 -# ----------- LOAD UTILITY FUNCTIONS -------------------------- -. ${O2_ROOT}/share/scripts/jobutils.sh # ----------- START ACTUAL JOB ----------------------------- NWORKERS=${NWORKERS:-8} MODULES="--skipModules ZDC" -SIMENGINE=${SIMENGINE:-TGeant3} +SIMENGINE=${SIMENGINE:-TGeant4} NSIGEVENTS=${NSIGEVENTS:-1} NBKGEVENTS=${NBKGEVENTS:-1} NTIMEFRAMES=${NTIMEFRAMES:-1} @@ -25,7 +23,7 @@ ENERGY=${ENERGY:-13600} [[ ${SPLITID} != "" ]] && SEED="-seed ${SPLITID}" || SEED="" # create workflow -${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM ${ENERGY} -col ${SYSTEM} -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -interactionRate ${INTRATE} -confKey "Diamond.width[0]=0.1;Diamond.width[1]=0.1;Diamond.width[2]=6." -e ${SIMENGINE} ${SEED} -mod "--skipModules ZDC" \ +${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM ${ENERGY} -col ${SYSTEM} -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -interactionRate ${INTRATE} -confKey "Diamond.width[0]=0.1;Diamond.width[1]=0.1;Diamond.width[2]=6." -e ${SIMENGINE} ${SEED} \ -ini ${O2DPG_ROOT}/MC/config/PWGLF/ini/GeneratorLFHyperNuclei${SYSTEM}Gap.ini # run workflow diff --git a/MC/run/PWGLF/run_HyperNucleiInjectedGapWithFlow.sh b/MC/run/PWGLF/run_HyperNucleiInjectedGapWithFlow.sh new file mode 100755 index 000000000..249c103f1 --- /dev/null +++ b/MC/run/PWGLF/run_HyperNucleiInjectedGapWithFlow.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +# make sure O2DPG + O2 is loaded +[ ! "${O2DPG_ROOT}" ] && echo "Error: This needs O2DPG loaded" && exit 1 +[ ! "${O2_ROOT}" ] && echo "Error: This needs O2 loaded" && exit 1 + +# ----------- CONFIGURE -------------------------- +export IGNORE_VALIDITYCHECK_OF_CCDB_LOCALCACHE=1 +#export ALICEO2_CCDB_LOCALCACHE=.ccdb + + +# ----------- START ACTUAL JOB ----------------------------- + +NWORKERS=${NWORKERS:-8} +SIMENGINE=${SIMENGINE:-TGeant4} +NSIGEVENTS=${NSIGEVENTS:-1} +NTIMEFRAMES=${NTIMEFRAMES:-1} +INTRATE=${INTRATE:-50000} +SYSTEM=${SYSTEM:-PbPb} +ENERGY=${ENERGY:-5360} +CFGINIFILE=${CFGINIFILE:-"${O2DPG_ROOT}/MC/config/PWGLF/ini/GeneratorLFHyperNucleiPbPbGapWithFlow.ini"} + +[[ ${SPLITID} != "" ]] && SEED="-seed ${SPLITID}" || SEED="-seed 0" + +echo "NWORKERS = $NWORKERS" + +# create workflow +O2_SIM_WORKFLOW=${O2_SIM_WORKFLOW:-"${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py"} +$O2_SIM_WORKFLOW -eCM ${ENERGY} -col ${SYSTEM} -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -interactionRate ${INTRATE} -confKey "Diamond.width[0]=0.1;Diamond.width[1]=0.1;Diamond.width[2]=6." -e ${SIMENGINE} ${SEED} -ini $CFGINIFILE + +# run workflow +O2_SIM_WORKFLOW_RUNNER=${O2_SIM_WORKFLOW_RUNNER:-"${O2DPG_ROOT}/MC/bin/o2_dpg_workflow_runner.py"} +$O2_SIM_WORKFLOW_RUNNER -f workflow.json -tt aod --cpu-limit $NWORKERS diff --git a/MC/run/PWGLF/run_HypertritonInjected.sh b/MC/run/PWGLF/run_HypertritonInjected.sh index 145f60615..27ed61b17 100644 --- a/MC/run/PWGLF/run_HypertritonInjected.sh +++ b/MC/run/PWGLF/run_HypertritonInjected.sh @@ -8,8 +8,6 @@ [ ! "${O2DPG_ROOT}" ] && echo "Error: This needs O2DPG loaded" && exit 1 [ ! "${O2_ROOT}" ] && echo "Error: This needs O2 loaded" && exit 1 -# ----------- LOAD UTILITY FUNCTIONS -------------------------- -. ${O2_ROOT}/share/scripts/jobutils.sh # ----------- START ACTUAL JOB ----------------------------- @@ -25,7 +23,7 @@ ENERGY=${ENERGY:-5520} [[ ${SPLITID} != "" ]] && SEED="-seed ${SPLITID}" || SEED="" # create workflow -${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM ${ENERGY} -col ${SYSTEM} -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -interactionRate ${INTRATE} -confKey "Diamond.width[0]=0.1;Diamond.width[1]=0.1;Diamond.width[2]=6." -e ${SIMENGINE} ${SEED} -mod "--skipModules ZDC" \ +${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM ${ENERGY} -col ${SYSTEM} -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -interactionRate ${INTRATE} -confKey "Diamond.width[0]=0.1;Diamond.width[1]=0.1;Diamond.width[2]=6." -e ${SIMENGINE} ${SEED} \ -ini ${O2DPG_ROOT}/MC/config/PWGLF/ini/GeneratorLFHypertriton${SYSTEM}.ini # run workflow diff --git a/MC/run/PWGLF/run_HypertritonInjectedGap.sh b/MC/run/PWGLF/run_HypertritonInjectedGap.sh index 6be9e7ea1..2802e2186 100644 --- a/MC/run/PWGLF/run_HypertritonInjectedGap.sh +++ b/MC/run/PWGLF/run_HypertritonInjectedGap.sh @@ -8,8 +8,6 @@ [ ! "${O2DPG_ROOT}" ] && echo "Error: This needs O2DPG loaded" && exit 1 [ ! "${O2_ROOT}" ] && echo "Error: This needs O2 loaded" && exit 1 -# ----------- LOAD UTILITY FUNCTIONS -------------------------- -. ${O2_ROOT}/share/scripts/jobutils.sh # ----------- START ACTUAL JOB ----------------------------- @@ -25,7 +23,7 @@ ENERGY=${ENERGY:-13600} [[ ${SPLITID} != "" ]] && SEED="-seed ${SPLITID}" || SEED="" # create workflow -${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM ${ENERGY} -col ${SYSTEM} -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -interactionRate ${INTRATE} -confKey "Diamond.width[0]=0.1;Diamond.width[1]=0.1;Diamond.width[2]=6." -e ${SIMENGINE} ${SEED} -mod "--skipModules ZDC" \ +${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM ${ENERGY} -col ${SYSTEM} -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -interactionRate ${INTRATE} -confKey "Diamond.width[0]=0.1;Diamond.width[1]=0.1;Diamond.width[2]=6." -e ${SIMENGINE} ${SEED} \ -ini ${O2DPG_ROOT}/MC/config/PWGLF/ini/GeneratorLFHypertriton${SYSTEM}Gap.ini # run workflow diff --git a/MC/run/PWGLF/run_NucleiFwdInjectedGap.sh b/MC/run/PWGLF/run_NucleiFwdInjectedGap.sh index e83d1aed2..faef33201 100755 --- a/MC/run/PWGLF/run_NucleiFwdInjectedGap.sh +++ b/MC/run/PWGLF/run_NucleiFwdInjectedGap.sh @@ -8,8 +8,6 @@ [ ! "${O2DPG_ROOT}" ] && echo "Error: This needs O2DPG loaded" && exit 1 [ ! "${O2_ROOT}" ] && echo "Error: This needs O2 loaded" && exit 1 -# ----------- LOAD UTILITY FUNCTIONS -------------------------- -. ${O2_ROOT}/share/scripts/jobutils.sh # ----------- START ACTUAL JOB ----------------------------- @@ -25,7 +23,7 @@ ENERGY=${ENERGY:-13600} [[ ${SPLITID} != "" ]] && SEED="-seed ${SPLITID}" || SEED="" # create workflow -${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM ${ENERGY} -col ${SYSTEM} -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -interactionRate ${INTRATE} -confKey "Diamond.width[0]=0.1;Diamond.width[1]=0.1;Diamond.width[2]=6." -e ${SIMENGINE} ${SEED} -mod "--skipModules ZDC" \ +${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM ${ENERGY} -col ${SYSTEM} -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -interactionRate ${INTRATE} -confKey "Diamond.width[0]=0.1;Diamond.width[1]=0.1;Diamond.width[2]=6." -e ${SIMENGINE} ${SEED} \ -ini ${O2DPG_ROOT}/MC/config/PWGLF/ini/GeneratorLFNucleiFwd${SYSTEM}Gap.ini # run workflow diff --git a/MC/run/PWGLF/run_OmegaInjected.sh b/MC/run/PWGLF/run_OmegaInjected.sh index 004e5dbc3..7c97201dc 100755 --- a/MC/run/PWGLF/run_OmegaInjected.sh +++ b/MC/run/PWGLF/run_OmegaInjected.sh @@ -8,8 +8,6 @@ [ ! "${O2DPG_ROOT}" ] && echo "Error: This needs O2DPG loaded" && exit 1 [ ! "${O2_ROOT}" ] && echo "Error: This needs O2 loaded" && exit 1 -# ----------- LOAD UTILITY FUNCTIONS -------------------------- -. ${O2_ROOT}/share/scripts/jobutils.sh # ----------- START ACTUAL JOB ----------------------------- @@ -27,7 +25,7 @@ SYSTEM=${SYSTEM:-pp} ENERGY=${ENERGY:-13600} [[ ${SPLITID} != "" ]] && SEED="-seed ${SPLITID}" || SEED="" -${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -e ${SIMENGINE} ${SEED} -eCM 13600 -col pp -colBkg pp -gen external -genBkg pythia8 -procBkg inel -j ${NWORKERS} -ns ${NSIGEVENTS} -nb ${NBKGEVENTS} -tf ${NTIMEFRAMES} -interactionRate 500000 -confKey "Diamond.width[2]=6." -mod "--skipModules ZDC" \ +${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -e ${SIMENGINE} ${SEED} -eCM 13600 -col pp -colBkg pp -gen external -genBkg pythia8 -procBkg inel -j ${NWORKERS} -ns ${NSIGEVENTS} -nb ${NBKGEVENTS} -tf ${NTIMEFRAMES} -interactionRate 500000 -confKey "Diamond.width[2]=6." \ --embedding -ini $O2DPG_ROOT/MC/config/PWGLF/ini/GeneratorLFOmegaEmb.ini diff --git a/MC/run/PWGLF/run_SigmaProtonTriggered.sh b/MC/run/PWGLF/run_SigmaProtonTriggered.sh new file mode 100644 index 000000000..6d3d01f50 --- /dev/null +++ b/MC/run/PWGLF/run_SigmaProtonTriggered.sh @@ -0,0 +1,36 @@ +#!/bin/bash + +# make sure O2DPG + O2 is loaded +[ ! "${O2DPG_ROOT}" ] && echo "Error: This needs O2DPG loaded" && exit 1 +[ ! "${O2_ROOT}" ] && echo "Error: This needs O2 loaded" && exit 1 + +# ----------- CONFIGURE -------------------------- +export IGNORE_VALIDITYCHECK_OF_CCDB_LOCALCACHE=1 +#export ALICEO2_CCDB_LOCALCACHE=.ccdb + + +# ----------- START ACTUAL JOB ----------------------------- + +NWORKERS=${NWORKERS:-8} +SIMENGINE=${SIMENGINE:-TGeant4} +NSIGEVENTS=${NSIGEVENTS:-100} +NTIMEFRAMES=${NTIMEFRAMES:-1} +INTRATE=${INTRATE:-500000} +SYSTEM=${SYSTEM:-pp} +ENERGY=${ENERGY:-13600} +CFGINIFILE=${CFGINIFILE:-"${O2DPG_ROOT}/MC/config/PWGLF/ini/GeneratorSigmaProtonTriggered.ini"} +SEED="-seed 1995" + +# create workflow +O2_SIM_WORKFLOW=${O2_SIM_WORKFLOW:-"${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py"} +$O2_SIM_WORKFLOW -eCM ${ENERGY} -col ${SYSTEM} -gen external \ + -j ${NWORKERS} \ + -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -interactionRate ${INTRATE} \ + -confKey "Diamond.width[0]=0.1;Diamond.width[1]=0.1;Diamond.width[2]=6.;" \ + ${SEED} \ + -e ${SIMENGINE} \ + -ini $CFGINIFILE + +# run workflow +O2_SIM_WORKFLOW_RUNNER=${O2_SIM_WORKFLOW_RUNNER:-"${O2DPG_ROOT}/MC/bin/o2_dpg_workflow_runner.py"} +$O2_SIM_WORKFLOW_RUNNER -f workflow.json -tt aod --cpu-limit $NWORKERS diff --git a/MC/run/PWGLF/run_StrangenessInjected.sh b/MC/run/PWGLF/run_StrangenessInjected.sh index 9c07c9271..afdc3c6af 100755 --- a/MC/run/PWGLF/run_StrangenessInjected.sh +++ b/MC/run/PWGLF/run_StrangenessInjected.sh @@ -12,8 +12,6 @@ export IGNORE_VALIDITYCHECK_OF_CCDB_LOCALCACHE=1 #export ALICEO2_CCDB_LOCALCACHE=.ccdb -# ----------- LOAD UTILITY FUNCTIONS -------------------------- -. ${O2_ROOT}/share/scripts/jobutils.sh # ----------- START ACTUAL JOB ----------------------------- diff --git a/MC/run/PWGLF/run_StrangenessInjected_PbPb.sh b/MC/run/PWGLF/run_StrangenessInjected_PbPb.sh new file mode 100755 index 000000000..b850cf662 --- /dev/null +++ b/MC/run/PWGLF/run_StrangenessInjected_PbPb.sh @@ -0,0 +1,42 @@ +#!/bin/bash + +# +# A example workflow MC->RECO->AOD for a simple pp min bias +# production, targetting test beam conditions. + +# make sure O2DPG + O2 is loaded +[ ! "${O2DPG_ROOT}" ] && echo "Error: This needs O2DPG loaded" && exit 1 +[ ! "${O2_ROOT}" ] && echo "Error: This needs O2 loaded" && exit 1 + +# ----------- CONFIGURE -------------------------- +export IGNORE_VALIDITYCHECK_OF_CCDB_LOCALCACHE=1 +#export ALICEO2_CCDB_LOCALCACHE=.ccdb + + +# ----------- START ACTUAL JOB ----------------------------- + +NWORKERS=${NWORKERS:-8} +SIMENGINE=${SIMENGINE:-TGeant4} +NSIGEVENTS=${NSIGEVENTS:-1} +NTIMEFRAMES=${NTIMEFRAMES:-1} +INTRATE=${INTRATE:-50000} +SYSTEM=${SYSTEM:-PbPb} +ENERGY=${ENERGY:-5360} +CFGINIFILE=${CFGINIFILE:-"${O2DPG_ROOT}/MC/config/PWGLF/ini/GeneratorLF_Strangeness_PbPb5360_injection.ini"} +[[ ${SPLITID} != "" ]] && SEED="-seed ${SPLITID}" || SEED="" + +echo "NWORKERS = $NWORKERS" + +# create workflow +O2_SIM_WORKFLOW=${O2_SIM_WORKFLOW:-"${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py"} +$O2_SIM_WORKFLOW -eCM ${ENERGY} -col ${SYSTEM} -gen external \ + -j ${NWORKERS} \ + -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -interactionRate ${INTRATE} \ + -confKey "Diamond.width[2]=6." \ + ${SEED} \ + -e ${SIMENGINE} \ + -ini $CFGINIFILE + +# run workflow +O2_SIM_WORKFLOW_RUNNER=${O2_SIM_WORKFLOW_RUNNER:-"${O2DPG_ROOT}/MC/bin/o2_dpg_workflow_runner.py"} +$O2_SIM_WORKFLOW_RUNNER -f workflow.json -tt aod --cpu-limit $NWORKERS diff --git a/MC/run/PWGLF/run_StrangenessTriggered.sh b/MC/run/PWGLF/run_StrangenessTriggered.sh index 412edf995..83b8e98e8 100755 --- a/MC/run/PWGLF/run_StrangenessTriggered.sh +++ b/MC/run/PWGLF/run_StrangenessTriggered.sh @@ -12,8 +12,6 @@ export IGNORE_VALIDITYCHECK_OF_CCDB_LOCALCACHE=1 #export ALICEO2_CCDB_LOCALCACHE=.ccdb -# ----------- LOAD UTILITY FUNCTIONS -------------------------- -. ${O2_ROOT}/share/scripts/jobutils.sh # ----------- START ACTUAL JOB ----------------------------- diff --git a/MC/run/PWGLF/run_StrangenessTriggered_pp900gev.sh b/MC/run/PWGLF/run_StrangenessTriggered_pp900gev.sh index dc0262c0e..09e93cde9 100755 --- a/MC/run/PWGLF/run_StrangenessTriggered_pp900gev.sh +++ b/MC/run/PWGLF/run_StrangenessTriggered_pp900gev.sh @@ -12,8 +12,6 @@ export IGNORE_VALIDITYCHECK_OF_CCDB_LOCALCACHE=1 #export ALICEO2_CCDB_LOCALCACHE=.ccdb -# ----------- LOAD UTILITY FUNCTIONS -------------------------- -. ${O2_ROOT}/share/scripts/jobutils.sh # ----------- START ACTUAL JOB ----------------------------- diff --git a/MC/run/PWGLF/run_SyntheticFlowXi_PbPb.sh b/MC/run/PWGLF/run_SyntheticFlowXi_PbPb.sh new file mode 100755 index 000000000..aa5526b4f --- /dev/null +++ b/MC/run/PWGLF/run_SyntheticFlowXi_PbPb.sh @@ -0,0 +1,42 @@ +#!/bin/bash + +# +# A example workflow MC->RECO->AOD for a simple pp min bias +# production, targetting test beam conditions. + +# make sure O2DPG + O2 is loaded +[ ! "${O2DPG_ROOT}" ] && echo "Error: This needs O2DPG loaded" && exit 1 +[ ! "${O2_ROOT}" ] && echo "Error: This needs O2 loaded" && exit 1 + +# ----------- CONFIGURE -------------------------- +export IGNORE_VALIDITYCHECK_OF_CCDB_LOCALCACHE=1 +#export ALICEO2_CCDB_LOCALCACHE=.ccdb + + +# ----------- START ACTUAL JOB ----------------------------- + +NWORKERS=${NWORKERS:-8} +SIMENGINE=${SIMENGINE:-TGeant4} +NSIGEVENTS=${NSIGEVENTS:-1} +NTIMEFRAMES=${NTIMEFRAMES:-1} +INTRATE=${INTRATE:-50000} +SYSTEM=${SYSTEM:-PbPb} +ENERGY=${ENERGY:-5360} +CFGINIFILE=${CFGINIFILE:-"${O2DPG_ROOT}/MC/config/PWGLF/ini/GeneratorLF_SyntheFlowXi.ini"} +[[ ${SPLITID} != "" ]] && SEED="-seed ${SPLITID}" || SEED="" + +echo "NWORKERS = $NWORKERS" + +# create workflow +O2_SIM_WORKFLOW=${O2_SIM_WORKFLOW:-"${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py"} +$O2_SIM_WORKFLOW -eCM ${ENERGY} -col ${SYSTEM} -gen external \ + -j ${NWORKERS} \ + -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -interactionRate ${INTRATE} \ + -confKey "Diamond.width[2]=6." \ + ${SEED} \ + -e ${SIMENGINE} \ + -ini $CFGINIFILE + +# run workflow +O2_SIM_WORKFLOW_RUNNER=${O2_SIM_WORKFLOW_RUNNER:-"${O2DPG_ROOT}/MC/bin/o2_dpg_workflow_runner.py"} +$O2_SIM_WORKFLOW_RUNNER -f workflow.json -tt aod --cpu-limit $NWORKERS diff --git a/MC/run/PWGLF/run_SyntheticFlow_OO.sh b/MC/run/PWGLF/run_SyntheticFlow_OO.sh new file mode 100644 index 000000000..4b6c2fef2 --- /dev/null +++ b/MC/run/PWGLF/run_SyntheticFlow_OO.sh @@ -0,0 +1,42 @@ +#!/bin/bash + +# +# A example workflow MC->RECO->AOD for a simple pp min bias +# production, targetting test beam conditions. + +# make sure O2DPG + O2 is loaded +[ ! "${O2DPG_ROOT}" ] && echo "Error: This needs O2DPG loaded" && exit 1 +[ ! "${O2_ROOT}" ] && echo "Error: This needs O2 loaded" && exit 1 + +# ----------- CONFIGURE -------------------------- +export IGNORE_VALIDITYCHECK_OF_CCDB_LOCALCACHE=1 +#export ALICEO2_CCDB_LOCALCACHE=.ccdb + + +# ----------- START ACTUAL JOB ----------------------------- + +NWORKERS=${NWORKERS:-8} +SIMENGINE=${SIMENGINE:-TGeant4} +NSIGEVENTS=${NSIGEVENTS:-1} +NTIMEFRAMES=${NTIMEFRAMES:-1} +INTRATE=${INTRATE:-50000} +SYSTEM=${SYSTEM:-OO} +ENERGY=${ENERGY:-5360} +CFGINIFILE=${CFGINIFILE:-"${O2DPG_ROOT}/MC/config/PWGLF/ini/GeneratorLF_SyntheFlowOO.ini"} +[[ ${SPLITID} != "" ]] && SEED="-seed ${SPLITID}" || SEED="" + +echo "NWORKERS = $NWORKERS" + +# create workflow +O2_SIM_WORKFLOW=${O2_SIM_WORKFLOW:-"${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py"} +$O2_SIM_WORKFLOW -eCM ${ENERGY} -col ${SYSTEM} -gen external \ + -j ${NWORKERS} \ + -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -interactionRate ${INTRATE} \ + -confKey "Diamond.width[2]=6." \ + ${SEED} \ + -e ${SIMENGINE} \ + -ini $CFGINIFILE + +# run workflow +O2_SIM_WORKFLOW_RUNNER=${O2_SIM_WORKFLOW_RUNNER:-"${O2DPG_ROOT}/MC/bin/o2_dpg_workflow_runner.py"} +$O2_SIM_WORKFLOW_RUNNER -f workflow.json -tt aod --cpu-limit $NWORKERS diff --git a/MC/run/PWGLF/run_SyntheticFlow_PbPb.sh b/MC/run/PWGLF/run_SyntheticFlow_PbPb.sh new file mode 100755 index 000000000..f6907eb42 --- /dev/null +++ b/MC/run/PWGLF/run_SyntheticFlow_PbPb.sh @@ -0,0 +1,42 @@ +#!/bin/bash + +# +# A example workflow MC->RECO->AOD for a simple pp min bias +# production, targetting test beam conditions. + +# make sure O2DPG + O2 is loaded +[ ! "${O2DPG_ROOT}" ] && echo "Error: This needs O2DPG loaded" && exit 1 +[ ! "${O2_ROOT}" ] && echo "Error: This needs O2 loaded" && exit 1 + +# ----------- CONFIGURE -------------------------- +export IGNORE_VALIDITYCHECK_OF_CCDB_LOCALCACHE=1 +#export ALICEO2_CCDB_LOCALCACHE=.ccdb + + +# ----------- START ACTUAL JOB ----------------------------- + +NWORKERS=${NWORKERS:-8} +SIMENGINE=${SIMENGINE:-TGeant4} +NSIGEVENTS=${NSIGEVENTS:-1} +NTIMEFRAMES=${NTIMEFRAMES:-1} +INTRATE=${INTRATE:-50000} +SYSTEM=${SYSTEM:-PbPb} +ENERGY=${ENERGY:-5360} +CFGINIFILE=${CFGINIFILE:-"${O2DPG_ROOT}/MC/config/PWGLF/ini/GeneratorLF_SyntheFlow.ini"} +[[ ${SPLITID} != "" ]] && SEED="-seed ${SPLITID}" || SEED="" + +echo "NWORKERS = $NWORKERS" + +# create workflow +O2_SIM_WORKFLOW=${O2_SIM_WORKFLOW:-"${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py"} +$O2_SIM_WORKFLOW -eCM ${ENERGY} -col ${SYSTEM} -gen external \ + -j ${NWORKERS} \ + -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -interactionRate ${INTRATE} \ + -confKey "Diamond.width[2]=6." \ + ${SEED} \ + -e ${SIMENGINE} \ + -ini $CFGINIFILE + +# run workflow +O2_SIM_WORKFLOW_RUNNER=${O2_SIM_WORKFLOW_RUNNER:-"${O2DPG_ROOT}/MC/bin/o2_dpg_workflow_runner.py"} +$O2_SIM_WORKFLOW_RUNNER -f workflow.json -tt aod --cpu-limit $NWORKERS diff --git a/MC/run/PWGLF/run_XSectionVariation.sh b/MC/run/PWGLF/run_XSectionVariation.sh new file mode 100644 index 000000000..96fb19571 --- /dev/null +++ b/MC/run/PWGLF/run_XSectionVariation.sh @@ -0,0 +1,31 @@ +#!/bin/bash + +# +# A example workflow MC->RECO->AOD for a simple pp min bias +# production, targetting test beam conditions. + +# make sure O2DPG + O2 is loaded +[ ! "${O2DPG_ROOT}" ] && echo "Error: This needs O2DPG loaded" && exit 1 +[ ! "${O2_ROOT}" ] && echo "Error: This needs O2 loaded" && exit 1 + + +# ----------- START ACTUAL JOB ----------------------------- + +NWORKERS=${NWORKERS:-8} +MODULES="--skipModules ZDC" +SIMENGINE=${SIMENGINE:-TGeant3} +NSIGEVENTS=${NSIGEVENTS:-1} +NBKGEVENTS=${NBKGEVENTS:-1} +NTIMEFRAMES=${NTIMEFRAMES:-1} +INTRATE=${INTRATE:-500000} +SYSTEM=${SYSTEM:-pp} +ENERGY=${ENERGY:-13600} +[[ ${SPLITID} != "" ]] && SEED="-seed ${SPLITID}" || SEED="" + +# create workflow +${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM ${ENERGY} -col ${SYSTEM} -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -interactionRate ${INTRATE} -confKey "Diamond.width[0]=0.1;Diamond.width[1]=0.1;Diamond.width[2]=6." -e ${SIMENGINE} ${SEED} \ + -ini ${O2DPG_ROOT}/MC/config/PWGLF/ini/GeneratorLFHyperNuclei${SYSTEM}Gap.ini + +# run workflow +# allow increased timeframe parallelism with --cpu-limit 32 +${O2DPG_ROOT}/MC/bin/o2_dpg_workflow_runner.py -f workflow.json -tt aod --cpu-limit 32 diff --git a/MC/run/PWGLF/run_coalescence_deuteron_pp_1360TeV.sh b/MC/run/PWGLF/run_coalescence_deuteron_pp_1360TeV.sh new file mode 100644 index 000000000..a12185824 --- /dev/null +++ b/MC/run/PWGLF/run_coalescence_deuteron_pp_1360TeV.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +# +# A example workflow MC->RECO->AOD for a simple pp min bias +# production, with the LF coalescence generator (GeneratorLF_Coalescence_pp1360TeV.ini) + +# make sure O2DPG + O2 is loaded +[ ! "${O2DPG_ROOT}" ] && echo "Error: This needs O2DPG loaded" && exit 1 +[ ! "${O2_ROOT}" ] && echo "Error: This needs O2 loaded" && exit 1 + + +# ----------- START ACTUAL JOB ----------------------------- + +NWORKERS=${NWORKERS:-8} +MODULES="--skipModules ZDC" +SIMENGINE=${SIMENGINE:-TGeant4} +NSIGEVENTS=${NSIGEVENTS:-1} +NTIMEFRAMES=${NTIMEFRAMES:-1} +INTRATE=${INTRATE:-500000} +SYSTEM=${SYSTEM:-pp} +ENERGY=${ENERGY:-13600} +[[ ${SPLITID} != "" ]] && SEED="-seed ${SPLITID}" || SEED="" + +# create workflow +${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM ${ENERGY} -col ${SYSTEM} -gen external -j ${NWORKERS} -ns ${NSIGEVENTS} -tf ${NTIMEFRAMES} -interactionRate ${INTRATE} -confKey "Diamond.width[0]=0.005;Diamond.width[1]=0.005;Diamond.width[2]=6." -e ${SIMENGINE} ${SEED} \ + -ini $O2DPG_ROOT/MC/config/PWGLF/ini/GeneratorLF_Coalescence_pp1360TeV.ini + +# run workflow +${O2DPG_ROOT}/MC/bin/o2_dpg_workflow_runner.py -f workflow.json -tt aod --cpu-limit 32 diff --git a/MC/run/PWGUD/runDiffEvents.sh b/MC/run/PWGUD/runDiffEvents.sh index 9d4bd065d..632742730 100755 --- a/MC/run/PWGUD/runDiffEvents.sh +++ b/MC/run/PWGUD/runDiffEvents.sh @@ -26,7 +26,6 @@ DVX=0.01 DVY=0.01 DVZ=6.00 -MODULES="--skipModules ZDC" # create workflow ${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py \ @@ -37,7 +36,6 @@ ${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py \ -ns ${NSIGPTF} \ -tf ${NTIMEFRAMES} \ -e TGeant4 \ - -mod "${MODULES}" \ -interactionRate ${SIGINTRATE} \ -confKey "HepMC.fileName="${FHEPMC}";HepMC.version=3;Diamond.width[0]="${DVX}";Diamond.width[1]="${DVY}";Diamond.width[2]="${DVZ}"" diff --git a/MC/run/PWGUD/runGraniittiANCHOR.sh b/MC/run/PWGUD/runGraniittiANCHOR.sh new file mode 100755 index 000000000..d88f5b206 --- /dev/null +++ b/MC/run/PWGUD/runGraniittiANCHOR.sh @@ -0,0 +1,32 @@ +# Run as: ${O2DPG_ROOT}/GRID/utils/grid_submit.sh --script ./runGraniittiANCHOR.sh --jobname SLtest --outputspec "*.log@disk=1","*.root@disk=2" --packagespec "VO_ALICE@O2sim::v20240626-1" --wait --fetch-output --asuser pbuhler --local + +export ALIEN_JDL_LPMANCHORPASSNAME=apass4 +export ALIEN_JDL_MCANCHOR=apass4 +export ALIEN_JDL_COLLISIONSYSTEM=pp +export ALIEN_JDL_CPULIMIT=8 +export ALIEN_JDL_LPMPASSNAME=apass4 +export ALIEN_JDL_LPMRUNNUMBER=535084 +export ALIEN_JDL_LPMPRODUCTIONTYPE=MC +export ALIEN_JDL_LPMINTERACTIONTYPE=PbPb +export ALIEN_JDL_LPMPRODUCTIONTAG=MyPass2Test +export ALIEN_JDL_LPMANCHORRUN=535084 +export ALIEN_JDL_LPMANCHORPRODUCTION=LHC23f +export ALIEN_JDL_LPMANCHORYEAR=2023 + +export NTIMEFRAMES=2 +export NSIGEVENTS=100 +export NBKGEVENTS=1 +export SPLITID=20 +export PRODSPLIT=100 +export CYCLE=30 +export ALIEN_PROC_ID=2963436952 + +# create generator config file +${O2DPG_ROOT}/MC/config/PWGUD/ini/makeGraniittiConfig.py --process kConRes_pipi --eCM 13600 --nEvents 300 --rapidity cent_eta + +# set sim options +export ALIEN_JDL_ANCHOR_SIM_OPTIONS="-gen external -ini ${PWD}/GenGraniitti.ini" +#export ALIEN_JDL_ANCHOR_SIM_OPTIONS="-gen external -ini ${PWD}/GenGraniitti.ini --embedding -nb ${NBKGEVENTS} -colBkg PbPb -genBkg pythia8 -procBkg heavy_ion" + +# run anchored simulations +${O2DPG_ROOT}/MC/run/ANCHOR/anchorMC.sh diff --git a/MC/run/PWGUD/runPythiaAndDiffEvents.sh b/MC/run/PWGUD/runPythiaAndDiffEvents.sh index 57928cee7..6484cbb8c 100755 --- a/MC/run/PWGUD/runPythiaAndDiffEvents.sh +++ b/MC/run/PWGUD/runPythiaAndDiffEvents.sh @@ -27,8 +27,6 @@ DVX=0.01 DVY=0.01 DVZ=6.00 -MODULES="--skipModules ZDC" - # create workflow SIGINTRATE=`echo "${BKGINTRATE}*${NSIGPTF}/${NBKGPTF}" | bc` NBKG=`echo "${NBKGPTF}*${NTIMEFRAMES}" | bc` @@ -38,7 +36,6 @@ ${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py \ -eCM ${ECM} \ -j ${NWORKERS} \ -e TGeant4 \ - -mod "${MODULES}" \ -tf ${NTIMEFRAMES} \ -interactionRate ${BKGINTRATE} \ -gen hepmc \ diff --git a/MC/run/PWGUD/runSTARlightANCHOR.sh b/MC/run/PWGUD/runSTARlightANCHOR.sh new file mode 100755 index 000000000..43a7b32b8 --- /dev/null +++ b/MC/run/PWGUD/runSTARlightANCHOR.sh @@ -0,0 +1,27 @@ +# Run as: ${O2DPG_ROOT}/GRID/utils/grid_submit.sh --script ./runSTARlightANCHOR.sh --jobname SLtest --outputspec "*.log@disk=1","*.root@disk=2" --packagespec "VO_ALICE@O2sim::v20240626-1" --wait --fetch-output --asuser mbroz --local + +export ALIEN_JDL_LPMANCHORPASSNAME=apass2 +export ALIEN_JDL_MCANCHOR=apass2 +export ALIEN_JDL_COLLISIONSYSTEM=PbPb +export ALIEN_JDL_CPULIMIT=8 +export ALIEN_JDL_LPMPASSNAME=apass2 +export ALIEN_JDL_LPMRUNNUMBER=544389 +export ALIEN_JDL_LPMPRODUCTIONTYPE=MC +export ALIEN_JDL_LPMINTERACTIONTYPE=PbPb +export ALIEN_JDL_LPMPRODUCTIONTAG=MyPass2Test +export ALIEN_JDL_LPMANCHORRUN=544389 +export ALIEN_JDL_LPMANCHORPRODUCTION=LHC23zzi +export ALIEN_JDL_LPMANCHORYEAR=2023 + +export NTIMEFRAMES=2 +export NSIGEVENTS=5 +export NBKGEVENTS=1 +export SPLITID=2 +export PRODSPLIT=100 +export CYCLE=0 +export ALIEN_PROC_ID=2963436952 + +export ALIEN_JDL_ANCHOR_SIM_OPTIONS="-gen external -ini ${PWD}/GenStarlight.ini --embedding -nb ${NBKGEVENTS} -colBkg PbPb -genBkg pythia8 -procBkg heavy_ion" + +${O2DPG_ROOT}/MC/config/PWGUD/ini/makeStarlightConfig.py --process kCohPsi2sToMuPi --collType PbPb --eCM 5360 --rapidity cent_eta +${O2DPG_ROOT}/MC/run/ANCHOR/anchorMC.sh diff --git a/MC/run/PWGUD/runUpcgenANCHOR.sh b/MC/run/PWGUD/runUpcgenANCHOR.sh new file mode 100755 index 000000000..6ea86f55e --- /dev/null +++ b/MC/run/PWGUD/runUpcgenANCHOR.sh @@ -0,0 +1,30 @@ +# Run as: ${O2DPG_ROOT}/GRID/utils/grid_submit.sh --script ./runUpcgenANCHOR.sh --jobname SLtest --outputspec "*.log@disk=1","*.root@disk=2" --packagespec "VO_ALICE@O2sim::v20240626-1" --wait --fetch-output --asuser pbuhler --local + +export ALIEN_JDL_LPMANCHORPASSNAME=apass2 +export ALIEN_JDL_MCANCHOR=apass2 +export ALIEN_JDL_COLLISIONSYSTEM=PbPb +export ALIEN_JDL_CPULIMIT=8 +export ALIEN_JDL_LPMPASSNAME=apass2 +export ALIEN_JDL_LPMRUNNUMBER=544389 +export ALIEN_JDL_LPMPRODUCTIONTYPE=MC +export ALIEN_JDL_LPMINTERACTIONTYPE=PbPb +export ALIEN_JDL_LPMPRODUCTIONTAG=MyPass2Test +export ALIEN_JDL_LPMANCHORRUN=544389 +export ALIEN_JDL_LPMANCHORPRODUCTION=LHC23zzi +export ALIEN_JDL_LPMANCHORYEAR=2023 + +export NTIMEFRAMES=2 +export NSIGEVENTS=5 +export NBKGEVENTS=1 +export SPLITID=2 +export PRODSPLIT=100 +export CYCLE=0 +export ALIEN_PROC_ID=2963436952 + + +#export ALIEN_JDL_ANCHOR_SIM_OPTIONS="-gen external -ini ${PWD}/GenUpcgen.ini --embedding -nb ${NBKGEVENTS} -colBkg PbPb -genBkg pythia8 -procBkg heavy_ion" + +${O2DPG_ROOT}/MC/config/PWGUD/ini/makeUpcgenConfig.py --process kDiTau --collType PbPb --eCM 5360 --rapidity cent_eta +export ALIEN_JDL_ANCHOR_SIM_OPTIONS="-gen external -ini ${PWD}/GenUpcgen.ini" + +${O2DPG_ROOT}/MC/run/ANCHOR/anchorMC.sh diff --git a/MC/run/common/detector_acceptance.C b/MC/run/common/detector_acceptance.C index 5cdd0b65a..a8f4b381b 100644 --- a/MC/run/common/detector_acceptance.C +++ b/MC/run/common/detector_acceptance.C @@ -18,16 +18,15 @@ enum EDetectorAcceptance_t { kAcceptance_PHSDMC, kAcceptance_EMC, kAcceptance_DMC, - kAcceptance_PHS + kAcceptance_PHS, + kAcceptance_FOC }; /// Open selection, minimum particle eta cut. // bool full(Float_t phi, Float_t eta) { - if (phi > 0. && - phi < 360. * TMath::DegToRad() && - TMath::Abs(eta) < 1.5) + if (TMath::Abs(eta) < 1.5) return true; else return false; @@ -122,6 +121,12 @@ bool barrel_calorimeters(Float_t phi, Float_t eta) return false; } + +/// Check if particle is in FOCAL +bool focal(Float_t phi, Float_t eta){ + return (eta > 3.4 && eta < 5.8); +} + /// \return True if particle in desired acceptance. /// /// \param acceptance : Detector acceptance to be checked. @@ -152,6 +157,8 @@ bool detector_acceptance(Int_t acceptance, Float_t phi, Float_t eta) case kAcceptance_EMCPHSDMC: return barrel_calorimeters(phi, eta); break; + case kAcceptance_FOC: + return focal(phi, eta); } return false; diff --git a/MC/run/common/run_PbPb_trigger_multiplicity_stableparticles_inFIT.sh b/MC/run/common/run_PbPb_trigger_multiplicity_stableparticles_inFIT.sh index 3f20e10eb..3d4d56cb5 100644 --- a/MC/run/common/run_PbPb_trigger_multiplicity_stableparticles_inFIT.sh +++ b/MC/run/common/run_PbPb_trigger_multiplicity_stableparticles_inFIT.sh @@ -5,8 +5,6 @@ [ ! "${O2_ROOT}" ] && echo "Error: This needs O2 loaded" && exit 1 -# ----------- LOAD UTILITY FUNCTIONS -------------------------- -. ${O2_ROOT}/share/scripts/jobutils.sh RNDSEED=${RNDSEED:-0} NEVENTS=${NEVENTS:-2} diff --git a/MC/run/examples/O2DPG_pp_minbias.sh b/MC/run/examples/O2DPG_pp_minbias.sh index be23c9d80..9643a2be3 100755 --- a/MC/run/examples/O2DPG_pp_minbias.sh +++ b/MC/run/examples/O2DPG_pp_minbias.sh @@ -9,8 +9,6 @@ [ ! "${O2DPG_ROOT}" ] && echo "Error: This needs O2DPG loaded" && exit 1 [ ! "${O2_ROOT}" ] && echo "Error: This needs O2 loaded" && exit 1 -# ----------- LOAD UTILITY FUNCTIONS -------------------------- -. ${O2_ROOT}/share/scripts/jobutils.sh # ----------- START ACTUAL JOB ----------------------------- @@ -38,7 +36,7 @@ MEMLIMIT=${MEMLIMIT:+--mem-limit ${MEMLIMIT}} CPULIMIT=${CPULIMIT:+--cpu-limit ${CPULIMIT}} # create workflow -${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM 13600 -col pp -gen pythia8 -proc inel -tf ${NTFS} \ +${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM 13600 -seed 12345 -col pp -gen pythia8 -proc inel -tf ${NTFS} \ -ns ${NEVENTS} -e ${SIMENGINE} -run 301000 \ -j ${NWORKERS} -interactionRate ${INTRATE} \ --include-qc --include-analysis @@ -58,7 +56,7 @@ fi RETANA=0 if [ "${DOANALYSIS}" != "" ] && [ "${RETMC}" = "0" ]; then # run test analyses if requested - ${O2DPG_ROOT}/MC/bin/o2_dpg_workflow_runner.py -f workflow.json -tt "Analysis_" ${MEMLIMIT} ${CPULIMIT} + ${O2DPG_ROOT}/MC/bin/o2_dpg_workflow_runner.py -f workflow.json --target-labels Analysis ${MEMLIMIT} ${CPULIMIT} RETANA=${?} fi diff --git a/MC/run/examples/UPC/upc_in_hadronic_pileup.sh b/MC/run/examples/UPC/upc_in_hadronic_pileup.sh new file mode 100755 index 000000000..eb5cf07c8 --- /dev/null +++ b/MC/run/examples/UPC/upc_in_hadronic_pileup.sh @@ -0,0 +1,81 @@ +#!/usr/bin/env bash +#JDL_PACKAGE=VO_ALICE@O2PDPSuite::MC-prod-2026-v13-1 +#JDL_OUTPUT=AO2D.root@disk=2,debug_log_archive.tgz@disk=2 +#JDL_ERROROUTPUT=debug_log_archive.tgz@disk=2 + +# Anchored Pb-Pb MC: STARlight UPC events on top of hadronic (Pythia8 Angantyr) pile-up. +# +# The hadronic Pb-Pb collisions follow the interaction rate of the anchor run. On top of them, +# UPC events are injected as an independent random stream at UPC_RATE, each in its own bunch +# crossing. The interaction-rate dependence is obtained by choosing anchor runs at different rates. +# In the AOD, the UPC collisions have generator ID 51 (mcCollision.getGeneratorId()). +# +# Knobs: +# UPC_PROCESS STARlight process (as in makeStarlightConfig.py) +# UPC_RATE rate of the UPC stream in Hz; 67000 is about one per ITS readout frame +# NBKG distinct Angantyr events per job, reused in turn; a repeated event +# appears milliseconds later, far beyond an ITS readout frame or the TPC drift time +# ANCHOR_RUN anchor run; the interaction rate is taken from its CTP scalers +# NTIMEFRAMES timeframes per job; the background is simulated once per job, +# so more timeframes per job is cheaper than many short jobs +# +# The timeframe length is taken from the anchor run (32 orbits for 2023 Pb-Pb). +# Do not force longer timeframes with ALIEN_JDL_MC_ORBITS_PER_TF: with realistic +# hadronic pile-up, TPC digitisation then exceeds the memory of a GRID slot. +# +# Use MC-prod-2026-v12-1 or later: older tags anchor the first job at the start of the run, +# where the ITS dead map masks all chips and the AOD has no ITS and no collisions. +# +# Submit to the GRID with +# ${O2DPG_ROOT}/GRID/utils/grid_submit.sh --script upc_in_hadronic_pileup.sh --jobname upc --cores 8 --ttl 43200 + +UPC_PROCESS=${UPC_PROCESS:-kCohJpsiToMu} +UPC_RATE=${UPC_RATE:-67000} +NBKG=${NBKG:-30} +ANCHOR_RUN=${ANCHOR_RUN:-544868} + +export O2DPG_MC_CONFIG_ROOT=${O2DPG_MC_CONFIG_ROOT:-${O2DPG_ROOT}} + +# anchoring: Pb-Pb 2023, apass5 +export ALIEN_JDL_LPMANCHORPASSNAME=apass5 +export ALIEN_JDL_MCANCHOR=apass5 +export ALIEN_JDL_LPMANCHORYEAR=2023 +export ALIEN_JDL_LPMINTERACTIONTYPE=PbPb +export ALIEN_JDL_LPMPRODUCTIONTYPE=MC +export ALIEN_JDL_LPMPRODUCTIONTAG=${ALIEN_JDL_LPMPRODUCTIONTAG:-UPCPileupExample} +export ALIEN_JDL_LPMRUNNUMBER=${ANCHOR_RUN} +export ALIEN_JDL_LPMANCHORRUN=${ANCHOR_RUN} +export ALIEN_JDL_LPMANCHORPRODUCTION=LHC23zzh +# reconstruction with the software of the data pass (2-tag setup) +export ALIEN_JDL_O2DPG_ASYNC_RECO_TAG=VO_ALICE@O2PDPSuite::async-async-2023-PbPb-apass5-v8-slc9-alidist-async-2023-PbPb-apass5-v8-1 +export ALIEN_JDL_TPCDEDXCLMASK=7 +export ALIEN_JDL_TPCCLUSTERFILTER=2 +export ALIEN_JDL_TPCCHICUTOPT=0 +export ALIEN_JDL_TPCCLUSEDGEREDEF=0.5 + +export NTIMEFRAMES=${NTIMEFRAMES:-2} +export NSIGEVENTS=10000 # no cap: the timeframe is filled by the interaction rate +export SPLITID=${ALIEN_O2DPG_GRIDSUBMIT_SUBJOBID:-1} +export PRODSPLIT=${ALIEN_O2DPG_GRIDSUBMIT_PRODSPLIT:-1} +export CYCLE=0 +export DISABLE_QC=1 +export PUBLISH_MCPRODINFO=0 # example: do not publish to CCDB + +cat > simconfig.json <<'EOF' +{ + "ConfigParams": { + "TPCEleParam": { + "doSaturationTail" : "1" + } + } +} +EOF + +${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGUD/ini/makeStarlightConfig.py --process ${UPC_PROCESS} --collType PbPb --eCM 5360 --rapidity cent_rap + +export ALIEN_JDL_ANCHOR_SIM_OPTIONS="-gen external -trigger external -ini ../GenStarlight.ini \ + -confKey 'PrimaryGenerator.id=51;SimCutParams.stepTrackRefHook=true' \ + --embedding -genBkg pythia8 -procBkg heavy_ion -nb ${NBKG} --embeddPattern ${UPC_RATE},1000000:1000000 \ + --overwrite-config simconfig.json --no-tpc-digitchunking --tpc-distortion-type 2 --tpc-corrmap-lumi-mode 2" + +${O2DPG_ROOT}/MC/run/ANCHOR/anchorMC.sh diff --git a/MC/run/examples/event_pool.sh b/MC/run/examples/event_pool.sh new file mode 100644 index 000000000..04da7faa7 --- /dev/null +++ b/MC/run/examples/event_pool.sh @@ -0,0 +1,55 @@ +#!/usr/bin/env bash + +# Example on how to produce an event pool and how to feed it +# to the O2DPG simulation workflow + +# make sure O2DPG + O2 is loaded +[ ! "${O2DPG_ROOT}" ] && echo "Error: This needs O2DPG loaded" && exit 1 +[ ! "${O2_ROOT}" ] && echo "Error: This needs O2 loaded" && exit 1 + +# Parse arguments +MAKE=false +INPUT="" + +help() { + echo "Usage: $0 [--make] [-i|--input ]" + echo " --make: Create the event pool" + echo " -i|--input: Input event pool file to be used in the simulation workflow. Alien paths are supported." + echo " A full path must be provided (use of environment variables allowed), otherwise generation will fail." + echo " -h|--help: Display this help message" + exit 0 +} + +while [[ "$#" -gt 0 ]]; do + case $1 in + --make) MAKE=true ;; + -i|--input) INPUT="$2"; shift ;; + -h|--help) help ;; + *) echo "Unknown operation requested: $1"; help ;; + esac + shift +done + +if $MAKE; then + echo "Started generation of event pool" + # Workflow creation. All the parameters are used as examples + # No transport will be executed. The workflow will stop at the event generation and will conclude with the merging of all the + # kinematic root files of the timeframes in a file called evtpool.root in the current working directory + ${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM 14000 -col pp -gen pythia8 -proc cdiff -tf 2 -ns 5000 --make-evtpool -seed 546 -interactionRate 500000 -productionTag "evtpoolcreation" -o evtpool + # Workflow runner + ${O2DPG_ROOT}/MC/bin/o2dpg_workflow_runner.py -f evtpool.json -tt pool +elif [[ -n "$INPUT" ]]; then + echo "Input file provided: $INPUT" + if [[ -f "$INPUT" && -s "$INPUT" ]] || [[ "$INPUT" == alien://* ]]; then + # Workflow creation. Phi Rotation is set manually, while the event randomisation of the pool is set by default + ${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py -eCM 14000 -confKey "GeneratorFromO2Kine.randomphi=true;GeneratorFromO2Kine.fileName=$INPUT" -gen extkinO2 -tf 2 -ns 10 -e TGeant4 -j 4 -interactionRate 500000 -seed 546 -productionTag "evtpooltest" + # Workflow runner. The rerun option is set in case you will run directly the script in the same folder (no need to manually delete files) + ${O2DPG_ROOT}/MC/bin/o2dpg_workflow_runner.py -f workflow.json -tt aod --rerun-from grpcreate + else + echo "Error: File does not exist or is empty: $INPUT" + exit 1 + fi +else + echo "Usage: $0 [--make] [-i|--input ]" + exit 1 +fi \ No newline at end of file diff --git a/MC/utils/AODBcRewriter.C b/MC/utils/AODBcRewriter.C new file mode 100644 index 000000000..4080c9044 --- /dev/null +++ b/MC/utils/AODBcRewriter.C @@ -0,0 +1,2111 @@ +// AODBcRewriter.C +// +// Usage: +// root -l -b -q 'AODBcRewriter.C("AO2D.root","AO2D_rewritten.root")' +// +// ----------------------------------------------------------------------------- +// PURPOSE +// ----------------------------------------------------------------------------- +// After merging two AO2D files the BC table (O2bc_*) can contain: +// (a) Non-monotonic fGlobalBC values — violating a framework requirement. +// (b) Duplicate fGlobalBC values — one logical BC spread across many rows. +// (c) Duplicate MCCollisions — the same MC event repeated because it +// appeared in both source files before merging. +// +// This tool fixes all three problems in one pass per DF_ directory: +// +// Stage 0 — Sort & deduplicate the BC table. Build BC permutation map: +// bcPerm[oldBCrow] = newBCrow. +// +// Stage 1 — Process every table that carries fIndexBCs / fIndexBC. +// Remap the index via bcPerm, sort rows by the new index, and +// record a permutation map for each such table so that tables +// paste-joined to it can follow. +// Special sub-case: O2mccollision_* is deduplicated here — +// rows whose (fIndexBCs, generator-level event ID) key has already +// been seen are dropped, and a FULL permutation map is produced +// (mcCollPerm[oldRow] = newRow, -1 = dropped). +// +// Stage 2 — Process every table that carries fIndexMcCollisions. +// Remap via mcCollPerm, sort, record mcCollXxxPerm if needed. +// +// Paste-join tables — tables that have NO index column but are implicitly +// joined row-for-row with another table (e.g. O2mccollisionlabel +// is paste-joined with O2collision). They must be reordered +// identically to their parent. The known paste-join relationships +// are listed in kPasteJoins below and are applied after the +// relevant stage has established the parent permutation. +// +// Unrelated tables — tables with no dependency on BCs or MCCollisions are +// copied verbatim. +// +// PLAN, THEN WRITE — every stage above only decides row orders and publishes +// the resulting permutations; processDF writes all tables +// afterwards. References are not a DAG: O2fwdtrack points at +// O2mfttrack (reordered in the same stage) and at itself, so a +// stage that wrote as it went could not remap them. It did not, +// and every global muon in every merged MC AO2D between June and +// July 2026 got a foreign MFT and MCH leg (O2-7098). +// +// ONE INDEX REGISTRY — kIndexRefs lists every fIndex* column and the table it +// points at. buildRemaps() turns it into a table's remap set, the +// validator range-checks against it, and any fIndex* column NOT in +// it is reported loudly. Do not reintroduce per-stage lists of +// "indices this stage knows how to remap". +// +// TESTING — MC/utils/tests/run_aodbcrewriter_tests.sh (ROOT only, seconds). +// AODBcRewriterCheckLinks(in, out) is the check that sees a +// mis-remapped index; the output-only checks cannot, because a +// wrong row number is still a valid one. +// +// ----------------------------------------------------------------------------- +// DATA MODEL DEPENDENCY GRAPH (relevant subset) +// ----------------------------------------------------------------------------- +// +// BCs (O2bc_*) [Stage 0] +// │ fIndexBCs +// ├─► Collisions (O2collision_*) [Stage 1] +// │ │ paste-join ► McCollisionLabels (O2mccollisionlabel_*) +// │ │ fIndexCollisions (in tracks etc. — tracked by collPerm) +// │ └─► Tracks (O2track_*, O2trackiu_*, ...) [Stage 1] +// │ paste-join ► McTrackLabels (O2mctracklabel_*) +// │ +// └─► MCCollisions (O2mccollision_*) [Stage 1, deduplicated] +// │ fIndexMcCollisions +// ├─► HepMCXSections (O2hepmcxsection_*) [Stage 2] +// ├─► HepMCPdfInfos (O2hepmcpdfinfo_*) [Stage 2] +// └─► HepMCHeavyIons (O2hepmcheavyion_*) [Stage 2] +// +// All other tables (detector hits, ZDC, FT0, FV0, FDD, …) that carry +// fIndexBCs are handled generically in Stage 1 without special-casing. +// +// ----------------------------------------------------------------------------- + +#ifndef __CLING__ +#include "RVersion.h" +#include "TBranch.h" +#include "TDirectory.h" +#include "TFile.h" +#include "TKey.h" +#include "TLeaf.h" +#include "TMap.h" +#include "TROOT.h" +#include "TString.h" +#include "TTree.h" +#include "TGrid.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +// ============================================================================ +// SECTION 1 — Types and small helpers +// ============================================================================ + +// A permutation map: permMap[oldRow] = newRow, -1 means "row was dropped". +using PermMap = std::vector; + +// Convenience: build an identity permutation of length n. +static PermMap identityPerm(Long64_t n) { + PermMap p(n); + std::iota(p.begin(), p.end(), 0); + return p; +} + +// Names of tables that begin with these prefixes are BC tables or flag tables +// and are handled specially in Stage 0. +static bool isBCTable(const char *name) { + return TString(name).BeginsWith("O2bc"); +} + +static bool isDF(const char *name) { + return TString(name).BeginsWith("DF_"); +} + +// Return the name of the BC index branch if present, else nullptr. +static const char *bcIndexBranch(TTree *t) { + if (!t) return nullptr; + if (t->GetBranch("fIndexBCs")) return "fIndexBCs"; + if (t->GetBranch("fIndexBC")) return "fIndexBC"; + return nullptr; +} + +// Return the name of the MCCollision index branch if present, else nullptr. +static const char *mcCollIndexBranch(TTree *t) { + if (!t) return nullptr; + if (t->GetBranch("fIndexMcCollisions")) return "fIndexMcCollisions"; + return nullptr; +} + +// Return the name of the Collision index branch if present, else nullptr. +static const char *collIndexBranch(TTree *t) { + if (!t) return nullptr; + if (t->GetBranch("fIndexCollisions")) return "fIndexCollisions"; + return nullptr; +} + +// ---------------------------------------------------------------------------- +// Paste-join relationships (authoritative; derived from AnalysisDataModel.h +// comments such as "Table joined to the collision table containing the MC +// index" and from the SOA EXTENDED_TABLE declarations for cov / extra tables). +// +// A paste-joined CHILD has NO row of its own — its row N corresponds to row N +// of its PARENT. If the parent is reordered or has rows dropped, the child +// must follow row-for-row to preserve the 1:1 alignment. Any index columns +// the child carries (e.g. fIndexMcCollisions in O2mccollisionlabel) are then +// remapped *value-wise* via the appropriate parent stage's permutation, but +// rows are NEVER added or dropped on the child's own initiative. +// +// Matching uses TString::BeginsWith on the child name; parent matching uses +// allPerms keys (so versioned names like O2collision_001 resolve via prefix). +// When several parent candidates are listed for the same child, the first one +// found in allPerms wins (this lets us prefer O2track_iu over O2track). +static const std::vector> kPasteJoins = { + // { paste-joined child prefix, parent prefix } + { "O2bcflag", "O2bc" }, // BCFlags joinable with BCs + { "O2mccollisionlabel", "O2collision" }, // McCollisionLabels -> Collisions + { "O2mctracklabel", "O2track_iu" }, // McTrackLabels -> Tracks (prefer _iu) + { "O2mctracklabel", "O2track" }, + { "O2mcfwdtracklabel", "O2fwdtrack" }, // McFwdTrackLabels -> FwdTracks + { "O2mcmfttracklabel", "O2mfttrack" }, // McMFTTrackLabels -> MFTTracks + { "O2mccalolabel", "O2calo" }, // McCaloLabels -> Calos + { "O2trackcov_iu", "O2track_iu" }, // TracksCovIU -> TracksIU (cov) + { "O2trackextra", "O2track_iu" }, // TracksExtra -> TracksIU + { "O2fwdtrackcov", "O2fwdtrack" }, // FwdTracksCov -> FwdTracks + // Note: O2mfttrackcov has its own fIndexMFTTracks column — it is NOT + // paste-joined and must NOT be listed here. +}; + +// True if the given tree name matches any registered paste-join child prefix. +static bool isPasteJoinChild(const std::string &tname) { + for (auto &kv : kPasteJoins) + if (TString(tname.c_str()).BeginsWith(kv.first.c_str())) return true; + return false; +} + +// ---------------------------------------------------------------------------- +// INDEX-REFERENCE REGISTRY — the single source of truth for "which fIndex* +// column points into which table". +// +// This ONE list drives three things: +// +// * the rewriter — buildRemaps() turns it into the complete set of value +// remaps for a table, so a reference can no longer be +// forgotten. Forgetting one is the O2-7098 bug class: +// Stage 1b reordered O2fwdtrack but nobody remapped its +// fIndexMFTTracks / fIndexFwdTracks_MatchMCHTrack, so every +// global muon silently got the wrong MFT and MCH leg. The +// values stayed in range, so the range check below passed. +// * the validator — the generic in-range check. +// * the drift guard — any fIndex* branch NOT listed here is reported, so a +// schema addition breaks the test instead of quietly +// producing mis-linked data. +// +// The value is a list of candidate table-name prefixes; the first one that +// resolves in this DF wins (that is how fIndexTracks* prefers O2track_iu over +// O2track). +// +// Referent resolution (isTableNamed): a tree named K is the table named by +// prefix P when K == P or K == P + "_" (the AO2D schema-version +// suffix). That deliberately separates O2mfttrack_001 from O2mfttrackcov, +// O2bc_001 from O2bcflag, and O2mccollision_001 from O2mccollisionlabel — a +// plain BeginsWith() would confuse them. +static const std::vector>> kIndexRefs = { + { "fIndexBCs", { "O2bc" } }, + { "fIndexBC", { "O2bc" } }, + { "fIndexSliceBCs", { "O2bc" } }, + { "fIndexCollisions", { "O2collision" } }, + { "fIndexCollision", { "O2collision" } }, + { "fIndexMcCollisions", { "O2mccollision" } }, + { "fIndexMcParticles", { "O2mcparticle" } }, + { "fIndexArrayMcParticles", { "O2mcparticle" } }, + // O2mcparticle intra-table links (mother list + [first,last] daughter slice). + { "fIndexArray_Mothers", { "O2mcparticle" } }, + { "fIndexSlice_Daughters", { "O2mcparticle" } }, + { "fIndexTracks", { "O2track_iu", "O2track" } }, + { "fIndexTracks_0", { "O2track_iu", "O2track" } }, + { "fIndexTracks_1", { "O2track_iu", "O2track" } }, + { "fIndexTracks_2", { "O2track_iu", "O2track" } }, + { "fIndexTracks_Pos", { "O2track_iu", "O2track" } }, + { "fIndexTracks_Neg", { "O2track_iu", "O2track" } }, + { "fIndexTracks_Bach", { "O2track_iu", "O2track" } }, + { "fIndexTracks_ITS", { "O2track_iu", "O2track" } }, + { "fIndexMFTTracks", { "O2mfttrack" } }, + { "fIndexFwdTracks", { "O2fwdtrack" } }, + { "fIndexFwdTracks_MatchMCHTrack", { "O2fwdtrack" } }, + { "fIndexV0s", { "O2v0" } }, + { "fIndexCascades", { "O2cascade" } }, + { "fIndexDecay3Bodys", { "O2decay3body" } }, +}; + +// K names the table with prefix P? See the comment on kIndexRefs. +static bool isTableNamed(const std::string &key, const std::string &prefix) { + if (key == prefix) return true; + if (key.size() <= prefix.size() + 1) return false; + if (key.compare(0, prefix.size(), prefix) != 0) return false; + if (key[prefix.size()] != '_') return false; + for (size_t i = prefix.size() + 1; i < key.size(); ++i) + if (!std::isdigit(static_cast(key[i]))) return false; + return true; +} + +// Every fIndex* branch of t that is NOT covered by kIndexRefs. "*_size" count +// branches of VLA index arrays are not references and are excluded. A non-empty +// result means the schema grew a link the tool does not know how to follow. +static std::vector unregisteredIndexBranches(TTree *t) { + std::vector out; + if (!t) return out; + for (auto *obj : *t->GetListOfBranches()) { + std::string bname = static_cast(obj)->GetName(); + if (bname.rfind("fIndex", 0) != 0) continue; + if (bname.size() > 5 && bname.compare(bname.size() - 5, 5, "_size") == 0) continue; + bool known = false; + for (auto &kv : kIndexRefs) if (kv.first == bname) { known = true; break; } + if (!known) out.push_back(bname); + } + return out; +} + +// ============================================================================ +// SECTION 2 — Generic ROOT branch I/O helpers +// ============================================================================ +// +// AO2D branches store plain scalar values (Int_t, ULong64_t, Float_t, …) or +// variable-length arrays (VLAs). We need to read and write them generically +// without knowing the concrete type at compile time. The trick is to allocate +// a raw byte buffer of the right size, set the branch address to it, and use +// the ScalarTag enum to know how to interpret it when we need to (e.g. for +// index remapping). + +enum class ScalarTag { + kInt, kUInt, kShort, kUShort, kLong64, kULong64, + kFloat, kDouble, kChar, kUChar, kBool, kUnknown +}; + +static ScalarTag tagOf(TLeaf *leaf) { + if (!leaf) return ScalarTag::kUnknown; + TString t = leaf->GetTypeName(); + if (t == "Int_t") return ScalarTag::kInt; + if (t == "UInt_t") return ScalarTag::kUInt; + if (t == "Short_t") return ScalarTag::kShort; + if (t == "UShort_t") return ScalarTag::kUShort; + if (t == "Long64_t") return ScalarTag::kLong64; + if (t == "ULong64_t") return ScalarTag::kULong64; + if (t == "Float_t") return ScalarTag::kFloat; + if (t == "Double_t") return ScalarTag::kDouble; + if (t == "Char_t") return ScalarTag::kChar; + if (t == "UChar_t") return ScalarTag::kUChar; + if (t == "Bool_t") return ScalarTag::kBool; + return ScalarTag::kUnknown; +} + +static size_t byteSize(ScalarTag t) { + switch (t) { + case ScalarTag::kInt: return sizeof(Int_t); + case ScalarTag::kUInt: return sizeof(UInt_t); + case ScalarTag::kShort: return sizeof(Short_t); + case ScalarTag::kUShort: return sizeof(UShort_t); + case ScalarTag::kLong64: return sizeof(Long64_t); + case ScalarTag::kULong64: return sizeof(ULong64_t); + case ScalarTag::kFloat: return sizeof(Float_t); + case ScalarTag::kDouble: return sizeof(Double_t); + case ScalarTag::kChar: return sizeof(Char_t); + case ScalarTag::kUChar: return sizeof(UChar_t); + case ScalarTag::kBool: return sizeof(Bool_t); + default: return 0; + } +} + +// Read an integer value from a raw buffer regardless of its stored type. +// Used to extract index values (fIndexBCs etc.) from their buffers. +static Long64_t readAsInt(const void *buf, ScalarTag tag) { + switch (tag) { + case ScalarTag::kInt: return *static_cast(buf); + case ScalarTag::kUInt: return *static_cast(buf); + case ScalarTag::kShort: return *static_cast(buf); + case ScalarTag::kUShort: return *static_cast(buf); + case ScalarTag::kLong64: return *static_cast(buf); + case ScalarTag::kULong64: return (Long64_t)*static_cast(buf); + default: return -1; + } +} + +// Write an integer value into a raw buffer. +static void writeAsInt(void *buf, ScalarTag tag, Long64_t val) { + switch (tag) { + case ScalarTag::kInt: *static_cast(buf) = (Int_t)val; break; + case ScalarTag::kUInt: *static_cast(buf) = (UInt_t)val; break; + case ScalarTag::kShort: *static_cast(buf) = (Short_t)val; break; + case ScalarTag::kUShort: *static_cast(buf) = (UShort_t)val; break; + case ScalarTag::kLong64: *static_cast(buf) = (Long64_t)val; break; + default: break; + } +} + +// A description of one branch in a tree: its name, scalar type tag, byte +// size, and whether it is a VLA (variable-length array). For VLAs we also +// keep the name of the count branch and the maximum observed element count +// (needed for buffer sizing). +struct BranchDesc { + std::string name; + ScalarTag tag = ScalarTag::kUnknown; + size_t elemSize = 0; // byte size of one element + int nElems = 1; // >1 for fixed-size arrays (e.g. fIndexSlice_Daughters[2]) + bool isVLA = false; + std::string countBranchName; // only for VLAs + Long64_t maxElems = 1; // only for VLAs +}; + +// Scan all branches of a tree and return their descriptors. Count branches +// for VLAs are represented only once (as the count side of the data branch) +// and are marked so they don't also appear as standalone entries. +static std::vector describeBranches(TTree *tree) { + std::vector result; + std::unordered_set countBranchNames; + + // First pass: identify all count branches for VLAs + for (auto *obj : *tree->GetListOfBranches()) { + TBranch *br = static_cast(obj); + TLeaf *leaf = static_cast(br->GetListOfLeaves()->At(0)); + if (!leaf) continue; + if (TLeaf *cnt = leaf->GetLeafCount()) + countBranchNames.insert(cnt->GetBranch()->GetName()); + } + + // Second pass: build descriptors + for (auto *obj : *tree->GetListOfBranches()) { + TBranch *br = static_cast(obj); + std::string bname = br->GetName(); + TLeaf *leaf = static_cast(br->GetListOfLeaves()->At(0)); + if (!leaf) { std::cerr << " [warn] branch without leaf: " << bname << "\n"; continue; } + + BranchDesc d; + d.name = bname; + d.tag = tagOf(leaf); + + if (TLeaf *cnt = leaf->GetLeafCount()) { + // This is a VLA data branch + d.isVLA = true; + d.countBranchName = cnt->GetBranch()->GetName(); + d.tag = tagOf(leaf); + d.elemSize = byteSize(d.tag); + + // Pre-scan to find the maximum array length (needed for buffer) + TBranch *cntBr = cnt->GetBranch(); + ScalarTag cntTag = tagOf(cnt); + size_t cntSz = byteSize(cntTag); + if (cntSz == 0) { std::cerr << " [warn] VLA count branch has unknown type: " << bname << "\n"; continue; } + std::vector cntBuf(cntSz, 0); + cntBr->SetAddress(cntBuf.data()); + Long64_t maxLen = 1; + for (Long64_t i = 0; i < tree->GetEntries(); ++i) { + cntBr->GetEntry(i); + Long64_t v = readAsInt(cntBuf.data(), cntTag); + if (v > maxLen) maxLen = v; + } + d.maxElems = maxLen; + + } else if (countBranchNames.count(bname)) { + // This is a count branch — skip it here; handled together with its VLA + continue; + } else { + // Plain scalar or fixed-size array branch (e.g. fIndexSlice_Daughters[2]) + d.isVLA = false; + d.elemSize = byteSize(d.tag); + d.nElems = leaf->GetLen(); // 1 for scalars, >1 for fixed arrays + if (d.elemSize == 0) { + std::cerr << " [warn] branch " << bname << " has unknown type " + << leaf->GetTypeName() << " — will be skipped\n"; + continue; + } + } + result.push_back(std::move(d)); + } + return result; +} + +// ============================================================================ +// SECTION 3 — Table rewriting engine +// ============================================================================ +// +// writeTable() is the single generic function that writes any table. +// It takes: +// - src : the source TTree +// - dirOut : directory to write the output TTree into +// - rowOrder : which source rows to include and in what order +// (a vector of source row indices, possibly a subset) +// - remaps : the index columns to remap in-place, each with its own PermMap +// +// There is deliberately NO "primary index" special case. Every index column — +// the one the table was sorted by as much as an intra-table mother/daughter +// link — goes through the same `remaps` list, which callers obtain from +// buildRemaps() and therefore from the kIndexRefs registry. The previous +// design had one privileged index plus an optional list of "extra" ones, and +// every stage had to remember to populate the extras; Stage 1b did not, which +// is precisely how O2fwdtrack's match indices were left dangling (O2-7098). + +// Describes one index column to remap. +struct IndexRemap { + std::string branchName; // branch whose integer values to remap + const PermMap *perm; // remapping table: newVal = (*perm)[oldVal] +}; + +// The row permutation implied by a rowOrder: perm[srcRow] = outRow, -1 if the +// row is not emitted. +static PermMap permFromRowOrder(Long64_t nSrc, + const std::vector &rowOrder) { + PermMap perm(nSrc, -1); + for (Long64_t outRow = 0; outRow < (Long64_t)rowOrder.size(); ++outRow) + perm[rowOrder[outRow]] = (Int_t)outRow; + return perm; +} + +// Remap n consecutive integers of the given type held in buf. Out-of-range and +// already-invalid values become -1 (the AO2D "no link" sentinel). +// Returns how many values actually changed. +static Long64_t remapBuffer(void *buf, ScalarTag tag, size_t elemSize, int n, + const PermMap &perm) { + Long64_t changed = 0; + auto *bytes = static_cast(buf); + for (int j = 0; j < n; ++j) { + void *slot = bytes + (size_t)j * elemSize; + Long64_t v = readAsInt(slot, tag); + Long64_t nv = (v < 0 || (size_t)v >= perm.size()) ? -1 : perm[(size_t)v]; + if (nv != v) { writeAsInt(slot, tag, nv); ++changed; } + } + return changed; +} + +static void writeTable(TTree *src, TDirectory *dirOut, + const std::vector &rowOrder, + const std::vector &remaps = {}) { + + Long64_t nSrc = src->GetEntries(); + + // Describe all branches + auto descs = describeBranches(src); + + // Allocate raw buffers: for each branch one buffer (for VLAs: data buffer + // sized maxElems * elemSize, plus a separate count buffer). + // We use a std::vector per branch (automatically memory-safe). + struct BranchIO { + BranchDesc desc; + std::vector dataBuf; // scalar: elemSize bytes; VLA: maxElems*elemSize bytes + std::vector countBuf; // VLA only + ScalarTag countTag = ScalarTag::kUnknown; + TBranch *inBr = nullptr; + TBranch *inCntBr = nullptr; + }; + std::vector ios; + ios.reserve(descs.size()); + + for (auto &d : descs) { + BranchIO io; + io.desc = d; + if (!d.isVLA) { + // Allocate for all elements (nElems>1 for fixed arrays like fIndexSlice_Daughters[2]) + io.dataBuf.assign(d.nElems * d.elemSize, 0); + } else { + io.dataBuf.assign(d.maxElems * d.elemSize, 0); + TBranch *cntBr = src->GetBranch(d.countBranchName.c_str()); + TLeaf *cntLeaf = cntBr ? static_cast(cntBr->GetListOfLeaves()->At(0)) : nullptr; + io.countTag = cntLeaf ? tagOf(cntLeaf) : ScalarTag::kUnknown; + io.countBuf.assign(byteSize(io.countTag), 0); + io.inCntBr = cntBr; + } + io.inBr = src->GetBranch(d.name.c_str()); + ios.push_back(std::move(io)); + } + + // Set input branch addresses + for (auto &io : ios) { + if (io.inBr) io.inBr->SetAddress(io.dataBuf.data()); + if (io.inCntBr) io.inCntBr->SetAddress(io.countBuf.data()); + } + + // Create output tree and set output branch addresses. + // We clone the tree structure (no entries) and reset addresses. + dirOut->cd(); + TTree *out = src->CloneTree(0, "fast"); + + // Output branches share the input buffers, so an in-place remap below is + // what out->Fill() sees. + for (auto &io : ios) { + TBranch *outBr = out->GetBranch(io.desc.name.c_str()); + if (!outBr) { std::cerr << " [warn] no output branch for " << io.desc.name << "\n"; continue; } + outBr->SetAddress(io.dataBuf.data()); + if (io.desc.isVLA) { + TBranch *outCntBr = out->GetBranch(io.desc.countBranchName.c_str()); + if (outCntBr) outCntBr->SetAddress(io.countBuf.data()); + } + } + + // Resolve each remap to its BranchIO once, up front, instead of searching the + // branch list per row (O2mcparticle has millions of rows). + struct ResolvedRemap { BranchIO *io; const PermMap *perm; }; + std::vector resolved; + for (auto &r : remaps) { + BranchIO *found = nullptr; + for (auto &io : ios) if (io.desc.name == r.branchName) { found = &io; break; } + if (!found) continue; // branch absent (caller probed, so this is unusual) + // Index columns are signed 32-bit in the AO2D schema. Refuse to touch + // anything else rather than write -1 into an unsigned field. + if (found->desc.tag != ScalarTag::kInt && found->desc.tag != ScalarTag::kShort && + found->desc.tag != ScalarTag::kLong64) { + std::cerr << " [warn] index branch " << r.branchName + << " has non-signed-integer type — NOT remapped\n"; + continue; + } + resolved.push_back({found, r.perm}); + } + + // Fill the output tree row by row in the requested order + Long64_t nRemapped = 0; + for (Long64_t srcRow : rowOrder) { + src->GetEntry(srcRow); + + for (auto &r : resolved) { + BranchIO &io = *r.io; + int n = io.desc.isVLA ? (int)readAsInt(io.countBuf.data(), io.countTag) + : io.desc.nElems; + nRemapped += remapBuffer(io.dataBuf.data(), io.desc.tag, io.desc.elemSize, + n, *r.perm); + } + + out->Fill(); + } + + std::cout << " wrote " << out->GetEntries() << " / " << nSrc + << " rows; " << nRemapped << " index values remapped\n"; + out->Write(); +} + +// Look up the permutation of the table named by the first prefix that resolves. +// Ambiguity (several schema versions of the same table in one DF) is resolved +// deterministically by taking the lexicographically smallest name. +static const PermMap *findPermFor( + const std::unordered_map &allPerms, + const std::vector &prefixes, + std::string *foundName = nullptr) { + for (auto &prefix : prefixes) { + const PermMap *best = nullptr; + std::string bestName; + for (auto &[name, perm] : allPerms) { + if (!isTableNamed(name, prefix)) continue; + if (bestName.empty() || name < bestName) { best = &perm; bestName = name; } + } + if (best) { if (foundName) *foundName = bestName; return best; } + } + return nullptr; +} + +// THE central safety net: derive the complete remap list for a table from the +// kIndexRefs registry. Every stage writes through this, so an index column can +// only be missed by being absent from kIndexRefs — which +// unregisteredIndexBranches() reports. +static std::vector buildRemaps( + TTree *src, const std::unordered_map &allPerms) { + std::vector remaps; + if (!src) return remaps; + for (auto &[branchName, prefixes] : kIndexRefs) { + if (!src->GetBranch(branchName.c_str())) continue; + const PermMap *perm = findPermFor(allPerms, prefixes); + if (!perm) continue; // referent table not in this DF — nothing to remap + remaps.push_back({branchName, perm}); + } + return remaps; +} + +// ============================================================================ +// SECTION 3b — Deferred write plan +// ============================================================================ +// +// Writing is deferred until every table's row order (and hence its permutation) +// is known, because references can point forwards and sideways: O2fwdtrack +// references O2mfttrack — written later in the same stage — and also itself, via +// fIndexFwdTracks_MatchMCHTrack. The old code wrote each table as soon as it +// had planned it, so those two permutations simply did not exist yet at write +// time. That is the mechanism behind O2-7098. +// +// So: every stage now only PLANS (appends a TablePlan, publishes its perm into +// allPerms), and processDF writes all plans afterwards in one pass. +struct TablePlan { + std::string name; // tree name; re-fetched from dirIn at write time + std::vector rowOrder; // source rows to emit, in output order +}; + +// ============================================================================ +// SECTION 4 — Stage 0: BC table sort + deduplication +// ============================================================================ +// +// Reads fGlobalBC from the BC tree, sorts rows, drops exact-duplicate BC +// values, and writes the compacted table. Returns bcPerm[oldRow] = newRow. +// +// IMPORTANT (history — do not "optimize" this back): +// A previous revision replaced this sort with an order-preserving in-place +// dedup that std::abort()ed unless the input BC table was already globalBC- +// sorted. That directly contradicts this tool's PURPOSE (a) at the top of the +// file — repairing *non-monotonic* fGlobalBC in MERGED AO2Ds — so it aborted on +// exactly the files it exists to fix ("doesn't run to completion"). We sort +// unconditionally instead. +// +// Sorting BCs does imply a reorder cascade: collisions are sorted by their +// remapped fIndexBCs in Stage 1, and the collision-grouped track tables must +// then be re-grouped to follow the new collision order in Stage 1b. That +// cascade is real and unavoidable for non-monotonic input; it is handled +// explicitly and completely by those stages. This is the correct fix — keep +// it. An "assert already sorted" shortcut here is a known dead end. + +struct BCStage0Result { + PermMap bcPerm; // bcPerm[oldRow] = newRow in sorted/deduped BC table + Long64_t nUnique = 0; +}; + +static BCStage0Result stage0_sortBCs(TTree *treeBCs, std::vector &plans) { + BCStage0Result res; + Long64_t n = treeBCs->GetEntries(); + if (n == 0) return res; + + TBranch *brGBC = treeBCs->GetBranch("fGlobalBC"); + if (!brGBC) { std::cerr << "ERROR: O2bc_* tree has no fGlobalBC branch!\n"; return res; } + + ULong64_t gbc = 0; + brGBC->SetAddress(&gbc); + std::vector gbcs(n); + for (Long64_t i = 0; i < n; ++i) { treeBCs->GetEntry(i); gbcs[i] = gbc; } + + // Sort row indices by fGlobalBC (stable, so equal-globalBC rows keep their + // input order before being collapsed below). + std::vector order(n); + std::iota(order.begin(), order.end(), 0); + std::stable_sort(order.begin(), order.end(), + [&](Long64_t a, Long64_t b){ return gbcs[a] < gbcs[b]; }); + + // Build the deduplicated row list and the permutation: rows sharing a + // globalBC collapse onto one output row. + res.bcPerm.assign(n, -1); + std::vector rowOrder; // source rows to keep, in output order + ULong64_t prev = 0; + Int_t newRow = -1; + for (Long64_t srcRow : order) { + if (newRow < 0 || gbcs[srcRow] != prev) { + ++newRow; + prev = gbcs[srcRow]; + rowOrder.push_back(srcRow); + } + // All rows with the same globalBC map to the same new row (deduplication) + res.bcPerm[srcRow] = newRow; + } + res.nUnique = rowOrder.size(); + + std::cout << " BC stage: " << n << " rows -> " << res.nUnique << " unique (sorted)\n"; + + // The BC table itself carries no index columns. + plans.push_back({treeBCs->GetName(), std::move(rowOrder)}); + + return res; +} + +// ============================================================================ +// SECTION 5 — Stage 0b: BC flags table (follows BC row order exactly) +// ============================================================================ + +static void stage0_copyBCFlags(TTree *treeFlags, std::vector &plans, + const PermMap &bcPerm) { + if (!treeFlags) return; + + // Build rowOrder: for each unique output BC row, pick the first source row + // that mapped to it + std::vector rowOrder; + std::map first; // newBCrow -> first srcRow + for (Long64_t i = 0; i < (Long64_t)bcPerm.size(); ++i) + if (bcPerm[i] >= 0) first.emplace(bcPerm[i], i); // emplace keeps first + rowOrder.reserve(first.size()); + for (auto &kv : first) rowOrder.push_back(kv.second); + + plans.push_back({treeFlags->GetName(), std::move(rowOrder)}); +} + +// ============================================================================ +// SECTION 6 — Stage 1: Tables indexed by BCs (generic + MCCollisions special) +// ============================================================================ +// +// Returns a map: treeName -> PermMap, containing the row permutation for +// every table processed at this stage. Callers use this for paste-joined +// tables and for Stage 2. + +// Key used to detect duplicate MCCollisions. +// +// Two MCCollision rows are considered identical when BOTH of the following hold: +// 1. They map to the same BC row after remapping (same globalBC). +// 2. They carry the same fEventWeight value. +// +// fEventWeight is a float written by the generator for every event and is +// unique enough (in combination with the BC) to distinguish distinct events +// from the same generator that happen to land in the same BC. +// +// IMPORTANT: if fEventWeight is absent from the tree we do NOT deduplicate at +// all, because we have no reliable way to distinguish distinct events that share +// the same BC. Deduplicating on BC alone would incorrectly merge different MC +// events that were placed in the same bunch crossing. +struct MCCollKey { + Long64_t newBCrow; + Float_t weight; + bool operator==(const MCCollKey &o) const { + return newBCrow == o.newBCrow && weight == o.weight; + } +}; +struct MCCollKeyHash { + size_t operator()(const MCCollKey &k) const { + size_t h1 = std::hash{}(k.newBCrow); + // Bit-cast float to uint32 for hashing — avoids UB and NaN weirdness + uint32_t wbits; + std::memcpy(&wbits, &k.weight, sizeof(wbits)); + return h1 ^ (size_t(wbits) << 32) ^ size_t(wbits); + } +}; + +static void +stage1_BCindexedTables(TDirectory *dirIn, std::vector &plans, + std::unordered_map &allPerms, + const PermMap &bcPerm) { + + TIter it(dirIn->GetListOfKeys()); + while (TKey *key = static_cast(it())) { + if (TString(key->GetClassName()) != "TTree") continue; + std::unique_ptr obj(key->ReadObj()); + TTree *src = dynamic_cast(obj.get()); + if (!src) continue; + + std::string tname = src->GetName(); + if (isBCTable(tname.c_str())) continue; // handled in stage 0 + + const char *idxBr = bcIndexBranch(src); + if (!idxBr) continue; // not BC-indexed — handled elsewhere + + std::cout << " Stage1 [BC-indexed]: " << tname << "\n"; + + Long64_t nSrc = src->GetEntries(); + + // Read all index values to build the sort order + TBranch *inIdxBr = src->GetBranch(idxBr); + TLeaf *idxLeaf = static_cast(inIdxBr->GetListOfLeaves()->At(0)); + ScalarTag idxTag = tagOf(idxLeaf); + size_t idxSz = byteSize(idxTag); + std::vector idxBuf(idxSz, 0); + inIdxBr->SetAddress(idxBuf.data()); + + // For MCCollision deduplication: also read fEventWeight if available. + // If absent, deduplication is disabled -- see MCCollKey comment for why. + bool isMCColl = TString(tname.c_str()).BeginsWith("O2mccollision"); + TBranch *wBr = isMCColl ? src->GetBranch("fEventWeight") : nullptr; + Float_t wVal = 0.f; + if (wBr) wBr->SetAddress(&wVal); + bool canDedup = isMCColl && (wBr != nullptr); + if (isMCColl && !canDedup) + std::cout << " MCCollision: fEventWeight absent -- deduplication disabled\n"; + + // Build (newBCrow, srcRow) pairs + struct SortEntry { Long64_t newBC; Long64_t srcRow; }; + std::vector entries; + entries.reserve(nSrc); + + std::unordered_set seenMCColl; + std::vector keep(nSrc, true); + + for (Long64_t i = 0; i < nSrc; ++i) { + inIdxBr->GetEntry(i); + if (wBr) wBr->GetEntry(i); + Long64_t oldBC = readAsInt(idxBuf.data(), idxTag); + Long64_t newBC = (oldBC >= 0 && oldBC < (Long64_t)bcPerm.size()) + ? bcPerm[oldBC] : -1; + + if (canDedup) { + // Deduplication: drop rows with a (newBC, weight) pair seen before. + // First occurrence in source row order is kept. + MCCollKey k{newBC, wVal}; + if (!seenMCColl.insert(k).second) { + keep[i] = false; + } + } + entries.push_back({newBC, i}); + } + + // Stable-sort by newBC (invalid = -1 sink to end) + std::stable_sort(entries.begin(), entries.end(), + [](const SortEntry &a, const SortEntry &b){ + if (a.newBC < 0 && b.newBC >= 0) return false; + if (a.newBC >= 0 && b.newBC < 0) return true; + return a.newBC < b.newBC; + }); + + // Build rowOrder, respecting the keep[] mask for MCCollisions + std::vector rowOrder; + rowOrder.reserve(nSrc); + for (auto &e : entries) { + if (keep[e.srcRow]) rowOrder.push_back(e.srcRow); + } + + if (isMCColl) { + Long64_t dropped = nSrc - (Long64_t)rowOrder.size(); + std::cout << " MCCollision dedup: dropped " << dropped + << " duplicate rows (" << rowOrder.size() << " kept)\n"; + } + + allPerms[tname] = permFromRowOrder(nSrc, rowOrder); + plans.push_back({tname, std::move(rowOrder)}); + } +} + +// ============================================================================ +// SECTION 7 — Stage 2: Tables indexed by MCCollisions +// ============================================================================ + +static void +stage2_MCCollIndexedTables(TDirectory *dirIn, std::vector &plans, + std::unordered_map &allPerms, + const PermMap &mcCollPerm) { + + TIter it(dirIn->GetListOfKeys()); + while (TKey *key = static_cast(it())) { + if (TString(key->GetClassName()) != "TTree") continue; + std::unique_ptr obj(key->ReadObj()); + TTree *src = dynamic_cast(obj.get()); + if (!src) continue; + + std::string tname = src->GetName(); + if (isBCTable(tname.c_str())) continue; + if (bcIndexBranch(src)) continue; // already handled in stage 1 + + const char *idxBr = mcCollIndexBranch(src); + if (!idxBr) continue; + + // A paste-join child (e.g. O2mccollisionlabel) MUST follow its parent's + // row order and never drop rows on its own. Defer it to + // processPasteJoinTables, which will remap any of its own index columns + // (fIndexMcCollisions, ...) value-wise without touching the row count. + if (isPasteJoinChild(tname)) { + std::cout << " Stage2: deferring paste-join child " << tname + << " to paste-join handler\n"; + continue; + } + + std::cout << " Stage2 [MCColl-indexed]: " << tname << "\n"; + + Long64_t nSrc = src->GetEntries(); + TBranch *inIdxBr = src->GetBranch(idxBr); + TLeaf *idxLeaf = static_cast(inIdxBr->GetListOfLeaves()->At(0)); + ScalarTag idxTag = tagOf(idxLeaf); + size_t idxSz = byteSize(idxTag); + std::vector idxBuf(idxSz, 0); + inIdxBr->SetAddress(idxBuf.data()); + + struct SortEntry { Long64_t newMCColl; Long64_t srcRow; }; + std::vector entries; + entries.reserve(nSrc); + + for (Long64_t i = 0; i < nSrc; ++i) { + inIdxBr->GetEntry(i); + Long64_t oldIdx = readAsInt(idxBuf.data(), idxTag); + Long64_t newIdx = (oldIdx >= 0 && oldIdx < (Long64_t)mcCollPerm.size()) + ? mcCollPerm[oldIdx] : -1; + entries.push_back({newIdx, i}); + } + + // Drop rows whose MCCollision parent was dropped (newIdx == -1 due to dedup) + // and sort the rest + std::stable_sort(entries.begin(), entries.end(), + [](const SortEntry &a, const SortEntry &b){ + if (a.newMCColl < 0 && b.newMCColl >= 0) return false; + if (a.newMCColl >= 0 && b.newMCColl < 0) return true; + return a.newMCColl < b.newMCColl; + }); + + std::vector rowOrder; + rowOrder.reserve(nSrc); + Long64_t dropped = 0; + for (auto &e : entries) { + if (e.newMCColl >= 0) rowOrder.push_back(e.srcRow); + else ++dropped; + } + if (dropped) + std::cout << " dropped " << dropped + << " rows whose MCCollision parent was deduplicated\n"; + + // O2mcparticle's intra-table mother/daughter links are remapped through this + // table's OWN permutation. No special case is needed any more: the perm is + // published here and buildRemaps() picks it up for fIndexArray_Mothers / + // fIndexSlice_Daughters at write time, because writing happens after all + // planning. The stable sort above preserves within-collision particle + // order, which keeps fIndexSlice_Daughters contiguous — so remapping + // [first,last] elementwise is correct. + allPerms[tname] = permFromRowOrder(nSrc, rowOrder); + plans.push_back({tname, std::move(rowOrder)}); + } +} + +// ============================================================================ +// SECTION 8 — Paste-join table handling +// ============================================================================ +// +// A paste-joined CHILD has no row of its own — its row N corresponds to row N +// of its PARENT table. When the parent is reordered, the child must follow +// row-for-row to preserve the 1:1 alignment. Paste-join children may still +// carry their own index columns; those values are remapped in-place via the +// appropriate parent-stage permutation. +// +// The list of paste-join pairs is in kPasteJoins (Section 1). +// +// This stage no longer enumerates which index columns it knows how to remap: +// buildRemaps() derives them from kIndexRefs at write time, for every table +// alike. The old hand-maintained enumeration here is what let O2fwdtrack slip +// through — the table was never routed to this function in the first place. +// +// When the named parent is not in allPerms (e.g. tracks aren't reordered in +// this build), the child is processed with identity row order so the +// value-wise remaps still apply but the row order is unchanged. + +// ============================================================================ +// SECTION 9b — Stage 1b: Collision-grouped track tables +// ============================================================================ +// +// The primary track tables (O2track_iu, O2mfttrack_*, O2fwdtrack) are GROUPED +// by collision. O2's slicing cache (ArrowTableSlicingCache::validateOrder) +// requires every fIndexCollisions group — including the "-1" ambiguous group — +// to be a single contiguous run; otherwise it aborts with +// "Table ... index fIndexCollisions has a group with index -1 that is split". +// +// When several MC sub-timeframes are merged into one DF_ folder (data-embedding +// anchoring, which stores MC timeframes under the same DF_ as the parent data +// file), each sub-frame contributes its own [collision-grouped][-1 ambiguous] +// block. Concatenating them splits the -1 group into N runs, so the table is +// no longer sliceable. Stage 1 only reorders BC-indexed tables, and tracks are +// otherwise written in input row order, so the split survives into the output. +// +// This stage re-establishes the grouping: it reorders each collision-grouped +// track table by its remapped fIndexCollisions (stable, with -1 sinking to the +// end so the ambiguous group is one contiguous run — matching the Stage 1 +// convention) and publishes the resulting row permutation. Downstream: +// * paste-join children (O2trackextra, O2trackcov_iu, O2mctracklabel, ...) +// follow the published parent permutation; +// * every fIndexTracks* / fIndexMFTTracks / fIndexFwdTracks reference is +// remapped through it — including the ones the track tables hold on EACH +// OTHER. O2fwdtrack points at O2mfttrack (fIndexMFTTracks) and at itself +// (fIndexFwdTracks_MatchMCHTrack), so its remap needs permutations that are +// only established later in this very stage. That is why this function +// PLANS all tables first and lets processDF write them afterwards; writing +// inline (as it used to) meant those two columns kept pre-reorder row +// numbers — in range, so no validator complained, but every global muon +// ended up with a foreign MFT and MCH leg. See O2-7098. +static bool isCollGroupedTrackTable(const std::string &tname) { + static const char *kPrefixes[] = {"O2track_iu", "O2track", + "O2mfttrack", "O2fwdtrack"}; + for (auto *p : kPrefixes) + if (TString(tname.c_str()).BeginsWith(p)) return true; + return false; +} + +static void stage1b_reorderTrackTables( + TDirectory *dirIn, std::vector &plans, + std::unordered_map &allPerms, + std::unordered_set &planned) { + + const PermMap *collPermP = findPermFor(allPerms, {"O2collision"}); + if (!collPermP) return; // no collisions present — nothing to regroup against + + TIter it(dirIn->GetListOfKeys()); + while (TKey *key = static_cast(it())) { + if (TString(key->GetClassName()) != "TTree") continue; + std::unique_ptr obj(key->ReadObj()); + TTree *src = dynamic_cast(obj.get()); + if (!src) continue; + + std::string tname = src->GetName(); + if (planned.count(tname)) continue; // BC-indexed tracks etc. already done + if (!isCollGroupedTrackTable(tname)) continue; + if (isPasteJoinChild(tname)) continue; // children follow their parent below + if (!src->GetBranch("fIndexCollisions")) continue; + + std::cout << " Stage1b [coll-grouped]: " << tname << "\n"; + + Long64_t nSrc = src->GetEntries(); + TBranch *inIdxBr = src->GetBranch("fIndexCollisions"); + TLeaf *idxLeaf = static_cast(inIdxBr->GetListOfLeaves()->At(0)); + ScalarTag idxTag = tagOf(idxLeaf); + std::vector idxBuf(byteSize(idxTag), 0); + inIdxBr->SetAddress(idxBuf.data()); + + struct SortEntry { Long64_t newColl; Long64_t srcRow; }; + std::vector entries; + entries.reserve(nSrc); + for (Long64_t i = 0; i < nSrc; ++i) { + inIdxBr->GetEntry(i); + Long64_t oldColl = readAsInt(idxBuf.data(), idxTag); + Long64_t newColl = (oldColl >= 0 && oldColl < (Long64_t)collPermP->size()) + ? (*collPermP)[oldColl] : -1; + entries.push_back({newColl, i}); + } + // Stable-sort by remapped collision; the ambiguous group (-1) sinks to the + // end as a single contiguous run. Stable keeps the within-collision order. + std::stable_sort(entries.begin(), entries.end(), + [](const SortEntry &a, const SortEntry &b){ + if (a.newColl < 0 && b.newColl >= 0) return false; + if (a.newColl >= 0 && b.newColl < 0) return true; + return a.newColl < b.newColl; + }); + std::vector rowOrder; + rowOrder.reserve(nSrc); + for (auto &e : entries) rowOrder.push_back(e.srcRow); + + // Publish the permutation now; the actual write (with the full set of + // remaps, this table's own included) happens in processDF once every + // permutation in the DF is known. + allPerms[tname] = permFromRowOrder(nSrc, rowOrder); + plans.push_back({tname, std::move(rowOrder)}); + planned.insert(tname); + } +} + +// ---------------------------------------------------------------------------- +// Re-sorting tables that are STORED SORTED BY a reference +// +// Stage 1b exists because a track table grouped by fIndexCollisions stops being +// sliceable once the collision table is reordered. Exactly the same is true of +// every other table stored sorted by a reference into a table this tool +// reorders — O2v0_002 and O2cascade_001 by fIndexCollisions, O2fwdtrkcl by +// fIndexFwdTracks, O2ambiguoustrack and O2trackqa_003 by fIndexTracks. +// Remapping their values while leaving their rows in place turns a sorted +// column into an unsorted one, which is the same defect that produced +// "Table ... index fIndexCollisions has a group with index -1 that is split". +// +// Rather than hardcode which tables are grouped by what — the enumeration habit +// that caused O2-7098 — the decision is derived from the data: IF a column is +// non-decreasing in the input, THEN it is an ordering the file carries and the +// output must preserve it. That is self-maintaining across schema changes, and +// it correctly leaves O2mfttrackcov alone: its fIndexMFTTracks is not sorted in +// the input, so there is no ordering to preserve. + +struct ResortCandidate { + size_t planIdx = 0; + std::string tableName; + std::string keyBranch; + std::vector keyValues; // raw input values of keyBranch + std::vector refPrefixes; // referent of keyBranch, from kIndexRefs +}; + +// Read a scalar Int_t-like index column. Returns false for VLA / fixed-array +// columns, which are not row orderings. +static bool readScalarIndexColumn(TTree *t, const char *branch, + std::vector &out) { + TBranch *br = t->GetBranch(branch); + if (!br) return false; + TLeaf *leaf = static_cast(br->GetListOfLeaves()->At(0)); + if (!leaf || leaf->GetLeafCount() || leaf->GetLen() != 1) return false; + ScalarTag tag = tagOf(leaf); + size_t sz = byteSize(tag); + if (sz == 0) return false; + std::vector buf(sz, 0); + br->SetAddress(buf.data()); + out.clear(); + out.reserve(t->GetEntries()); + for (Long64_t i = 0; i < t->GetEntries(); ++i) { + br->GetEntry(i); + out.push_back(readAsInt(buf.data(), tag)); + } + br->ResetAddress(); + return true; +} + +// The convention this tool writes (and Stage 1b establishes): valid values +// ascending, the -1 "ambiguous" group as one contiguous run at the end. +static bool isOrderedWithNullsLast(const std::vector &v) { + Long64_t prev = -1; + bool seenNull = false; + for (auto x : v) { + if (x < 0) { seenNull = true; continue; } + if (seenNull) return false; // a valid value after a null: not this convention + if (x < prev) return false; + prev = x; + } + return true; +} + +// Pick the column a table is stored sorted by, if any. fIndexCollisions wins +// when several qualify, since that is the grouping O2's slicing cache checks. +static bool findGroupingColumn(TTree *src, std::string &keyBranch, + std::vector &keyValues, + std::vector &refPrefixes) { + if (!src || src->GetEntries() < 2) return false; + bool found = false; + for (auto &[branchName, prefixes] : kIndexRefs) { + std::vector vals; + if (!readScalarIndexColumn(src, branchName.c_str(), vals)) continue; + if (!isOrderedWithNullsLast(vals)) continue; + bool preferred = (branchName == "fIndexCollisions"); + if (found && !preferred) continue; + keyBranch = branchName; + keyValues = std::move(vals); + refPrefixes = prefixes; + found = true; + if (preferred) break; + } + return found; +} + +// Re-sort each candidate by its remapped grouping column. Iterated to a fixed +// point because these tables reference each other: O2cascade_001 is sorted by +// fIndexV0s, and O2v0_002 may itself have just been re-sorted. +static void resortByGroupingColumn( + std::vector &plans, + std::unordered_map &allPerms, + const std::vector &candidates) { + + const int kMaxPasses = 8; + int pass = 0; + for (; pass < kMaxPasses; ++pass) { + bool changed = false; + for (auto &cand : candidates) { + const PermMap *refPerm = findPermFor(allPerms, cand.refPrefixes); + if (!refPerm) continue; // referent absent from this DF + + Long64_t n = (Long64_t)cand.keyValues.size(); + struct SortEntry { Long64_t key; Long64_t srcRow; }; + std::vector entries; + entries.reserve(n); + for (Long64_t i = 0; i < n; ++i) { + Long64_t old = cand.keyValues[i]; + Long64_t nw = (old >= 0 && old < (Long64_t)refPerm->size()) ? (*refPerm)[old] : -1; + entries.push_back({nw, i}); + } + // Same ordering convention as Stage 1: -1 sinks to a contiguous tail. + std::stable_sort(entries.begin(), entries.end(), + [](const SortEntry &a, const SortEntry &b) { + if (a.key < 0 && b.key >= 0) return false; + if (a.key >= 0 && b.key < 0) return true; + return a.key < b.key; + }); + std::vector rowOrder; + rowOrder.reserve(n); + for (auto &e : entries) rowOrder.push_back(e.srcRow); + + if (rowOrder == plans[cand.planIdx].rowOrder) continue; + plans[cand.planIdx].rowOrder = rowOrder; + allPerms[cand.tableName] = permFromRowOrder(n, rowOrder); + changed = true; + std::cout << " Re-sort: " << cand.tableName << " by remapped " + << cand.keyBranch << " (was sorted by it on input)\n"; + } + if (!changed) break; + } + if (pass == kMaxPasses) + std::cerr << " [warn] re-sort did not reach a fixed point after " + << kMaxPasses << " passes — check for a reference cycle\n"; +} + +// Plan every table not yet claimed by an earlier stage: paste-join children +// follow their parent's row order, everything else keeps its own — unless it is +// stored sorted by a reference, in which case resortByGroupingColumn() below +// re-establishes that ordering. The index columns these tables carry are NOT +// enumerated here — buildRemaps() derives them from kIndexRefs when processDF +// writes. +static void planRemainingTables( + TDirectory *dirIn, std::vector &plans, + std::unordered_map &allPerms, + std::unordered_set &planned) { + + std::vector resortCandidates; + + TIter it(dirIn->GetListOfKeys()); + while (TKey *key = static_cast(it())) { + if (TString(key->GetClassName()) != "TTree") continue; + std::unique_ptr obj(key->ReadObj()); + TTree *src = dynamic_cast(obj.get()); + if (!src) continue; + + std::string tname = src->GetName(); + if (planned.count(tname)) continue; + if (isBCTable(tname.c_str())) continue; + // Stage-1 BC-indexed and Stage-2 MCColl-indexed non-paste-join tables are + // already planned. A paste-join child carrying its own MCColl index + // (e.g. O2mccollisionlabel) was deferred from stage2 and lands here. + if (bcIndexBranch(src)) continue; + if (mcCollIndexBranch(src) && !isPasteJoinChild(tname)) continue; + + Long64_t nSrc = src->GetEntries(); + + // Find a paste-join parent for this table (kPasteJoins lookup). + const PermMap *parentPerm = nullptr; + std::string parentName; + for (auto &[pastePrefix, parentPrefix] : kPasteJoins) { + if (!TString(tname.c_str()).BeginsWith(pastePrefix.c_str())) continue; + parentPerm = findPermFor(allPerms, {parentPrefix}, &parentName); + if (parentPerm) break; + } + + std::vector rowOrder; + if (parentPerm) { + // perm[srcRow] = outRow (-1 = dropped); invert it into an output order. + std::vector> pairs; + pairs.reserve(parentPerm->size()); + for (Long64_t srcRow = 0; srcRow < (Long64_t)parentPerm->size(); ++srcRow) + if ((*parentPerm)[srcRow] >= 0) pairs.push_back({(*parentPerm)[srcRow], srcRow}); + std::sort(pairs.begin(), pairs.end()); + rowOrder.reserve(pairs.size()); + for (auto &p : pairs) rowOrder.push_back(p.second); + } + + if (parentPerm && (Long64_t)rowOrder.size() == nSrc) { + std::cout << " Paste-join: " << tname << " follows " << parentName << "\n"; + } else { + if (parentPerm) { + // Schema drift: the child cannot be aligned to its parent. Keep its own + // row order (the index remaps still get applied at write time, so at + // least no value is left dangling) and shout — the validator's + // paste-join parity check will fail on the output. + std::cerr << " [warn] paste-join size mismatch: " << tname + << " has " << nSrc << " rows but parent perm covers " + << rowOrder.size() << " — keeping own row order\n"; + } + rowOrder.resize(nSrc); + std::iota(rowOrder.begin(), rowOrder.end(), 0LL); + } + + // A table that arrives here with its own row order may still be STORED + // SORTED BY one of its index columns; if that column's referent gets + // reordered, the sortedness has to be re-established. Record what is + // needed for that; the decision is made below, once every table in this + // stage has a permutation. + if (!parentPerm) { + ResortCandidate cand; + if (findGroupingColumn(src, cand.keyBranch, cand.keyValues, cand.refPrefixes)) { + cand.planIdx = plans.size(); + cand.tableName = tname; + resortCandidates.push_back(std::move(cand)); + } + } + + allPerms[tname] = permFromRowOrder(nSrc, rowOrder); + plans.push_back({tname, std::move(rowOrder)}); + planned.insert(tname); + } + + resortByGroupingColumn(plans, allPerms, resortCandidates); +} + +// ============================================================================ +// SECTION 9 — Non-tree object copying (TMap metadata etc.) +// ============================================================================ + +static void copyNonTreeObjects(TDirectory *dirIn, TDirectory *dirOut) { + TIter it(dirIn->GetListOfKeys()); + while (TKey *key = static_cast(it())) { + if (TString(key->GetClassName()) == "TTree") continue; + std::unique_ptr obj(key->ReadObj()); + dirOut->cd(); + if (obj->IsA()->InheritsFrom(TMap::Class())) + dirOut->WriteTObject(obj.get(), key->GetName(), "Overwrite"); + else + obj->Write(key->GetName(), TObject::kOverwrite); + } +} + +// ============================================================================ +// SECTION 10 — Per-DF directory driver +// ============================================================================ + +static void processDF(TDirectory *dirIn, TDirectory *dirOut) { + std::cout << "========================================\n"; + std::cout << "Processing " << dirIn->GetName() << "\n"; + + // ---- Find BC tree and optional flags tree ---- + TTree *treeBCs = nullptr; + TTree *treeFlags = nullptr; + { + TIter it(dirIn->GetListOfKeys()); + while (TKey *key = static_cast(it())) { + if (TString(key->GetClassName()) != "TTree") continue; + TTree *t = static_cast(dirIn->Get(key->GetName())); + if (!t) continue; + TString tname = t->GetName(); + if (tname.BeginsWith("O2bc_")) { treeBCs = t; } + if (tname.BeginsWith("O2bcflag")){ treeFlags = t; } + } + } + + if (!treeBCs) { + // No BC table — deep-copy everything unchanged + std::cout << " No BC table found — copying directory verbatim\n"; + TIter it(dirIn->GetListOfKeys()); + while (TKey *key = static_cast(it())) { + std::unique_ptr obj(key->ReadObj()); + dirOut->cd(); + if (obj->InheritsFrom(TTree::Class())) { + TTree *c = static_cast(obj.get())->CloneTree(-1, "fast"); + c->SetDirectory(dirOut); c->Write(); + } else if (obj->IsA()->InheritsFrom(TMap::Class())) { + dirOut->WriteTObject(obj.get(), key->GetName(), "Overwrite"); + } else { + obj->Write(key->GetName(), TObject::kOverwrite); + } + } + return; + } + + // ========================================================================== + // PLAN PHASE — every stage only decides row orders and publishes the + // resulting permutations. Nothing is written yet, because a table's index + // columns may point at tables planned later in the same or a later stage + // (O2fwdtrack -> O2mfttrack, and O2fwdtrack -> itself). + // ========================================================================== + std::vector plans; + std::unordered_map allPerms; + std::unordered_set planned; + + // ---- Stage 0: sort & deduplicate BCs ---- + std::cout << "-- Stage 0: BCs --\n"; + BCStage0Result s0 = stage0_sortBCs(treeBCs, plans); + if (treeFlags) stage0_copyBCFlags(treeFlags, plans, s0.bcPerm); + planned.insert(treeBCs->GetName()); + if (treeFlags) planned.insert(treeFlags->GetName()); + // bcPerm is a dedup map (several old rows collapse onto one new row), not a + // plain row permutation — which is exactly the mapping references into the BC + // table need. Publishing it under the BC tree's real name lets buildRemaps() + // resolve fIndexBCs / fIndexBC / fIndexSliceBCs like any other reference. + allPerms[treeBCs->GetName()] = s0.bcPerm; + + // ---- Stage 1: BC-indexed tables (including MCCollisions dedup) ---- + std::cout << "-- Stage 1: BC-indexed tables --\n"; + stage1_BCindexedTables(dirIn, plans, allPerms, s0.bcPerm); + for (auto &kv : allPerms) planned.insert(kv.first); + + // ---- Stage 2: MCCollision-indexed tables ---- + std::cout << "-- Stage 2: MCCollision-indexed tables --\n"; + const PermMap *mcCollPermP = findPermFor(allPerms, {"O2mccollision"}); + if (mcCollPermP) { + PermMap mcCollPerm = *mcCollPermP; // copy: allPerms grows below + stage2_MCCollIndexedTables(dirIn, plans, allPerms, mcCollPerm); + for (auto &kv : allPerms) planned.insert(kv.first); + } else { + std::cout << " (no MCCollision table found — skipping stage 2)\n"; + } + + // ---- Stage 1b: regroup collision-grouped track tables ---- + // Must run after Stage 1 (needs the collision permutation). + std::cout << "-- Stage 1b: collision-grouped track tables --\n"; + stage1b_reorderTrackTables(dirIn, plans, allPerms, planned); + + // ---- Paste-join tables + unrelated tables ---- + std::cout << "-- Paste-join and unrelated tables --\n"; + planRemainingTables(dirIn, plans, allPerms, planned); + + // ========================================================================== + // WRITE PHASE — all permutations are known, so every table can now have + // ALL of its index columns remapped, whichever table they point at. + // ========================================================================== + std::cout << "-- Writing " << plans.size() << " tables --\n"; + for (auto &plan : plans) { + TTree *src = dynamic_cast(dirIn->Get(plan.name.c_str())); + if (!src) { std::cerr << " [warn] lost tree " << plan.name << " before write\n"; continue; } + + // Drift guard: a link the registry does not know about cannot be remapped, + // so say so loudly here as well as in the validator. + for (auto &b : unregisteredIndexBranches(src)) + std::cerr << " [warn] " << plan.name << "." << b + << " is not in kIndexRefs — it will NOT be remapped\n"; + + auto remaps = buildRemaps(src, allPerms); + + bool identity = ((Long64_t)plan.rowOrder.size() == src->GetEntries()); + for (Long64_t i = 0; identity && i < (Long64_t)plan.rowOrder.size(); ++i) + if (plan.rowOrder[i] != i) identity = false; + + if (identity && remaps.empty()) { + std::cout << " Copy (no dependency): " << plan.name << "\n"; + dirOut->cd(); + TTree *c = src->CloneTree(-1, "fast"); + c->SetDirectory(dirOut); + c->Write(); + continue; + } + + std::cout << " Write: " << plan.name << " (" << remaps.size() << " index column(s))\n"; + writeTable(src, dirOut, plan.rowOrder, remaps); + } + + // ---- Non-tree objects (TMap metadata) ---- + copyNonTreeObjects(dirIn, dirOut); + + std::cout << "Done: " << dirIn->GetName() << "\n"; +} + +// ============================================================================ +// SECTION 11 — Post-write validation +// ============================================================================ +// +// AODBcRewriterValidate() opens a rewritten AO2D and checks key invariants: +// 1. BC table is strictly monotonic in fGlobalBC. +// 2. MC particle intra-table daughter/mother indices are in range and point +// to particles belonging to the same MC collision. +// 3. Every paste-joined child table has the same row count as its parent +// (e.g. O2mccollisionlabel matches O2collision). +// 4. Every fIndex* value across the DF is in range w.r.t. its referent +// table (value -1 is always permitted as the "no link" sentinel). +// 5. Every fIndex* branch present is covered by kIndexRefs — an unknown link +// is a link nothing remapped. +// +// Returns true if all checks pass. Prints [FAIL] lines for each violation. +// +// NOTE on what these checks can and cannot see: they are all *structural*. The +// O2-7098 corruption was in-range and structurally perfect — the row numbers +// were simply the wrong ones. Catching that needs the input file too; see +// AODBcRewriterCheckLinks() at the end of this section. + +// Find a tree in d whose name matches the given table prefix (see isTableNamed). +// Returns the number of entries, or -1 if not found. +static Long64_t treeEntriesByPrefix(TDirectory *d, const char *prefix) { + TIter it(d->GetListOfKeys()); + TKey *k; + while ((k = (TKey*)it())) { + if (!isTableNamed(k->GetName(), prefix)) continue; + TObject *obj = d->Get(k->GetName()); + if (!obj || !obj->InheritsFrom(TTree::Class())) continue; + return ((TTree*)obj)->GetEntries(); + } + return -1; +} + +// Entries of the table a given index branch refers to (first prefix that +// resolves wins), or -1 if the referent is not in this DF. +static Long64_t referentEntries(TDirectory *d, + const std::vector &prefixes) { + for (auto &p : prefixes) { + Long64_t n = treeEntriesByPrefix(d, p.c_str()); + if (n >= 0) return n; + } + return -1; +} + +// Generic in-range check for every fIndex* branch listed above. Reads the +// branch's leaf (scalar, fixed-array, or VLA), iterates all entries, and +// counts how many values are outside [-1, nReferent). +static Long64_t checkIndexRange(TTree *t, const char *branchName, + Long64_t nReferent) { + TBranch *br = t->GetBranch(branchName); + if (!br) return 0; + TLeaf *leaf = (TLeaf*)br->GetListOfLeaves()->At(0); + if (!leaf) return 0; + if (TString(leaf->GetTypeName()) != "Int_t") return 0; // only Int_t indices + + TLeaf *cntLeaf = leaf->GetLeafCount(); // VLA? + int fixedN = leaf->GetLen(); // 1 for scalar, >1 for fixed array + + // Allocate worst-case buffer. For a VLA we need a prescan to size it. + Long64_t maxLen = fixedN; + if (cntLeaf) { + // simple prescan + Int_t cnt = 0; + TBranch *cntBr = cntLeaf->GetBranch(); + cntBr->SetAddress(&cnt); + for (Long64_t i = 0; i < t->GetEntries(); ++i) { + cntBr->GetEntry(i); + if (cnt > maxLen) maxLen = cnt; + } + } + std::vector buf(std::max(1, maxLen), 0); + Int_t cnt = fixedN; + TBranch *cntBr = cntLeaf ? cntLeaf->GetBranch() : nullptr; + br->SetAddress(buf.data()); + if (cntBr) cntBr->SetAddress(&cnt); + + Long64_t bad = 0; + for (Long64_t i = 0; i < t->GetEntries(); ++i) { + br->GetEntry(i); + if (cntBr) cntBr->GetEntry(i); + int n = cntBr ? (int)cnt : fixedN; + for (int j = 0; j < n; ++j) { + Int_t v = buf[j]; + if (v < -1) { ++bad; continue; } + if (v >= (Int_t)nReferent) { ++bad; continue; } + } + } + br->ResetAddress(); + if (cntBr) cntBr->ResetAddress(); + return bad; +} + +// Verify the collision-grouping invariant that O2's ArrowTableSlicingCache +// (validateOrder) enforces on the consumer side: in a table grouped by +// fIndexCollisions, every distinct index value — including the -1 "ambiguous" +// group — must occupy a single contiguous run of rows. A split group is +// exactly the failure that crashed event-selection downstream +// ("Table ... index fIndexCollisions has a group with index -1 that is split +// by N"). This is the post-write counterpart to Stage 1b's regrouping. +// Returns the number of groups found split into >1 run (0 = OK). +static Long64_t checkCollisionGroupContiguity(TTree *t) { + TBranch *br = t->GetBranch("fIndexCollisions"); + if (!br) return 0; + TLeaf *leaf = (TLeaf*)br->GetListOfLeaves()->At(0); + if (!leaf || TString(leaf->GetTypeName()) != "Int_t") return 0; + if (leaf->GetLen() != 1 || leaf->GetLeafCount()) return 0; // scalar only + + Int_t idx = 0; + br->SetAddress(&idx); + std::unordered_set closed; // groups whose run has already ended + Int_t cur = 0; bool have = false; + Long64_t split = 0; + for (Long64_t i = 0; i < t->GetEntries(); ++i) { + br->GetEntry(i); + if (have && idx == cur) continue; // current run continues + if (have) closed.insert(cur); // previous run just ended + if (closed.count(idx)) ++split; // re-opening a group seen earlier + cur = idx; have = true; + } + br->ResetAddress(); + return split; +} + +static bool validateDF(TDirectory *d) { + bool ok = true; + + // ---- discover key trees ---- + TIter it(d->GetListOfKeys()); + TKey *k; + TTree *bcTree = nullptr; + TTree *mcpTree = nullptr; + while ((k = (TKey*)it())) { + TObject *obj = d->Get(k->GetName()); + if (!obj || !obj->InheritsFrom(TTree::Class())) continue; + TTree *t = (TTree*)obj; + TString tn = t->GetName(); + if (tn.BeginsWith("O2bc_")) bcTree = t; + if (tn.BeginsWith("O2mcparticle")) mcpTree = t; + } + + if (bcTree) { + ULong64_t gbc = 0, prev = 0; + bcTree->SetBranchAddress("fGlobalBC", &gbc); + Long64_t nBC = bcTree->GetEntries(); + Long64_t nBad = 0; + for (Long64_t i = 0; i < nBC; ++i) { + bcTree->GetEntry(i); + if (i > 0 && gbc <= prev) ++nBad; + prev = gbc; + } + if (nBad > 0) { + std::cerr << " [FAIL] " << bcTree->GetName() + << ": " << nBad << " non-monotonic BC entries\n"; + ok = false; + } + } + + // ---- MC particle intra-table indices ---- + if (mcpTree) { + Long64_t nMcp = mcpTree->GetEntries(); + Int_t daughters[2] = {-1,-1}, mcCollIdx = -1, motherSize = 0, mothers[200] = {}; + mcpTree->SetBranchStatus("*", 0); + mcpTree->SetBranchStatus("fIndexSlice_Daughters", 1); + mcpTree->SetBranchStatus("fIndexMcCollisions", 1); + mcpTree->SetBranchStatus("fIndexArray_Mothers_size",1); + mcpTree->SetBranchStatus("fIndexArray_Mothers", 1); + mcpTree->SetBranchAddress("fIndexSlice_Daughters", daughters); + mcpTree->SetBranchAddress("fIndexMcCollisions", &mcCollIdx); + mcpTree->SetBranchAddress("fIndexArray_Mothers_size",&motherSize); + mcpTree->SetBranchAddress("fIndexArray_Mothers", mothers); + + // Pre-load MC collision index for cross-collision check + std::vector allMcColl(nMcp); + for (Long64_t i = 0; i < nMcp; ++i) { mcpTree->GetEntry(i); allMcColl[i] = mcCollIdx; } + + Long64_t badSlice = 0, badMother = 0, badXcoll = 0; + for (Long64_t i = 0; i < nMcp; ++i) { + mcpTree->GetEntry(i); + if (daughters[0] >= 0) { + if (daughters[0] >= nMcp || daughters[1] >= nMcp || daughters[0] > daughters[1]) + ++badSlice; + else for (Int_t d2 = daughters[0]; d2 <= daughters[1]; ++d2) + if (allMcColl[d2] != mcCollIdx) ++badXcoll; + } + for (int m = 0; m < std::min(motherSize, 200); ++m) { + if (mothers[m] >= 0) { + if (mothers[m] >= nMcp) ++badMother; + else if (allMcColl[mothers[m]] != mcCollIdx) ++badXcoll; + } + } + } + if (badSlice || badMother || badXcoll) { + std::cerr << " [FAIL] " << mcpTree->GetName() + << ": bad_slice=" << badSlice + << " bad_mother=" << badMother + << " cross_coll=" << badXcoll << "\n"; + ok = false; + } + mcpTree->SetBranchStatus("*", 1); + } + + // ---- Paste-join row-count parity ---- + // For every (child, parent) pair in kPasteJoins, if both are present in the + // DF their row counts must be identical. This catches the class of bugs + // where a child was sorted/dropped on its own index (e.g. a previous + // version dropped O2mccollisionlabel rows on MC-collision dedup while + // leaving O2collision_001 intact, producing an off-by-N mismatch). + for (auto &[childPrefix, parentPrefix] : kPasteJoins) { + Long64_t nChild = treeEntriesByPrefix(d, childPrefix.c_str()); + Long64_t nParent = treeEntriesByPrefix(d, parentPrefix.c_str()); + if (nChild < 0 || nParent < 0) continue; // pair not both present + if (nChild != nParent) { + std::cerr << " [FAIL] paste-join size mismatch: " << childPrefix << "*" + << " has " << nChild << " rows but parent " << parentPrefix << "*" + << " has " << nParent << "\n"; + ok = false; + } + } + + // ---- Generic fIndex* range check + registry drift guard ---- + // For each table in the DF, scan all fIndex* branches and confirm every + // value lies in [-1, nReferent). This catches stale pointers across + // tables (cross-table index drift) which a per-DF-tree-only check misses. + // + // Any fIndex* branch that is NOT in kIndexRefs is a hard failure: the + // rewriter cannot have remapped it, so if the referent table was reordered + // the column is now silently wrong. Adding the entry to kIndexRefs is the + // fix — that is the whole point of having one registry. + TIter it2(d->GetListOfKeys()); + TKey *k2; + while ((k2 = (TKey*)it2())) { + TObject *obj = d->Get(k2->GetName()); + if (!obj || !obj->InheritsFrom(TTree::Class())) continue; + TTree *t = (TTree*)obj; + for (auto &b : unregisteredIndexBranches(t)) { + std::cerr << " [FAIL] " << t->GetName() << "." << b + << " is not registered in kIndexRefs — nothing remaps it\n"; + ok = false; + } + for (auto &[branchName, referentPrefixes] : kIndexRefs) { + if (!t->GetBranch(branchName.c_str())) continue; + Long64_t nRef = referentEntries(d, referentPrefixes); + if (nRef < 0) continue; // referent not in this DF; skip silently + Long64_t bad = checkIndexRange(t, branchName.c_str(), nRef); + if (bad > 0) { + std::cerr << " [FAIL] " << t->GetName() << "." << branchName + << ": " << bad << " value(s) out of range [-1, " << nRef << ")\n"; + ok = false; + } + } + } + + // ---- Collision-group contiguity (slicing invariant) ---- + // O2's slicing cache requires each fIndexCollisions group (incl. -1) to be a + // single contiguous run. This is the exact invariant whose violation crashed + // event-selection; it is what Stage 1b re-establishes. Check every + // collision-grouped track table (paste-join children follow their parent's + // row order, so checking the parent suffices). + TIter it3(d->GetListOfKeys()); + TKey *k3; + while ((k3 = (TKey*)it3())) { + TObject *obj = d->Get(k3->GetName()); + if (!obj || !obj->InheritsFrom(TTree::Class())) continue; + TTree *t = (TTree*)obj; + if (!isCollGroupedTrackTable(t->GetName())) continue; + if (isPasteJoinChild(t->GetName())) continue; + Long64_t split = checkCollisionGroupContiguity(t); + if (split > 0) { + std::cerr << " [FAIL] " << t->GetName() + << ": fIndexCollisions has " << split + << " group(s) split into non-contiguous runs (slicing will abort)\n"; + ok = false; + } + } + + return ok; +} + +bool AODBcRewriterValidate(const char *fname = "AO2D_rewritten.root") { + std::cout << "Validating " << fname << "\n"; + std::unique_ptr f(TFile::Open(fname, "READ")); + if (!f || f->IsZombie()) { std::cerr << "Cannot open " << fname << "\n"; return false; } + + bool allOk = true; + int nDF = 0; + TIter top(f->GetListOfKeys()); + TKey *k; + while ((k = (TKey*)top())) { + if (!TString(k->GetName()).BeginsWith("DF_")) continue; + TDirectory *d = (TDirectory*)f->Get(k->GetName()); + bool dfOk = validateDF(d); + if (!dfOk) std::cerr << " -> FAILED in " << k->GetName() << "\n"; + allOk = allOk && dfOk; + ++nDF; + } + f->Close(); + if (allOk) + std::cout << "VALIDATION PASSED (" << nDF << " DFs checked)\n"; + else + std::cout << "VALIDATION FAILED — see [FAIL] lines above\n"; + return allOk; +} + +// ============================================================================ +// SECTION 11b — Link preservation check (input vs output) +// ============================================================================ +// +// WHY THIS EXISTS +// +// Everything in Section 11 is a *structural* check of the output alone: are the +// BCs monotonic, are the paste-join row counts equal, is every index value in +// range. O2-7098 passed all of them. Stage 1b reordered O2fwdtrack without +// remapping fIndexMFTTracks / fIndexFwdTracks_MatchMCHTrack, so every global +// muon pointed at a perfectly valid, perfectly in-range, completely unrelated +// MFT track. No structural check can see that — you have to compare against +// the input. +// +// THE INVARIANT +// +// The rewriter permutes rows and drops duplicates. It must never change what a +// row *is* or what it *points at*. So for every table, the multiset of tuples +// +// ( payload fingerprint of the row, +// payload fingerprint of the row each index column points at, ... ) +// +// must be the same before and after — up to rows that were legitimately +// dropped. Row identity comes from the payload fingerprint (a hash of every +// non-fIndex* branch), so no permutation map, no synthetic tagging and no +// assumption about ordering is needed. It runs on real production AO2Ds. +// +// Dedup is handled by canonicalising the INPUT side: if a referenced input row +// has no counterpart in the output (it was dropped), the link is rewritten to +// the "no link" sentinel first, which is exactly what the rewriter does. A +// tuple that exists in the output but in no input row is then always a bug. + +static const ULong64_t kFnvOffset = 14695981039346656037ULL; +static const ULong64_t kFnvPrime = 1099511628211ULL; +static const ULong64_t kNullFp = 0ULL; // reserved: "points at nothing" + +static ULong64_t fnv1a(ULong64_t h, const void *data, size_t n) { + const unsigned char *p = static_cast(data); + for (size_t i = 0; i < n; ++i) { h ^= p[i]; h *= kFnvPrime; } + return h; +} + +// Per-row hash of every non-index branch. Two rows with the same fingerprint +// are interchangeable as far as this check is concerned, which is the right +// semantics: identical rows may be freely permuted among themselves. +// +// `onlyBranch`, when given, restricts the hash to that single branch. It is +// used for the BC table, and only for it: Stage 0 deduplicates BCs by +// fGlobalBC and *redirects* references onto the surviving row rather than +// nulling them, so two BC rows that share a fGlobalBC have to be treated as one +// identity here. Every other dedup in this tool drops rows and nulls the +// references, which the survivor canonicalisation below handles. +static std::vector payloadFingerprints(TTree *t, + const char *onlyBranch = nullptr) { + std::vector fps; + if (!t) return fps; + Long64_t n = t->GetEntries(); + fps.resize(n, kFnvOffset); + + auto descs = describeBranches(t); + struct IO { + BranchDesc desc; + std::vector dataBuf, countBuf; + ScalarTag countTag = ScalarTag::kUnknown; + TBranch *br = nullptr, *cntBr = nullptr; + }; + std::vector ios; + for (auto &d : descs) { + if (onlyBranch) { if (d.name != onlyBranch) continue; } + else if (d.name.rfind("fIndex", 0) == 0) continue; // index columns are not payload + IO io; io.desc = d; + io.dataBuf.assign((d.isVLA ? d.maxElems : d.nElems) * d.elemSize, 0); + io.br = t->GetBranch(d.name.c_str()); + if (d.isVLA) { + io.cntBr = t->GetBranch(d.countBranchName.c_str()); + TLeaf *cl = io.cntBr ? static_cast(io.cntBr->GetListOfLeaves()->At(0)) : nullptr; + io.countTag = cl ? tagOf(cl) : ScalarTag::kUnknown; + io.countBuf.assign(byteSize(io.countTag), 0); + } + ios.push_back(std::move(io)); + } + for (auto &io : ios) { + if (io.br) io.br->SetAddress(io.dataBuf.data()); + if (io.cntBr) io.cntBr->SetAddress(io.countBuf.data()); + } + + for (Long64_t i = 0; i < n; ++i) { + ULong64_t h = kFnvOffset; + for (auto &io : ios) { + if (io.br) io.br->GetEntry(i); + if (io.cntBr) io.cntBr->GetEntry(i); + if (io.desc.isVLA) { + Long64_t cnt = readAsInt(io.countBuf.data(), io.countTag); + h = fnv1a(h, &cnt, sizeof(cnt)); + h = fnv1a(h, io.dataBuf.data(), (size_t)std::max(0, cnt) * io.desc.elemSize); + } else { + h = fnv1a(h, io.dataBuf.data(), (size_t)io.desc.nElems * io.desc.elemSize); + } + } + fps[i] = (h == kNullFp) ? 1ULL : h; // never collide with the NULL sentinel + } + t->ResetBranchAddresses(); + return fps; +} + +// One index column of a table, resolved to the referent's fingerprint table. +struct LinkColumn { + std::string branchName; + const std::vector *refFp = nullptr; // referent fingerprints, same file + const std::unordered_set *survivors = nullptr; // input side only +}; + +// Multiset (as a count map) of the per-row tuples described at the top of this +// section, hashed down to a single 64-bit key. +static std::map linkTupleCounts( + TTree *t, const std::vector &ownFp, + const std::vector &links) { + + std::map counts; + Long64_t n = t->GetEntries(); + + struct IO { + BranchDesc desc; + std::vector dataBuf, countBuf; + ScalarTag countTag = ScalarTag::kUnknown; + TBranch *br = nullptr, *cntBr = nullptr; + const LinkColumn *link = nullptr; + }; + auto descs = describeBranches(t); + std::vector ios; + for (auto &lk : links) { + for (auto &d : descs) { + if (d.name != lk.branchName) continue; + IO io; io.desc = d; io.link = &lk; + io.dataBuf.assign((d.isVLA ? d.maxElems : d.nElems) * d.elemSize, 0); + io.br = t->GetBranch(d.name.c_str()); + if (d.isVLA) { + io.cntBr = t->GetBranch(d.countBranchName.c_str()); + TLeaf *cl = io.cntBr ? static_cast(io.cntBr->GetListOfLeaves()->At(0)) : nullptr; + io.countTag = cl ? tagOf(cl) : ScalarTag::kUnknown; + io.countBuf.assign(byteSize(io.countTag), 0); + } + ios.push_back(std::move(io)); + break; + } + } + for (auto &io : ios) { + if (io.br) io.br->SetAddress(io.dataBuf.data()); + if (io.cntBr) io.cntBr->SetAddress(io.countBuf.data()); + } + + for (Long64_t i = 0; i < n; ++i) { + ULong64_t h = fnv1a(kFnvOffset, &ownFp[i], sizeof(ULong64_t)); + for (auto &io : ios) { + if (io.br) io.br->GetEntry(i); + if (io.cntBr) io.cntBr->GetEntry(i); + int cnt = io.desc.isVLA ? (int)readAsInt(io.countBuf.data(), io.countTag) + : io.desc.nElems; + for (int j = 0; j < cnt; ++j) { + Long64_t v = readAsInt(io.dataBuf.data() + (size_t)j * io.desc.elemSize, + io.desc.tag); + ULong64_t fp = kNullFp; + if (v >= 0 && v < (Long64_t)io.link->refFp->size()) { + fp = (*io.link->refFp)[v]; + // Input side: a referent row that did not survive into the output is + // expected to become a null link, so canonicalise it to one here. + if (io.link->survivors && !io.link->survivors->count(fp)) fp = kNullFp; + } + h = fnv1a(h, &fp, sizeof(fp)); + } + } + ++counts[h]; + } + t->ResetBranchAddresses(); + return counts; +} + +static bool checkLinksDF(TDirectory *din, TDirectory *dout, const char *dfName) { + bool ok = true; + + // Collect the tables present in both files. + std::vector tables; + TIter it(din->GetListOfKeys()); + while (TKey *k = static_cast(it())) { + if (TString(k->GetClassName()) != "TTree") continue; + std::string tn = k->GetName(); + if (!dout->Get(tn.c_str())) { + std::cerr << " [FAIL] " << dfName << ": table " << tn << " missing from output\n"; + ok = false; + continue; + } + tables.push_back(tn); + } + + // Fingerprint every table in both files once — referents are looked up by name. + std::unordered_map> fpIn, fpOut; + std::unordered_map> survivors; + for (auto &tn : tables) { + const char *only = isTableNamed(tn, "O2bc") ? "fGlobalBC" : nullptr; + fpIn[tn] = payloadFingerprints(dynamic_cast(din->Get(tn.c_str())), only); + fpOut[tn] = payloadFingerprints(dynamic_cast(dout->Get(tn.c_str())), only); + survivors[tn].insert(fpOut[tn].begin(), fpOut[tn].end()); + } + + for (auto &tn : tables) { + TTree *tIn = dynamic_cast(din->Get(tn.c_str())); + TTree *tOut = dynamic_cast(dout->Get(tn.c_str())); + if (!tIn || !tOut) continue; + + if (tOut->GetEntries() > tIn->GetEntries()) { + std::cerr << " [FAIL] " << dfName << ": " << tn << " grew from " + << tIn->GetEntries() << " to " << tOut->GetEntries() << " rows\n"; + ok = false; + } + + // Resolve this table's index columns to their referent fingerprint tables. + std::vector linksIn, linksOut; + for (auto &[branchName, prefixes] : kIndexRefs) { + if (!tIn->GetBranch(branchName.c_str())) continue; + std::string refName; + for (auto &p : prefixes) { + for (auto &cand : tables) if (isTableNamed(cand, p)) { refName = cand; break; } + if (!refName.empty()) break; + } + if (refName.empty()) continue; // referent not in this DF + linksIn.push_back({branchName, &fpIn[refName], &survivors[refName]}); + linksOut.push_back({branchName, &fpOut[refName], nullptr}); + } + + // Ordering preservation: a column that is sorted on input describes a + // grouping the file carries, and O2's slicing cache relies on it. Remapping + // the values while leaving the rows in place silently destroys it — the same + // defect as the split "-1" group, just in a different table. + for (auto &[branchName, prefixes] : kIndexRefs) { + std::vector vIn, vOut; + if (!readScalarIndexColumn(tIn, branchName.c_str(), vIn)) continue; + if (!isOrderedWithNullsLast(vIn)) continue; // no ordering to preserve + if (!readScalarIndexColumn(tOut, branchName.c_str(), vOut)) continue; + if (!isOrderedWithNullsLast(vOut)) { + std::cerr << " [FAIL] " << dfName << ": " << tn << "." << branchName + << " was sorted on input but is not on output" + " (grouping destroyed — slicing will misbehave)\n"; + ok = false; + } + } + + auto cIn = linkTupleCounts(tIn, fpIn[tn], linksIn); + auto cOut = linkTupleCounts(tOut, fpOut[tn], linksOut); + + // Every output tuple must be accounted for by an input tuple. The reverse + // is not required: dedup legitimately removes rows. + Long64_t unexplained = 0; + for (auto &[key, nOut] : cOut) { + auto found = cIn.find(key); + Long64_t nIn = (found == cIn.end()) ? 0 : found->second; + if (nOut > nIn) unexplained += nOut - nIn; + } + if (unexplained > 0) { + std::cerr << " [FAIL] " << dfName << ": " << tn << " — " << unexplained + << " of " << tOut->GetEntries() + << " output row(s) have a payload/link combination that no input" + " row had (rows or references were mis-permuted)\n"; + ok = false; + } + } + return ok; +} + +// Compare a rewritten AO2D against the file it was produced from and verify +// that no row changed what it points at. This is the check that catches the +// O2-7098 bug class; run it whenever AODBcRewriter.C is touched. +bool AODBcRewriterCheckLinks(const char *inFileName = "AO2D_pre.root", + const char *outFileName = "AO2D_rewritten.root") { + std::cout << "Checking link preservation: " << inFileName + << " -> " << outFileName << "\n"; + if (TString(inFileName).BeginsWith("alien:") || + TString(outFileName).BeginsWith("alien:")) TGrid::Connect("alien"); + std::unique_ptr fin(TFile::Open(inFileName, "READ")); + std::unique_ptr fout(TFile::Open(outFileName, "READ")); + if (!fin || fin->IsZombie()) { std::cerr << "Cannot open " << inFileName << "\n"; return false; } + if (!fout || fout->IsZombie()) { std::cerr << "Cannot open " << outFileName << "\n"; return false; } + + bool allOk = true; + int nDF = 0; + TIter top(fin->GetListOfKeys()); + while (TKey *k = static_cast(top())) { + if (!isDF(k->GetName())) continue; + TDirectory *din = dynamic_cast(fin->Get(k->GetName())); + TDirectory *dout = dynamic_cast(fout->Get(k->GetName())); + if (!din) continue; + if (!dout) { + std::cerr << " [FAIL] " << k->GetName() << " missing from output\n"; + allOk = false; + continue; + } + allOk = checkLinksDF(din, dout, k->GetName()) && allOk; + ++nDF; + } + fin->Close(); + fout->Close(); + if (allOk) std::cout << "LINK CHECK PASSED (" << nDF << " DFs checked)\n"; + else std::cout << "LINK CHECK FAILED — see [FAIL] lines above\n"; + return allOk; +} + +// ============================================================================ +// SECTION 12 — Top-level entry point +// ============================================================================ + +void AODBcRewriter(const char *inFileName = "AO2D.root", + const char *outFileName = "AO2D_rewritten.root") { + + std::cout << "AODBcRewriter: input=" << inFileName + << " output=" << outFileName << "\n"; + if (TString(inFileName).BeginsWith("alien:")) { + TGrid::Connect("alien"); + } + std::unique_ptr fin(TFile::Open(inFileName, "READ")); + if (!fin || fin->IsZombie()) { std::cerr << "ERROR: cannot open " << inFileName << "\n"; return; } + + int algo = fin->GetCompressionAlgorithm(); + int lvl = fin->GetCompressionLevel(); + +#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 30, 0) + std::unique_ptr fout(TFile::Open(outFileName, "RECREATE", "", lvl)); +#else + std::unique_ptr fout(TFile::Open(outFileName, "RECREATE")); +#endif + if (!fout || fout->IsZombie()) { std::cerr << "ERROR: cannot create " << outFileName << "\n"; return; } + fout->SetCompressionAlgorithm(algo); + fout->SetCompressionLevel(lvl); + + TIter top(fin->GetListOfKeys()); + while (TKey *key = static_cast(top())) { + TString name = key->GetName(); + std::unique_ptr obj(key->ReadObj()); + + if (obj->InheritsFrom(TDirectory::Class()) && isDF(name)) { + TDirectory *din = static_cast(obj.get()); + TDirectory *dout = fout->mkdir(name); + processDF(din, dout); + } else { + // Top-level non-DF objects (metadata TMaps etc.) + fout->cd(); + if (obj->IsA()->InheritsFrom(TMap::Class())) + fout->WriteTObject(obj.get(), name, "Overwrite"); + else + obj->Write(name, TObject::kOverwrite); + } + } + + fout->Write("", TObject::kOverwrite); + fout->Close(); + fin->Close(); + std::cout << "All done. Output: " << outFileName << "\n"; +} diff --git a/MC/utils/AOD_check_globalBC_monotonic.C b/MC/utils/AOD_check_globalBC_monotonic.C new file mode 100644 index 000000000..b92472d5e --- /dev/null +++ b/MC/utils/AOD_check_globalBC_monotonic.C @@ -0,0 +1,141 @@ +// Scans all DF_* folders for O2bc_001 TTrees and checks that the +// fGlobalBC branch (ULong64_t) is monotonically non-decreasing. + +#ifndef __CLING__ +#include "TBranch.h" +#include "TDirectory.h" +#include "TFile.h" +#include "TKey.h" +#include "TLeaf.h" +#include "TTree.h" +#include +#include +#include +#endif + +struct BCReport { + bool hasBranch = false; + bool monotonic = true; + Long64_t entries = 0; + Long64_t firstViolationEntry = -1; + Long64_t nViolations = 0; + ULong64_t maxBackwardJump = 0; + std::vector>> samples; +}; + +static BCReport checkO2bcTree(TTree *t) { + BCReport r; + if (!t) + return r; + + TBranch *br = t->GetBranch("fGlobalBC"); + if (!br) + return r; + r.hasBranch = true; + + ULong64_t buf = 0; + br->SetAddress(&buf); + + r.entries = t->GetEntries(); + if (r.entries <= 1) + return r; + + bool havePrev = false; + ULong64_t prevVal = 0; + + for (Long64_t i = 0; i < r.entries; ++i) { + t->GetEntry(i); + ULong64_t v = buf; + + if (!havePrev) { + prevVal = v; + havePrev = true; + continue; + } + + if (v < prevVal) { + if (r.firstViolationEntry < 0) + r.firstViolationEntry = i; + ++r.nViolations; + r.monotonic = false; + ULong64_t jump = prevVal - v; + if (jump > r.maxBackwardJump) + r.maxBackwardJump = jump; + if (r.samples.size() < 5) + r.samples.push_back({i, {prevVal, v}}); + } + + prevVal = v; + } + + return r; +} + +void AOD_check_globalBC_monotonic(const char *inFileName = "AO2D.root") { + std::string inFileStr(inFileName); + if (inFileStr.find("alien:") != std::string::npos) { + TGrid::Connect("alien"); + } + + std::cout << "Opening file: " << inFileName << std::endl; + TFile *f = TFile::Open(inFileName, "READ"); + if (!f || f->IsZombie()) { + std::cerr << "ERROR: cannot open input file.\n"; + return; + } + + Long64_t totalTrees = 0; + Long64_t totalWithBranch = 0; + Long64_t totalViolations = 0; + + TIter topKeys(f->GetListOfKeys()); + while (TKey *k = (TKey *)topKeys()) { + TObject *obj = k->ReadObj(); + if (!obj->InheritsFrom(TDirectory::Class())) + continue; + + TDirectory *dir = (TDirectory *)obj; + TString dname = dir->GetName(); + if (!dname.BeginsWith("DF_")) + continue; + + TTree *t = (TTree *)dir->Get("O2bc_001"); + if (!t) + continue; + ++totalTrees; + + BCReport r = checkO2bcTree(t); + + if (!r.hasBranch) { + std::cout << "[skip] " << dir->GetName() + << "/O2bc_001 has no fGlobalBC\n"; + continue; + } + ++totalWithBranch; + + if (r.monotonic) { + std::cout << "[ OK ] " << dir->GetName() << "/O2bc_001 — " << r.entries + << " entries, monotonic\n"; + } else { + ++totalViolations; + std::cout << "[BAD] " << dir->GetName() << "/O2bc_001 — " << r.entries + << " entries, first violation at entry " + << r.firstViolationEntry + << ", total violations: " << r.nViolations + << ", max backward jump: " << r.maxBackwardJump << "\n"; + + for (auto &s : r.samples) { + std::cout << " entry " << s.first << ": " << s.second.first + << " -> " << s.second.second << "\n"; + } + } + } + + std::cout << "\n==================== SUMMARY ====================\n"; + std::cout << "O2bc_001 trees checked: " << totalTrees << "\n"; + std::cout << "With fGlobalBC branch: " << totalWithBranch << "\n"; + std::cout << "Trees NOT monotonic: " << totalViolations << "\n"; + std::cout << "=================================================\n"; + + f->Close(); +} diff --git a/MC/utils/AOD_detect_unsorted_fIndexBCs.C b/MC/utils/AOD_detect_unsorted_fIndexBCs.C new file mode 100644 index 000000000..13571ac85 --- /dev/null +++ b/MC/utils/AOD_detect_unsorted_fIndexBCs.C @@ -0,0 +1,260 @@ +// Scans DF_* folders, finds trees with an Int-like "fIndexBCs" branch, +// and reports those where fIndexBCs is not monotonically non-decreasing. +// Negative values (e.g. -1) are ignored for the monotonicity check. + +#ifndef __CLING__ +#include "TBranch.h" +#include "TDirectory.h" +#include "TFile.h" +#include "TKey.h" +#include "TLeaf.h" +#include "TString.h" +#include "TTree.h" +#include +#include +#include +#include +#include +#endif + +struct MonotonicReport { + bool hasBranch = false; // tree has fIndexBCs + bool monotonic = true; // true if non-decreasing + Long64_t entries = 0; + Long64_t firstViolationEntry = -1; // entry index of first backward step + Long64_t nViolations = 0; // count of backward steps + Long64_t maxBackwardJump = 0; // biggest (prevValid - curr) observed + std::vector>> + samples; // (entry, (prev, curr)) +}; + +/// Try to bind a branch named "fIndexBCs" with an integral buffer. +/// Supports common integer POD types (Int_t, UInt_t, Long64_t, ULong64_t). +/// Returns: pointer to bound buffer as int64_t-compatible view (value is +/// copied), and sets branch address appropriately. +class FIndexBinder { +public: + TBranch *br = nullptr; + std::string type; + // One of these will be used based on the branch's leaf type: + Int_t buf_i = 0; + UInt_t buf_ui = 0; + Long64_t buf_l = 0; + ULong64_t buf_ul = 0; + + // which is active + enum Kind { KNone, KInt, KUInt, KLong64, KULong64 } kind = KNone; + + bool bind(TTree *t, const char *name = "fIndexBCs") { + br = t->GetBranch(name); + if (!br) + return false; + if (br->GetListOfLeaves()->GetEntries() <= 0) + return false; + TLeaf *leaf = (TLeaf *)br->GetListOfLeaves()->At(0); + type = leaf->GetTypeName(); + + if (type == "Int_t") { + kind = KInt; + br->SetAddress(&buf_i); + } else if (type == "UInt_t") { + kind = KUInt; + br->SetAddress(&buf_ui); + } else if (type == "Long64_t") { + kind = KLong64; + br->SetAddress(&buf_l); + } else if (type == "ULong64_t") { + kind = KULong64; + br->SetAddress(&buf_ul); + } else { + // not an integer POD we handle + kind = KNone; + br = nullptr; + return false; + } + return true; + } + + // Read the current value as signed 64-bit (for comparisons). + // For unsigned, cast safely to signed domain if within range; otherwise + // clamp. + Long64_t valueAsI64() const { + switch (kind) { + case KInt: + return (Long64_t)buf_i; + case KUInt: + return (buf_ui <= (UInt_t)std::numeric_limits::max() + ? (Long64_t)buf_ui + : (Long64_t)std::numeric_limits::max()); + case KLong64: + return buf_l; + case KULong64: + return (buf_ul <= (ULong64_t)std::numeric_limits::max() + ? (Long64_t)buf_ul + : (Long64_t)std::numeric_limits::max()); + default: + return 0; + } + } +}; + +static MonotonicReport checkTreeMonotonic(TTree *t, bool verbose = false) { + MonotonicReport r; + if (!t) + return r; + + // Speed up: only read the target branch + t->SetBranchStatus("*", 0); + if (t->GetBranch("fIndexBCs")) + t->SetBranchStatus("fIndexBCs", 1); + + FIndexBinder binder; + if (!binder.bind(t, "fIndexBCs")) { + r.hasBranch = false; + // Re-enable all for safety if user continues using the tree later + t->SetBranchStatus("*", 1); + return r; + } + r.hasBranch = true; + + r.entries = t->GetEntries(); + if (r.entries <= 1) { + r.monotonic = true; + t->SetBranchStatus("*", 1); + return r; + } + + bool havePrev = false; + Long64_t prevValid = 0; + + for (Long64_t i = 0; i < r.entries; ++i) { + t->GetEntry(i); + Long64_t v = binder.valueAsI64(); + + // Ignore negatives (e.g. -1 sentinel values) + if (v < 0) + continue; + + if (!havePrev) { + prevValid = v; + havePrev = true; + continue; + } + + if (v < prevValid) { + // backward step + if (r.firstViolationEntry < 0) + r.firstViolationEntry = i; + ++r.nViolations; + r.monotonic = false; + Long64_t jump = prevValid - v; + if (jump > r.maxBackwardJump) + r.maxBackwardJump = jump; + if (r.samples.size() < 5) + r.samples.push_back({i, {prevValid, v}}); + // Do not update prevValid here; we keep comparing to last valid + // non-decreasing reference + continue; + } + + // normal non-decreasing step + prevValid = v; + } + + // Restore statuses + t->SetBranchStatus("*", 1); + return r; +} + +void AOD_detect_unsorted_fIndexBCs(const char *inFileName = "AO2D.root", + bool verbosePerTree = false) { + std::cout << "Opening file: " << inFileName << std::endl; + TFile *f = TFile::Open(inFileName, "READ"); + if (!f || f->IsZombie()) { + std::cerr << "ERROR: cannot open input file.\n"; + return; + } + + Long64_t totalTreesChecked = 0; + Long64_t totalWithBranch = 0; + Long64_t totalViolations = 0; + + std::cout << "Scanning top-level for DF_* folders...\n"; + + // Iterate top-level keys + TIter kIt(f->GetListOfKeys()); + while (TKey *k = (TKey *)kIt()) { + TString kname = k->GetName(); + TObject *obj = k->ReadObj(); + + if (!obj->InheritsFrom(TDirectory::Class()) || !kname.BeginsWith("DF_")) { + continue; + } + + auto *dir = (TDirectory *)obj; + std::cout << "\n====================================================\n"; + std::cout << "DF folder: " << dir->GetName() << "\n"; + + // Iterate all keys in this DF directory + TIter dIt(dir->GetListOfKeys()); + while (TKey *dk = (TKey *)dIt()) { + TObject *tobj = dir->Get(dk->GetName()); + if (!tobj->InheritsFrom(TTree::Class())) { + continue; // only trees are relevant + } + + TTree *t = (TTree *)tobj; + ++totalTreesChecked; + + // Only consider trees that *have* fIndexBCs + if (!t->GetBranch("fIndexBCs")) { + if (verbosePerTree) { + std::cout << " [skip] " << t->GetName() << " (no fIndexBCs)\n"; + } + continue; + } + ++totalWithBranch; + + MonotonicReport r = checkTreeMonotonic(t, verbosePerTree); + + if (!r.hasBranch) { + // Shouldn't happen due to prior check, but keep robust + if (verbosePerTree) { + std::cout << " [skip] " << t->GetName() + << " (failed to bind branch)\n"; + } + continue; + } + + if (r.monotonic) { + if (verbosePerTree) { + std::cout << " [ OK ] " << t->GetName() + << " — entries: " << r.entries << " (non-decreasing)\n"; + } + } else { + ++totalViolations; + std::cout << " [BAD] " << t->GetName() << " — entries: " << r.entries + << ", first violation at entry " << r.firstViolationEntry + << ", total backward steps: " << r.nViolations + << ", max backward jump: " << r.maxBackwardJump << "\n"; + + // Print a few examples of (prev, curr) causing violation + if (!r.samples.empty()) { + std::cout << " sample backward steps (entry: prev -> curr):\n"; + for (auto &s : r.samples) { + std::cout << " " << s.first << ": " << s.second.first + << " -> " << s.second.second << "\n"; + } + } + } + } + } + + std::cout << "\n==================== SUMMARY ====================\n"; + std::cout << "Trees visited: " << totalTreesChecked << "\n"; + std::cout << "Trees with fIndexBCs: " << totalWithBranch << "\n"; + std::cout << "Trees NOT monotonic: " << totalViolations << "\n"; + std::cout << "=================================================\n"; + + f->Close(); +} diff --git a/MC/utils/CLAUDE.md b/MC/utils/CLAUDE.md new file mode 100644 index 000000000..13d799382 --- /dev/null +++ b/MC/utils/CLAUDE.md @@ -0,0 +1,548 @@ +# CLAUDE.md — AODBcRewriter Development Handoff + +## Current work state (2026-07-28) — O2-7098 + +**Branch:** `swenzel/O2-7098-aodbcrewriter-index-remap`. + +### The bug: O2fwdtrack's match indices were reordered but not remapped + +Reported by Maurice Coquet (JIRA **O2-7098**, patch proposal +[O2DPG#2418](https://github.com/AliceO2Group/O2DPG/pull/2418)): in anchored MC +AO2Ds produced since early June 2026, `O2fwdtrack.fIndexMFTTracks` and +`O2fwdtrack.fIndexFwdTracks_MatchMCHTrack` point at the wrong rows, so the MFT +leg and the MCH leg of every global muon belong to different MC particles +(`sameParticle=0`). + +**Cause.** Stage 1b (added by #2370, merged 4 Jun 2026) re-sorts `O2track_iu`, +`O2mfttrack_*` and `O2fwdtrack`, but wrote each table the moment it had planned +it. `O2fwdtrack` points at `O2mfttrack` — reordered later in the same loop — and +at *itself*; neither permutation existed yet at write time, so the two columns +kept pre-reorder row numbers. They stayed in range, so every structural check +passed. + +**Scope.** The corruption does *not* need the BC→collision reorder cascade. +Stage 1b re-sorts unconditionally to make the split `-1` group contiguous, so in +practice **every merged MC AO2D since 4 Jun 2026 is affected**. + +**Reproduced** on `example_AOD/AO2D_pre.root`: 171 fwd tracks with an MFT match, +`sameParticle` 30 → **0** with the pre-fix code, 30 → **30** with the fix. + +### What the fix does + +1. **One index registry, `kIndexRefs` (Section 1).** Every `fIndex*` column and + the table it points at, in one place, used by the rewriter, the validator and + the drift guard alike. Referent resolution is `isTableNamed()` (`K == P` or + `K == P_`), which — unlike the old `BeginsWith` — keeps + `O2mfttrack_001` apart from `O2mfttrackcov`, `O2bc_001` from `O2bcflag` and + `O2calo` from `O2calotrigger`. +2. **`buildRemaps()` (Section 3)** derives a table's complete remap list from + that registry. The privileged "primary index + optional extras" split in + `rewriteTable` is gone — there is one `writeTable(src, dirOut, rowOrder, + remaps)` and every column goes through the same path. Forgetting a column is + no longer possible; the old design *required* each stage to remember. +3. **Plan, then write (Sections 3b + 10).** Every stage now only appends a + `TablePlan` and publishes its permutation; `processDF` writes all plans + afterwards, when every permutation in the DF is known. This is what makes the + forward reference (fwd→mft) and the self reference (fwd→fwd) work. +4. **Drift guard.** Any `fIndex*` branch not in `kIndexRefs` is a `[warn]` at + rewrite time and a `[FAIL]` in the validator. Schema growth now breaks the + test instead of silently mis-linking data. +5. **`AODBcRewriterCheckLinks()` (Section 11b)** — the check that can actually + see this bug class; see "Testing" below. +6. **Re-sort tables stored sorted by a reference** (`findGroupingColumn` / + `resortByGroupingColumn`, Section 8). The same family: `O2fwdtrkcl` and + `O2trackqa_003` were coming out unsorted in every DF of two of the three + sample files, which breaks O2's slicing the same way the split `-1` group + did. Derived from the data, not from a list — see resolved gap 1. + +Maurice's #2418 has the same shape (defer `O2fwdtrack`, remap via the MFT/Fwd +perms) and is correct; this generalises it from one table to all of them. + +### Previous work state (2026-06-09): tracks' `-1` collision group split + +Downstream O2 analysis (`o2-analysis-event-selection`) was crashing with: + +``` +[FATAL] Table Tracks_IU index fIndexCollisions has a group with index -1 + that is split by 776 +``` + +O2's `ArrowTableSlicingCache::validateOrder` requires every `fIndexCollisions` +group in a track table — **including the `-1` "ambiguous" group** — to be one +contiguous run of rows. Two commits broke this: + +- **`b11cd3de`** added a value-wise remap of tracks' `fIndexCollisions` via + `collPerm` but left the track **rows in input order**. Since Stage 1 reorders + `O2collision` (sort by remapped `fIndexBCs`), the remapped values no longer + formed contiguous groups → the split. (b11cd3de made the *values* correct at + the cost of the *grouping*; the complete fix needs **both** — reorder rows + *and* remap values.) +- **`28b44ef`** (a colleague's attempt) then replaced the Stage 0 BC **sort** + with an order-preserving dedup that `std::abort()`s unless the input BC table + is already `globalBC`-sorted. That contradicts PURPOSE (a) — repairing + *non-monotonic* BCs in merged files — so it aborted on exactly the files the + tool exists to fix ("doesn't run to completion"). + +### What the current fix does + +1. **Reverted only the Stage 0 change** of `28b44ef`: restored `stage0_sortBCs` + (sort + dedup) and removed the abort. Non-monotonic merged BCs are repaired + again, as intended. +2. **Kept `28b44ef`'s Stage 1b** (`stage1b_reorderTrackTables`, Section 9b) and + the `fIndexTracks*` / `fIndexMFTTracks` / `fIndexFwdTracks` remaps in + `processPasteJoinTables`. This regroup-tracks-by-remapped-`fIndexCollisions` + mechanism is the *correct* fix for the split; it only ever failed because the + aborting Stage 0 stopped it from running. Rewriting it from scratch was + judged higher-risk than keeping the reviewed logic. +3. **Added validator check** `checkCollisionGroupContiguity` (Section 11): + mirrors O2's slicing invariant — flags any `fIndexCollisions` group split + into >1 run. Runs over every collision-grouped track table. + +**Design note / cascade:** sorting BCs is unavoidable for non-monotonic input +and forces a reorder cascade **BC (Stage 0) → collisions (Stage 1) → tracks +(Stage 1b)**, propagated to paste-join children and all track references. Do +**not** re-introduce an "assert already sorted / order-preserving" Stage 0 — it +is a known dead end (see the history note in the Section 4 code comment). + +### Status of the 2026-06-09 items + +- ~~**UNVALIDATED on real data.**~~ Now run on `example_AOD/AO2D_pre.root`, + `bigger/` and `bigger2/` (3–4 DFs each, up to 6M MC particles): rewrite clean, + `AODBcRewriterValidate` and `AODBcRewriterCheckLinks` both pass. The macro is + still interpreter-only; `.L AODBcRewriter.C+` (ACLiC) fails on missing std + includes — pre-existing, not a regression. +- ~~**Known fragility (whack-a-mole).**~~ Resolved by `kIndexRefs` + + `buildRemaps()` + the drift guard (see above). There is now exactly one list, + and an entry missing from it fails the test rather than corrupting output. +- ~~**Biggest gap: no executable CI.**~~ `MC/utils/tests/` now provides one that + needs nothing but ROOT and runs in seconds. Still open: wiring it into the + ALICE CI job list — `.github/workflows/` has no ROOT runner, so it needs a + line wherever `test/run_*_tests.sh` are invoked. **Confirm with the O2DPG CI + owners rather than guessing.** + +--- + +## Testing + +```bash +${O2DPG_ROOT}/MC/utils/tests/run_aodbcrewriter_tests.sh +``` + +Needs only ROOT — no simulation, no O2Physics, no GRID, no committed binary +fixture. `makeTestAOD.C` builds a few-kB synthetic AO2D carrying every pathology +the tool exists to repair (non-monotonic + duplicate BCs, duplicate MC +collisions, a split `-1` track group from two sub-timeframe blocks, fwd↔MFT and +fwd→fwd matches, V0/cascade links, `fIndexSliceBCs`, mcparticle mother/daughter +links, and a link-free table for the fast-clone path). + +Three independent layers, in increasing specificity: + +| layer | what it sees | +|---|---| +| `AODBcRewriterValidate(out)` | structural invariants of the output alone | +| `AODBcRewriterCheckLinks(in, out)` | input vs output: no row changed what it points at | +| `testAODBcRewriter(in, out)` | named, physics-readable assertions | + +**The middle layer is the one that matters for this bug class.** Everything in +`AODBcRewriterValidate` is structural, and O2-7098 was structurally perfect — +the row numbers were simply the wrong ones, and in range. Catching that requires +comparing against the input. `AODBcRewriterCheckLinks` does it without +permutation dumps or synthetic tagging: it fingerprints each row from its +non-`fIndex*` branches and compares, per table, the multiset of + +``` +( payload fingerprint of the row, payload fingerprint of each row it points at ) +``` + +before and after. Dedup is handled by canonicalising the *input* side — a +referenced row that did not survive becomes the null link, which is what the +rewriter does — so a tuple present in the output but in no input row is always a +bug. The BC table is fingerprinted on `fGlobalBC` alone, because Stage 0 +*redirects* references onto the surviving row rather than nulling them. + +Because it needs no synthetic data, run it on real production files too: + +```cpp +root -l -b -q -e '.L AODBcRewriter.C' \ + -e 'AODBcRewriterCheckLinks("AO2D_pre.root","AO2D.root")' +``` + +Regression-locked: against the pre-fix `master` rewriter the suite reports +`O2fwdtrack: links not preserved` and `MFT-MCH match: sameParticle=1/6`. + +--- + +## What this tool does + +`AODBcRewriter.C` is a ROOT macro that fixes structural integrity problems in +ALICE Run3 AO2D files after merging. AO2D files are ROOT files containing +`DF_*` subdirectories, each holding a set of TTrees that form a relational +schema (similar to a database). After merging two AO2D files with `hadd` or +similar tools, three problems can arise: + +1. **Non-monotonic `fGlobalBC`** in the BC table — the framework requires + strictly increasing values. +2. **Duplicate `fGlobalBC` entries** — the same bunch crossing represented by + multiple rows. +3. **Duplicate MCCollision entries** — the same MC event appearing twice + because it was present in both source files before merging. + +Run with: +```bash +root -l -b -q 'AODBcRewriter.C("AO2D.root","AO2D_rewritten.root")' +``` + +--- + +## AO2D data model (relevant subset) + +The tables form a dependency graph. Every stage of the tool processes one +level of this graph and produces a **PermMap** (`vector`, +`permMap[oldRow] = newRow`, -1 = row dropped) which the next stage consumes. + +``` +BCs (O2bc_*) [Stage 0] + │ fIndexBCs / fIndexBC + ├─► Collisions (O2collision_*) [Stage 1] + │ │ paste-join ──► McCollisionLabels (O2mccollisionlabel_*) + │ └─► Tracks (O2track_*, O2trackiu_*, ...) [Stage 1] + │ paste-join ─► McTrackLabels (O2mctracklabel_*) + │ paste-join ─► McFwdTrackLabels, McMFTTrackLabels + │ + └─► MCCollisions (O2mccollision_*) [Stage 1, deduped] + │ fIndexMcCollisions + ├─► HepMCXSections (O2hepmcxsection_*) [Stage 2] + ├─► HepMCPdfInfos (O2hepmcpdfinfo_*) [Stage 2] + └─► HepMCHeavyIons (O2hepmcheavyion_*) [Stage 2] +``` + +**Index joins** (`fIndexBCs`, `fIndexCollisions`, `fIndexMcCollisions`) are +explicit integer columns pointing to a row in another table by position. + +**Paste joins** are implicit: table row N of a paste-joined table corresponds +to row N of its parent table. These tables have *no index column*. They must +be reordered to match their parent whenever the parent is reordered. + +The known paste-join relationships are hardcoded in `kPasteJoins` (Section 1). +The list is authoritative — derived from `AnalysisDataModel.h` comments +("Table joined to the collision table containing the MC index", etc.) and +from the SOA `EXTENDED_TABLE` declarations for cov / extra tables: +``` +O2bcflag → parent: O2bc_* (BCFlags joinable with BCs) +O2mccollisionlabel → parent: O2collision_* (McCollisionLabels) +O2mctracklabel → parent: O2track_iu (or O2track) +O2mcfwdtracklabel → parent: O2fwdtrack +O2mcmfttracklabel → parent: O2mfttrack +O2mccalolabel → parent: O2calo (McCaloLabels) +O2trackcov_iu → parent: O2track_iu (TracksCovIU extension) +O2trackextra → parent: O2track_iu (TracksExtra extension) +O2fwdtrackcov → parent: O2fwdtrack (FwdTracksCov extension) +``` +NOT in this list (despite the suffix): `O2mfttrackcov` carries its own +`fIndexMFTTracks` and is **index-linked**, not paste-joined. + +A child may carry its own index columns (e.g. `O2mccollisionlabel` carries +`fIndexMcCollisions`). Those values are remapped *value-wise* through the +appropriate parent-stage permutation, but the child's row count and row +order strictly follow its paste-join parent. + +--- + +## Code structure (11 sections) + +| Section | Function(s) | Purpose | +|---------|-------------|---------| +| 1 | `PermMap`, `isBCTable`, `bcIndexBranch`, `mcCollIndexBranch`, `collIndexBranch`, `kPasteJoins`, `isPasteJoinChild`, **`kIndexRefs`**, **`isTableNamed`**, **`unregisteredIndexBranches`** | Core types, name-probe helpers, the paste-join list, and the authoritative index-reference registry | +| 2 | `ScalarTag`, `tagOf`, `byteSize`, `readAsInt`, `writeAsInt`, `BranchDesc`, `describeBranches` | Generic ROOT branch I/O over raw byte buffers | +| 3 | **`writeTable`**, `permFromRowOrder`, `remapBuffer`, `findPermFor`, **`buildRemaps`** | **Central engine**: writes any table in a given row order, remapping *every* index column it carries | +| 3b | `TablePlan` | Deferred write plan — why planning and writing are separate phases | +| 4 | `BCStage0Result`, `stage0_sortBCs` | Sort + deduplicate the BC table; produce `bcPerm` | +| 5 | `stage0_copyBCFlags` | Plan the BC flags table following BC row selection | +| 6 | `MCCollKey`, `MCCollKeyHash`, `stage1_BCindexedTables` | Plan all BC-indexed tables; deduplicate MCCollisions | +| 7 | `stage2_MCCollIndexedTables` | Plan all MCCollision-indexed tables; drop rows whose parent was deduped | +| 9b | `isCollGroupedTrackTable`, `stage1b_reorderTrackTables` | **Stage 1b**: regroup collision-grouped track tables (`O2track_iu`, `O2mfttrack`, `O2fwdtrack`) by remapped `fIndexCollisions` (`-1` sinks to a contiguous tail); publish track perms so children/references follow. Restores the O2 slicing invariant after the BC→collision reorder cascade | +| 8 | `planRemainingTables` | Paste-joined tables follow their parent's row order; everything else keeps its own | +| 9 | `copyNonTreeObjects` | Copy TMap metadata and other non-TTree objects | +| 10 | `processDF` | Orchestrates the **plan phase** then the **write phase** for one `DF_*` directory | +| 11 | `AODBcRewriterValidate` and helpers | Structural validation of an output file | +| 11b | `payloadFingerprints`, `linkTupleCounts`, **`AODBcRewriterCheckLinks`** | Input-vs-output link preservation — the check that sees the O2-7098 bug class | +| 12 | `AODBcRewriter` | Top-level entry: opens files, iterates `DF_*` dirs, preserves compression | + +### `writeTable` — the central engine + +```cpp +void writeTable(TTree *src, TDirectory *dirOut, + const vector &rowOrder, + const vector &remaps); +``` + +- `rowOrder`: which source rows to emit and in what sequence (may be a subset + for deduplication, or reordered for sorting) +- `remaps`: **every** index column to remap, each with its own PermMap — + obtained from `buildRemaps(src, allPerms)` and therefore from `kIndexRefs` + +There is deliberately **no privileged "primary index"** parameter any more. The +old signature took one nominated index column plus an optional list of "extras", +which meant every stage had to remember to populate the extras. Stage 1b did +not, and that is O2-7098. Do not reintroduce the distinction. + +The row permutation a `rowOrder` implies is `permFromRowOrder(nSrc, rowOrder)` +(`perm[srcRow] = outRow`, -1 if dropped). Stages publish it into `allPerms` +*before* anything is written, which is what lets a table's remaps refer to +tables written later — or to itself. + +The function handles both scalar branches and VLA (variable-length array) +branches generically. For VLAs it pre-scans the count branch to find the +maximum array length and allocates buffers accordingly. Input and output +branches share the same raw byte buffers; ROOT handles the VLA count +implicitly through the shared count buffer. + +### The two phases in `processDF` + +``` +PLAN stage0 -> stage1 -> stage2 -> stage1b -> planRemainingTables + each appends a TablePlan and publishes its PermMap into allPerms +WRITE for every plan: buildRemaps(src, allPerms) then writeTable(...) + (identity row order and no remaps -> fast CloneTree instead) +``` + +Note `allPerms` holds **reference-remapping** semantics, not plain row +permutations: for the BC table it is `bcPerm`, where several old rows collapse +onto the surviving one, because that is what references into a deduplicated BC +table need. For `O2mccollision` a dropped duplicate maps to -1 instead — that is +the existing, deliberate behaviour (see resolved gap 7), not an oversight. + +--- + +## MCCollision deduplication + +Implemented in `stage1_BCindexedTables` when the current table begins with +`O2mccollision`. + +**Key**: `MCCollKey { Long64_t newBCrow; Float_t weight; }` using `fEventWeight`. + +**Important constraint**: deduplication is only enabled when `fEventWeight` is +present in the tree. If it is absent, all rows are kept (only reordered). This +is intentional: deduplicating on `newBCrow` alone would incorrectly collapse +distinct MC events that happen to share the same bunch crossing. + +When a MCCollision row is dropped (PermMap entry = -1), Stage 2 propagates +the drop: any `O2hepmcxsection_*` / `O2hepmcpdfinfo_*` / `O2hepmcheavyion_*` +row whose `fIndexMcCollisions` pointed to a dropped row is also dropped. + +--- + +## Known gaps / TODO items + +These were identified during the refactor but not yet implemented: + +### ~~1. Tables SORTED BY a reference into a reordered table are not re-sorted~~ (RESOLVED) + +Stage 1b reorders `O2track_iu` / `O2mfttrack` / `O2fwdtrack`. Several other +tables are *stored sorted by* a reference into one of those (or into +`O2collision`). Their values were remapped but their rows left where they were, +so the ordering was destroyed: + +| table | key | sorted in input? | +|---|---|---| +| `O2fwdtrkcl` | `fIndexFwdTracks` | yes | +| `O2ambiguoustrack` | `fIndexTracks` | yes | +| `O2trackqa_003` | `fIndexTracks` | yes | +| `O2v0_002`, `O2cascade_001`, `O2decay3body` | `fIndexCollisions` | yes | +| `O2mfttrackcov` | `fIndexMFTTracks` | **no** — so not an invariant there | + +(measured on `example_AOD/AO2D_pre.root`.) Same failure mode as the split `-1` +group that produced the `ArrowTableSlicingCache::validateOrder` FATAL. **Not +theoretical**: on `example_AOD/AO2D_pre.root` and `bigger2/`, `O2fwdtrkcl` and +`O2trackqa_003` came out unsorted in *every* DF. + +**Fix**: `findGroupingColumn` + `resortByGroupingColumn` (Section 8), driven by +the data rather than by another hardcoded list — *if `T.B` is non-decreasing in +the input and `B`'s referent gets reordered, re-sort `T` by the remapped `B`*. +Self-maintaining across schema changes, and it correctly leaves `O2mfttrackcov` +alone (its `fIndexMFTTracks` is not sorted on input, so there is no ordering to +preserve). Iterated to a fixed point, because these tables reference each other: +`O2cascade_001` is sorted by `fIndexV0s` and `O2v0_002` may itself have just +been re-sorted. + +Policed by a new check in `AODBcRewriterCheckLinks`: a column sorted on input +must be sorted on output. + +### ~~2. `fIndexCollisions` inside `O2mccollision` is not remapped~~ (MOOT) + +Checked against three real AO2Ds (`example_AOD/`, `bigger/`, `bigger2/`): +`O2mccollision_001` carries **only** `fIndexBCs` in the current schema, so there +is nothing to remap. Should the column reappear, `kIndexRefs` now handles it +with no code change. + +### 3. Deduplication key could be strengthened + +The current `(newBCrow, fEventWeight)` key is a good heuristic. A more robust +key would additionally include `fImpactParameter` and/or `fGeneratorsID` if +those branches are present. Consider making the key construction a small +helper function that probes which fields are available and builds the strongest +possible key. + +### ~~4. `O2mccollision` has two potential parents for paste-join lookup~~ (PARTLY RESOLVED) + +Referent resolution now goes through `findPermFor` / `isTableNamed`, which +matches `P` or `P_` and, when several schema versions coexist, picks the +lexicographically smallest name **deterministically** instead of whatever the +`unordered_map` happened to yield first. Still no warning for the ambiguous +case — add one if schema-version coexistence ever becomes real. + +### 5. Paste-join size-mismatch fallback is silent-ish + +When a paste-joined table has a different row count from its parent (schema +drift), the tool now keeps the child's own row order — index remaps still get +applied, so nothing is left dangling — and prints a `[warn]`. The output is +still structurally inconsistent and the validator's paste-join parity check will +`[FAIL]` on it. Consider making it a hard error, or reconciling row counts. + +### ~~5. No validation pass~~ (RESOLVED) + +`AODBcRewriterValidate(fname)` (Section 11) now validates BC monotonicity, +MC-particle intra-table index integrity, paste-join row-count parity, and +generic `fIndex*` range against the referent table. Call it after rewriting +to confirm output correctness. + +### ~~6. fIndexArray_Mothers / fIndexSlice_Daughters not remapped~~ (RESOLVED) + +This was the root cause of the O2Physics FATAL +`MC particle N has daughter with index M > MC particle table size`. +After Stage 2 reorders `O2mcparticle`, the intra-table mother/daughter indices +get remapped through the table's own permutation. + +`fIndexMcParticles` in label tables (`O2mctracklabel`, `O2mcfwdtracklabel`, +`O2mcmfttracklabel`, `O2mccalolabel`) is remapped via the MC-particle +permutation. + +*(Since O2-7098 both are plain `kIndexRefs` entries and need no special +casing — `buildRemaps` picks them up like any other column.)* + +### ~~8. fIndexSliceBCs in O2ambiguous* not remapped after BC dedup~~ (RESOLVED) + +`fIndexSliceBCs` is a SOA `SLICE_INDEX_COLUMN(BC, bc)` (header line 1029), +stored on disk as a fixed `[2]/I` `{first, last}` pair pointing into the BC +table. It appears in `O2ambiguoustrack`, `O2ambiguousmfttr`, +`O2ambiguousfwdtr` — none of which carry `fIndexBCs` and therefore none +were processed by Stage 1. After BC dedup the slice endpoints would then +point past the compacted table. + +**Fix**: `bcPerm` is published into `allPerms` under the BC tree's name, so +`buildRemaps` applies it to any `fIndexSliceBCs` / `fIndexBCs` / `fIndexBC` +column it finds. Validated against `example_AOD/AO2D_pre.root`: pre-fix the +rewritten output had 7 and 19 out-of-range slice endpoints in +DF_3594457012003; post-fix the validator reports zero. `testAODBcRewriter` +also asserts the endpoints still name the same bunch crossings. + +### ~~7. Paste-join row-count drift on MC-collision dedup~~ (RESOLVED) + +`O2mccollisionlabel` is paste-joined to `O2collision_*` (row N ↔ row N) but +also carries `fIndexMcCollisions`. The previous code routed it through Stage +2 (because of the MC-collision index), which sorted it by new MC-collision +position and *dropped* rows whose MC collision had been deduplicated. That +left `O2mccollisionlabel` shorter than `O2collision_*` by N rows — leading +to downstream "O2collision_001 is one larger than O2mccollisionlabel" crashes. + +**Fix**: `kPasteJoins` was extended to cover every joined pair from +`AnalysisDataModel.h`. Paste-join children are now *deferred* from Stage 2 +to `planRemainingTables`, where they take the parent's row order and have +their own index columns remapped value-wise. Rows that lose their MC label +on dedup now correctly produce `fIndexMcCollisions == -1`, and the row count +matches the parent collision table. + +The new validator catches the regression class as +`[FAIL] paste-join size mismatch: O2mccollisionlabel* has N rows but parent + O2collision* has M`. + +### ~~9. Tracks' `-1` collision group split after BC/collision reorder~~ (RESOLVED — pending validation) + +See the **Current work state** section at the top for the full story. In short: +after Stage 1 reorders `O2collision`, the collision-grouped track tables must be +**reordered** (not just have their `fIndexCollisions` values remapped) so each +group — including the `-1` ambiguous group — stays one contiguous run, as O2's +`ArrowTableSlicingCache::validateOrder` requires. + +**Fix:** Stage 1b (`stage1b_reorderTrackTables`, Section 9b) stable-sorts each +track table by remapped `fIndexCollisions` (`-1` to a contiguous tail) and +publishes the track perm; paste-join children follow it and every +`fIndexTracks*` reference is remapped through it. Validator check +`checkCollisionGroupContiguity` flags split groups as +`[FAIL] ... fIndexCollisions has N group(s) split into non-contiguous runs`. + +**Status:** on `master` since `5597f516`, now run on real merged AO2Ds. Note +this fix is what *introduced* gap 10 below. + +### ~~10. O2fwdtrack match indices reordered but not remapped (O2-7098)~~ (RESOLVED) + +See the **Current work state** section at the top. Stage 1b wrote each table as +soon as it had planned it, so `O2fwdtrack`'s `fIndexMFTTracks` (pointing at a +table reordered later in the same loop) and `fIndexFwdTracks_MatchMCHTrack` +(pointing at itself) kept pre-reorder row numbers. + +**Fix:** planning and writing are now separate phases, and every index column is +derived from the single `kIndexRefs` registry by `buildRemaps` instead of being +enumerated per stage. + +--- + +## Testing checklist + +Automated: `${O2DPG_ROOT}/MC/utils/tests/run_aodbcrewriter_tests.sh` (see the +**Testing** section above). Run it for any change to this tool. + +When testing a real AO2D by hand: + +1. Run `AODBcRewriterValidate("AO2D_rewritten.root")` (Section 11). + It checks BC monotonicity, MC-particle intra-table integrity, paste-join + row-count parity for every pair in `kPasteJoins`, `fIndex*` value + ranges against the referent table, and that no `fIndex*` column is missing + from `kIndexRefs`. Failures appear as `[FAIL] ...` lines. +2. Run `AODBcRewriterCheckLinks("AO2D_pre.root","AO2D.root")` (Section 11b). + **Do not skip this one** — it is the only check that compares against the + input, and therefore the only one that can see a mis-remapped index. + Roughly 30 s for a 3-DF file with 6M MC particles. +3. Check stdout from the rewrite run itself for any `[warn]` lines — these + indicate branches or tables that fell through to a fallback path, or an + index column the registry does not know about. +4. If deduplication ran, verify the dropped count is as expected by comparing + the input DF MCCollision count vs. output. + +A standalone minimal validation script (kept here for reference; in practice +just call `AODBcRewriterValidate`): +```cpp +// validate.C +void validate(const char *fname) { + TFile *f = TFile::Open(fname); + TIter top(f->GetListOfKeys()); + while (TKey *k = (TKey*)top()) { + if (!TString(k->GetName()).BeginsWith("DF_")) continue; + TDirectory *d = (TDirectory*)f->Get(k->GetName()); + // check BC monotonicity + TTree *bc = (TTree*)d->Get("O2bc_001"); // adjust suffix + if (bc) { + ULong64_t gbc, prev = 0; bool ok = true; + bc->SetBranchAddress("fGlobalBC", &gbc); + for (Long64_t i = 0; i < bc->GetEntries(); ++i) { + bc->GetEntry(i); + if (i > 0 && gbc <= prev) { printf("BC non-monotonic at row %lld\n", i); ok=false; } + prev = gbc; + } + if (ok) printf("%s: BCs OK (%lld entries)\n", k->GetName(), bc->GetEntries()); + } + } +} +``` + +--- + +## Data model reference + +Full table schema: https://aliceo2group.github.io/analysis-framework/docs/datamodel/ao2dTables.html + +Source definitions: `AliceO2/Framework/Core/include/Framework/AnalysisDataModel.h` + +The upstream PR this work improves upon: https://github.com/AliceO2Group/O2DPG/pull/2317 + +Target file location in O2DPG: `MC/utils/AODBcRewriter.C` \ No newline at end of file diff --git a/MC/utils/MCFilteringTools/filterAO2DGapTriggeredMCEvents.C b/MC/utils/MCFilteringTools/filterAO2DGapTriggeredMCEvents.C new file mode 100644 index 000000000..ad0a172d5 --- /dev/null +++ b/MC/utils/MCFilteringTools/filterAO2DGapTriggeredMCEvents.C @@ -0,0 +1,1261 @@ +//============================================================================= +// filterAO2DGapTriggeredMCEvents.C +// +// Filter MC particles in gap-triggered AO2Ds while keeping all reconstructed +// information unchanged. minimumBiasSubGeneratorId identifies MB events; any +// other sub-generator is treated as interesting. +// +// Keep: +// - all particles from interesting MC events; +// - MB particles matched to tracks reconstructed in interesting events; +// - recursively, all mothers and descendants of rescued cross-event particles. +// Removed MC-particle references are remapped to -1. Daughter slices are kept +// for the surviving daughters, preserving their order. +// +// Rewritten: O2mcparticle_001, O2mctracklabel, O2mcmfttracklabel, +// O2mcfwdtracklabel, O2mccalolabel_001. +// All other AO2D objects are copied unchanged. +// +// Usage: +// root -l -b -q 'filterAO2DGapTriggeredMCEvents.C("AO2D.root","AO2D_filter.root",0)' +// +// Alberto Calivà (alberto.caliva@cern.ch) +//============================================================================= +#include +#include +#include +#include +#include +#include "SimulationDataFormat/MCGenProperties.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +namespace { +constexpr int kMaxMothers = 256; +constexpr int kMaxCaloLabels = 4096; +struct LabelDiagnostics { + Long64_t crossEventAssociations = 0; + Long64_t uniqueParticlesAdded = 0; + Long64_t rows = 0; + Long64_t invalidated = 0; +}; +struct DirectoryDiagnostics { + std::string name; + Long64_t mcCollisionsTotal = 0; + Long64_t mcCollisionsInteresting = 0; + Long64_t mcCollisionsNonInteresting = 0; + Long64_t recoCollisionsTotal = 0; + Long64_t recoCollisionsInteresting = 0; + Long64_t particlesInitial = 0; + Long64_t particlesInterestingEvents = 0; + Long64_t particlesMinimumBiasEvents = 0; + Long64_t particlesInterestingSeeds = 0; + Long64_t particlesCrossEventAdded = 0; + Long64_t particlesDirectDaughtersAdded = 0; + Long64_t particlesMotherAncestryAdded = 0; + Long64_t particlesRetained = 0; + Long64_t particlesRemoved = 0; + Long64_t emptyDaughterSlicesCleared = 0; + LabelDiagnostics tracks; + LabelDiagnostics mft; + LabelDiagnostics fwd; + Long64_t caloRows = 0; + Long64_t caloInvalidated = 0; +}; +struct GlobalDiagnostics { + Long64_t directoriesProcessed = 0; + Long64_t directoriesFailed = 0; + Long64_t mcCollisionsTotal = 0; + Long64_t mcCollisionsInteresting = 0; + Long64_t mcCollisionsNonInteresting = 0; + Long64_t recoCollisionsTotal = 0; + Long64_t recoCollisionsInteresting = 0; + Long64_t particlesInitial = 0; + Long64_t particlesInterestingEvents = 0; + Long64_t particlesMinimumBiasEvents = 0; + Long64_t particlesInterestingSeeds = 0; + Long64_t particlesCrossEventAdded = 0; + Long64_t particlesDirectDaughtersAdded = 0; + Long64_t particlesMotherAncestryAdded = 0; + Long64_t particlesRetained = 0; + Long64_t particlesRemoved = 0; + Long64_t emptyDaughterSlicesCleared = 0; + LabelDiagnostics tracks; + LabelDiagnostics mft; + LabelDiagnostics fwd; + Long64_t caloRows = 0; + Long64_t caloInvalidated = 0; +}; +void accumulateLabelDiagnostics(LabelDiagnostics& global, const LabelDiagnostics& local) { + global.crossEventAssociations += local.crossEventAssociations; + global.uniqueParticlesAdded += local.uniqueParticlesAdded; + global.rows += local.rows; + global.invalidated += local.invalidated; +} +void accumulateDirectoryDiagnostics(GlobalDiagnostics& global, + const DirectoryDiagnostics& local) { + ++global.directoriesProcessed; + global.mcCollisionsTotal += local.mcCollisionsTotal; + global.mcCollisionsInteresting += local.mcCollisionsInteresting; + global.mcCollisionsNonInteresting += local.mcCollisionsNonInteresting; + global.recoCollisionsTotal += local.recoCollisionsTotal; + global.recoCollisionsInteresting += local.recoCollisionsInteresting; + global.particlesInitial += local.particlesInitial; + global.particlesInterestingEvents += local.particlesInterestingEvents; + global.particlesMinimumBiasEvents += local.particlesMinimumBiasEvents; + global.particlesInterestingSeeds += local.particlesInterestingSeeds; + global.particlesCrossEventAdded += local.particlesCrossEventAdded; + global.particlesDirectDaughtersAdded += local.particlesDirectDaughtersAdded; + global.particlesMotherAncestryAdded += local.particlesMotherAncestryAdded; + global.particlesRetained += local.particlesRetained; + global.particlesRemoved += local.particlesRemoved; + global.emptyDaughterSlicesCleared += local.emptyDaughterSlicesCleared; + accumulateLabelDiagnostics(global.tracks, local.tracks); + accumulateLabelDiagnostics(global.mft, local.mft); + accumulateLabelDiagnostics(global.fwd, local.fwd); + global.caloRows += local.caloRows; + global.caloInvalidated += local.caloInvalidated; +} +double fractionPercent(Long64_t numerator, Long64_t denominator) { + return denominator > 0 ? 100.0 * static_cast(numerator) / static_cast(denominator) : 0.0; +} +Long64_t getFileSizeBytes(const char* fileName) { + std::ifstream stream(fileName, std::ios::binary | std::ios::ate); + if (!stream) { + return -1; + } + return static_cast(stream.tellg()); +} +std::string makeDefaultLogFileName(const char* outputFileName) { + std::string name(outputFileName); + const std::string suffix = ".root"; + if (name.size() >= suffix.size() && + name.compare(name.size() - suffix.size(), suffix.size(), suffix) == 0) { + name.resize(name.size() - suffix.size()); + } + return name + "_log.txt"; +} +std::string currentTimeString() { + const std::time_t now = std::time(nullptr); + char buffer[64] = {}; + const std::tm* localTime = std::localtime(&now); + if (!localTime) { + return "unknown"; + } + std::strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", localTime); + return buffer; +} +void writeDirectoryDiagnostics(std::ostream& out, const DirectoryDiagnostics& d) { + out << "------------------------------------------------------------\n" + << d.name << "\n" + << "------------------------------------------------------------\n\n"; + out << "MC collisions:\n" + << " total: " << d.mcCollisionsTotal << "\n" + << " interesting: " << d.mcCollisionsInteresting << "\n" + << " non-interesting / MB: " << d.mcCollisionsNonInteresting << "\n\n"; + out << "Reconstructed collisions:\n" + << " total: " << d.recoCollisionsTotal << "\n" + << " associated with interesting MC events: " << d.recoCollisionsInteresting << "\n\n"; + out << "MC particles:\n" + << " initial: " << d.particlesInitial << "\n" + << " particles in interesting MC events: " << d.particlesInterestingEvents << "\n" + << " particles in minimum-bias MC events: " << d.particlesMinimumBiasEvents << "\n" + << std::fixed << std::setprecision(4) + << " interesting / MB particle ratio: " + << (d.particlesMinimumBiasEvents > 0 + ? static_cast(d.particlesInterestingEvents) / + static_cast(d.particlesMinimumBiasEvents) + : 0.0) + << "\n" + << " interesting-event seeds: " << d.particlesInterestingSeeds << "\n" + << " cross-event MB particles added: " << d.particlesCrossEventAdded << "\n" + << " descendants added: " << d.particlesDirectDaughtersAdded << "\n" + << " mother-ancestry particles added: " << d.particlesMotherAncestryAdded << "\n" + << " retained: " << d.particlesRetained << "\n" + << " removed: " << d.particlesRemoved << "\n" + << std::fixed << std::setprecision(2) + << " retained fraction: " + << fractionPercent(d.particlesRetained, d.particlesInitial) << " %\n" + << " removed fraction: " + << fractionPercent(d.particlesRemoved, d.particlesInitial) << " %\n\n"; + const Long64_t interestingParticles = d.particlesInterestingSeeds; + const Long64_t mbParticles = d.particlesInitial - d.particlesInterestingSeeds; + out << std::fixed << std::setprecision(2) + << "Average MC-particle multiplicity:\n" + << " particles / interesting MC collision: " + << (d.mcCollisionsInteresting > 0 + ? static_cast(interestingParticles) / + static_cast(d.mcCollisionsInteresting) + : 0.0) + << "\n" + << " particles / non-interesting MC collision: " + << (d.mcCollisionsNonInteresting > 0 + ? static_cast(mbParticles) / + static_cast(d.mcCollisionsNonInteresting) + : 0.0) + << "\n\n"; + out << "Cross-event associations (interesting reco -> non-interesting MC):\n" + << " central-barrel track labels: " << d.tracks.crossEventAssociations << "\n" + << " unique MC particles newly added: " << d.tracks.uniqueParticlesAdded << "\n" + << " MFT track labels: " << d.mft.crossEventAssociations << "\n" + << " unique MC particles newly added: " << d.mft.uniqueParticlesAdded << "\n" + << " forward track labels: " << d.fwd.crossEventAssociations << "\n" + << " unique MC particles newly added: " << d.fwd.uniqueParticlesAdded << "\n\n"; + out << "Label remapping:\n" + << " O2mctracklabel rows: " << d.tracks.rows << "\n" + << " newly invalidated: " << d.tracks.invalidated << "\n" + << " O2mcmfttracklabel rows: " << d.mft.rows << "\n" + << " newly invalidated: " << d.mft.invalidated << "\n" + << " O2mcfwdtracklabel rows: " << d.fwd.rows << "\n" + << " newly invalidated: " << d.fwd.invalidated << "\n" + << " O2mccalolabel_001 rows: " << d.caloRows << "\n" + << " newly invalidated references: " << d.caloInvalidated << "\n\n"; + out << "Genealogy:\n" + << " empty daughter slices cleared: " + << d.emptyDaughterSlicesCleared << "\n\n"; +} +void writeGlobalDiagnostics(std::ostream& out, + const GlobalDiagnostics& g, + Long64_t inputSizeBytes, + Long64_t outputSizeBytes) { + out << "============================================================\n" + << "GLOBAL SUMMARY\n" + << "============================================================\n\n"; + out << "Directories:\n" + << " successfully processed: " << g.directoriesProcessed << "\n" + << " failed: " << g.directoriesFailed << "\n\n"; + out << "MC collisions:\n" + << " total: " << g.mcCollisionsTotal << "\n" + << " interesting: " << g.mcCollisionsInteresting << "\n" + << " non-interesting / MB: " << g.mcCollisionsNonInteresting << "\n\n"; + out << "Reconstructed collisions:\n" + << " total: " << g.recoCollisionsTotal << "\n" + << " associated with interesting MC events: " << g.recoCollisionsInteresting << "\n\n"; + out << "MC particles:\n" + << " initial: " << g.particlesInitial << "\n" + << " particles in interesting MC events: " << g.particlesInterestingEvents << "\n" + << " particles in minimum-bias MC events: " << g.particlesMinimumBiasEvents << "\n" + << std::fixed << std::setprecision(4) + << " interesting / MB particle ratio: " + << (g.particlesMinimumBiasEvents > 0 + ? static_cast(g.particlesInterestingEvents) / + static_cast(g.particlesMinimumBiasEvents) + : 0.0) + << "\n" + << std::fixed << std::setprecision(2) + << " avg particles / interesting MC event: " + << (g.mcCollisionsInteresting > 0 + ? static_cast(g.particlesInterestingEvents) / static_cast(g.mcCollisionsInteresting) + : 0.0) + << "\n" + << " avg particles / minimum-bias MC event: " + << (g.mcCollisionsNonInteresting > 0 + ? static_cast(g.particlesMinimumBiasEvents) / static_cast(g.mcCollisionsNonInteresting) + : 0.0) + << "\n" + << " interesting-event seeds: " << g.particlesInterestingSeeds << "\n" + << " cross-event MB particles added: " << g.particlesCrossEventAdded << "\n" + << " descendants added: " << g.particlesDirectDaughtersAdded << "\n" + << " mother-ancestry particles added: " << g.particlesMotherAncestryAdded << "\n" + << " retained: " << g.particlesRetained << "\n" + << " removed: " << g.particlesRemoved << "\n" + << std::fixed << std::setprecision(2) + << " retained fraction: " + << fractionPercent(g.particlesRetained, g.particlesInitial) << " %\n" + << " removed fraction: " + << fractionPercent(g.particlesRemoved, g.particlesInitial) << " %\n\n"; + const Long64_t rescuedMBParticles = + g.particlesCrossEventAdded + g.particlesDirectDaughtersAdded + g.particlesMotherAncestryAdded; + out << "MB rescue contribution:\n" + << " rescued MB particles: " << rescuedMBParticles << "\n" + << " fraction of retained MC particles: " + << fractionPercent(rescuedMBParticles, g.particlesRetained) << " %\n\n"; + out << "Cross-event associations:\n" + << " central barrel: " << g.tracks.crossEventAssociations << "\n" + << " MFT: " << g.mft.crossEventAssociations << "\n" + << " forward: " << g.fwd.crossEventAssociations << "\n\n"; + out << "Label remapping:\n" + << " track labels newly invalidated: " << g.tracks.invalidated << "\n" + << " MFT labels newly invalidated: " << g.mft.invalidated << "\n" + << " forward labels newly invalidated: " << g.fwd.invalidated << "\n" + << " calorimeter references invalidated: " << g.caloInvalidated << "\n\n"; + out << "Genealogy:\n" + << " empty daughter slices cleared: " + << g.emptyDaughterSlicesCleared << "\n\n"; + if (inputSizeBytes >= 0 && outputSizeBytes >= 0) { + const double inputMB = static_cast(inputSizeBytes) / (1024.0 * 1024.0); + const double outputMB = static_cast(outputSizeBytes) / (1024.0 * 1024.0); + const double reduction = + inputSizeBytes > 0 + ? 100.0 * (1.0 - static_cast(outputSizeBytes) / + static_cast(inputSizeBytes)) + : 0.0; + const double compression = + outputSizeBytes > 0 + ? static_cast(inputSizeBytes) / + static_cast(outputSizeBytes) + : 0.0; + out << std::fixed << std::setprecision(2) + << "File size:\n" + << " input: " << inputMB << " MB\n" + << " output: " << outputMB << " MB\n" + << " storage reduction: " << reduction << " %\n" + << " compression factor: " << compression << "x\n\n"; + } +} +struct StoredMCParticle { + int mcCollisionId = -1; + int pdgCode = 0; + int statusCode = 0; + UChar_t flags = 0; + std::vector mothers; + std::array daughters = {-1, -1}; + float weight = 0.f; + float px = 0.f; + float py = 0.f; + float pz = 0.f; + float e = 0.f; + float vx = 0.f; + float vy = 0.f; + float vz = 0.f; + float vt = 0.f; +}; +int getSubGeneratorIdFromGeneratorsID(Short_t generatorsID) { + return o2::mcgenid::getSubGeneratorId(generatorsID); +} +bool isInterestingSubGenerator(int subGeneratorId, int minimumBiasSubGeneratorId) { + return subGeneratorId != minimumBiasSubGeneratorId; +} +int remapMCParticleId(int oldId, const std::vector& oldToNew) { + if (oldId < 0 || oldId >= static_cast(oldToNew.size())) { + return -1; + } + return oldToNew[oldId]; +} +bool isRewrittenTree(const std::string& treeName) { + static const std::unordered_set rewrittenTrees = { + "O2mcparticle_001", + "O2mctracklabel", + "O2mcmfttracklabel", + "O2mcfwdtracklabel", + "O2mccalolabel_001"}; + return rewrittenTrees.count(treeName) != 0; +} +TTree* findTree(TDirectory* directory, std::initializer_list candidates) { + for (const char* name : candidates) { + if (auto* tree = dynamic_cast(directory->Get(name))) { + return tree; + } + } + return nullptr; +} +TTree* findMCCollisionTree(TDirectory* directory) { + return findTree(directory,{"O2mccollision_001", "O2mccollision", "O2mccollision_000"}); +} +TTree* findMCCollisionLabelTree(TDirectory* directory) { + return findTree(directory,{"O2mccollisionlabel", "O2mccollisionlabel_001", "O2mccollisionlabel_000"}); +} +TTree* findCollisionTree(TDirectory* directory) { + return findTree(directory,{"O2collision_001", "O2collision", "O2collision_000"}); +} +TTree* findTrackTreeForLabel(TDirectory* directory, const char* labelTreeName, Long64_t expectedEntries) { + TTree* tree = nullptr; + const std::string labelName(labelTreeName); + if (labelName == "O2mctracklabel") { + tree = findTree(directory, {"O2track_iu", "O2track", "O2track_001", "O2track_000"}); + } else if (labelName == "O2mcmfttracklabel") { + tree = findTree(directory, {"O2mfttrack_001", "O2mfttrack", "O2mfttrack_000"}); + } else if (labelName == "O2mcfwdtracklabel") { + tree = findTree(directory, {"O2fwdtrack_001", "O2fwdtrack", "O2fwdtrack_000"}); + } + if (!tree) { + return nullptr; + } + if (tree->GetEntries() != expectedEntries) { + std::cerr << "Warning: " << labelTreeName << " has " << expectedEntries + << " rows, but candidate reconstructed track table " << tree->GetName() + << " has " << tree->GetEntries() + << " rows. Cross-event seeding from this table will be skipped.\n"; + return nullptr; + } + if (!tree->GetBranch("fIndexCollisions")) { + std::cerr << "Warning: " << tree->GetName() + << " has no fIndexCollisions branch. Cross-event seeding from " + << labelTreeName << " will be skipped.\n"; + return nullptr; + } + return tree; +} +bool buildMCCollisionSubGeneratorMap(TDirectory* inputDirectory, std::vector& subGeneratorIdByCollision) { + auto* mcCollisionTree = findMCCollisionTree(inputDirectory); + if (!mcCollisionTree) { + std::cerr << "Missing MC-collision table\n"; + return false; + } + if (!mcCollisionTree->GetBranch("fGeneratorsID")) { + std::cerr << mcCollisionTree->GetName() + << ": missing required branch fGeneratorsID\n"; + return false; + } + Short_t generatorsID = 0; + mcCollisionTree->SetBranchAddress("fGeneratorsID", &generatorsID); + const Long64_t numberOfCollisions = mcCollisionTree->GetEntries(); + subGeneratorIdByCollision.resize(numberOfCollisions, -1); + std::unordered_map collisionCountsBySubGeneratorId; + for (Long64_t collisionId = 0; collisionId < numberOfCollisions; ++collisionId) { + mcCollisionTree->GetEntry(collisionId); + const int subGeneratorId = getSubGeneratorIdFromGeneratorsID(generatorsID); + subGeneratorIdByCollision[collisionId] = subGeneratorId; + ++collisionCountsBySubGeneratorId[subGeneratorId]; + } + std::cout << mcCollisionTree->GetName() << ": decoded sub-generator-ID population:\n"; + for (const auto& entry : collisionCountsBySubGeneratorId) { + std::cout << " sub-generator ID " << entry.first << ": " << entry.second << " MC collisions\n"; + } + return true; +} +bool buildRecoCollisionInterestingMap(TDirectory* inputDirectory, + const std::vector& subGeneratorIdByCollision, + int minimumBiasSubGeneratorId, + std::vector& isInterestingRecoCollision, + DirectoryDiagnostics& diagnostics) { + auto* collisionTree = findCollisionTree(inputDirectory); + auto* mcCollisionLabelTree = findMCCollisionLabelTree(inputDirectory); + if (!collisionTree) { + std::cerr << "Missing reconstructed collision table; cannot identify " + << "interesting reconstructed events\n"; + return false; + } + if (!mcCollisionLabelTree) { + std::cerr << "Missing O2mccollisionlabel table; cannot identify " + << "interesting reconstructed events\n"; + return false; + } + if (!mcCollisionLabelTree->GetBranch("fIndexMcCollisions")) { + std::cerr << mcCollisionLabelTree->GetName() + << ": missing fIndexMcCollisions\n"; + return false; + } + const Long64_t numberOfRecoCollisions = collisionTree->GetEntries(); + if (mcCollisionLabelTree->GetEntries() != numberOfRecoCollisions) { + std::cerr << "Mismatch: " << collisionTree->GetName() << " has " + << numberOfRecoCollisions << " rows while " + << mcCollisionLabelTree->GetName() << " has " + << mcCollisionLabelTree->GetEntries() << " rows\n"; + return false; + } + int mcCollisionId = -1; + mcCollisionLabelTree->SetBranchAddress("fIndexMcCollisions", &mcCollisionId); + isInterestingRecoCollision.assign(numberOfRecoCollisions, 0); + Long64_t interestingRecoCollisions = 0; + Long64_t invalidMcCollisionLabels = 0; + for (Long64_t recoCollisionId = 0; + recoCollisionId < numberOfRecoCollisions; + ++recoCollisionId) { + mcCollisionLabelTree->GetEntry(recoCollisionId); + if (mcCollisionId < 0 || + mcCollisionId >= static_cast(subGeneratorIdByCollision.size())) { + if (mcCollisionId >= 0) { + ++invalidMcCollisionLabels; + } + continue; + } + if (isInterestingSubGenerator(subGeneratorIdByCollision[mcCollisionId], minimumBiasSubGeneratorId)) { + isInterestingRecoCollision[recoCollisionId] = 1; + ++interestingRecoCollisions; + } + } + diagnostics.recoCollisionsTotal = numberOfRecoCollisions; + diagnostics.recoCollisionsInteresting = interestingRecoCollisions; + std::cout << "Reco collisions associated with non-MB MC events: " << interestingRecoCollisions + << " / " << numberOfRecoCollisions << "\n"; + if (invalidMcCollisionLabels > 0) { + std::cerr << "Warning: " << invalidMcCollisionLabels + << " reconstructed collisions have out-of-range MC-collision labels\n"; + } + return true; +} +void copyUnchangedObjects(TDirectory* inputDirectory, TDirectory* outputDirectory) { + TIter nextKey(inputDirectory->GetListOfKeys()); + TKey* key = nullptr; + while ((key = static_cast(nextKey()))) { + if (key->GetCycle() != inputDirectory->GetKey(key->GetName())->GetCycle()) { + continue; + } + TObject* object = key->ReadObj(); + const std::string objectName = object->GetName(); + if (object->InheritsFrom(TDirectory::Class())) { + outputDirectory->cd(); + auto* newDirectory = outputDirectory->mkdir(objectName.c_str()); + copyUnchangedObjects(static_cast(object), newDirectory); + delete object; + continue; + } + if (object->InheritsFrom(TTree::Class())) { + if (isRewrittenTree(objectName)) { + delete object; + continue; + } + auto* inputTree = static_cast(object); + outputDirectory->cd(); + auto* outputTree = inputTree->CloneTree(-1, "fast"); + outputTree->Write(objectName.c_str(), TObject::kOverwrite); + delete object; + continue; + } + outputDirectory->cd(); + object->Write(objectName.c_str(), TObject::kOverwrite); + delete object; + } +} +void markCrossEventTrackReferences(TDirectory* inputDirectory, + const char* labelTreeName, + const std::vector& isInterestingRecoCollision, + const std::vector& particleMcCollisionId, + const std::vector& subGeneratorIdByCollision, + int minimumBiasSubGeneratorId, + std::vector& keepParticle, + std::vector& motherWorkList, + std::vector& crossEventSeeds, + LabelDiagnostics& diagnostics) { + auto* labelTree = dynamic_cast(inputDirectory->Get(labelTreeName)); + if (!labelTree) { + return; + } + if (!labelTree->GetBranch("fIndexMcParticles")) { + std::cerr << labelTreeName + << ": missing fIndexMcParticles; cross-event seeding skipped\n"; + return; + } + auto* trackTree = findTrackTreeForLabel(inputDirectory, + labelTreeName, + labelTree->GetEntries()); + if (!trackTree) { + return; + } + int mcParticleId = -1; + int recoCollisionId = -1; + labelTree->SetBranchAddress("fIndexMcParticles", &mcParticleId); + trackTree->SetBranchAddress("fIndexCollisions", &recoCollisionId); + Long64_t crossEventMatches = 0; + Long64_t newlyMarked = 0; + Long64_t invalidParticleReferences = 0; + Long64_t invalidRecoCollisionReferences = 0; + const Long64_t numberOfRows = labelTree->GetEntries(); + for (Long64_t row = 0; row < numberOfRows; ++row) { + labelTree->GetEntry(row); + trackTree->GetEntry(row); + if (mcParticleId < 0) { + continue; + } + if (mcParticleId >= static_cast(particleMcCollisionId.size())) { + ++invalidParticleReferences; + continue; + } + if (recoCollisionId < 0 || + recoCollisionId >= static_cast(isInterestingRecoCollision.size())) { + if (recoCollisionId >= 0) { + ++invalidRecoCollisionReferences; + } + continue; + } + // Only tracks reconstructed in an interesting event may rescue an MB MC + // particle. Normal MB-reco -> MB-MC labels do not seed the keep mask. + if (!isInterestingRecoCollision[recoCollisionId]) { + continue; + } + const int mcCollisionId = particleMcCollisionId[mcParticleId]; + if (mcCollisionId < 0 || + mcCollisionId >= static_cast(subGeneratorIdByCollision.size())) { + continue; + } + // If the MC particle already belongs to the interesting event, it is already + // kept by the primary seed rule. We only count/mark genuine cross-event cases. + if (isInterestingSubGenerator(subGeneratorIdByCollision[mcCollisionId], minimumBiasSubGeneratorId)) { + continue; + } + ++crossEventMatches; + // Record every unique genuine cross-event particle. This separate seed list is + // used later to keep ONLY its direct daughters. + if (std::find(crossEventSeeds.begin(), crossEventSeeds.end(), mcParticleId) == + crossEventSeeds.end()) { + crossEventSeeds.push_back(mcParticleId); + } + if (!keepParticle[mcParticleId]) { + keepParticle[mcParticleId] = 1; + motherWorkList.push_back(mcParticleId); + ++newlyMarked; + } + } + diagnostics.crossEventAssociations = crossEventMatches; + diagnostics.uniqueParticlesAdded = newlyMarked; + std::cout << labelTreeName << ": found " << crossEventMatches + << " interesting-reco -> non-interesting-MC associations; added " + << newlyMarked << " MC particles to the keep set\n"; + if (invalidParticleReferences > 0) { + std::cerr << "Warning: " << labelTreeName << " contains " + << invalidParticleReferences + << " out-of-range MC-particle references\n"; + } + if (invalidRecoCollisionReferences > 0) { + std::cerr << "Warning: " << trackTree->GetName() << " contains " + << invalidRecoCollisionReferences + << " out-of-range reconstructed-collision references\n"; + } +} +bool rewriteMCParticles(TDirectory* inputDirectory, + TDirectory* outputDirectory, + const std::vector& subGeneratorIdByCollision, + const std::vector& isInterestingRecoCollision, + int minimumBiasSubGeneratorId, + std::vector& oldToNew, + DirectoryDiagnostics& diagnostics) { + auto* inputTree = dynamic_cast(inputDirectory->Get("O2mcparticle_001")); + if (!inputTree) { + std::cerr << "Missing O2mcparticle_001\n"; + return false; + } + int mcCollisionId = -1; + int pdgCode = 0; + int statusCode = 0; + UChar_t flags = 0; + int mothersSize = 0; + int mothers[kMaxMothers] = {}; + int daughtersSlice[2] = {-1, -1}; + float weight = 0.f; + float px = 0.f; + float py = 0.f; + float pz = 0.f; + float e = 0.f; + float vx = 0.f; + float vy = 0.f; + float vz = 0.f; + float vt = 0.f; + inputTree->SetBranchAddress("fIndexMcCollisions", &mcCollisionId); + inputTree->SetBranchAddress("fPdgCode", &pdgCode); + inputTree->SetBranchAddress("fStatusCode", &statusCode); + inputTree->SetBranchAddress("fFlags", &flags); + inputTree->SetBranchAddress("fIndexArray_Mothers_size", &mothersSize); + inputTree->SetBranchAddress("fIndexArray_Mothers", mothers); + inputTree->SetBranchAddress("fIndexSlice_Daughters", daughtersSlice); + inputTree->SetBranchAddress("fWeight", &weight); + inputTree->SetBranchAddress("fPx", &px); + inputTree->SetBranchAddress("fPy", &py); + inputTree->SetBranchAddress("fPz", &pz); + inputTree->SetBranchAddress("fE", &e); + inputTree->SetBranchAddress("fVx", &vx); + inputTree->SetBranchAddress("fVy", &vy); + inputTree->SetBranchAddress("fVz", &vz); + inputTree->SetBranchAddress("fVt", &vt); + const Long64_t numberOfParticles = inputTree->GetEntries(); + diagnostics.particlesInitial = numberOfParticles; + oldToNew.assign(numberOfParticles, -1); + std::vector particleMcCollisionId(numberOfParticles, -1); + std::vector> originalMothers(numberOfParticles); + std::vector> originalDaughters(numberOfParticles, {-1, -1}); + std::vector keepParticle(numberOfParticles, 0); + std::vector motherWorkList; + std::vector crossEventSeeds; + motherWorkList.reserve(numberOfParticles); + crossEventSeeds.reserve(1024); + Long64_t particlesFromInterestingEvents = 0; + Long64_t particlesFromMinimumBiasEvents = 0; + Long64_t invalidCollisionReferences = 0; + Long64_t invalidMotherReferences = 0; + Long64_t invalidDaughterSlices = 0; + const int maxMothersSize = static_cast(inputTree->GetMaximum("fIndexArray_Mothers_size")); + if (maxMothersSize > kMaxMothers) { + std::cerr << "Error: maximum number of mothers (" << maxMothersSize + << ") exceeds buffer size (" << kMaxMothers << ")\n"; + return false; + } + // First pass: store genealogy and seed with ALL particles from interesting MC events. + for (Long64_t oldParticleId = 0; oldParticleId < numberOfParticles; ++oldParticleId) { + inputTree->GetEntry(oldParticleId); + particleMcCollisionId[oldParticleId] = mcCollisionId; + const int safeMothersSize = std::min(mothersSize, kMaxMothers); + if (mothersSize > kMaxMothers) { + std::cerr << "Warning: particle " << oldParticleId << " has " << mothersSize + << " mothers; only the first " << kMaxMothers << " are read\n"; + } + for (int motherIndex = 0; motherIndex < safeMothersSize; ++motherIndex) { + const int motherId = mothers[motherIndex]; + if (motherId < 0) { + continue; + } + if (motherId >= numberOfParticles) { + ++invalidMotherReferences; + continue; + } + originalMothers[oldParticleId].push_back(motherId); + } + const int firstDaughter = daughtersSlice[0]; + const int lastDaughter = daughtersSlice[1]; + if (firstDaughter >= 0 || lastDaughter >= 0) { + if (firstDaughter >= 0 && + lastDaughter >= firstDaughter && + lastDaughter < numberOfParticles) { + originalDaughters[oldParticleId] = {firstDaughter, lastDaughter}; + } else { + ++invalidDaughterSlices; + } + } + if (mcCollisionId < 0 || + mcCollisionId >= static_cast(subGeneratorIdByCollision.size())) { + ++invalidCollisionReferences; + continue; + } + if (isInterestingSubGenerator(subGeneratorIdByCollision[mcCollisionId], minimumBiasSubGeneratorId)) { + keepParticle[oldParticleId] = 1; + ++particlesFromInterestingEvents; + } else { + ++particlesFromMinimumBiasEvents; + } + } + diagnostics.particlesInterestingEvents = particlesFromInterestingEvents; + diagnostics.particlesMinimumBiasEvents = particlesFromMinimumBiasEvents; + diagnostics.particlesInterestingSeeds = particlesFromInterestingEvents; + std::cout << "O2mcparticle_001: initially marked " + << particlesFromInterestingEvents + << " particles from non-MB MC events\n"; + // Cross-event exceptions only: + // interesting reco track -> MC particle from a non-interesting MC event. + markCrossEventTrackReferences(inputDirectory, + "O2mctracklabel", + isInterestingRecoCollision, + particleMcCollisionId, + subGeneratorIdByCollision, + minimumBiasSubGeneratorId, + keepParticle, + motherWorkList, + crossEventSeeds, + diagnostics.tracks); + markCrossEventTrackReferences(inputDirectory, + "O2mcmfttracklabel", + isInterestingRecoCollision, + particleMcCollisionId, + subGeneratorIdByCollision, + minimumBiasSubGeneratorId, + keepParticle, + motherWorkList, + crossEventSeeds, + diagnostics.mft); + markCrossEventTrackReferences(inputDirectory, + "O2mcfwdtracklabel", + isInterestingRecoCollision, + particleMcCollisionId, + subGeneratorIdByCollision, + minimumBiasSubGeneratorId, + keepParticle, + motherWorkList, + crossEventSeeds, + diagnostics.fwd); + diagnostics.particlesCrossEventAdded = + diagnostics.tracks.uniqueParticlesAdded + + diagnostics.mft.uniqueParticlesAdded + + diagnostics.fwd.uniqueParticlesAdded; + // Keep all descendants recursively for the genuine cross-event seed particles. + // Every newly retained descendant is also added to the mother-ancestry work list + // so that every retained particle still has valid mother references. + Long64_t directDaughtersAdded = 0; + std::vector daughterWorkList = crossEventSeeds; + std::vector daughterQueued(numberOfParticles, 0); + for (int crossEventParticleId : crossEventSeeds) { + daughterQueued[crossEventParticleId] = 1; + } + std::size_t nextDaughterParticle = 0; + while (nextDaughterParticle < daughterWorkList.size()) { + const int particleId = daughterWorkList[nextDaughterParticle++]; + const int firstDaughter = originalDaughters[particleId][0]; + const int lastDaughter = originalDaughters[particleId][1]; + if (firstDaughter < 0 || lastDaughter < firstDaughter) { + continue; + } + for (int daughterId = firstDaughter; daughterId <= lastDaughter; ++daughterId) { + if (!keepParticle[daughterId]) { + keepParticle[daughterId] = 1; + motherWorkList.push_back(daughterId); + ++directDaughtersAdded; + } + if (!daughterQueued[daughterId]) { + daughterQueued[daughterId] = 1; + daughterWorkList.push_back(daughterId); + } + } + } + diagnostics.particlesDirectDaughtersAdded = directDaughtersAdded; + std::cout << "O2mcparticle_001: added " << directDaughtersAdded + << " daughters through recursive descendant closure\n"; + // Recursive mother-ancestry closure. This is applied only to rescued cross-event + // particles and all their recursively retained descendants. Particles from interesting + // MC events are already kept together with their full event and do not seed this traversal. + // Newly retained mothers do NOT cause their other daughters/siblings to be kept. + std::size_t nextParticle = 0; + Long64_t particlesAddedByMotherAncestry = 0; + while (nextParticle < motherWorkList.size()) { + const int particleId = motherWorkList[nextParticle++]; + for (int motherId : originalMothers[particleId]) { + if (!keepParticle[motherId]) { + keepParticle[motherId] = 1; + motherWorkList.push_back(motherId); + ++particlesAddedByMotherAncestry; + } + } + } + diagnostics.particlesMotherAncestryAdded = particlesAddedByMotherAncestry; + std::cout << "O2mcparticle_001: added " << particlesAddedByMotherAncestry + << " particles through recursive mother-ancestry closure\n"; + Long64_t keptCount = 0; + for (Long64_t oldParticleId = 0; oldParticleId < numberOfParticles; ++oldParticleId) { + if (keepParticle[oldParticleId]) { + oldToNew[oldParticleId] = static_cast(keptCount++); + } + } + std::vector retainedParticles; + retainedParticles.reserve(keptCount); + Long64_t brokenMotherReferences = 0; + Long64_t emptyDaughterSlices = 0; + for (Long64_t oldParticleId = 0; oldParticleId < numberOfParticles; ++oldParticleId) { + if (!keepParticle[oldParticleId]) { + continue; + } + inputTree->GetEntry(oldParticleId); + StoredMCParticle particle; + particle.mcCollisionId = mcCollisionId; + particle.pdgCode = pdgCode; + particle.statusCode = statusCode; + particle.flags = flags; + particle.weight = weight; + particle.px = px; + particle.py = py; + particle.pz = pz; + particle.e = e; + particle.vx = vx; + particle.vy = vy; + particle.vz = vz; + particle.vt = vt; + for (int oldMotherId : originalMothers[oldParticleId]) { + const int newMotherId = remapMCParticleId(oldMotherId, oldToNew); + if (newMotherId >= 0) { + particle.mothers.push_back(newMotherId); + } else { + ++brokenMotherReferences; + } + } + const int oldFirstDaughter = originalDaughters[oldParticleId][0]; + const int oldLastDaughter = originalDaughters[oldParticleId][1]; + if (oldFirstDaughter >= 0 && oldLastDaughter >= oldFirstDaughter) { + // A daughter slice represents a contiguous interval + // [firstDaughter, lastDaughter]. Preserve the surviving daughters. + int newFirstDaughter = -1; + int previousNewDaughter = -1; + for (int oldDaughterId = oldFirstDaughter; + oldDaughterId <= oldLastDaughter; + ++oldDaughterId) { + const int newDaughterId = remapMCParticleId(oldDaughterId, oldToNew); + if (newDaughterId < 0) { + continue; + } + if (newFirstDaughter < 0) { + newFirstDaughter = newDaughterId; + } + previousNewDaughter = newDaughterId; + } + if (newFirstDaughter >= 0) { + particle.daughters = {newFirstDaughter, previousNewDaughter}; + } else { + particle.daughters = {-1, -1}; + ++emptyDaughterSlices; + } + } + retainedParticles.push_back(std::move(particle)); + } + outputDirectory->cd(); + auto* outputTree = inputTree->CloneTree(0); + for (const auto& particle : retainedParticles) { + mcCollisionId = particle.mcCollisionId; + pdgCode = particle.pdgCode; + statusCode = particle.statusCode; + flags = particle.flags; + weight = particle.weight; + px = particle.px; + py = particle.py; + pz = particle.pz; + e = particle.e; + vx = particle.vx; + vy = particle.vy; + vz = particle.vz; + vt = particle.vt; + mothersSize = static_cast(particle.mothers.size()); + for (int motherIndex = 0; motherIndex < mothersSize; ++motherIndex) { + mothers[motherIndex] = particle.mothers[motherIndex]; + } + daughtersSlice[0] = particle.daughters[0]; + daughtersSlice[1] = particle.daughters[1]; + outputTree->Fill(); + } + outputTree->Write("O2mcparticle_001", TObject::kOverwrite); + const double survivalFraction = numberOfParticles > 0 + ? static_cast(keptCount) / + static_cast(numberOfParticles) + : 0.; + diagnostics.particlesRetained = keptCount; + diagnostics.particlesRemoved = numberOfParticles - keptCount; + diagnostics.emptyDaughterSlicesCleared = emptyDaughterSlices; + std::cout << "O2mcparticle_001: retained " << keptCount << " / " + << numberOfParticles << " particles; survival fraction = " + << survivalFraction << "\n"; + if (invalidCollisionReferences > 0) { + std::cerr << "Warning: " << invalidCollisionReferences + << " MC particles had invalid MC-collision references\n"; + } + if (invalidMotherReferences > 0) { + std::cerr << "Warning: " << invalidMotherReferences + << " out-of-range mother references were found\n"; + } + if (invalidDaughterSlices > 0) { + std::cerr << "Warning: " << invalidDaughterSlices + << " invalid daughter slices were found\n"; + } + if (brokenMotherReferences > 0) { + std::cerr << "Warning: genealogy closure left " << brokenMotherReferences + << " broken mother references. This should normally be zero.\n"; + } + if (emptyDaughterSlices > 0) { + std::cout << "O2mcparticle_001: cleared " << emptyDaughterSlices + << " empty daughter slices after filtering\n"; + } + return true; +} +void rewriteSingleMCLabelTable(TDirectory* inputDirectory, + TDirectory* outputDirectory, + const char* treeName, + const std::vector& oldToNew, + LabelDiagnostics& diagnostics) { + auto* inputTree = dynamic_cast(inputDirectory->Get(treeName)); + if (!inputTree) { + return; + } + if (!inputTree->GetBranch("fIndexMcParticles")) { + std::cerr << treeName << ": missing fIndexMcParticles; copying unchanged\n"; + outputDirectory->cd(); + auto* outputTree = inputTree->CloneTree(-1, "fast"); + outputTree->Write(treeName, TObject::kOverwrite); + return; + } + int mcParticleId = -1; + inputTree->SetBranchAddress("fIndexMcParticles", &mcParticleId); + outputDirectory->cd(); + auto* outputTree = inputTree->CloneTree(0); + const Long64_t numberOfLabels = inputTree->GetEntries(); + Long64_t newlyInvalidatedLabels = 0; + for (Long64_t labelId = 0; labelId < numberOfLabels; ++labelId) { + inputTree->GetEntry(labelId); + const int oldMcParticleId = mcParticleId; + const int remappedId = remapMCParticleId(oldMcParticleId, oldToNew); + if (oldMcParticleId >= 0 && remappedId < 0) { + ++newlyInvalidatedLabels; + } + mcParticleId = remappedId; + outputTree->Fill(); + } + outputTree->Write(treeName, TObject::kOverwrite); + diagnostics.rows = numberOfLabels; + diagnostics.invalidated = newlyInvalidatedLabels; + std::cout << treeName << ": remapped " << numberOfLabels + << " rows; newly invalidated " << newlyInvalidatedLabels + << " labels\n"; +} +void rewriteCaloMCLabels(TDirectory* inputDirectory, + TDirectory* outputDirectory, + const std::vector& oldToNew, + DirectoryDiagnostics& diagnostics) { + constexpr const char* treeName = "O2mccalolabel_001"; + auto* inputTree = dynamic_cast(inputDirectory->Get(treeName)); + if (!inputTree) { + return; + } + auto* sizeBranch = inputTree->GetBranch("fIndexArrayMcParticles_size"); + auto* idsBranch = inputTree->GetBranch("fIndexArrayMcParticles"); + if (!sizeBranch || !idsBranch) { + std::cerr << treeName + << ": expected MC-particle array branches not found; copying unchanged\n"; + outputDirectory->cd(); + auto* outputTree = inputTree->CloneTree(-1, "fast"); + outputTree->Write(treeName, TObject::kOverwrite); + return; + } + int idsSize = 0; + int ids[kMaxCaloLabels] = {}; + inputTree->SetBranchAddress("fIndexArrayMcParticles_size", &idsSize); + inputTree->SetBranchAddress("fIndexArrayMcParticles", ids); + outputDirectory->cd(); + auto* outputTree = inputTree->CloneTree(0); + const Long64_t numberOfRows = inputTree->GetEntries(); + const int maxCaloLabelsSize = static_cast(inputTree->GetMaximum("fIndexArrayMcParticles_size")); + if (maxCaloLabelsSize > kMaxCaloLabels) { + std::cerr << "Error: maximum number of calorimeter labels (" << maxCaloLabelsSize + << ") exceeds buffer size (" << kMaxCaloLabels << ")\n"; + return; + } + Long64_t newlyInvalidatedReferences = 0; + for (Long64_t row = 0; row < numberOfRows; ++row) { + inputTree->GetEntry(row); + if (idsSize > kMaxCaloLabels) { + std::cerr << "Error: " << treeName << " row " << row << " contains " + << idsSize << " labels, exceeding buffer size " + << kMaxCaloLabels << ". Aborting this table rewrite.\n"; + delete outputTree; + return; + } + for (int labelIndex = 0; labelIndex < idsSize; ++labelIndex) { + const int oldMcParticleId = ids[labelIndex]; + ids[labelIndex] = remapMCParticleId(oldMcParticleId, oldToNew); + if (oldMcParticleId >= 0 && ids[labelIndex] < 0) { + ++newlyInvalidatedReferences; + } + } + outputTree->Fill(); + } + outputTree->Write(treeName, TObject::kOverwrite); + diagnostics.caloRows = numberOfRows; + diagnostics.caloInvalidated = newlyInvalidatedReferences; + std::cout << treeName + << ": remapped all MC-particle references; newly invalidated " + << newlyInvalidatedReferences << " references\n"; +} +} // namespace +void filterAO2DGapTriggeredMCEvents(const char* inputFileName = "AO2D.root", + const char* outputFileName = "AO2D_filter.root", + int minimumBiasSubGeneratorId = 0, + const char* logFileName = nullptr) { + const std::string resolvedLogFileName = logFileName ? std::string(logFileName) : makeDefaultLogFileName(outputFileName); + const Long64_t inputFileSizeBytes = getFileSizeBytes(inputFileName); + std::ofstream logFile(resolvedLogFileName); + if (!logFile) { + std::cerr << "Cannot create diagnostic log file " << resolvedLogFileName << "\n"; + return; + } + logFile << "AO2D GAP-TRIGGER MC FILTER REPORT\n\n" + << "Date: " << currentTimeString() << "\n" + << "Input: " << inputFileName << "\n" + << "Output: " << outputFileName << "\n" + << "Minimum-bias sub-generator ID: " << minimumBiasSubGeneratorId << "\n\n" + << "Filtering strategy:\n" + << " - keep all reconstructed AO2D tables\n" + << " - keep all MC particles from interesting events\n" + << " - rescue interesting-reco -> non-interesting-MC cross particles\n" + << " - keep all daughters recursively for rescued cross particles\n" + << " - keep all mothers recursively\n" + << " - remove all remaining MC particles and remap references\n\n"; + auto* inputFile = TFile::Open(inputFileName, "READ"); + if (!inputFile || inputFile->IsZombie()) { + std::cerr << "Cannot open input file " << inputFileName << "\n"; + return; + } + auto* outputFile = TFile::Open(outputFileName, "RECREATE", "", 505); + if (!outputFile || outputFile->IsZombie()) { + std::cerr << "Cannot create output file " << outputFileName << "\n"; + inputFile->Close(); + return; + } + std::cout << "Filtering MC particles in gap-trigger production [V2]\n" + << " input file: " << inputFileName << "\n" + << " output file: " << outputFileName << "\n" + << " minimum-bias sub-gen ID: " << minimumBiasSubGeneratorId << "\n" + << " reconstructed tables: kept unchanged\n" + << " extra MC seeds: interesting-reco -> MB-MC only\n" + << " genealogy: recursive mothers + recursive daughters of cross-event MB particles\n"; + TIter nextKey(inputFile->GetListOfKeys()); + TKey* key = nullptr; + int processedDirectories = 0; + int failedDirectories = 0; + GlobalDiagnostics globalDiagnostics; + while ((key = static_cast(nextKey()))) { + if (key->GetCycle() != inputFile->GetKey(key->GetName())->GetCycle()) { + continue; + } + TObject* object = key->ReadObj(); + const std::string objectName = object->GetName(); + if (std::string(key->GetName()) == "metaData") { + outputFile->cd(); + object->Write("metaData", TObject::kSingleKey | TObject::kOverwrite); + delete object; + continue; + } + if (key->GetName() == std::string("parentFiles")) { + outputFile->cd(); + object->Write("parentFiles", TObject::kSingleKey | TObject::kOverwrite); + delete object; + continue; + } + if (!object->InheritsFrom(TDirectory::Class())) { + outputFile->cd(); + object->Write(objectName.c_str(), TObject::kOverwrite); + delete object; + continue; + } + auto* inputDirectory = static_cast(object); + outputFile->cd(); + auto* outputDirectory = outputFile->mkdir(objectName.c_str()); + std::cout << "Processing directory " << objectName << "\n"; + DirectoryDiagnostics directoryDiagnostics; + directoryDiagnostics.name = objectName; + copyUnchangedObjects(inputDirectory, outputDirectory); + std::vector subGeneratorIdByCollision; + std::vector isInterestingRecoCollision; + std::vector oldToNew; + const bool subGeneratorMapBuilt = + buildMCCollisionSubGeneratorMap(inputDirectory, subGeneratorIdByCollision); + if (subGeneratorMapBuilt) { + directoryDiagnostics.mcCollisionsTotal = + static_cast(subGeneratorIdByCollision.size()); + for (int subGeneratorId : subGeneratorIdByCollision) { + if (isInterestingSubGenerator(subGeneratorId, minimumBiasSubGeneratorId)) { + ++directoryDiagnostics.mcCollisionsInteresting; + } else { + ++directoryDiagnostics.mcCollisionsNonInteresting; + } + } + } + const bool recoCollisionMapBuilt = + subGeneratorMapBuilt && + buildRecoCollisionInterestingMap(inputDirectory, + subGeneratorIdByCollision, + minimumBiasSubGeneratorId, + isInterestingRecoCollision, + directoryDiagnostics); + const bool particlesRewritten = + recoCollisionMapBuilt && + rewriteMCParticles(inputDirectory, + outputDirectory, + subGeneratorIdByCollision, + isInterestingRecoCollision, + minimumBiasSubGeneratorId, + oldToNew, + directoryDiagnostics); + if (!particlesRewritten) { + std::cerr << "Failed to filter directory " << objectName << "\n"; + ++failedDirectories; + ++globalDiagnostics.directoriesFailed; + delete object; + continue; + } + rewriteSingleMCLabelTable(inputDirectory, + outputDirectory, + "O2mctracklabel", + oldToNew, + directoryDiagnostics.tracks); + rewriteSingleMCLabelTable(inputDirectory, + outputDirectory, + "O2mcmfttracklabel", + oldToNew, + directoryDiagnostics.mft); + rewriteSingleMCLabelTable(inputDirectory, + outputDirectory, + "O2mcfwdtracklabel", + oldToNew, + directoryDiagnostics.fwd); + rewriteCaloMCLabels(inputDirectory, + outputDirectory, + oldToNew, + directoryDiagnostics); + writeDirectoryDiagnostics(logFile, directoryDiagnostics); + accumulateDirectoryDiagnostics(globalDiagnostics, directoryDiagnostics); + ++processedDirectories; + delete object; + } + outputFile->Close(); + inputFile->Close(); + const Long64_t outputFileSizeBytes = getFileSizeBytes(outputFileName); + globalDiagnostics.directoriesFailed = failedDirectories; + writeGlobalDiagnostics(logFile, + globalDiagnostics, + inputFileSizeBytes, + outputFileSizeBytes); + logFile.close(); + if (failedDirectories > 0) { + std::cerr << "Completed with failures: " << failedDirectories + << " directory/directories could not be filtered.\n"; + } + std::cout << "Done. Successfully filtered " << processedDirectories + << " directory/directories and wrote " << outputFileName << "\n" + << "Diagnostic report written to " << resolvedLogFileName << "\n"; + const Long64_t rescuedMBParticles = + globalDiagnostics.particlesCrossEventAdded + + globalDiagnostics.particlesDirectDaughtersAdded + + globalDiagnostics.particlesMotherAncestryAdded; + std::cout << "\n============================================================\n" + << "GLOBAL SUMMARY [V3]\n" + << "============================================================\n" + << "MC particles initial: " << globalDiagnostics.particlesInitial << "\n" + << "MC particles interesting events: " << globalDiagnostics.particlesInterestingEvents << "\n" + << "MC particles minimum-bias events: " << globalDiagnostics.particlesMinimumBiasEvents << "\n" + << std::fixed << std::setprecision(4) + << "Interesting / MB particle ratio: " + << (globalDiagnostics.particlesMinimumBiasEvents > 0 + ? static_cast(globalDiagnostics.particlesInterestingEvents) / + static_cast(globalDiagnostics.particlesMinimumBiasEvents) + : 0.0) + << "\n" + << std::fixed << std::setprecision(2) + << "Avg particles / interesting event: " + << (globalDiagnostics.mcCollisionsInteresting > 0 + ? static_cast(globalDiagnostics.particlesInterestingEvents) / + static_cast(globalDiagnostics.mcCollisionsInteresting) + : 0.0) + << "\n" + << "Avg particles / minimum-bias event: " + << (globalDiagnostics.mcCollisionsNonInteresting > 0 + ? static_cast(globalDiagnostics.particlesMinimumBiasEvents) / + static_cast(globalDiagnostics.mcCollisionsNonInteresting) + : 0.0) + << "\n" + << "MC particles retained: " << globalDiagnostics.particlesRetained << "\n" + << "MC particles removed: " << globalDiagnostics.particlesRemoved << "\n" + << std::fixed << std::setprecision(2) + << "Retained fraction: " + << fractionPercent(globalDiagnostics.particlesRetained, + globalDiagnostics.particlesInitial) + << " %\n" + << "Removed fraction: " + << fractionPercent(globalDiagnostics.particlesRemoved, + globalDiagnostics.particlesInitial) + << " %\n" + << "Cross-event MB seeds: " + << globalDiagnostics.particlesCrossEventAdded << "\n" + << "Descendants added: " + << globalDiagnostics.particlesDirectDaughtersAdded << "\n" + << "Mother ancestry added: " + << globalDiagnostics.particlesMotherAncestryAdded << "\n" + << "Rescued MB particles: " << rescuedMBParticles << "\n" + << "MB rescue fraction: " + << fractionPercent(rescuedMBParticles, + globalDiagnostics.particlesRetained) + << " % of retained MC particles\n"; + if (inputFileSizeBytes >= 0 && outputFileSizeBytes >= 0) { + const double inputMB = + static_cast(inputFileSizeBytes) / (1024.0 * 1024.0); + const double outputMB = + static_cast(outputFileSizeBytes) / (1024.0 * 1024.0); + const double reduction = + inputFileSizeBytes > 0 + ? 100.0 * (1.0 - static_cast(outputFileSizeBytes) / + static_cast(inputFileSizeBytes)) + : 0.0; + std::cout << "Input file size: " << inputMB << " MB\n" + << "Output file size: " << outputMB << " MB\n" + << "Storage reduction: " << reduction << " %\n"; + } +} +//============================================================================================================================== diff --git a/MC/utils/MCFilteringTools/filterAO2DMCParticlesPdg.C b/MC/utils/MCFilteringTools/filterAO2DMCParticlesPdg.C new file mode 100644 index 000000000..d477a6799 --- /dev/null +++ b/MC/utils/MCFilteringTools/filterAO2DMCParticlesPdg.C @@ -0,0 +1,1293 @@ +//============================================================================= +// filterAO2DMCParticlesPdg.C +// +// Filter AO2D MC particles using a list of PDG-code seeds. +// +// Selection policy: +// +// keep = selected PDG particles +// + all ancestors of selected PDG particles +// + all descendants of selected PDG particles +// +// IMPORTANT: +// +// The upward and downward traversals are completely independent. +// +// - Ancestor traversal: +// seed -> mother -> grandmother -> ... +// Ancestors DO NOT become daughter-expansion seeds. +// +// - Descendant traversal: +// seed -> daughter -> granddaughter -> ... +// Descendants DO NOT become mother-expansion seeds. +// +// Therefore sibling branches of an ancestor are NOT automatically retained. +// +// All surviving MC-particle indices are remapped. +// Reconstructed tables are copied unchanged, while MC-label tables are remapped. +// +// fIndexSlice_Daughters stores: +// {firstDaughter, lastDaughterInclusive} +// +// Usage: +// +// root -l -b -q 'filterAO2DMCParticlesPdg.C("AO2D.root","AO2D_filter.root",{3122,-3122})' +// +//============================================================================= + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace +{ + +// Mirror everything written to std::cout/std::cerr to a text log file. +class TeeBuf : public std::streambuf +{ + public: + TeeBuf(std::streambuf* first, std::streambuf* second) + : mFirst(first), mSecond(second) + { + } + + protected: + int overflow(int c) override + { + if (c == traits_type::eof()) { + return traits_type::not_eof(c); + } + + const char ch = static_cast(c); + const bool firstOk = mFirst->sputc(ch) != traits_type::eof(); + const bool secondOk = mSecond->sputc(ch) != traits_type::eof(); + + return (firstOk && secondOk) ? c : traits_type::eof(); + } + + int sync() override + { + const int firstStatus = mFirst->pubsync(); + const int secondStatus = mSecond->pubsync(); + return (firstStatus == 0 && secondStatus == 0) ? 0 : -1; + } + + private: + std::streambuf* mFirst = nullptr; + std::streambuf* mSecond = nullptr; +}; + +constexpr int kMaxMothers = 256; +constexpr int kMaxCaloLabels = 4096; + +//_____________________________________________________________________________ +struct GlobalSummary { + Long64_t directoriesProcessed = 0; + + Long64_t mcParticlesInitial = 0; + Long64_t pdgSeeds = 0; + Long64_t ancestorsAdded = 0; + Long64_t descendantsAdded = 0; + Long64_t mcParticlesKept = 0; + Long64_t mcParticlesRemoved = 0; + + Long64_t preservedDaughterSlices = 0; + Long64_t clearedDaughterSlices = 0; + + Long64_t invalidMotherReferences = 0; + Long64_t invalidDaughterSlices = 0; + + Long64_t validTrackLabels = 0; + Long64_t invalidatedTrackLabels = 0; + Long64_t validMFTTrackLabels = 0; + Long64_t invalidatedMFTTrackLabels = 0; + Long64_t validFwdTrackLabels = 0; + Long64_t invalidatedFwdTrackLabels = 0; + Long64_t validCaloLabels = 0; + Long64_t invalidatedCaloLabels = 0; +}; + +//_____________________________________________________________________________ +struct KeptParticle { + int oldId = -1; + int mcCollisionId = -1; + int pdgCode = 0; + int statusCode = 0; + UChar_t flags = 0; + + std::vector mothers; + + int firstDaughter = -1; + int lastDaughter = -1; + + float weight = 0.f; + float px = 0.f; + float py = 0.f; + float pz = 0.f; + float e = 0.f; + float vx = 0.f; + float vy = 0.f; + float vz = 0.f; + float vt = 0.f; +}; + +//_____________________________________________________________________________ +int remapId(int oldId, const std::vector& oldToNew) +{ + if (oldId < 0 || oldId >= static_cast(oldToNew.size())) { + return -1; + } + return oldToNew[oldId]; +} + +//_____________________________________________________________________________ +bool hasBranch(TTree* tree, const char* branchName) +{ + if (!tree->GetBranch(branchName)) { + std::cerr << "Missing branch " << branchName + << " in tree " << tree->GetName() << "\n"; + return false; + } + return true; +} + +//_____________________________________________________________________________ +bool isAffectedTree(const std::string& name) +{ + static const std::unordered_set affected = { + "O2mcparticle_001", + "O2mctracklabel", + "O2mcmfttracklabel", + "O2mcfwdtracklabel", + "O2mccalolabel_001" + }; + + return affected.count(name) > 0; +} + +//_____________________________________________________________________________ +void copyDirectory(TDirectory* inDir, TDirectory* outDir) +{ + TIter next(inDir->GetListOfKeys()); + TKey* key = nullptr; + + while ((key = static_cast(next()))) { + const TKey* latestKey = inDir->GetKey(key->GetName()); + if (latestKey && key->GetCycle() != latestKey->GetCycle()) { + continue; + } + TObject* obj = key->ReadObj(); + if (!obj) { + continue; + } + + const std::string name = obj->GetName(); + + if (obj->InheritsFrom(TDirectory::Class())) { + outDir->cd(); + auto* newDir = outDir->mkdir(name.c_str()); + + if (!newDir) { + std::cerr << "Could not create output directory " + << name << "\n"; + delete obj; + continue; + } + + copyDirectory(static_cast(obj), newDir); + delete obj; + continue; + } + + if (obj->InheritsFrom(TTree::Class())) { + if (isAffectedTree(name)) { + delete obj; + continue; + } + + auto* inTree = static_cast(obj); + + outDir->cd(); + auto* outTree = inTree->CloneTree(-1, "fast"); + + if (!outTree) { + std::cerr << "Could not clone tree " << name << "\n"; + delete obj; + continue; + } + + outTree->Write(name.c_str(), TObject::kOverwrite); + delete obj; + continue; + } + + outDir->cd(); + obj->Write(name.c_str(), TObject::kOverwrite); + delete obj; + } +} + +//_____________________________________________________________________________ +bool rewriteMCParticles(TDirectory* inDir, + TDirectory* outDir, + std::vector& oldToNew, + const std::unordered_set& keptPDGs, + GlobalSummary& summary) +{ + auto* inTree = + dynamic_cast(inDir->Get("O2mcparticle_001")); + + if (!inTree) { + std::cerr << "Missing O2mcparticle_001 in directory " + << inDir->GetName() << "\n"; + return false; + } + + const std::vector requiredBranches = { + "fIndexMcCollisions", + "fPdgCode", + "fStatusCode", + "fFlags", + "fIndexArray_Mothers_size", + "fIndexArray_Mothers", + "fIndexSlice_Daughters", + "fWeight", + "fPx", + "fPy", + "fPz", + "fE", + "fVx", + "fVy", + "fVz", + "fVt" + }; + + for (const char* branchName : requiredBranches) { + if (!hasBranch(inTree, branchName)) { + return false; + } + } + + const int maximumMothers = + static_cast(inTree->GetMaximum("fIndexArray_Mothers_size")); + + if (maximumMothers > kMaxMothers) { + std::cerr + << "Fatal: maximum mother-array size in " + << inDir->GetName() + << " is " << maximumMothers + << ", while the allocated buffer supports only " + << kMaxMothers << "\n"; + return false; + } + + int mcCollisionId = -1; + int pdgCode = 0; + int statusCode = 0; + UChar_t flags = 0; + + int mothersSize = 0; + int mothers[kMaxMothers] = {}; + + int daughtersSlice[2] = {-1, -1}; + + float weight = 0.f; + float px = 0.f; + float py = 0.f; + float pz = 0.f; + float e = 0.f; + float vx = 0.f; + float vy = 0.f; + float vz = 0.f; + float vt = 0.f; + + inTree->SetBranchAddress("fIndexMcCollisions", &mcCollisionId); + inTree->SetBranchAddress("fPdgCode", &pdgCode); + inTree->SetBranchAddress("fStatusCode", &statusCode); + inTree->SetBranchAddress("fFlags", &flags); + inTree->SetBranchAddress("fIndexArray_Mothers_size", &mothersSize); + inTree->SetBranchAddress("fIndexArray_Mothers", mothers); + inTree->SetBranchAddress("fIndexSlice_Daughters", daughtersSlice); + inTree->SetBranchAddress("fWeight", &weight); + inTree->SetBranchAddress("fPx", &px); + inTree->SetBranchAddress("fPy", &py); + inTree->SetBranchAddress("fPz", &pz); + inTree->SetBranchAddress("fE", &e); + inTree->SetBranchAddress("fVx", &vx); + inTree->SetBranchAddress("fVy", &vy); + inTree->SetBranchAddress("fVz", &vz); + inTree->SetBranchAddress("fVt", &vt); + + const Long64_t nEntries = inTree->GetEntries(); + + std::vector> originalMothers(nEntries); + std::vector firstDaughter(nEntries, -1); + std::vector lastDaughter(nEntries, -1); + + std::vector selectedPDG(nEntries, 0); + std::vector keep(nEntries, 0); + + Long64_t nPDGSelected = 0; + Long64_t nInvalidMotherReferences = 0; + Long64_t nInvalidDaughterSlices = 0; + + //=========================================================================== + // First pass: cache genealogy and mark the original PDG seeds. + //=========================================================================== + for (Long64_t i = 0; i < nEntries; ++i) { + inTree->GetEntry(i); + + if (mothersSize < 0 || mothersSize > kMaxMothers) { + std::cerr << "Invalid mother-array size " + << mothersSize + << " at MC-particle entry " + << i << "\n"; + return false; + } + + originalMothers[i].reserve(mothersSize); + + for (int im = 0; im < mothersSize; ++im) { + const int mother = mothers[im]; + + if (mother < 0) { + continue; + } + + if (mother >= nEntries) { + ++nInvalidMotherReferences; + continue; + } + + originalMothers[i].push_back(mother); + } + + const int first = daughtersSlice[0]; + const int last = daughtersSlice[1]; + + if (first < 0 || last < first) { + firstDaughter[i] = -1; + lastDaughter[i] = -1; + } else if (first >= nEntries || last >= nEntries) { + ++nInvalidDaughterSlices; + firstDaughter[i] = -1; + lastDaughter[i] = -1; + } else { + firstDaughter[i] = first; + lastDaughter[i] = last; + } + + if (keptPDGs.count(pdgCode) > 0) { + selectedPDG[i] = 1; + keep[i] = 1; + ++nPDGSelected; + } + } + + //=========================================================================== + // UPWARD traversal. + // + // Start ONLY from original PDG seeds. + // Newly found mothers can generate further mothers, but NEVER daughters. + //=========================================================================== + std::vector visitedUp(nEntries, 0); + std::vector ancestorQueue; + ancestorQueue.reserve(nPDGSelected); + + for (Long64_t i = 0; i < nEntries; ++i) { + if (selectedPDG[i]) { + visitedUp[i] = 1; + ancestorQueue.push_back(static_cast(i)); + } + } + + Long64_t nAncestorsAdded = 0; + size_t upPosition = 0; + + while (upPosition < ancestorQueue.size()) { + const int particleId = ancestorQueue[upPosition++]; + + for (const int mother : originalMothers[particleId]) { + if (!visitedUp[mother]) { + visitedUp[mother] = 1; + ancestorQueue.push_back(mother); + + if (!keep[mother]) { + keep[mother] = 1; + ++nAncestorsAdded; + } + } + } + } + + //=========================================================================== + // DOWNWARD traversal. + // + // Again start ONLY from original PDG seeds. + // Newly found daughters can generate further daughters, but NEVER mothers. + //=========================================================================== + std::vector visitedDown(nEntries, 0); + std::vector descendantQueue; + descendantQueue.reserve(nPDGSelected); + + for (Long64_t i = 0; i < nEntries; ++i) { + if (selectedPDG[i]) { + visitedDown[i] = 1; + descendantQueue.push_back(static_cast(i)); + } + } + + Long64_t nDescendantsAdded = 0; + size_t downPosition = 0; + + while (downPosition < descendantQueue.size()) { + const int particleId = descendantQueue[downPosition++]; + + const int first = firstDaughter[particleId]; + const int last = lastDaughter[particleId]; + + if (first < 0 || last < first) { + continue; + } + + for (int daughter = first; daughter <= last; ++daughter) { + if (!visitedDown[daughter]) { + visitedDown[daughter] = 1; + descendantQueue.push_back(daughter); + + if (!keep[daughter]) { + keep[daughter] = 1; + ++nDescendantsAdded; + } + } + } + } + + //=========================================================================== + // Construct old-to-new index map. + //=========================================================================== + oldToNew.assign(nEntries, -1); + + int newId = 0; + + for (Long64_t i = 0; i < nEntries; ++i) { + if (keep[i]) { + oldToNew[i] = newId++; + } + } + + const Long64_t nKept = newId; + const Long64_t nRemoved = nEntries - nKept; + + std::cout + << "O2mcparticle_001: PDG seeds = " + << nPDGSelected << " / " << nEntries << "\n"; + + std::cout + << "O2mcparticle_001: ancestors added = " + << nAncestorsAdded << "\n"; + + std::cout + << "O2mcparticle_001: descendants added = " + << nDescendantsAdded << "\n"; + + std::cout + << "O2mcparticle_001: kept after selection = " + << nKept + << " / " << nEntries + << ", survival fraction = " + << (nEntries > 0 + ? static_cast(nKept) / + static_cast(nEntries) + : 0.0) + << "\n"; + + //=========================================================================== + // Second pass: cache retained particle payloads and remap mothers. + //=========================================================================== + std::vector kept; + kept.reserve(newId); + + for (Long64_t i = 0; i < nEntries; ++i) { + if (!keep[i]) { + continue; + } + + inTree->GetEntry(i); + + KeptParticle particle; + + particle.oldId = static_cast(i); + particle.mcCollisionId = mcCollisionId; + particle.pdgCode = pdgCode; + particle.statusCode = statusCode; + particle.flags = flags; + particle.weight = weight; + particle.px = px; + particle.py = py; + particle.pz = pz; + particle.e = e; + particle.vx = vx; + particle.vy = vy; + particle.vz = vz; + particle.vt = vt; + + // Keep only mother references that survived. + for (const int oldMother : originalMothers[i]) { + const int newMother = remapId(oldMother, oldToNew); + if (newMother >= 0) { + particle.mothers.push_back(newMother); + } + } + + particle.firstDaughter = firstDaughter[i]; + particle.lastDaughter = lastDaughter[i]; + + kept.push_back(std::move(particle)); + } + + //=========================================================================== + // Create output tree. + //=========================================================================== + outDir->cd(); + + auto* outTree = inTree->CloneTree(0); + + if (!outTree) { + std::cerr << "Could not create output O2mcparticle_001\n"; + return false; + } + + Long64_t nPreservedDaughterSlices = 0; + Long64_t nClearedDaughterSlices = 0; + + //=========================================================================== + // Third pass: write retained particles with remapped indices. + // + // Preserve the surviving daughters if their remapped indices remain + // contiguous in the filtered table. + //=========================================================================== + for (const auto& particle : kept) { + mcCollisionId = particle.mcCollisionId; + pdgCode = particle.pdgCode; + statusCode = particle.statusCode; + flags = particle.flags; + weight = particle.weight; + px = particle.px; + py = particle.py; + pz = particle.pz; + e = particle.e; + vx = particle.vx; + vy = particle.vy; + vz = particle.vz; + vt = particle.vt; + + mothersSize = + static_cast(particle.mothers.size()); + + if (mothersSize > kMaxMothers) { + std::cerr + << "Fatal: remapped mother list has size " + << mothersSize + << ", exceeding the buffer size " + << kMaxMothers << "\n"; + delete outTree; + return false; + } + + for (int im = 0; im < kMaxMothers; ++im) { + mothers[im] = -1; + } + + for (int im = 0; im < mothersSize; ++im) { + mothers[im] = particle.mothers[im]; + } + + daughtersSlice[0] = -1; + daughtersSlice[1] = -1; + + if (particle.firstDaughter >= 0 && + particle.lastDaughter >= particle.firstDaughter) { + + int firstNewDaughter = -1; + int lastNewDaughter = -1; + + for (int oldDaughter = particle.firstDaughter; + oldDaughter <= particle.lastDaughter; + ++oldDaughter) { + + const int newDaughter = + remapId(oldDaughter, oldToNew); + + if (newDaughter < 0) { + continue; + } + + if (firstNewDaughter < 0) { + firstNewDaughter = newDaughter; + } + + lastNewDaughter = newDaughter; + } + + if (firstNewDaughter >= 0 && + lastNewDaughter >= firstNewDaughter) { + + daughtersSlice[0] = firstNewDaughter; + daughtersSlice[1] = lastNewDaughter; + ++nPreservedDaughterSlices; + + } else { + ++nClearedDaughterSlices; + } + } + + outTree->Fill(); + } + + outTree->Write( + "O2mcparticle_001", + TObject::kOverwrite); + + std::cout + << "O2mcparticle_001: preserved daughter slices = " + << nPreservedDaughterSlices << "\n"; + + std::cout + << "O2mcparticle_001: cleared daughter slices = " + << nClearedDaughterSlices << "\n"; + + summary.mcParticlesInitial += nEntries; + summary.pdgSeeds += nPDGSelected; + summary.ancestorsAdded += nAncestorsAdded; + summary.descendantsAdded += nDescendantsAdded; + summary.mcParticlesKept += nKept; + summary.mcParticlesRemoved += nRemoved; + summary.preservedDaughterSlices += nPreservedDaughterSlices; + summary.clearedDaughterSlices += nClearedDaughterSlices; + summary.invalidMotherReferences += nInvalidMotherReferences; + summary.invalidDaughterSlices += nInvalidDaughterSlices; + + return true; +} + +//_____________________________________________________________________________ +void rewriteSingleLabelTable( + TDirectory* inDir, + TDirectory* outDir, + const char* treeName, + const std::vector& oldToNew, + Long64_t& globalValidLabels, + Long64_t& globalInvalidatedLabels) +{ + auto* inTree = + dynamic_cast(inDir->Get(treeName)); + + if (!inTree) { + return; + } + + if (!hasBranch(inTree, "fIndexMcParticles")) { + std::cerr + << "Tree " << treeName + << " will not be written because its expected " + << "MC-particle index branch is missing.\n"; + return; + } + + int mcParticleId = -1; + + inTree->SetBranchAddress( + "fIndexMcParticles", + &mcParticleId); + + outDir->cd(); + + auto* outTree = inTree->CloneTree(0); + + if (!outTree) { + std::cerr << "Could not create output tree " + << treeName << "\n"; + return; + } + + const Long64_t nEntries = inTree->GetEntries(); + + Long64_t nValidLabels = 0; + Long64_t nInvalidatedLabels = 0; + + for (Long64_t i = 0; i < nEntries; ++i) { + inTree->GetEntry(i); + + mcParticleId = + remapId(mcParticleId, oldToNew); + + if (mcParticleId >= 0) { + ++nValidLabels; + } else { + ++nInvalidatedLabels; + } + + outTree->Fill(); + } + + outTree->Write( + treeName, + TObject::kOverwrite); + + std::cout + << treeName + << ": valid remapped labels = " + << nValidLabels + << ", invalidated labels = " + << nInvalidatedLabels + << "\n"; + + globalValidLabels += nValidLabels; + globalInvalidatedLabels += nInvalidatedLabels; +} + +//_____________________________________________________________________________ +void rewriteCaloLabels( + TDirectory* inDir, + TDirectory* outDir, + const std::vector& oldToNew, + GlobalSummary& summary) +{ + auto* inTree = + dynamic_cast( + inDir->Get("O2mccalolabel_001")); + + if (!inTree) { + return; + } + + auto* sizeBranch = + inTree->GetBranch( + "fIndexArrayMcParticles_size"); + + auto* idsBranch = + inTree->GetBranch( + "fIndexArrayMcParticles"); + + if (!sizeBranch || !idsBranch) { + std::cout + << "O2mccalolabel_001: expected array branches " + << "not found; copying unchanged\n"; + + outDir->cd(); + + auto* outTree = + inTree->CloneTree(-1, "fast"); + + if (outTree) { + outTree->Write( + "O2mccalolabel_001", + TObject::kOverwrite); + } + + return; + } + + const int maximumLabels = + static_cast( + inTree->GetMaximum("fIndexArrayMcParticles_size")); + + if (maximumLabels > kMaxCaloLabels) { + std::cerr + << "Fatal: maximum number of calorimeter labels is " + << maximumLabels + << ", while the buffer supports only " + << kMaxCaloLabels + << ". O2mccalolabel_001 will not be written.\n"; + return; + } + + int idsSize = 0; + int ids[kMaxCaloLabels] = {}; + + inTree->SetBranchAddress( + "fIndexArrayMcParticles_size", + &idsSize); + + inTree->SetBranchAddress( + "fIndexArrayMcParticles", + ids); + + outDir->cd(); + + auto* outTree = inTree->CloneTree(0); + + if (!outTree) { + std::cerr + << "Could not create output O2mccalolabel_001\n"; + return; + } + + const Long64_t nEntries = inTree->GetEntries(); + + Long64_t nValidLabels = 0; + Long64_t nInvalidatedLabels = 0; + + for (Long64_t i = 0; i < nEntries; ++i) { + inTree->GetEntry(i); + + if (idsSize < 0 || idsSize > kMaxCaloLabels) { + std::cerr + << "Invalid calorimeter-label array size " + << idsSize + << " at entry " << i << "\n"; + delete outTree; + return; + } + + for (int j = 0; j < idsSize; ++j) { + ids[j] = remapId(ids[j], oldToNew); + + if (ids[j] >= 0) { + ++nValidLabels; + } else { + ++nInvalidatedLabels; + } + } + + outTree->Fill(); + } + + outTree->Write( + "O2mccalolabel_001", + TObject::kOverwrite); + + std::cout + << "O2mccalolabel_001: valid remapped labels = " + << nValidLabels + << ", invalidated labels = " + << nInvalidatedLabels + << "\n"; + + summary.validCaloLabels += nValidLabels; + summary.invalidatedCaloLabels += nInvalidatedLabels; +} + +//_____________________________________________________________________________ +double bytesToMB(Long64_t bytes) +{ + return static_cast(bytes) / (1024.0 * 1024.0); +} + +//_____________________________________________________________________________ +void printGlobalSummary(const GlobalSummary& summary, + Long64_t inputBytes, + Long64_t outputBytes) +{ + const double retainedFraction = + summary.mcParticlesInitial > 0 + ? 100.0 * + static_cast(summary.mcParticlesKept) / + static_cast(summary.mcParticlesInitial) + : 0.0; + + const double removedFraction = + summary.mcParticlesInitial > 0 + ? 100.0 * + static_cast(summary.mcParticlesRemoved) / + static_cast(summary.mcParticlesInitial) + : 0.0; + + const double seedFraction = + summary.mcParticlesInitial > 0 + ? 100.0 * + static_cast(summary.pdgSeeds) / + static_cast(summary.mcParticlesInitial) + : 0.0; + + const double ancestorFractionOfKept = + summary.mcParticlesKept > 0 + ? 100.0 * + static_cast(summary.ancestorsAdded) / + static_cast(summary.mcParticlesKept) + : 0.0; + + const double descendantFractionOfKept = + summary.mcParticlesKept > 0 + ? 100.0 * + static_cast(summary.descendantsAdded) / + static_cast(summary.mcParticlesKept) + : 0.0; + + const double storageReduction = + inputBytes > 0 + ? 100.0 * + (1.0 - + static_cast(outputBytes) / + static_cast(inputBytes)) + : 0.0; + + std::cout + << "\n============================================================\n" + << "\nGLOBAL SUMMARY [ASYMMETRIC PDG FILTER]\n" + << "\n============================================================\n\n"; + + std::cout << std::fixed << std::setprecision(2); + + std::cout + << "Directories processed: " + << summary.directoriesProcessed << "\n"; + + std::cout + << "MC particles initial: " + << summary.mcParticlesInitial << "\n"; + + std::cout + << "PDG-selected seed particles: " + << summary.pdgSeeds + << " (" << seedFraction << " % of initial)\n"; + + std::cout + << "Ancestors added: " + << summary.ancestorsAdded + << " (" << ancestorFractionOfKept << " % of kept)\n"; + + std::cout + << "Descendants added: " + << summary.descendantsAdded + << " (" << descendantFractionOfKept << " % of kept)\n"; + + std::cout + << "MC particles kept: " + << summary.mcParticlesKept << "\n"; + + std::cout + << "MC particles removed: " + << summary.mcParticlesRemoved << "\n"; + + std::cout + << "Retained fraction: " + << retainedFraction << " %\n"; + + std::cout + << "Removed fraction: " + << removedFraction << " %\n"; + + std::cout + << "Preserved daughter slices: " + << summary.preservedDaughterSlices << "\n"; + + std::cout + << "Cleared daughter slices: " + << summary.clearedDaughterSlices << "\n"; + + if (summary.invalidMotherReferences > 0) { + std::cout + << "Invalid original mother refs: " + << summary.invalidMotherReferences << "\n"; + } + + if (summary.invalidDaughterSlices > 0) { + std::cout + << "Invalid original daughter slices: " + << summary.invalidDaughterSlices << "\n"; + } + + std::cout + << "\nTrack labels valid / invalidated: " + << summary.validTrackLabels + << " / " << summary.invalidatedTrackLabels << "\n"; + + std::cout + << "MFT labels valid / invalidated: " + << summary.validMFTTrackLabels + << " / " << summary.invalidatedMFTTrackLabels << "\n"; + + std::cout + << "Fwd labels valid / invalidated: " + << summary.validFwdTrackLabels + << " / " << summary.invalidatedFwdTrackLabels << "\n"; + + std::cout + << "Calo labels valid / invalidated: " + << summary.validCaloLabels + << " / " << summary.invalidatedCaloLabels << "\n"; + + std::cout + << "\nInput file size: " + << bytesToMB(inputBytes) << " MB\n"; + + std::cout + << "Output file size: " + << bytesToMB(outputBytes) << " MB\n"; + + std::cout + << "Storage reduction: " + << storageReduction << " %\n"; + + std::cout + << "\n============================================================\n"; +} + +} // namespace + +//_____________________________________________________________________________ +void filterAO2DMCParticlesPdg( + const char* input = "AO2D.root", + const char* output = "AO2D_filter.root", + const std::vector& pdgs = {}, + const char* logFileName = "filterAO2DMCParticlesPdg.log") +{ + if (!input || !output) { + std::cerr << "Input or output filename is null\n"; + return; + } + + if (pdgs.empty()) { + std::cerr + << "The requested PDG-code list is empty. " + << "Nothing will be filtered.\n"; + return; + } + + std::ofstream logFile; + std::streambuf* originalCoutBuf = std::cout.rdbuf(); + std::streambuf* originalCerrBuf = std::cerr.rdbuf(); + + if (logFileName && logFileName[0] != '\0') { + logFile.open(logFileName, std::ios::out | std::ios::trunc); + + if (!logFile.is_open()) { + std::cerr << "Cannot create log file " + << logFileName << "\n"; + return; + } + } + + TeeBuf coutTee(originalCoutBuf, logFile.is_open() ? logFile.rdbuf() : originalCoutBuf); + TeeBuf cerrTee(originalCerrBuf, logFile.is_open() ? logFile.rdbuf() : originalCerrBuf); + + if (logFile.is_open()) { + std::cout.rdbuf(&coutTee); + std::cerr.rdbuf(&cerrTee); + } + + // Restore the original stream buffers automatically before returning. + struct StreamRestorer { + std::streambuf* coutBuf; + std::streambuf* cerrBuf; + ~StreamRestorer() + { + std::cout.flush(); + std::cerr.flush(); + std::cout.rdbuf(coutBuf); + std::cerr.rdbuf(cerrBuf); + } + } streamRestorer{originalCoutBuf, originalCerrBuf}; + + const std::unordered_set keptPDGs( + pdgs.begin(), + pdgs.end()); + + auto* inFile = TFile::Open(input, "READ"); + + if (!inFile || inFile->IsZombie()) { + std::cerr + << "Cannot open input file " + << input << "\n"; + + if (inFile) { + inFile->Close(); + delete inFile; + } + + return; + } + + const Long64_t inputBytes = inFile->GetSize(); + + auto* outFile = TFile::Open(output, "RECREATE", "", 505); + + if (!outFile || outFile->IsZombie()) { + std::cerr + << "Cannot create output file " + << output << "\n"; + + if (outFile) { + outFile->Close(); + delete outFile; + } + + inFile->Close(); + delete inFile; + return; + } + + std::cout + << "\nFiltering MC particles by PDG code\n\n"; + + std::cout + << " input file: " << input << "\n" + << " output file: " << output << "\n" + << " log file: " + << (logFile.is_open() ? logFileName : "(disabled)") << "\n" + << " PDG seeds: "; + + for (const int pdg : pdgs) { + std::cout << " " << pdg; + } + + std::cout + << "\n" + << " ancestry: recursive mothers from original PDG seeds only\n" + << " descendants: recursive daughters from original PDG seeds only\n" + << " side branches from ancestors: NOT kept\n" + << " reco tables: kept unchanged; MC-particle labels remapped\n"; + + GlobalSummary summary; + + TIter next(inFile->GetListOfKeys()); + TKey* key = nullptr; + + while ((key = static_cast(next()))) { + const TKey* latestKey = inFile->GetKey(key->GetName()); + if (latestKey && key->GetCycle() != latestKey->GetCycle()) { + continue; + } + TObject* obj = key->ReadObj(); + + if (!obj) { + continue; + } + + if (std::string(key->GetName()) == "metaData") { + outFile->cd(); + obj->Write("metaData", TObject::kSingleKey | TObject::kOverwrite); + delete obj; + continue; + } + + const std::string name = obj->GetName(); + + if (name == "parentFiles") { + outFile->cd(); + obj->Write("parentFiles", TObject::kSingleKey | TObject::kOverwrite); + delete obj; + continue; + } + + if (!obj->InheritsFrom(TDirectory::Class())) { + outFile->cd(); + obj->Write(name.c_str(), TObject::kOverwrite); + delete obj; + continue; + } + + auto* inDir = + static_cast(obj); + + outFile->cd(); + + auto* outDir = + outFile->mkdir(name.c_str()); + + if (!outDir) { + std::cerr + << "Could not create output directory " + << name << "\n"; + delete obj; + continue; + } + + std::cout + << "\nProcessing directory " + << name << "\n"; + + copyDirectory(inDir, outDir); + + std::vector oldToNew; + + const bool mcParticlesRewritten = + rewriteMCParticles( + inDir, + outDir, + oldToNew, + keptPDGs, + summary); + + if (mcParticlesRewritten) { + ++summary.directoriesProcessed; + + rewriteSingleLabelTable( + inDir, + outDir, + "O2mctracklabel", + oldToNew, + summary.validTrackLabels, + summary.invalidatedTrackLabels); + + rewriteSingleLabelTable( + inDir, + outDir, + "O2mcmfttracklabel", + oldToNew, + summary.validMFTTrackLabels, + summary.invalidatedMFTTrackLabels); + + rewriteSingleLabelTable( + inDir, + outDir, + "O2mcfwdtracklabel", + oldToNew, + summary.validFwdTrackLabels, + summary.invalidatedFwdTrackLabels); + + rewriteCaloLabels( + inDir, + outDir, + oldToNew, + summary); + + } else { + std::cerr + << "MC particles were not rewritten in directory " + << name + << "; associated label tables were not rewritten.\n"; + } + + delete obj; + } + + outFile->Flush(); + + const Long64_t outputBytes = outFile->GetSize(); + + printGlobalSummary( + summary, + inputBytes, + outputBytes); + + outFile->Close(); + inFile->Close(); + + delete outFile; + delete inFile; + + std::cout + << "\nDone. Written " + << output << "\n"; + + if (logFile.is_open()) { + std::cout + << "Log written to " + << logFileName << "\n"; + } +} diff --git a/MC/utils/metrics/README.md b/MC/utils/metrics/README.md new file mode 100644 index 000000000..38043aade --- /dev/null +++ b/MC/utils/metrics/README.md @@ -0,0 +1,3 @@ +Set of scripts and utilities for the creation and processing +of workflow metrics (CPU, memory, walltime) for tasks executed +in o2dpg_workflow_runner.py and derived from its pipeline_metric*.log monitoring. \ No newline at end of file diff --git a/MC/utils/metrics/metrics_harvester.py b/MC/utils/metrics/metrics_harvester.py new file mode 100644 index 000000000..30585ed26 --- /dev/null +++ b/MC/utils/metrics/metrics_harvester.py @@ -0,0 +1,224 @@ +#!/usr/bin/env python3 + +# A python tool, that calculates mean O2DPG workflow metrics by +# harvesting from data from pipeline_metric files from the GRID (for a given lpm production tag). + +import json +import subprocess +from collections import defaultdict +import re +import os +import argparse +import random +from pathlib import Path +import sys +import time +from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor, as_completed + +# add the parent directory of the current file to sys.path to find the o2dpg_sim_metric +sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) +from o2dpg_sim_metrics import json_stat_impl + +def alien_find(path, pattern="*", logging=False): + cmd = ["alien.py", "find", path, pattern] + if logging: + print (f"Performing {cmd}") + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return [line.strip() for line in result.stdout.splitlines() if line.strip()] + + +def alien_cp(alien_path, local_path, parent=None, fatal=False, logging=False): + cmd = ["alien.py", "cp"] + if parent != None: + cmd = cmd + ["-parent", f"{parent}"] + cmd = cmd + [f"alien://{alien_path}", f"file://{local_path}"] + if logging: + print (f"Performing {cmd}") + try: + subprocess.run(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=True) + except subprocess.CalledProcessError as e: + pass + +def alien_cp_inputfile(inputfile, logging=False): + cmd = ["alien.py", "cp", "-input", f"{inputfile}"] + if logging: + print (f"Performing {cmd}") + subprocess.run(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=True) + +def parse_workflow_path(path, prod_tag): + parts = path.strip("/").split("/") + try: + idx = parts.index(prod_tag) + except ValueError: + return None + + after = parts[idx + 1 :] + if len(after) < 2: + return None + + if after[0].isdigit() and len(after[0]) == 1: + cycle = int(after[0]) + run_number = int(after[1]) + split = after[2] + else: + cycle = None + run_number = int(after[0]) + split = after[1] + + return cycle, run_number, split + + +def calculate_statistics(selecteddirs, prod_tag, run_number, batchcopy = False): + """ + downloads the metrics files and calculates aggregates statistics + """ + targetdir = f"/tmp/o2dpg_metrics_harvester/{prod_tag}/{run_number}" + if not os.path.exists(targetdir): + os.makedirs(targetdir) + + start=time.time() + # determine target dir based on tag and run_number + if batchcopy == True: + # make an inputfile + inputfile = f"{targetdir}/cp_input.list" + with open(inputfile,'w') as f: + for dir in selecteddirs: + path = Path(dir) + # Get the last 1 components --> mimics -parent which does not work with inputlists + last_N = Path(*path.parts[-1:]) + f.write(f"{dir}/pipeline_metr* file:{targetdir}/{last_N}\n") + + # copy with the input-file + alien_cp_inputfile(inputfile, logging=True) + + else: + for dir in selecteddirs: + # avoid copy if we can ! + # we need to keep 2-top level dirs + alien_cp(f"{dir}/pipeline_metr*", targetdir, parent=1) + + end=time.time() + print(f"Copy took {end-start:.4f} seconds") + + # construct the list of all inputfiles + input_files = [str(p) for p in Path(targetdir).rglob('pipeline_metr*')] + print(input_files) + + # calculate the stats with all the files in targetdir + outputfilename=f"{targetdir}/merged_metrics.json" + meta_info = {"prod-tag" : prod_tag, "run-number" : run_number} + json_stat_impl(input_files, outputfilename, meta_info) + +import os +from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor + +def treat_parallel(func, data, use_threads=False, max_workers=None): + """ + Apply `func` to each element of `data` in parallel. + + Parameters + ---------- + func : callable + The function to apply to each element. + data : iterable + The data to process. + use_threads : bool, default=False + If True, use threads (good for I/O-bound tasks). + If False, use processes (good for CPU-bound tasks). + max_workers : int, optional + Number of workers to use. Defaults to number of CPUs for processes. + + Returns + ------- + list + The results in the same order as `data`. + """ + if max_workers is None: + max_workers = os.cpu_count() if not use_threads else min(32, os.cpu_count() * 5) + + Executor = ThreadPoolExecutor if use_threads else ProcessPoolExecutor + + # --- Use map to preserve order --- + with Executor(max_workers=max_workers) as executor: + results = list(executor.map(func, data)) + + return results + +def treat_one_run(data_element): + """ + The final worker function to execute for each run. + Expects it's input parameters in a list to work well with Thread/ProcessExecutor and treat_parallel above. + + data_element should be a tuple, where + index 0 --> the run_number + index 1 --> a list of tuples(cycle, split, directory) + index 2 --> production tag + index 3 --> sample_size + """ + run_number, candidates = data_element[0], data_element[1] + prod_tag = data_element[2] + sample_size = data_element[3] + universe = [ w[2] for w in candidates ] + selected_dirs = random.sample(universe, min(len(universe), sample_size)) + print (f"For {run_number} selected {selected_dirs}") + calculate_statistics(selected_dirs, prod_tag, run_number, batchcopy=True) + + +def process_prod_tag(prod_tag, year="2025", ccdb_url=None, username=None, overwrite=False, samplesize=20): + base_path = f"/alice/sim/{year}/{prod_tag}" + + pipelinemetric_files = alien_find(base_path, "pipeline_metric*") + + # exclude some unnecessary paths + pipelinemetric_files = [ + zf for zf in pipelinemetric_files + if "/AOD/" not in zf and "/QC/" not in zf and "/TimeseriesTPCmerging/" not in zf and "/Stage" not in zf + ] + print (f"Found {len(pipelinemetric_files)} pipeline metric files") + + # directories containing workflow.json + workflow_dirs = {os.path.dirname(wf) for wf in pipelinemetric_files} + print (f"Found {len(workflow_dirs)} workflow dirs") + + # Step 2: group by run_number + runs = defaultdict(list) + for dir in workflow_dirs: + parsed = parse_workflow_path(dir, prod_tag) + if parsed is None: + continue + cycle, run_number, split = parsed + runs[run_number].append((cycle, split, dir)) + print(f"Found {len(runs)} run numbers") + + # Step 3: for each run_number, pick samplesize files for the final calculation + # for run_number, candidates in sorted(runs.items()): + # universe = [ w[2] for w in candidates ] + # selected_dirs = random.sample(universe, min(len(universe), samplesize)) + # print (f"For {run_number} selected {selected_dirs}") + + # # calculate merged statistics from the sample + # calculate_statistics(selected_dirs, prod_tag, run_number, batchcopy=False) + + data = [ (d[0], d[1], prod_tag, samplesize) for d in sorted(runs.items()) ] + do_parallel = True + if do_parallel == True: + treat_parallel(treat_one_run, data, use_threads=False, max_workers=8) + else: + for data_element in data: + treat_one_run(data_element) + +def main(): + parser = argparse.ArgumentParser( + description="Harvest MC metrics from AlienGRID; aggregate; and publish to CCDB" + ) + parser.add_argument("--prod_tag", required=True, help="Production tag (e.g. prod2025a)") + parser.add_argument("--ccdb", required=False, default="https://alice-ccdb.cern.ch", help="CCDB server URL") + parser.add_argument("--username", required=False, help="GRID username (needs appropriate AliEn token initialized)") + parser.add_argument("--year", default="2025", help="Production year (default: 2025)") + parser.add_argument("--overwrite", action="store_true", help="Overwrite existing entries") + args = parser.parse_args() + + process_prod_tag(args.prod_tag, year=args.year, ccdb_url=args.ccdb, username=args.username, overwrite=args.overwrite) + +if __name__ == "__main__": + main() diff --git a/MC/utils/metrics/server.py b/MC/utils/metrics/server.py new file mode 100644 index 000000000..6c768c961 --- /dev/null +++ b/MC/utils/metrics/server.py @@ -0,0 +1,92 @@ + +# A simple HTTPS server with an endpoint on which +# callers can inject O2DPG_workflow_runner stat json metrics. +# The service is supposed to run as aggregator of these individual metrics +# and to provide high-quality merged statistics on resource estimates. These +# estimates can then be used to improve the scheduling of o2dpg_workflow_runner workflows. + +from fastapi import FastAPI +from pydantic import BaseModel +from asyncio import Queue, create_task +import asyncio, json, time +import aiofiles +from fastapi import Request + +import sys, os + +# add the parent directory of the current file to sys.path to find the o2dpg_sim_metric +sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) +from o2dpg_sim_metrics import merge_stats_into + +app = FastAPI() +queue = Queue() +agg = {} # {(metric_name): {"sum": 0.0, "count": 0}} +flush_interval = 5 # seconds +outfile = "metrics.json" + +# Global state +agg_by_tag = {} # { production_tag: cached_result } + + +@app.post("/metric") +async def receive_metric(request : Request): + # just enqueue, return quickly + payload = await request.json() + await queue.put(payload) + return {"status": "ok"} + +def init_cache(): + """ + Initializes the cache of results from files + """ + pass + +def flush_to_disc(tag): + """ + flushes result for tag to disc + """ + metrics = agg_by_tag.get(tag, {}) + filename = f"aggr_metrics_tag_{tag}.json" + with open(filename, 'w') as f: + json.dump(metrics, f) + +async def worker(): + """ + Function performing the metrics aggregation + """ + while True: + payload = await queue.get() + + # Extract production-tag from metadata + meta = payload.get("meta-data", {}) + tag = meta.get("production-tag", "default") + + print (f"Worker is treating payload for tag {tag}") + + current = agg_by_tag.get(tag, {}) # fetch existing aggregate + updated = merge_stats_into([payload, current], None, meta) # merge new payload with cached + agg_by_tag[tag] = updated # store back in cache + + flush_to_disc(tag) + + queue.task_done() + + +async def flusher(): + while True: + await asyncio.sleep(flush_interval) + snapshot = { + k: (v["sum"] / v["count"]) if v["count"] else 0 + for k, v in agg.items() + } + async with aiofiles.open(outfile, "w") as f: + await f.write(json.dumps(snapshot, indent=2)) + print(f"Flushed {len(snapshot)} metrics at {time.ctime()}") + + +@app.on_event("startup") +async def startup(): + # start multiple workers for parallelism + for _ in range(8): # one per CPU core + create_task(worker()) + # create_task(flusher()) diff --git a/MC/utils/metrics/top_metrics.sh b/MC/utils/metrics/top_metrics.sh new file mode 100644 index 000000000..5f9e748ae --- /dev/null +++ b/MC/utils/metrics/top_metrics.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash + +# a simple top-N metrics query for a couple of examples using jq +# assumes JSON produced by o2dpg_sim_metrics.py + +# top CPU-time consumers +jq 'to_entries + | map(select(.value.cpu?.mean != null and .value.lifetime?.mean != null)) + | sort_by(-(.value.cpu.mean * .value.lifetime.mean)) + | .[:5] + | map({name: .key, + cpu_mean: .value.cpu.mean, + lifetime_mean: .value.lifetime.mean, + product: (.value.cpu.mean * .value.lifetime.mean)})' merged_metrics.json + +# top mem consumers + + +# top cpu consumers +jq 'to_entries + | map(select(.value.cpu?.mean != null)) + | sort_by(-.value.cpu.mean) + | .[:5] + | map({name: .key, cpu_mean: .value.cpu.mean})' + +# top walltime consumers +jq 'to_entries + | map(select(.value.lifetime?.mean != null)) + | sort_by(-.value.lifetime.mean) + | .[:5] + | map({name: .key, lifetime_mean: .value.lifetime.mean})' \ No newline at end of file diff --git a/MC/utils/o2dpg_data_embedding_utils.py b/MC/utils/o2dpg_data_embedding_utils.py new file mode 100644 index 000000000..b152d991e --- /dev/null +++ b/MC/utils/o2dpg_data_embedding_utils.py @@ -0,0 +1,344 @@ +# Set of python modules/util functions for the MC-to-DATA embedding +# Mostly concerning extraction of MC collision context from existing data AO2D.root + +import warnings +warnings.filterwarnings( + "ignore", + message="pandas.Int64Index is deprecated", + category=FutureWarning, +) + +import ROOT +import uproot +import pandas as pd +import re +from ROOT import o2 # for CCDB +import argparse +import sys + +class lhc_constants: + LHCMaxBunches = 3564 # max N bunches + LHCRFFreq = 400.789e6 # LHC RF frequency in Hz + LHCBunchSpacingNS = 10 * 1.e9 / LHCRFFreq # bunch spacing in ns (10 RFbuckets) + LHCOrbitNS = LHCMaxBunches * LHCBunchSpacingNS # orbit duration in ns + LHCRevFreq = 1.e9 / LHCOrbitNS # revolution frequency + LHCBunchSpacingMUS = LHCBunchSpacingNS * 1e-3 # bunch spacing in \mus (10 RFbuckets) + LHCOrbitMUS = LHCOrbitNS * 1e-3 + +def thin_AO2D_file(input_file): + """ + A function to thin an existing AO2D file by just keeping a single DF_ folder + """ + + # Open the input ROOT file + infile = ROOT.TFile.Open(input_file, "READ") + + # Find the first TDirectory starting with "DF_" + df_dir = None + dir_name = "" + for key in infile.GetListOfKeys(): + name = key.GetName() + if name.startswith("DF_"): + # Access the TDirectory + df_dir = infile.Get(name) + dir_name = name + break + + if not df_dir: + raise RuntimeError("No TDirectory starting with 'DF_' found.") + + # Open the output file (create if not exist) + output_file = "AO2D_reduced_" + str(dir_name) + ".root" + outfile = ROOT.TFile.Open(output_file, "RECREATE") + + # Create the same directory structure in the output file + df_dir_copy = outfile.mkdir(dir_name) + + # Move to the newly created directory + df_dir_copy.cd() + + # Loop over the keys (trees) inside the "DF_" directory and copy them + for key in df_dir.GetListOfKeys(): + obj = df_dir.Get(key.GetName()) + if isinstance(obj, ROOT.TTree): # Check if it's a TTree + # Clone the tree and write it to the corresponding directory in the output file + obj.CloneTree(-1).Write(key.GetName(), ROOT.TObject.kOverwrite) # Copy the tree + + # Now handle the metaData;1 key (TMap) in the top-level directory + meta_data = infile.Get("metaData") + if meta_data: + if isinstance(meta_data, ROOT.TMap): + copied_meta_data = meta_data.Clone() + outfile.cd() # Make sure we're at the top-level in the output file + outfile.WriteObject(meta_data, "metaData") + + # Iterate over the map + iter = meta_data.MakeIterator() + entry = iter.Next() + while entry: + key = entry + value = meta_data.GetValue(key) + + # Convert TObjString to Python string + key_str = key.GetName() + value_str = value.GetName() if value else "None" + print(f"{key_str}: {value_str}") + entry = iter.Next() + + # Close the files + outfile.Close() + infile.Close() + + print(f"Copied all trees from TDirectory '{dir_name}' to '{output_file}'.") + + +def retrieve_Aggregated_RunInfos(run_number): + """ + Retrieves the aggregated runinfo object ... augmented with the number of timeframes + """ + runInfo = o2.parameters.AggregatedRunInfo.buildAggregatedRunInfo(o2.ccdb.BasicCCDBManager.instance(), run_number) + detList = o2.detectors.DetID.getNames(runInfo.grpECS.getDetsReadOut()) + assert (run_number == runInfo.runNumber) + assert (run_number == runInfo.grpECS.getRun()) + + run_info = {"SOR" : runInfo.sor, + "EOR" : runInfo.eor, + "FirstOrbit" : runInfo.orbitSOR, + "LastOrbit" : runInfo.orbitEOR, + "OrbitReset" : runInfo.orbitReset, + "OrbitsPerTF" : int(runInfo.orbitsPerTF), + "detList" : detList} + + # update num of timeframes + # figure out how many timeframes fit into this run range + # take the number of orbits per timeframe and multiply by orbit duration to calculate how many timeframes fit into this run + time_length_inmus = 1000 * (run_info["EOR"] - run_info["SOR"]) + ntimeframes = time_length_inmus / (run_info["OrbitsPerTF"] * lhc_constants.LHCOrbitMUS) + run_info["ntimeframes"] = ntimeframes + + return run_info + + +def get_bc_with_timestamps(bc_data, run_info): + """ + bc_data is a pandas df containing the AO2D basic bunch crossing data. + Returns the bc table with additional information on timeframeID etc. + """ + + # add a new column to the bc table dynamically + # this is the time in mu s + bc_data["timestamp"] = run_info["OrbitReset"] + (bc_data["fGlobalBC"] * lhc_constants.LHCBunchSpacingMUS).astype("int64") + bc_data["timeframeID"] = ((bc_data["fGlobalBC"] - (run_info["FirstOrbit"] * lhc_constants.LHCMaxBunches)) / (lhc_constants.LHCMaxBunches * run_info["OrbitsPerTF"])).astype("int64") + bc_data["orbit"] = (bc_data["fGlobalBC"] // lhc_constants.LHCMaxBunches).astype("int64") + bc_data["bc_within_orbit"] = (bc_data["fGlobalBC"] % lhc_constants.LHCMaxBunches).astype("int64") + return bc_data + + +def get_timeframe_structure(filepath, run_info, max_folders=1, include_dataframe = False, folder_filter=None): + """ + run_info: The aggregated run_info object for this run + """ + def find_tree_key(keys, pattern): + for key in keys: + key_clean = key + if re.search(pattern, key_clean, re.IGNORECASE): + return key_clean + return None + + file = uproot.open(filepath) + raw_keys = file.keys() + + folders = { k.split("/")[0] : 1 for k in raw_keys if "O2bc_001" in k } + folders = [ k for k in folders.keys() ] + folders = folders[:max_folders] + + print ("have ", len(raw_keys), f" in file {filepath}") + + merged = {} # data containers per file + for folder in folders: + if folder_filter != None and folder != folder_filter: + continue + #print (f"Looking into {folder}") + + # Find correct table names using regex + bc_key = find_tree_key(raw_keys, f"^{folder}/O2bc_001") + bc_data = file[bc_key].arrays(library="pd") + + # collision data + coll_key = find_tree_key(raw_keys, f"^{folder}/O2coll.*_001") + coll_data = file[coll_key].arrays(library="pd") + + # extend the data + bc_data = get_bc_with_timestamps(bc_data, run_info) + + # do the splice with collision data + bc_data_coll = bc_data.iloc[coll_data["fIndexBCs"]].reset_index(drop=True) + # this is the combined table containing collision data associated to bc and time information + combined = pd.concat([bc_data_coll, coll_data], axis = 1) + + # do the actual timeframe structure calculation; we only take collisions with a trigger decision attached + triggered = combined[combined["fTriggerMask"] != 0] + timeframe_structure = triggered.groupby('timeframeID').apply( + lambda g: list(zip(g['fGlobalBC'], g['fPosX'], g['fPosY'], g['fPosZ'], g['orbit'], g['bc_within_orbit'], g['fCollisionTime'])) + ).reset_index(name='position_vectors') + + folderkey = folder + '@' + filepath + merged[folderkey] = timeframe_structure # data per folder + if include_dataframe: + merged["data"] = combined + + # annotate which timeframes are available here and from which file + return merged + + +def fetch_bccoll_to_localFile(alien_file, local_filename): + """ + A function to remotely talk to a ROOT file ... and fetching only + BC and collision tables for minimal network transfer. Creates a ROOT file locally + of name local_filename. + + Returns True if success, otherwise False + """ + + # make sure we have a TGrid connection + # Connect to AliEn grid + if not ROOT.gGrid: + ROOT.TGrid.Connect("alien://") + + if not ROOT.gGrid: + print (f"Not TGrid object found ... aborting") + return False + + # Open the remote file via AliEn + infile = ROOT.TFile.Open(alien_file, "READ") + if not infile or infile.IsZombie(): + raise RuntimeError(f"Failed to open {alien_file}") + return False + + # Output local file + outfile = ROOT.TFile.Open(local_filename, "RECREATE") + + # List of trees to copy + trees_to_copy = ["O2bc_001", "O2collision_001"] + + # Loop over top-level keys to find DF_ folders + for key in infile.GetListOfKeys(): + obj = key.ReadObj() + if obj.InheritsFrom("TDirectory") and key.GetName().startswith("DF_"): + df_name = key.GetName() + df_dir = infile.Get(df_name) + + # Create corresponding folder in output file + out_df_dir = outfile.mkdir(df_name) + out_df_dir.cd() + + # Copy only specified trees if they exist + for tree_name in trees_to_copy: + if df_dir.GetListOfKeys().FindObject(tree_name): + tree = df_dir.Get(tree_name) + cloned_tree = tree.CloneTree(-1) # copy all entries + cloned_tree.Write(tree_name) + + outfile.cd() # go back to top-level for next DF_ + + # Close files + outfile.Close() + infile.Close() + return True + + +def convert_to_digicontext(aod_timeframe, df_folder, timeframeID=-1): + """ + converts AOD collision information from AO2D to collision context + which can be used for MC + """ + # we create the digitization context object + digicontext=o2.steer.DigitizationContext() + + # we can fill this container + parts = digicontext.getEventParts() + # we can fill this container + records = digicontext.getEventRecords() + # copy over information + maxParts = 1 + + entry = 0 + vertices = ROOT.std.vector("o2::math_utils::Point3D")() + vertices.resize(len(aod_timeframe)) + + colindex = 0 + for colindex, col in enumerate(aod_timeframe): + # we make an event interaction record + pvector = ROOT.std.vector("o2::steer::EventPart")() + pvector.push_back(o2.steer.EventPart(0, colindex)) + parts.push_back(pvector) + + orbit = col[4] + bc_within_orbit = col[5] + interaction_rec = o2.InteractionRecord(bc_within_orbit, orbit) + col_time_relative_to_bc = col[6] # in NS + time_interaction_rec = o2.InteractionTimeRecord(interaction_rec, col_time_relative_to_bc) + records.push_back(time_interaction_rec) + vertices[colindex].SetX(col[1]) + vertices[colindex].SetY(col[2]) + vertices[colindex].SetZ(col[3]) + + digicontext.setInteractionVertices(vertices) + digicontext.setNCollisions(vertices.size()) + digicontext.setMaxNumberParts(maxParts) + + # set the bunch filling ---> NEED to fetch it from CCDB + # digicontext.setBunchFilling(bunchFillings[0]); + + # TODO: set the interaction rate (for TPC loopers) + # digicontext.mDigitizerInteractionRate = ... + + prefixes = ROOT.std.vector("std::string")(); + prefixes.push_back("sgn") + + digicontext.setSimPrefixes(prefixes); + digicontext.printCollisionSummary(); + digicontext.saveToFile(f"collission_context_{df_folder}:{timeframeID}.root") + + +def process_data_AO2D(file_name, run_number, upper_limit = -1): + """ + Creates all the collision contexts + """ + timeframe_data = [] + + local_filename = "local.root" + fetch_bccoll_to_localFile(file_name, local_filename) + + # fetch run_info object + run_info = retrieve_Aggregated_RunInfos(run_number) + merged = get_timeframe_structure(local_filename, run_info, max_folders=1000) + print ("Got " + str(len(merged)) + " datasets") + timeframe_data.append(merged) + + counter = 0 + for d in timeframe_data: + for key in d: + result = d[key] + for index, row in result.iterrows(): + if upper_limit >= 0 and counter >= upper_limit: + break + tf = row['timeframeID'] + cols = row['position_vectors'] + df = key.split('@')[0] # this is the DF folder name + convert_to_digicontext(cols, df, tf) + counter = counter + 1 + + +def main(): + parser = argparse.ArgumentParser(description='Extracts collision contexts from reconstructed AO2D') + + parser.add_argument("--run-number", type=int, help="Run number to anchor to", required=True) + parser.add_argument("--aod-file", type=str, help="Data AO2D file (can be on AliEn)", required=True) + parser.add_argument("--limit", type=int, default=-1, help="Upper limit of timeframes to be extracted (-1 is no limit)") + args = parser.parse_args() + + process_data_AO2D(args.aod_file, args.run_number, args.limit) + +if __name__ == "__main__": + sys.exit(main()) diff --git a/MC/utils/o2dpg_sim_metrics.py b/MC/utils/o2dpg_sim_metrics.py index 6631c0bc2..c9367b7be 100755 --- a/MC/utils/o2dpg_sim_metrics.py +++ b/MC/utils/o2dpg_sim_metrics.py @@ -3,6 +3,7 @@ import sys from os.path import join, exists, basename from os import makedirs +import os from copy import deepcopy import argparse import re @@ -12,8 +13,8 @@ import matplotlib import json import numpy as np +import math import pandas as pd -import seaborn as sns ############################################################################ # # @@ -63,9 +64,16 @@ METRIC_NAME_USS = "uss" METRIC_NAME_PSS = "pss" METRIC_NAME_TIME = "time" +METRIC_NAME_CGROUP_CPU = "cgroup_cpu" +METRIC_NAME_CGROUP_MEM = "cgroup_mem" # metrics that are extracted by the o2_dpg_workflow_runner and put in pipeline_metric*.log METRICS = [METRIC_NAME_CPU, METRIC_NAME_USS, METRIC_NAME_PSS, METRIC_NAME_TIME] +# additional cgroup-based metrics produced when runner uses --systemd-run +CGROUP_METRICS = [METRIC_NAME_CGROUP_CPU, METRIC_NAME_CGROUP_MEM] + +# task names matching this pattern are internal runner markers, not real tasks +_INTERNAL_NAME_PREFIX = "__" # some features of MC runs, these can be extracted from the meta information # use these when multiple pipelines are given and we want to extract comparison plots for those based on these features @@ -149,9 +157,16 @@ def __init__(self, pipeline_path=None): self.name = None # use this as an id in the dataframe later self.timestamp = int(time_ns() / 1000) + # actual elapsed walltime derived from log timestamps (more accurate than + # iter_count × monitor_interval, which ignores per-pass overhead) + self.wall_time_s = None + # cgroup global entries (iter → {cpu_pct, mem_mb}); populated when the + # metric log contains __cgroup_global__ rows from the systemd-run backend + self.cgroup_global_rows = [] if pipeline_path: self.extract_from_pipeline(pipeline_path) + self.pipeline_file = pipeline_path def __add__(self, other): """ @@ -201,15 +216,19 @@ def convert_columns_to_float_if_possible(self): def clean_cpu(self): """ - Sometimes we have negative CPU values, set them to 0 + Sometimes we have negative CPU values, set them to 0. + Also normalise cgroup_cpu by the same factor (100) so it is in #cores. """ - if METRIC_NAME_CPU not in self.dict_for_df: - return - - cpu_list = self.dict_for_df[METRIC_NAME_CPU] - for i, value in enumerate(cpu_list): - # if negative, set to 0; in addition, divide by 100 since we are counting number of CPUs while psutil is doing in % - cpu_list[i] = max(0, value) / 100 + for col in (METRIC_NAME_CPU, METRIC_NAME_CGROUP_CPU): + cpu_list = self.dict_for_df.get(col) + if cpu_list is None: + continue + for i, value in enumerate(cpu_list): + # if negative, set to 0; divide by 100 to convert % → #cores + if value is None: + cpu_list[i] = None + else: + cpu_list[i] = max(0, value) / 100 def compute_time_delta(self): """ @@ -255,6 +274,10 @@ def add_iteration(self, iteration): and derive the timeframe and parent category as well """ + # Skip internal runner markers (e.g. __cgroup_global__, __global_init_task__) + if str(iteration.get("name", "")).startswith(_INTERNAL_NAME_PREFIX): + return + for key, value in iteration.items(): if key == "name": try: @@ -293,8 +316,16 @@ def extract_from_pipeline(self, pipeline_path): continue if "iter" in d: - # That is an iteration, add it to the dictionary - self.add_iteration(d) + # Intercept __cgroup_global__ rows before they reach add_iteration. + # These carry slice-level CPU (% units) and memory.current (MB). + if str(d.get("name", "")) == "__cgroup_global__": + self.cgroup_global_rows.append({ + 'iter': d.get('iter'), + 'cpu_pct': d.get('cpu'), # % (100 = 1 core) + 'mem_mb': d.get('pss'), # memory.current in MB + }) + else: + self.add_iteration(d) continue if not self.meta: # at this point, the only other line in the pipeline_metric is the meta info, so when we end up here, we know that it is meta info @@ -307,6 +338,13 @@ def extract_from_pipeline(self, pipeline_path): if not self.check(): return False + # Capture actual elapsed walltime from absolute log timestamps before + # compute_time_delta overwrites them with per-task deltas. + raw_times = [t for t in self.dict_for_df.get(METRIC_NAME_TIME, []) + if t is not None and isinstance(t, (int, float))] + if len(raw_times) >= 2: + self.wall_time_s = max(raw_times) - min(raw_times) + self.add_meta() self.convert_columns_to_float_if_possible() self.clean_cpu() @@ -744,6 +782,603 @@ def extract_resources(pipelines): # Collect all metrics we got, here we want to have the median from all the iterations return [Resources(p) for p in pipelines] +def merge_stats(elementary, running): + """ + Merge an incoming elementary JSON into a running stats structure. + Also maintains running std using Welford's method. + + Each metric stores: + mean, std, M2, min, max, count + """ + if not elementary: + return running + + n_new_total = int(elementary.get("count", 1)) + running["count"] = running.get("count", 0) + n_new_total + + for name, metrics in elementary.items(): + if name == "count": + continue + + if name not in running: + running[name] = {"count": 0} + + # existing count for this name + n_old_name = running[name].get("count", 0) + + for metric, vals in metrics.items(): + if not isinstance(vals, dict): + continue + + if metric not in running[name]: + running[name][metric] = { + "min": vals.get("min"), + "max": vals.get("max"), + "mean": vals.get("mean"), + "std": 0.0, + "M2": 0.0, + "count": n_new_total + } + continue + + rmetric = running[name][metric] + n_old = rmetric.get("count", 0) + n_new = n_new_total + + # update min / max + e_min = vals.get("min") + e_max = vals.get("max") + if e_min is not None: + rmetric["min"] = e_min if rmetric["min"] is None else min(rmetric["min"], e_min) + if e_max is not None: + rmetric["max"] = e_max if rmetric["max"] is None else max(rmetric["max"], e_max) + + # combine means & M2 + mean_a = rmetric.get("mean") + mean_b = vals.get("mean") + + # If either mean is missing, use the one that exists + if mean_a is None and mean_b is None: + # Nothing to do + continue + elif mean_a is None: + rmetric["mean"] = mean_b + rmetric["M2"] = 0.0 + rmetric["count"] = n_new + elif mean_b is None: + # keep existing stats + rmetric["mean"] = mean_a + rmetric["M2"] = rmetric.get("M2", 0.0) + rmetric["count"] = n_old + else: + # both defined → do weighted merge + delta = mean_b - mean_a + new_count = n_old + n_new + new_mean = mean_a + delta * (n_new / new_count) + new_M2 = rmetric.get("M2", 0.0) + 0.0 + (delta**2) * (n_old * n_new / new_count) + + rmetric["mean"] = new_mean + rmetric["M2"] = new_M2 + rmetric["count"] = new_count + + # update std from M2 + c = rmetric["count"] + rmetric["std"] = math.sqrt(rmetric["M2"] / c) if c > 1 else 0.0 + + running[name]["count"] = n_old_name + n_new_total + + # round mean and std for readability + for name, metrics in running.items(): + if name == "count": + continue + for metric, vals in metrics.items(): + if not isinstance(vals, dict): + continue + if "mean" in vals: + vals["mean"] = r3(vals["mean"]) + if "std" in vals: + vals["std"] = r3(vals["std"]) + if "min" in vals: + vals["min"] = r3(vals["min"]) + if "max" in vals: + vals["max"] = r3(vals["max"]) + + return running + +def print_statistics(resource_object): + """ + prints resource statistics for one dataframe of pipeline resources + """ + print ("<--- Extracted resource summary from file ", resource_object.pipeline_file) + dframe = resource_object.df + meta = resource_object.meta + + max_iter = dframe['iter'].max() + print("Iterations: ", max_iter) + # Prefer actual walltime from log timestamps (last ts - first ts); fall + # back to iter_count × monitor_interval when timestamps are unavailable. + if getattr(resource_object, 'wall_time_s', None) is not None: + print("Actual walltime (s): ", round(resource_object.wall_time_s, 1)) + else: + monitor_interval = 5 + if meta and 'monitor_interval_cpu' in meta: + try: + monitor_interval = float(meta['monitor_interval_cpu']) + except (TypeError, ValueError): + pass + print("Estimated runtime (s): ", max_iter * monitor_interval) + + #(a) PSS memory + summed_pss_per_iter=dframe.groupby("iter")['pss'].sum() + mean_pss = summed_pss_per_iter.mean() + max_pss = summed_pss_per_iter.max() + print ("Mean-PSS (MB): ", mean_pss) + print ("Max-PSS (MB): ", max_pss) + + #(b) CPU consumption — prefer cgroup global (slice-level, immune to + # per-task psutil sampling artefacts) over the psutil per-task sum. + cg_rows = resource_object.cgroup_global_rows + if cg_rows: + cg_df = pd.DataFrame(cg_rows).dropna(subset=['cpu_pct']) + cg_by_iter = cg_df.groupby('iter')['cpu_pct'].mean() + mean_cpu = cg_by_iter.mean() / 100.0 # % → cores + max_cpu = cg_by_iter.max() / 100.0 + print ("Mean-CPU (cores, cgroup): ", mean_cpu) + print ("Max-CPU (cores, cgroup): ", max_cpu) + print ("CPU-efficiency (cgroup): ", mean_cpu / meta["cpu_limit"]) + # also print cgroup memory if available + cg_mem_df = pd.DataFrame(cg_rows).dropna(subset=['mem_mb']) + if not cg_mem_df.empty: + cg_mem = cg_mem_df.groupby('iter')['mem_mb'].mean() + print ("Mean-mem (MB, cgroup): ", cg_mem.mean()) + print ("Max-mem (MB, cgroup): ", cg_mem.max()) + else: + summed_cpu_per_iter=dframe.groupby("iter")['cpu'].sum() + mean_cpu = summed_cpu_per_iter.mean() + max_cpu = summed_cpu_per_iter.max() + print ("Mean-CPU (cores): ", mean_cpu) + print ("Max-CPU (cores): ", max_cpu) + print ("CPU-efficiency: ", mean_cpu / meta["cpu_limit"]) + + #(c) Top N memory consumers by name + top_n = 5 + top_mem = ( + dframe.groupby('name')['pss'] + .max() # peak PSS for each component + .sort_values(ascending=False) # sort by memory usage + .head(top_n) + ) + print(f"\nTop-{top_n} memory consumers (by peak PSS):") + for comp, mem in top_mem.items(): + print(f" {comp:<20s} {mem:10.2f} MB") + + #(d) max disc consumption + if 'disc' in dframe: + print ("\nMax-DISC usage (MB): ", dframe['disc'].max()) + print ("Mean-DISC usage (MB): ", dframe['disc'].mean()) + print ("---> ") + +def r3(x): + """Round to 3 decimals, return None for None/NaN.""" + if x is None: + return None + try: + xf = float(x) + except Exception: + return None + if math.isnan(xf): + return None + return round(xf, 3) + +def produce_json_stat(resource_object): + print ("<--- Producing resource json from file ", resource_object.pipeline_file) + dframe = resource_object.df + meta = resource_object.meta + + # also write json summary; This is a file that can be used + # to adjust the resource estimates in o2dpg_workflow_runner.py + # + resource_json = {} + # group by 'name' and compute all needed stats for each metric. + # Include cgroup metrics when present (runner with --systemd-run). + agg_spec = { + 'pss': ['min', 'max', 'mean'], + 'uss': ['min', 'max', 'mean'], + 'cpu': ['min', 'max', 'mean'], + } + for cg_col in CGROUP_METRICS: + if cg_col in dframe.columns and dframe[cg_col].notna().any(): + agg_spec[cg_col] = ['min', 'max', 'mean'] + + stats = dframe.groupby('name').agg(agg_spec) + + # turn the multi-level columns into flat names + stats.columns = [f"{col[0]}_{col[1]}" for col in stats.columns] + stats = stats.reset_index() + + # ----- compute lifetime (walltime in seconds) per (timeframe, name) ----- + # Use the 'time' column which compute_time_delta() already converted to + # seconds elapsed since the first observation of each (task, timeframe). + # max(time) for a group therefore equals the observed walltime in seconds. + # Filter out nice==19 tasks because they run at background priority and + # would inflate walltime estimates for the foreground scheduling budget. + df_nice_filtered = dframe[dframe['nice'] != 19].copy() + + lifetime_per_tf = ( + df_nice_filtered + .groupby(['timeframe', 'name'])['time'] + .max() # time is delta from task start → max = observed walltime [s] + .reset_index(name='lifetime') + ) + + mean_lifetime = lifetime_per_tf.groupby('name')['lifetime'].mean() + max_lifetime = lifetime_per_tf.groupby('name')['lifetime'].max() + min_lifetime = lifetime_per_tf.groupby('name')['lifetime'].min() + std_lifetime = lifetime_per_tf.groupby('name')['lifetime'].std().fillna(0.0) + + resource_json["count"] = 1 # basic sample size + + # convert to nested dictionary + for _, row in stats.iterrows(): + name = row['name'] + entry = { + 'pss': { + 'min': r3(row['pss_min']), + 'max': r3(row['pss_max']), + 'mean': r3(row['pss_mean']) + }, + 'uss': { + 'min': r3(row['uss_min']), + 'max': r3(row['uss_max']), + 'mean': r3(row['uss_mean']) + }, + 'cpu': { + 'min': r3(row['cpu_min']), + 'max': r3(row['cpu_max']), + 'mean': r3(row['cpu_mean']) + }, + 'lifetime': { + 'min' : r3(float(min_lifetime.get(name, np.nan))), + 'max' : r3(float(max_lifetime.get(name, np.nan))), + 'mean': r3(float(mean_lifetime.get(name, np.nan))), + 'std' : r3(float(std_lifetime.get(name, 0.0))), + } + } + # include cgroup metrics when available + for cg_col in CGROUP_METRICS: + min_col = f"{cg_col}_min" + if min_col in row.index: + entry[cg_col] = { + 'min': r3(row[f"{cg_col}_min"]), + 'max': r3(row[f"{cg_col}_max"]), + 'mean': r3(row[f"{cg_col}_mean"]), + } + resource_json[name] = entry + return resource_json + +def stat(args): + """ + providing simple global statistics of resources + """ + resources = extract_resources(args.pipelines) + # iterate over all resource objects and make individual statistics + for res in resources: + print_statistics(res) + + +def merge_stats_into(list_of_json_stats, outputfile, metadata): + running = {} + # read all the inputs + for inp_json in list_of_json_stats: + # we may have to strip the meta-data section first of all + running = merge_stats(inp_json, running) + + # attach meta-data + running["meta-data"] = metadata + + # now write out the result into the output file + if running and outputfile != None: + with open(outputfile, 'w') as f: + json.dump(running, f) + + return running + + +def build_meta_header(arg): + meta = {} + if type(arg) == str: + if arg != "": + meta = json.loads(arg) + elif type(arg) == dict: + meta = deepcopy(arg) + else: + print ("Unsupported Meta input type") + return meta + +def _read_log_time_file(fpath): + """Parse a *.log_time file written by GNU time via taskwrapper. + + Returns a dict with 'walltime' [s], 'cpu_cores', 'maxmem_mb', or None + if the file cannot be parsed. + + GNU time has ~10ms wall-clock resolution, so sub-10ms tasks report + walltime=0.00. When that happens we fall back to usertime (CPU time in + user space) which sometimes captures the real duration better. The + effective walltime is max(walltime, usertime) so we never under-count. + """ + result = {} + try: + with open(fpath) as f: + for line in f: + line = line.strip().lstrip('#') + parts = line.split() + if not parts: + continue + key = parts[0] + if key == 'walltime' and len(parts) > 1: + result['walltime'] = float(parts[1]) + elif key == 'usertime' and len(parts) > 1: + result['usertime'] = float(parts[1]) + elif key == 'CPU' and len(parts) > 1: + result['cpu_cores'] = float(parts[1].rstrip('%')) / 100.0 + elif key == 'maxmem' and len(parts) > 1: + result['maxmem_mb'] = float(parts[1]) / 1024.0 # KB -> MB + except (OSError, ValueError, IndexError): + return None + if 'walltime' not in result: + return None + # Only fall back to usertime when walltime rounded to 0 (GNU time has ~10ms + # resolution). Do NOT use max(walltime, usertime): for multithreaded tasks + # usertime = sum of CPU time across all workers >> walltime, so max() would + # replace a correct 45s wall time with an incorrect 116s usertime. + if result['walltime'] == 0.0: + result['walltime'] = result.get('usertime', 0.0) + return result + + +def _collect_log_times(search_path): + """Collect *.log_time files from search_path and one level of subdirectories. + + Returns {base_task_name: [measurement_dict, ...]} where each measurement + dict has the keys returned by _read_log_time_file(). + """ + import glob + collected = {} + patterns = [ + os.path.join(search_path, '*.log_time'), + os.path.join(search_path, '*', '*.log_time'), + ] + for pattern in patterns: + for fpath in sorted(glob.glob(pattern)): + fname = os.path.basename(fpath).replace('.log_time', '') + # Strip timeframe suffix _N to get base task name + parts = fname.split('_') + base = '_'.join(parts[:-1]) if (parts and parts[-1].isdigit() and len(parts) > 1) else fname + data = _read_log_time_file(fpath) + if data is not None: + collected.setdefault(base, []).append(data) + return collected + + +def _stats_dict(values): + """Build a min/max/mean/std/count dict from a list of floats. + + std is None when n=1 (sample std is undefined, not zero). + """ + n = len(values) + if n == 0: + return None + mean = sum(values) / n + std = round((sum((x - mean) ** 2 for x in values) / (n - 1)) ** 0.5, 3) if n > 1 else None + return { + 'min': round(min(values), 3), + 'max': round(max(values), 3), + 'mean': round(mean, 3), + 'std': std, + 'M2': 0.0, 'count': n, + } + + +def incorporate_log_times(json_path, search_path): + """Update a json-stat file with walltime (and cpu/mem) from *.log_time files. + + For tasks already in the stat: overwrite lifetime with log_time walltime + (GNU time is more accurate than 1 Hz psutil sampling, especially for + short-lived tasks that only appear in one or two monitoring windows). + + For tasks absent from the stat (finished before the first monitor sample): + add a new entry with walltime, approximate cpu, and approximate pss from + maxmem (RSS ≈ PSS for short-lived tasks). + """ + log_times = _collect_log_times(search_path) + if not log_times: + print(f' No *.log_time files found under {search_path}', file=sys.stderr) + return + + with open(json_path) as f: + stat = json.load(f) + + n_updated = n_added = 0 + for base_name, measurements in sorted(log_times.items()): + walltimes = [m['walltime'] for m in measurements] + cpu_cores = [m['cpu_cores'] for m in measurements if 'cpu_cores' in m] + maxmem_mbs = [m['maxmem_mb'] for m in measurements if 'maxmem_mb' in m] + + lifetime_entry = _stats_dict(walltimes) + + if base_name in stat: + # Task is in the stat: replace lifetime with ground-truth log_time values. + stat[base_name]['lifetime'] = lifetime_entry + n_updated += 1 + else: + # Task is absent (too short for monitor sampling): create a minimal entry. + entry = {'lifetime': lifetime_entry} + cpu_entry = _stats_dict(cpu_cores) + if cpu_entry: + entry['cpu'] = cpu_entry + mem_entry = _stats_dict(maxmem_mbs) + if mem_entry: + # maxmem (RSS) is a reasonable PSS proxy for short-lived tasks + entry['pss'] = mem_entry + entry['uss'] = mem_entry # same approximation + stat[base_name] = entry + n_added += 1 + + with open(json_path, 'w') as f: + json.dump(stat, f, indent=2) + + print(f' log_time: updated lifetime for {n_updated} tasks, ' + f'added {n_added} new tasks from {search_path}') + + +_NWORKER_CMD_PATTERNS = [ + re.compile(r'\$\{O2DPG_DYNAMIC_NWORKER_OVERWRITE:-(\d+)\}'), # ${VAR:-N} — default value + re.compile(r'(? n_ref: + return None + t_serial = walltime_ref * (n_ref - cpu_mean_ref) / (n_ref - 1) + t_parallel_tot = max(0.0, walltime_ref * n_ref * (cpu_mean_ref - 1) / (n_ref - 1)) + return { + 't_serial': round(t_serial, 3), + 't_parallel_tot': round(t_parallel_tot, 3), + 'n_ref': n_ref, + 'cpu_mean_ref': round(cpu_mean_ref, 3), + 'min_workers': int(min_workers), + 'max_workers': int(max_workers if max_workers is not None else n_ref), + } + + +def incorporate_amdahl_models(json_path, workflow_path): + """Build Amdahl scaling models and add them to the json-stat file. + + Reads the original workflow.json to obtain n_ref (the worker count used + during the measurement run, i.e. the pre-update resources.cpu value for + tasks that carry O2DPG_DYNAMIC_NWORKER_OVERWRITE or a 'scaling' block). + Combines with cpu.mean and lifetime.mean from the json-stat to derive + t_serial and t_parallel_tot for each scalable task. + """ + try: + with open(workflow_path) as f: + wf_raw = json.load(f) + except (OSError, json.JSONDecodeError) as e: + print(f' WARNING: could not read workflow {workflow_path}: {e}', file=sys.stderr) + return + + stages = wf_raw.get('stages', []) + + # Collect n_ref and bounds per base task name from the workflow. + scalable = {} + for task in stages: + cmd = task.get('cmd', '') + res = task.get('resources', {}) or {} + scaling = res.get('scaling') + is_scalable = ( + res.get('amdahl_scalable', False) or + 'O2DPG_DYNAMIC_NWORKER_OVERWRITE' in cmd or + scaling is not None + ) + if not is_scalable: + continue + name = task['name'] + tf = task.get('timeframe', -1) + base = '_'.join(name.split('_')[:-1]) if (tf >= 1 and name.split('_')[-1].isdigit()) else name + if base in scalable: + continue # use the first TF instance as representative + + # n_ref = actual worker count used during the measurement run. + # Read from the cmd string (the ground truth) rather than resources.cpu + # which is a scheduler booking estimate and may be fractional/rounded. + n_ref_cmd = _extract_n_workers_from_cmd(cmd) + n_ref = n_ref_cmd if n_ref_cmd else max(1, int(round(float(res.get('cpu', 1))))) + + min_w, max_w = 1, n_ref + if scaling: + min_w = int(scaling.get('min_workers', 1)) + max_w = int(scaling.get('max_workers', n_ref)) + scalable[base] = {'n_ref': n_ref, 'min_workers': min_w, 'max_workers': max_w} + + with open(json_path) as f: + stat = json.load(f) + + n_built = 0 + for base_name, info in scalable.items(): + if base_name not in stat: + continue + entry = stat[base_name] + cpu_mean = (entry.get('cpu') or {}).get('mean') + walltime_ref = (entry.get('lifetime') or {}).get('mean') + if cpu_mean is None or walltime_ref is None or walltime_ref <= 0: + continue + model = _build_amdahl_model( + walltime_ref, cpu_mean, info['n_ref'], + info['min_workers'], info['max_workers'], + ) + if model: + stat[base_name]['amdahl'] = model + n_built += 1 + + with open(json_path, 'w') as f: + json.dump(stat, f, indent=2) + + print(f' Amdahl models built for {n_built}/{len(scalable)} scalable task(s).') + + +def json_stat_impl(pipelines, output, header_data, log_time_path=None, workflow_path=None): + resources = extract_resources(pipelines) + all_stats = [produce_json_stat(res) for res in resources] + merge_stats_into(all_stats, output, build_meta_header(header_data)) + + if log_time_path: + incorporate_log_times(output, log_time_path) + + if workflow_path: + incorporate_amdahl_models(output, workflow_path) + + +def json_stat(args): + log_time_path = getattr(args, 'log_time_path', None) + workflow_path = getattr(args, 'workflow_path', None) + json_stat_impl(args.pipelines, args.output, args.header_data, + log_time_path=log_time_path, workflow_path=workflow_path) + +def merge_json_stats(args): + all_stats = [] + for inp in args.inputs: + # load the json as a dict + with open(inp,'r') as f: + all_stats.append(json.load(f)) + + merge_stats_into(all_stats, args.output, build_meta_header(args.header_data)) def history(args): """ @@ -988,7 +1623,38 @@ def main(): parser = argparse.ArgumentParser(description="Metrics evaluation of O2 simulation workflow") sub_parsers = parser.add_subparsers(dest="command") - plot_parser = sub_parsers.add_parser("history", help="Plot (multiple) metrcis from extracted metrics JSON file(s)") + stat_parser = sub_parsers.add_parser("stat", help="Print simple summary of resource usage") + stat_parser.set_defaults(func=stat) + stat_parser.add_argument("-p", "--pipelines", nargs="*", help="pipeline_metric files from o2_dpg_workflow_runner", required=True) + + json_stat_parser = sub_parsers.add_parser("json-stat", help="Produce basic json stat (compatible with o2dog_workflow_runner injection)") + json_stat_parser.set_defaults(func=json_stat) + json_stat_parser.add_argument("-p", "--pipelines", nargs="*", help="Pipeline_metric files from o2_dpg_workflow_runner; Merges information", required=True) + json_stat_parser.add_argument("-o", "--output", type=str, help="Output json filename", required=True) + json_stat_parser.add_argument("-hd", "--header-data", type=str, default='', help="Some meta-data headers to be included in the JSON") + json_stat_parser.add_argument("-w", "--workflow", dest="workflow_path", default=None, + metavar="WORKFLOW_JSON", + help="Original workflow.json used for the measurement run. " + "When provided, Amdahl scaling models are computed for " + "tasks that carry O2DPG_DYNAMIC_NWORKER_OVERWRITE or a " + "'scaling' block, using the original resources.cpu as " + "n_ref combined with learned cpu.mean and lifetime.mean.") + json_stat_parser.add_argument("--log-time-path", dest="log_time_path", default=None, + metavar="DIR", + help="Directory to search for *.log_time files written by " + "taskwrapper (GNU time). Searched recursively one level " + "deep (covers tf1/, tf2/, ... subdirs). When provided, " + "lifetime values are replaced with the more accurate " + "GNU-time walltime, and tasks too short to appear in the " + "1 Hz monitor log are added from the log_time data.") + + merge_stat_parser = sub_parsers.add_parser("merge-json-stats", help="Merge information from json-stats into an aggregated stat") + merge_stat_parser.set_defaults(func=merge_json_stats) + merge_stat_parser.add_argument("-i", "--inputs", nargs="*", help="List of incoming/input json stat files", required=True) + merge_stat_parser.add_argument("-o", "--output", type=str, help="Output json filename", required=True) + merge_stat_parser.add_argument("-hd", "--header-data", type=str, default="", help="Some meta-data headers to be included in the JSON") + + plot_parser = sub_parsers.add_parser("history", help="Plot (multiple) metrics from extracted metrics JSON file(s)") plot_parser.set_defaults(func=history) plot_parser.add_argument("-p", "--pipelines", nargs="*", help="pipeline_metric files from o2_dpg_workflow_runner", required=True) plot_parser.add_argument("--output", help="output directory", default="resource_history") diff --git a/MC/utils/tests/makeTestAOD.C b/MC/utils/tests/makeTestAOD.C new file mode 100644 index 000000000..e87cf02dc --- /dev/null +++ b/MC/utils/tests/makeTestAOD.C @@ -0,0 +1,356 @@ +// makeTestAOD.C +// +// Builds a tiny synthetic AO2D that reproduces, in a few kB, every pathology +// AODBcRewriter.C exists to repair — so the tool can be tested in seconds +// without a simulation, a GRID job or a committed binary fixture. +// +// root -l -b -q 'makeTestAOD.C("AO2D_test.root")' +// +// ----------------------------------------------------------------------------- +// WHAT THE FIXTURE CONTAINS +// ----------------------------------------------------------------------------- +// One DF holding two "sub-timeframe" blocks, as o2-aod-merger produces when MC +// timeframes are merged into the parent data file's DF (data-embedding +// anchoring). That layout is what makes all of the following true at once: +// +// * fGlobalBC is non-monotonic — block B's BCs are earlier than block A's +// * fGlobalBC has a duplicate — BC 201 appears in both blocks +// * MCCollisions have a duplicate — same (BC, fEventWeight) in both blocks +// * the tracks' "-1" ambiguous group is SPLIT into two runs, one per block +// +// so the BC sort cascades into a collision reorder, which cascades into a track +// regroup. Every table then has to follow, and every index column has to be +// remapped — which is the whole job. +// +// Row payloads are unique by construction (fX = 10+i for tracks, 1000+i for MFT +// tracks, 2000+i for fwd tracks, ...) so a test can state ground truth as +// "fwd track 2000+i must still point at MFT track 1000+i" and check it after +// the rows have moved. +// +// Cross-table links present here, and why each one matters: +// +// O2fwdtrack.fIndexMFTTracks -> another Stage-1b table +// O2fwdtrack.fIndexFwdTracks_MatchMCHTrack -> ITSELF +// These two are O2-7098. Both referents are reordered in the same stage +// as the referrer, so they only work if writing is deferred until every +// permutation is known. +// O2v0/O2cascade fIndexTracks_Pos/_Neg/fIndexTracks, fIndexV0s +// references into reordered tables from tables that are not reordered. +// O2ambiguoustrack.fIndexSliceBCs -> a [2] slice into a DEDUPLICATED +// table; endpoints must follow the compaction. +// O2mcparticle fIndexArray_Mothers (VLA) / fIndexSlice_Daughters ([2]) +// intra-table links that must follow the table's own reordering. +// paste-join children (O2trackextra_002, O2fwdtrackcov, O2mc*label) +// no index column of their own for the row order — they must follow +// their parent row-for-row. +// O2origin +// no links at all — exercises the fast-clone path. + +#ifndef __CLING__ +#include "TFile.h" +#include "TTree.h" +#include +#include +#include +#endif + +namespace +{ + +// --------------------------------------------------------------------------- +// The fixture layout, spelled out once so tests can refer to it. +// +// BCs (input row -> fGlobalBC): block A: 0:200 1:201 2:202 +// block B: 3:100 4:101 5:201 (5 duplicates 1) +// -> after Stage 0: [100,101,200,201,202], bcPerm = [2,3,4,0,1,3] +// +// Collisions: 0->bc0 1->bc2 | 2->bc3 3->bc4 +// -> remapped to 2,4,0,1 -> reordered to [2,3,0,1] +// +// MCCollisions: 0->bc0 w1.5 1->bc1 w2.5 | 2->bc3 w3.5 3->bc4 w4.5 +// 4->bc5 w2.5 <- same (newBC,weight) as 1: dropped as duplicate +// +// Tracks: A: 0,1 -> coll0 2 -> coll1 3 -> -1 +// B: 4 -> coll2 5 -> coll3 6 -> -1 (-1 group is split) +// MFT tracks: A: 0 -> coll0 1 -> coll1 2 -> -1 +// B: 3 -> coll2 4 -> coll3 5 -> -1 +// Fwd tracks: A: 0 -> coll0 1 -> coll1 2 -> -1 +// B: 3 -> coll2 4 -> coll3 5 -> -1 +// fwd i matches MFT i (all six). fwd 1 -> fwd 0 and fwd 4 -> fwd 3 as +// MCH matches; the rest have none. +// --------------------------------------------------------------------------- + +const int kNBC = 6; +const uint64_t kGlobalBC[] = {200, 201, 202, 100, 101, 201}; + +const int kNColl = 4; +const int kCollBC[] = {0, 2, 3, 4}; + +const int kNMcColl = 5; +const int kMcCollBC[] = {0, 1, 3, 4, 5}; +const float kMcCollWeight[]= {1.5f, 2.5f, 3.5f, 4.5f, 2.5f}; + +const int kNPartPerMcColl = 3; // mother + two daughters +const int kNPart = kNMcColl * kNPartPerMcColl; + +const int kNTrack = 7; +const int kTrackColl[] = {0, 0, 1, -1, 2, 3, -1}; +const int kTrackMcPart[] = {0, 1, 3, -1, 6, 9, -1}; + +const int kNMft = 6; +const int kMftColl[] = {0, 1, -1, 2, 3, -1}; +const int kMftMcPart[] = {0, 3, -1, 6, 9, -1}; + +const int kNFwd = 6; +const int kFwdColl[] = {0, 1, -1, 2, 3, -1}; +const int kFwdMft[] = {0, 1, 2, 3, 4, 5}; // fwd i <-> MFT i +const int kFwdMatch[] = {-1, 0, -1, -1, 3, -1};// fwd -> fwd self-reference +// The MC particle of a fwd track is the one of its matched MFT track: a correct +// rewrite keeps "same particle" at 100%, a broken one collapses it to ~0 — +// exactly the metric O2-7098 was reported with. +const int kFwdMcPart[] = {0, 3, -1, 6, 9, -1}; + +// V0s and cascades are stored SORTED BY fIndexCollisions, as reconstruction +// writes them. The collision table gets reordered, so these have to be +// re-sorted or the grouping is destroyed. The cascades are additionally sorted +// by fIndexV0s, whose referent is itself one of these re-sorted tables — a +// two-level dependency that a single pass would not resolve. +const int kNV0 = 2; +const int kV0Coll[] = {0, 2}; +const int kV0Pos[] = {0, 4}; +const int kV0Neg[] = {1, 5}; + +const int kNCasc = 2; +const int kCascColl[] = {0, 2}; +const int kCascV0[] = {0, 1}; +const int kCascBach[] = {2, 4}; + +// Sorted by fIndexTracks / fIndexFwdTracks respectively — both referents are +// reordered by Stage 1b. +const int kNAmb = 2; +const int kAmbTrack[] = {3, 6}; +const int kAmbBCFirst[] = {0, 3}; +const int kAmbBCLast[] = {2, 4}; + +const int kNFwdCl = 4; +const int kFwdClTrack[] = {0, 1, 3, 4}; + +} // namespace + +void makeTestAOD(const char *outFileName = "AO2D_test.root") +{ + TFile f(outFileName, "RECREATE"); + TDirectory *df = f.mkdir("DF_1000000000001"); + df->cd(); + + // ---- O2bc_001 ----------------------------------------------------------- + { + TTree t("O2bc_001", "bcs"); + uint64_t globalBC; int runNumber; uint64_t triggerMask; + t.Branch("fRunNumber", &runNumber, "fRunNumber/I"); + t.Branch("fGlobalBC", &globalBC, "fGlobalBC/l"); + t.Branch("fTriggerMask", &triggerMask, "fTriggerMask/l"); + for (int i = 0; i < kNBC; ++i) { + runNumber = 300000; globalBC = kGlobalBC[i]; triggerMask = 1000 + i; + t.Fill(); + } + t.Write(); + } + + // ---- O2bcflag (paste-joined to O2bc) ------------------------------------ + { + TTree t("O2bcflag", "bc flags"); + uint8_t flags; + t.Branch("fBCFlags", &flags, "fBCFlags/b"); + for (int i = 0; i < kNBC; ++i) { flags = (uint8_t)(i + 1); t.Fill(); } + t.Write(); + } + + // ---- O2collision_001 ---------------------------------------------------- + { + TTree t("O2collision_001", "collisions"); + int bc; float posX; + t.Branch("fIndexBCs", &bc, "fIndexBCs/I"); + t.Branch("fPosX", &posX, "fPosX/F"); + for (int i = 0; i < kNColl; ++i) { bc = kCollBC[i]; posX = 500.f + i; t.Fill(); } + t.Write(); + } + + // ---- O2mccollision_001 -------------------------------------------------- + { + TTree t("O2mccollision_001", "mc collisions"); + int bc; float weight, posX; + t.Branch("fIndexBCs", &bc, "fIndexBCs/I"); + t.Branch("fPosX", &posX, "fPosX/F"); + t.Branch("fEventWeight",&weight, "fEventWeight/F"); + for (int i = 0; i < kNMcColl; ++i) { + bc = kMcCollBC[i]; weight = kMcCollWeight[i]; + // The duplicate must be byte-identical to the row it duplicates, as it + // would be in a real merged file. + posX = (i == 4) ? 600.f + 1 : 600.f + i; + t.Fill(); + } + t.Write(); + } + + // ---- O2mcparticle_001 --------------------------------------------------- + { + TTree t("O2mcparticle_001", "mc particles"); + int mcColl, pdg, motherSize, mothers[4], daughters[2]; + t.Branch("fIndexMcCollisions", &mcColl, "fIndexMcCollisions/I"); + t.Branch("fPdgCode", &pdg, "fPdgCode/I"); + t.Branch("fIndexArray_Mothers_size",&motherSize, "fIndexArray_Mothers_size/I"); + t.Branch("fIndexArray_Mothers", mothers, "fIndexArray_Mothers[fIndexArray_Mothers_size]/I"); + t.Branch("fIndexSlice_Daughters", daughters, "fIndexSlice_Daughters[2]/I"); + for (int c = 0; c < kNMcColl; ++c) { + int base = c * kNPartPerMcColl; + for (int j = 0; j < kNPartPerMcColl; ++j) { + mcColl = c; + pdg = 100 + base + j; // unique per particle + if (j == 0) { // the mother: daughters are the next two rows + motherSize = 0; + daughters[0] = base + 1; + daughters[1] = base + 2; + } else { // a daughter: its mother is the first row + motherSize = 1; + mothers[0] = base; + daughters[0] = -1; + daughters[1] = -1; + } + t.Fill(); + } + } + t.Write(); + } + + // ---- O2track_iu + paste-join children ----------------------------------- + { + TTree t("O2track_iu", "tracks"); + int coll; float x; + t.Branch("fIndexCollisions", &coll, "fIndexCollisions/I"); + t.Branch("fX", &x, "fX/F"); + for (int i = 0; i < kNTrack; ++i) { coll = kTrackColl[i]; x = 10.f + i; t.Fill(); } + t.Write(); + } + { + TTree t("O2trackextra_002", "track extras"); + uint8_t nClsFindable; + t.Branch("fTPCNClsFindable", &nClsFindable, "fTPCNClsFindable/b"); + for (int i = 0; i < kNTrack; ++i) { nClsFindable = (uint8_t)(50 + i); t.Fill(); } + t.Write(); + } + { + TTree t("O2mctracklabel", "track mc labels"); + int mcPart; uint16_t mask; + t.Branch("fIndexMcParticles", &mcPart, "fIndexMcParticles/I"); + t.Branch("fMcMask", &mask, "fMcMask/s"); + for (int i = 0; i < kNTrack; ++i) { mcPart = kTrackMcPart[i]; mask = (uint16_t)i; t.Fill(); } + t.Write(); + } + + // ---- O2mfttrack_001 + label --------------------------------------------- + { + TTree t("O2mfttrack_001", "mft tracks"); + int coll; float x; + t.Branch("fIndexCollisions", &coll, "fIndexCollisions/I"); + t.Branch("fX", &x, "fX/F"); + for (int i = 0; i < kNMft; ++i) { coll = kMftColl[i]; x = 1000.f + i; t.Fill(); } + t.Write(); + } + { + TTree t("O2mcmfttracklabel", "mft mc labels"); + int mcPart; uint16_t mask; + t.Branch("fIndexMcParticles", &mcPart, "fIndexMcParticles/I"); + t.Branch("fMcMask", &mask, "fMcMask/s"); + for (int i = 0; i < kNMft; ++i) { mcPart = kMftMcPart[i]; mask = (uint16_t)(100 + i); t.Fill(); } + t.Write(); + } + + // ---- O2fwdtrack + cov + label ------------------------------------------- + // The table O2-7098 is about: two index columns pointing into tables that are + // reordered in the same stage as this one, one of them itself. + { + TTree t("O2fwdtrack", "fwd tracks"); + int coll, mft, match; float x; + t.Branch("fIndexCollisions", &coll, "fIndexCollisions/I"); + t.Branch("fX", &x, "fX/F"); + t.Branch("fIndexMFTTracks", &mft, "fIndexMFTTracks/I"); + t.Branch("fIndexFwdTracks_MatchMCHTrack", &match, "fIndexFwdTracks_MatchMCHTrack/I"); + for (int i = 0; i < kNFwd; ++i) { + coll = kFwdColl[i]; mft = kFwdMft[i]; match = kFwdMatch[i]; x = 2000.f + i; + t.Fill(); + } + t.Write(); + } + { + TTree t("O2fwdtrackcov", "fwd track cov"); + float sigmaX; + t.Branch("fSigmaX", &sigmaX, "fSigmaX/F"); + for (int i = 0; i < kNFwd; ++i) { sigmaX = 0.5f + i; t.Fill(); } + t.Write(); + } + { + TTree t("O2mcfwdtracklabel", "fwd mc labels"); + int mcPart; uint16_t mask; + t.Branch("fIndexMcParticles", &mcPart, "fIndexMcParticles/I"); + t.Branch("fMcMask", &mask, "fMcMask/s"); + for (int i = 0; i < kNFwd; ++i) { mcPart = kFwdMcPart[i]; mask = (uint16_t)(200 + i); t.Fill(); } + t.Write(); + } + + // ---- O2v0_002 / O2cascade_001 ------------------------------------------- + { + TTree t("O2v0_002", "v0s"); + int coll, pos, neg; + t.Branch("fIndexCollisions", &coll, "fIndexCollisions/I"); + t.Branch("fIndexTracks_Pos", &pos, "fIndexTracks_Pos/I"); + t.Branch("fIndexTracks_Neg", &neg, "fIndexTracks_Neg/I"); + for (int i = 0; i < kNV0; ++i) { coll = kV0Coll[i]; pos = kV0Pos[i]; neg = kV0Neg[i]; t.Fill(); } + t.Write(); + } + { + TTree t("O2cascade_001", "cascades"); + int coll, v0, bach; + t.Branch("fIndexCollisions", &coll, "fIndexCollisions/I"); + t.Branch("fIndexV0s", &v0, "fIndexV0s/I"); + t.Branch("fIndexTracks", &bach, "fIndexTracks/I"); + for (int i = 0; i < kNCasc; ++i) { coll = kCascColl[i]; v0 = kCascV0[i]; bach = kCascBach[i]; t.Fill(); } + t.Write(); + } + + // ---- O2fwdtrkcl (grouped by fIndexFwdTracks, a Stage-1b table) --------- + { + TTree t("O2fwdtrkcl", "fwd track clusters"); + int fwd; float x; + t.Branch("fIndexFwdTracks", &fwd, "fIndexFwdTracks/I"); + t.Branch("fX", &x, "fX/F"); + for (int i = 0; i < kNFwdCl; ++i) { fwd = kFwdClTrack[i]; x = 3000.f + i; t.Fill(); } + t.Write(); + } + + // ---- O2ambiguoustrack (slice into the deduplicated BC table) ------------ + { + TTree t("O2ambiguoustrack", "ambiguous tracks"); + int track, sliceBCs[2]; + t.Branch("fIndexTracks", &track, "fIndexTracks/I"); + t.Branch("fIndexSliceBCs", sliceBCs,"fIndexSliceBCs[2]/I"); + for (int i = 0; i < kNAmb; ++i) { + track = kAmbTrack[i]; sliceBCs[0] = kAmbBCFirst[i]; sliceBCs[1] = kAmbBCLast[i]; + t.Fill(); + } + t.Write(); + } + + // ---- O2origin (no links at all — exercises the fast-clone path) --------- + { + TTree t("O2origin", "origins"); + int dataframeID; + t.Branch("fDataframeID", &dataframeID, "fDataframeID/I"); + dataframeID = 1; t.Fill(); + t.Write(); + } + + f.Write(); + f.Close(); + std::cout << "makeTestAOD: wrote " << outFileName << "\n"; +} diff --git a/MC/utils/tests/run_aodbcrewriter_tests.sh b/MC/utils/tests/run_aodbcrewriter_tests.sh new file mode 100755 index 000000000..f8bda9432 --- /dev/null +++ b/MC/utils/tests/run_aodbcrewriter_tests.sh @@ -0,0 +1,105 @@ +#!/bin/bash +# +# Self-contained regression test for MC/utils/AODBcRewriter.C. +# +# ${O2DPG_ROOT}/MC/utils/tests/run_aodbcrewriter_tests.sh +# +# Needs nothing but ROOT — no simulation, no O2Physics, no GRID, no committed +# binary fixture. Runs in a couple of seconds. +# +# It builds a synthetic AO2D carrying every pathology the rewriter exists to +# repair (see makeTestAOD.C), rewrites it, and then applies three independent +# layers of checking: +# +# 1. AODBcRewriterValidate() — structural invariants of the output alone +# (BC monotonicity, paste-join row parity, +# index ranges, collision-group contiguity, +# and that no fIndex* column is unregistered) +# 2. AODBcRewriterCheckLinks() — input vs output: no row changed what it +# points at. This is the layer that catches +# O2-7098; the structural checks cannot, since +# a mis-remapped index is still perfectly in +# range. +# 3. testAODBcRewriter() — named, physics-readable assertions, headed by +# the MFT/MCH same-particle check the bug was +# reported with. +# +# Exit code 0 = pass. + +set -o pipefail + +MY_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +UTILS_DIR="$(dirname "${MY_DIR}")" +REWRITER="${UTILS_DIR}/AODBcRewriter.C" + +if ! command -v root > /dev/null 2>&1; then + echo "ERROR: no ROOT in PATH — load an O2/O2Physics/ROOT environment first" >&2 + exit 1 +fi +if [ ! -f "${REWRITER}" ]; then + echo "ERROR: cannot find ${REWRITER}" >&2 + exit 1 +fi + +WORKDIR=$(mktemp -d "${TMPDIR:-/tmp}/aodbcrewriter-test-XXXXXX") +trap 'rm -rf "${WORKDIR}"' EXIT +cd "${WORKDIR}" || exit 1 + +IN="AO2D_test.root" +OUT="AO2D_test_rewritten.root" +RC=0 + +run_step() +{ + local name="$1"; shift + echo "=== ${name} ===" + if ! "$@" > "${name}.log" 2>&1; then + echo "--- ${name} FAILED, log follows ---" >&2 + cat "${name}.log" >&2 + RC=1 + return 1 + fi + # ROOT macros signal failure through their return value, which the shell + # does not see; look for the markers as well. + if grep -q '\[FAIL\]' "${name}.log"; then + echo "--- ${name} reported [FAIL] lines ---" >&2 + grep -E '\[FAIL\]|FAILED' "${name}.log" >&2 + RC=1 + return 1 + fi + return 0 +} + +echo "Working in ${WORKDIR}" + +run_step "01-make-fixture" \ + root -l -b -q "${MY_DIR}/makeTestAOD.C(\"${IN}\")" + +run_step "02-rewrite" \ + root -l -b -q "${REWRITER}(\"${IN}\",\"${OUT}\")" + +# From here on every step needs the rewritten file. +if [ ! -f "${OUT}" ]; then + echo "ERROR: rewriter produced no output" >&2 + exit 1 +fi + +run_step "03-validate" \ + root -l -b -q -e ".L ${REWRITER}" -e "if (!AODBcRewriterValidate(\"${OUT}\")) gSystem->Exit(1);" + +run_step "04-check-links" \ + root -l -b -q -e ".L ${REWRITER}" -e "if (!AODBcRewriterCheckLinks(\"${IN}\",\"${OUT}\")) gSystem->Exit(1);" + +run_step "05-assertions" \ + root -l -b -q -e ".L ${REWRITER}" -e ".L ${MY_DIR}/testAODBcRewriter.C" \ + -e "if (testAODBcRewriter(\"${IN}\",\"${OUT}\") != 0) gSystem->Exit(1);" + +# Surface the readable assertion results even on success. +[ -f 05-assertions.log ] && grep -E '\[ ok \]|\[FAIL\]|PASSED|FAILED' 05-assertions.log + +if [ "${RC}" -eq 0 ]; then + echo "AODBcRewriter tests: PASSED" +else + echo "AODBcRewriter tests: FAILED" +fi +exit "${RC}" diff --git a/MC/utils/tests/testAODBcRewriter.C b/MC/utils/tests/testAODBcRewriter.C new file mode 100644 index 000000000..aa9e524ee --- /dev/null +++ b/MC/utils/tests/testAODBcRewriter.C @@ -0,0 +1,328 @@ +// testAODBcRewriter.C +// +// Physics-readable assertions on a rewritten AO2D, on top of the generic +// AODBcRewriterValidate() / AODBcRewriterCheckLinks() checks. +// +// root -l -b -q 'testAODBcRewriter.C("AO2D_test.root","AO2D_test_rewritten.root")' +// +// Returns 0 on success, the number of failed assertions otherwise. +// +// Expectations are derived from the INPUT file rather than hard-coded, so the +// fixture in makeTestAOD.C can grow without this file having to be kept in +// sync. What is hard-coded is only the *invariant*: a rewrite permutes rows, +// it never changes which row points at which. +// +// The headline assertion is the one from the O2-7098 report: for a fwd track +// with an MFT match, the MFT leg and the MCH leg must belong to the same MC +// particle. In this fixture the matching is perfect by construction, so the +// correct answer is 100% and a mis-remapped fIndexMFTTracks gives ~0% — +// the same signature seen in the anchored pO production. + +#ifndef __CLING__ +#include "TFile.h" +#include "TTree.h" +#include +#include +#include +#include +#include +#include +#endif + +namespace +{ + +int gFailures = 0; + +void fail(const std::string &what) +{ + std::cerr << " [FAIL] " << what << "\n"; + ++gFailures; +} + +void pass(const std::string &what) +{ + std::cout << " [ ok ] " << what << "\n"; +} + +TTree *get(TDirectory *d, const char *name) +{ + TTree *t = dynamic_cast(d->Get(name)); + if (!t) fail(std::string("table ") + name + " missing"); + return t; +} + +std::vector readInt(TTree *t, const char *branch) +{ + std::vector v; + if (!t || !t->GetBranch(branch)) return v; + Int_t x = 0; + t->ResetBranchAddresses(); + t->SetBranchAddress(branch, &x); + v.reserve(t->GetEntries()); + for (Long64_t i = 0; i < t->GetEntries(); ++i) { t->GetEntry(i); v.push_back(x); } + t->ResetBranchAddresses(); + return v; +} + +std::vector readFloat(TTree *t, const char *branch) +{ + std::vector v; + if (!t || !t->GetBranch(branch)) return v; + Float_t x = 0; + t->ResetBranchAddresses(); + t->SetBranchAddress(branch, &x); + v.reserve(t->GetEntries()); + for (Long64_t i = 0; i < t->GetEntries(); ++i) { t->GetEntry(i); v.push_back(x); } + t->ResetBranchAddresses(); + return v; +} + +std::vector readULong(TTree *t, const char *branch) +{ + std::vector v; + if (!t || !t->GetBranch(branch)) return v; + ULong64_t x = 0; + t->ResetBranchAddresses(); + t->SetBranchAddress(branch, &x); + v.reserve(t->GetEntries()); + for (Long64_t i = 0; i < t->GetEntries(); ++i) { t->GetEntry(i); v.push_back(x); } + t->ResetBranchAddresses(); + return v; +} + +// The multiset of (referrer payload, referent payload) pairs implied by an +// index column. Rows with no link contribute (payload, sentinel). +std::multimap linkPairs(const std::vector &idx, + const std::vector &ownPayload, + const std::vector &refPayload) +{ + std::multimap pairs; + for (size_t i = 0; i < idx.size() && i < ownPayload.size(); ++i) { + float ref = -1.f; + if (idx[i] >= 0 && (size_t)idx[i] < refPayload.size()) ref = refPayload[idx[i]]; + pairs.emplace(ownPayload[i], ref); + } + return pairs; +} + +void expectSameLinks(const char *what, + const std::multimap &in, + const std::multimap &out) +{ + if (in.size() != out.size()) { + fail(std::string(what) + ": row count changed (" + + std::to_string(in.size()) + " -> " + std::to_string(out.size()) + ")"); + return; + } + std::vector> a(in.begin(), in.end()), b(out.begin(), out.end()); + std::sort(a.begin(), a.end()); + std::sort(b.begin(), b.end()); + if (a != b) { + size_t shown = 0; + for (size_t i = 0; i < a.size() && shown < 5; ++i) { + if (a[i] != b[i]) { + std::cerr << " row " << a[i].first << " pointed at " << a[i].second + << " before, at " << b[i].second << " after\n"; + ++shown; + } + } + fail(std::string(what) + ": links not preserved"); + return; + } + pass(std::string(what) + ": links preserved (" + std::to_string(a.size()) + " rows)"); +} + +} // namespace + +int testAODBcRewriter(const char *inFileName = "AO2D_test.root", + const char *outFileName = "AO2D_test_rewritten.root") +{ + gFailures = 0; + std::cout << "testAODBcRewriter: " << inFileName << " -> " << outFileName << "\n"; + + TFile fin(inFileName, "READ"); + TFile fout(outFileName, "READ"); + if (fin.IsZombie()) { std::cerr << "cannot open " << inFileName << "\n"; return 1; } + if (fout.IsZombie()) { std::cerr << "cannot open " << outFileName << "\n"; return 1; } + + const char *dfName = "DF_1000000000001"; + TDirectory *din = dynamic_cast(fin.Get(dfName)); + TDirectory *dout = dynamic_cast(fout.Get(dfName)); + if (!din || !dout) { std::cerr << "DF " << dfName << " missing\n"; return 1; } + + // ---- Stage 0 did its job ------------------------------------------------- + { + auto bc = readULong(get(dout, "O2bc_001"), "fGlobalBC"); + bool mono = true; + for (size_t i = 1; i < bc.size(); ++i) if (bc[i] <= bc[i - 1]) mono = false; + if (!mono) fail("O2bc_001: fGlobalBC not strictly monotonic after rewrite"); + else pass("O2bc_001: fGlobalBC strictly increasing"); + if (bc.size() != 5) fail("O2bc_001: expected 5 unique BCs, got " + std::to_string(bc.size())); + else pass("O2bc_001: duplicate BC removed (6 -> 5)"); + } + + // ---- MCCollision deduplication ------------------------------------------ + { + Long64_t n = get(dout, "O2mccollision_001")->GetEntries(); + if (n != 4) fail("O2mccollision_001: expected 4 rows after dedup, got " + std::to_string(n)); + else pass("O2mccollision_001: duplicate MC collision removed (5 -> 4)"); + } + + // ---- The O2-7098 links --------------------------------------------------- + { + auto fwdXIn = readFloat(get(din, "O2fwdtrack"), "fX"); + auto fwdXOut = readFloat(get(dout, "O2fwdtrack"), "fX"); + auto mftXIn = readFloat(get(din, "O2mfttrack_001"), "fX"); + auto mftXOut = readFloat(get(dout, "O2mfttrack_001"), "fX"); + + expectSameLinks("O2fwdtrack.fIndexMFTTracks", + linkPairs(readInt(get(din, "O2fwdtrack"), "fIndexMFTTracks"), fwdXIn, mftXIn), + linkPairs(readInt(get(dout, "O2fwdtrack"), "fIndexMFTTracks"), fwdXOut, mftXOut)); + + expectSameLinks("O2fwdtrack.fIndexFwdTracks_MatchMCHTrack (self-reference)", + linkPairs(readInt(get(din, "O2fwdtrack"), "fIndexFwdTracks_MatchMCHTrack"), fwdXIn, fwdXIn), + linkPairs(readInt(get(dout, "O2fwdtrack"), "fIndexFwdTracks_MatchMCHTrack"), fwdXOut, fwdXOut)); + } + + // ---- The reported symptom: MFT leg and MCH leg of the same global muon --- + // must carry the same MC particle. This is Maurice Coquet's tableMakerMC + // check, reduced to the label tables. Perfect matching by construction here. + { + auto mftIdx = readInt(get(dout, "O2fwdtrack"), "fIndexMFTTracks"); + auto fwdLbl = readInt(get(dout, "O2mcfwdtracklabel"), "fIndexMcParticles"); + auto mftLbl = readInt(get(dout, "O2mcmfttracklabel"), "fIndexMcParticles"); + int checked = 0, same = 0; + for (size_t i = 0; i < mftIdx.size() && i < fwdLbl.size(); ++i) { + if (mftIdx[i] < 0 || (size_t)mftIdx[i] >= mftLbl.size()) continue; + ++checked; + if (fwdLbl[i] == mftLbl[mftIdx[i]]) ++same; + } + if (checked == 0) fail("MFT-MCH match check: nothing to check"); + else if (same != checked) fail("MFT-MCH match: sameParticle=" + std::to_string(same) + + "/" + std::to_string(checked) + ", expected all (this is O2-7098)"); + else pass("MFT-MCH match: sameParticle=" + std::to_string(same) + + "/" + std::to_string(checked) + " (100%)"); + } + + // ---- References into the reordered track tables from elsewhere ----------- + { + auto trkXIn = readFloat(get(din, "O2track_iu"), "fX"); + auto trkXOut = readFloat(get(dout, "O2track_iu"), "fX"); + + // V0s have no float payload of their own; identify them by their positive + // leg, which is unique in this fixture. + auto v0PosIn = readInt(get(din, "O2v0_002"), "fIndexTracks_Pos"); + auto v0PosOut = readInt(get(dout, "O2v0_002"), "fIndexTracks_Pos"); + auto v0NegIn = readInt(get(din, "O2v0_002"), "fIndexTracks_Neg"); + auto v0NegOut = readInt(get(dout, "O2v0_002"), "fIndexTracks_Neg"); + std::vector v0IdIn, v0IdOut; + for (auto p : v0PosIn) v0IdIn.push_back(p >= 0 && (size_t)p < trkXIn.size() ? trkXIn[p] : -1.f); + for (auto p : v0PosOut) v0IdOut.push_back(p >= 0 && (size_t)p < trkXOut.size() ? trkXOut[p] : -1.f); + expectSameLinks("O2v0_002.fIndexTracks_Neg", + linkPairs(v0NegIn, v0IdIn, trkXIn), + linkPairs(v0NegOut, v0IdOut, trkXOut)); + } + + // ---- Paste-join children still line up with their parent ---------------- + { + auto fwdXOut = readFloat(get(dout, "O2fwdtrack"), "fX"); + auto sigmaIn = readFloat(get(din, "O2fwdtrackcov"), "fSigmaX"); + auto sigmaOut= readFloat(get(dout, "O2fwdtrackcov"), "fSigmaX"); + auto fwdXIn = readFloat(get(din, "O2fwdtrack"), "fX"); + std::map expect; // fX -> fSigmaX, from the input + for (size_t i = 0; i < fwdXIn.size() && i < sigmaIn.size(); ++i) expect[fwdXIn[i]] = sigmaIn[i]; + bool aligned = (fwdXOut.size() == sigmaOut.size()); + for (size_t i = 0; aligned && i < fwdXOut.size(); ++i) + if (expect.count(fwdXOut[i]) == 0 || expect[fwdXOut[i]] != sigmaOut[i]) aligned = false; + if (!aligned) fail("O2fwdtrackcov: paste-join alignment with O2fwdtrack broken"); + else pass("O2fwdtrackcov: follows O2fwdtrack row-for-row"); + } + + // ---- Intra-table MC particle links -------------------------------------- + { + TTree *mcp = get(dout, "O2mcparticle_001"); + auto pdg = readInt(mcp, "fPdgCode"); + Int_t daughters[2] = {-1, -1}; + mcp->ResetBranchAddresses(); + mcp->SetBranchAddress("fIndexSlice_Daughters", daughters); + int checked = 0, bad = 0; + for (Long64_t i = 0; i < mcp->GetEntries(); ++i) { + mcp->GetEntry(i); + if (daughters[0] < 0) continue; + ++checked; + // In the fixture a mother with fPdgCode P has daughters P+1 and P+2. + if (daughters[1] - daughters[0] != 1) { ++bad; continue; } + if (pdg[daughters[0]] != pdg[i] + 1 || pdg[daughters[1]] != pdg[i] + 2) ++bad; + } + mcp->ResetBranchAddresses(); + if (checked == 0) fail("O2mcparticle_001: no daughter slices found"); + else if (bad) fail("O2mcparticle_001: " + std::to_string(bad) + "/" + + std::to_string(checked) + " daughter slices point at the wrong particles"); + else pass("O2mcparticle_001: all " + std::to_string(checked) + + " daughter slices still point at the right particles"); + } + + // ---- Grouping preserved for tables stored sorted by a reference --------- + // These are written sorted by an index into a table the rewriter reorders. + // Remapping the values without re-sorting the rows leaves the column + // unsorted, which breaks O2's slicing cache the same way a split "-1" group + // does. The referenced tables here are all genuinely reordered, so a + // no-op implementation cannot pass this by accident. + { + const char *cases[][2] = { + {"O2v0_002", "fIndexCollisions"}, + {"O2cascade_001", "fIndexCollisions"}, + {"O2cascade_001", "fIndexV0s"}, // referent is itself re-sorted + {"O2fwdtrkcl", "fIndexFwdTracks"}, + {"O2ambiguoustrack", "fIndexTracks"}, + }; + for (auto &c : cases) { + auto vIn = readInt(get(din, c[0]), c[1]); + auto vOut = readInt(get(dout, c[0]), c[1]); + auto ordered = [](const std::vector &v) { + Int_t prev = -1; bool seenNull = false; + for (auto x : v) { + if (x < 0) { seenNull = true; continue; } + if (seenNull || x < prev) return false; + prev = x; + } + return true; + }; + std::string what = std::string(c[0]) + "." + c[1]; + if (vIn.empty()) { fail(what + ": column missing from input"); continue; } + if (!ordered(vIn)) { fail(what + ": fixture is not sorted on input — fix makeTestAOD.C"); continue; } + if (!ordered(vOut)) fail(what + ": sorted on input, unsorted on output (grouping destroyed)"); + else pass(what + ": still sorted after the rewrite"); + } + } + + // ---- Slice into the deduplicated BC table ------------------------------- + { + auto bcIn = readULong(get(din, "O2bc_001"), "fGlobalBC"); + auto bcOut = readULong(get(dout, "O2bc_001"), "fGlobalBC"); + TTree *aIn = get(din, "O2ambiguoustrack"); + TTree *aOut = get(dout, "O2ambiguoustrack"); + Int_t sIn[2] = {-1, -1}, sOut[2] = {-1, -1}; + aIn->ResetBranchAddresses(); aIn->SetBranchAddress("fIndexSliceBCs", sIn); + aOut->ResetBranchAddresses(); aOut->SetBranchAddress("fIndexSliceBCs", sOut); + int bad = 0; + Long64_t n = std::min(aIn->GetEntries(), aOut->GetEntries()); + for (Long64_t i = 0; i < n; ++i) { + aIn->GetEntry(i); aOut->GetEntry(i); + for (int j = 0; j < 2; ++j) { + if (sIn[j] < 0 || (size_t)sIn[j] >= bcIn.size()) continue; + if (sOut[j] < 0 || (size_t)sOut[j] >= bcOut.size()) { ++bad; continue; } + if (bcIn[sIn[j]] != bcOut[sOut[j]]) ++bad; + } + } + aIn->ResetBranchAddresses(); aOut->ResetBranchAddresses(); + if (bad) fail("O2ambiguoustrack.fIndexSliceBCs: " + std::to_string(bad) + + " endpoint(s) no longer point at the same bunch crossing"); + else pass("O2ambiguoustrack.fIndexSliceBCs: endpoints follow the BC compaction"); + } + + if (gFailures == 0) std::cout << "testAODBcRewriter: ALL CHECKS PASSED\n"; + else std::cout << "testAODBcRewriter: " << gFailures << " CHECK(S) FAILED\n"; + return gFailures; +} diff --git a/MC/workflow_runner/o2dpg_runner/README.md b/MC/workflow_runner/o2dpg_runner/README.md new file mode 100644 index 000000000..768ba592b --- /dev/null +++ b/MC/workflow_runner/o2dpg_runner/README.md @@ -0,0 +1,304 @@ +# o2dpg_runner — modular rewrite of the O2DPG workflow runner + +This package is a modular rewrite of the single-file runner +`MC/bin/o2_dpg_workflow_runner.py` (~2000 lines, module-global state). +Both are installed; which one runs is chosen at run time, so the two can be +compared on the same job. Every flag the original parser accepts, this one +accepts, with the same semantics. + +## Where it lives, and how it is selected + +The package and its entry point live in `MC/workflow_runner/`: + +``` +MC/workflow_runner/ + o2dpg_workflow_runner.py # entry point + o2dpg_schedule_simulator.py + o2dpg_runner/ + cli.py # argparse -> RunnerConfig -> Executor + config.py # RunnerConfig dataclass + workflow.py # load / filter / build DAG + graph.py # Kahn topo sort, memoized descendants + resources.py # TaskResources, ResourceManager + monitoring.py # threaded psutil monitor + scheduler/ # timeframe (default), critical_path, best_fit + executor.py # main control loop + cleanup.py # early file removal, log archival + alienv.py # alienv env resolution + cache.py # _done + _done.json fingerprint cache + tests/ +``` + +Nothing calls it directly. `MC/bin/o2_dpg_workflow_runner.py` is a +dispatcher that execs either this runner or the original one, and every +existing call site goes through it unchanged: + +```bash +ALIEN_O2DPG_WORKFLOW_RUNNER=new $O2DPG_ROOT/MC/bin/o2_dpg_workflow_runner.py -f workflow.json +``` + +`legacy` is the default and runs `MC/bin/o2dpg_workflow_runner_legacy.py`. +An unknown value is an error, not a silent fallback. + +No new required dependencies. `psutil` and optionally `graphviz` as before. + +## What changed behaviorally + +The default invocation should reproduce prototype behavior bit-for-bit +(same scheduling decisions, same output files, same log formats). +Everything new is opt-in. + +### New CLI flags (all default to current behavior) + +| Flag | Default | Effect | +| ----------------------------- | ------------- | ------------------------------------------------------------------------------ | +| `--scheduler-policy` | `timeframe` | Choose: `timeframe` (legacy), `critical-path`, or `best-fit`. | +| `--drop-should-break` | off | In `timeframe`, let light tasks slip past a non-fitting heavy task. | +| `--monitor-interval-cpu` | `1.0` (s) | CPU polling cadence for the background monitor thread. | +| `--monitor-interval-mem` | `5.0` (s) | PSS polling cadence (much cheaper to read less often). | +| `--monitor-backend` | `psutil` | Reserved for a future cgroup-v2 backend. | +| `--cache-policy` | `off` | Task-completion cache: `off` (legacy), `lenient`, `strict`. See below. | + +### Removed flags + +- `--webhook` — the debug Mattermost channel integration is gone. The + flag is still accepted (for compatibility) but ignored. +- `--checkpoint-on-failure` — the tarball + `alien.py cp` failure + checkpoint was only used for Grid debugging. Same: accepted, ignored. +- `--cgroup` — superseded by `--systemd-run`. Same: accepted, ignored. + All three log a warning when passed, and none of them aborts, so a JDL + passing one through `ALIEN_O2DPG_ADDITIONAL_WORKFLOW_RUNNER_ARGS` still + runs. + +### Monitoring + +The old runner polled psutil synchronously in the main scheduling loop, +costing 10–20 % of one core on realistic workflows. The new monitor runs +in a background thread with two independent cadences — CPU (cheap, +~1 Hz) and PSS (expensive, ~0.2 Hz). The scheduler reads the latest +snapshot non-blockingly. Result: the runner's self-CPU drops to ~1-2 %. + +The scheduler's dynamic-resource sampling (`--dynamic-resources`) still +triggers in `ResourceManager.unbook()`, preserving ordering with +respect to task completions. + +### Scheduler policies + +Three policies ship, switchable via `--scheduler-policy`: + +- **`timeframe`** (default) — exact prototype behavior. Sort by + `(timeframe, -num_descendants)`; first non-fitting task in the default + pass breaks the pass (this is the legacy quirk that blocks light tasks + behind heavy ones). Set `--drop-should-break` to disable that quirk. +- **`critical-path`** — sort by longest-remaining CPU-weighted path to a + leaf. Standard HEFT-style heuristic; tends to win when resource + estimates are accurate (after `--update-resources`). +- **`best-fit`** — iterative best-fit bin-packing over the candidate set + against the remaining CPU/MEM budget. Maximizes parallelism at the cost + of slightly less predictable task ordering. + +Comparing these three on the same workflow with the same +estimates gives a direct A/B measurement of scheduling strategy impact. + +### Cache policy + +`_done` files remain the primary skip marker (O2 taskwrapper compatibility +is preserved). With `--cache-policy lenient`, a sidecar `_done.json` is +written containing a fingerprint of (command, env subset, software tag, +needs). On rerun, if the command or `needs` list changed, the `_done` +file is removed and the task re-runs. `strict` additionally invalidates +on env/software changes. + +No behavior change unless you pass the flag. + +## What to know + +Things that only came out of running this, and that cost time to find again. + +**A task's first CPU reading is always 0.** `psutil.cpu_percent(interval=None)` +has no baseline on its first call and returns `0.0` by construction. That is +why `sample_resources()` refuses to learn from fewer than three samples, and +why a sampled CPU of `0` is treated as missing information rather than as a +task that needs no CPU. Propagating a zero would make every sibling look free +and admit them all at once. + +**A monitor tick is not a poll.** The wait loop polls at 0.1 s ramping to 1 s +while the monitor thread fires at `--monitor-interval-cpu`. Samples must be +taken once per tick; recording per poll grows the sample lists without bound +over a long job and, worse, satisfies the three-sample guard with copies of +one reading. + +**A tracer or a monitor has to sit inside the systemd scope.** With +`--systemd-run`, wrapping the outside of `systemd-run` means observing +`systemd-run` and nothing else. The wrapper goes on the inner command. + +**`--maxjobs 1` serialises a workflow; `--cpu-limit 1` does not.** The latter +makes every task that declares more than one core unschedulable, and the run +stops with `ResourceLimitExceeded`. + +**The prototype and this runner disagree on short tasks, in this runner's +favour.** The prototype samples from its scheduling loop at roughly 5 s, so a +task shorter than ~15 s never reaches three samples and `--dynamic-resources` +learns nothing from it. The monitor thread here runs at 1 s. + +## Bug fixes incorporated + +Silently fixed relative to the prototype: + +1. `TaskResources.is_within_limits()` compared CPU to mem_limit instead + of MEM to mem_limit; the memory safety net was effectively disabled. +2. `find_all_dependent_tasks()` cached duplicates but returned + deduped; cache hits returned different values than cache misses. +3. `filter_workflow()` aliased the caller's dict and mutated it in place. +4. `getallrequirements()` recursed without memoization — exponential on + diamond DAGs; `sys.setrecursionlimit(100000)` was a workaround. +5. `send_webhook()` shell-interpolated task names into `os.system` + (command injection). Removed along with the webhook feature. +6. `SIGHandler` only caught SIGINT; Grid preemption via SIGTERM was + ignored. Now both are handled. +7. The emitted `produce_script` output used `cd $OLDPWD` which broke if + a task `cd`s internally. Now uses subshells: `( cd "$workdir" && ... )`. +8. `candidates` list used `.count()` for membership checks (O(n)); + replaced by set-based lookups. +9. A sampled CPU of `0` was propagated to the un-started sibling tasks, + which then all looked free and were admitted at once. It is now treated + as missing information, as a sampled memory of `0` already was. The + prototype carries this too, masked by its slower monitoring. +10. The dry-run path returned a plain `subprocess.Popen`, which has no + `.nice()`. Fixed here the same way it was later fixed in the + prototype: by returning a `psutil.Popen`. + +## Running the tests + +```bash +cd MC/workflow_runner +python -m pytest o2dpg_runner/tests/ -q +``` + +`pytest` is required. The tests run in CI, in the `Workflow-runner unit +tests` job of `.github/workflows/syntax-checks.yml`. + +The tests cover: +- `test_graph.py` — Kahn topological sort, memoized descendants/ancestors, + longest path, diamond + deep-chain cases. +- `test_workflow.py` — load, global-init extraction, filtering by target + and by label, regex, resource-estimate update. +- `test_resources.py` — booking/unbooking, semaphores, related-task + grouping, dynamic sampling, limit enforcement. +- `test_scheduler.py` — all three policies, the `should_break` quirk + and its removal, `n_backfill_max` cap, semaphore blocking. +- `test_cache.py` — cache policies (off/lenient/strict), fingerprint + sensitivity, sidecar round-trip. +- `test_executor_e2e.py` — the tiny fixture workflow driven end-to-end + with real subprocesses, exercising each policy, `--dry-run`, + `--produce-script`, rerun-from-cache behavior. +- `test_simulator.py` — simulator-only coverage for Amdahl-derived + critical-path weights, unschedulable-task handling, and simulated + backfill behaviour (`slowdown` and `holefill`). + +Integration test (from the prototype, still valid): +```bash +NSIGEVENTS=5 NTIMEFRAMES=2 bash MC/bin/tests/wf_test_pp.sh +``` + +## A/B measurement + +The Python entry point accepts the same `workflow.json` under all +scheduling policies. A typical comparison: + +```bash +# baseline (prototype-equivalent) +./o2dpg_workflow_runner.py -f wf.json \ + --metric-logfile metric_timeframe.log + +# drop the should_break quirk +./o2dpg_workflow_runner.py -f wf.json --drop-should-break \ + --metric-logfile metric_timeframe_nobrk.log + +# critical path +./o2dpg_workflow_runner.py -f wf.json --scheduler-policy critical-path \ + --metric-logfile metric_cp.log + +# best-fit bin-packing +./o2dpg_workflow_runner.py -f wf.json --scheduler-policy best-fit \ + --metric-logfile metric_bf.log +``` + +The metric logs have the same schema as before (`o2dpg_sim_metrics.py` +post-processing is unaffected), plus the run's meta line now records +`scheduler_policy`, `drop_should_break`, and `cache_policy` for easy +downstream grouping. + +## Simulator notes + +`MC/bin/o2dpg_schedule_simulator.py` is an offline discrete-event model +of the runner. It exists to compare policies and tune worker-count / +resource parameters quickly, not to emulate Linux scheduling perfectly. + +- The simulator now uses the same walltime-weighted critical-path input + as the runner when learned `resources.walltime` data is available. +- Amdahl worker overrides are applied before simulator scheduler-state + construction, so optimization runs evaluate policies against the same + task costs they simulate. +- Tasks that exceed the hard simulated CPU/MEM limits are kept in the + workflow model and reported as unschedulable, rather than being + dropped from resource bookkeeping. + +### Simulated backfill + +The real runner has a two-lane admission model: default tasks stay +within the hard budget, while backfill tasks may use a bounded amount of +overcommit and run at lower priority. The simulator now offers a +"sweet-spot" family of approximations for that behaviour: + +- `--backfill-model off` — no backfill, one hard budget only. +- `--backfill-model structural` — replay the runner's second admission + lane (`n_backfill`, CPU factor, MEM factor), but do not change task + duration. +- `--backfill-model slowdown` — same structural backfill admission, plus + a single fitted slowdown factor applied to backfill task walltimes. +- `--backfill-model holefill` — preferred realistic mode. Foreground + tasks keep their nominal duration; backfill tasks consume only the CPU + left idle by currently running foreground tasks. When the foreground + hole is smaller than the task's nominal CPU demand, the task slows + down proportionally. When the hole is large enough, it runs at nominal + speed. + +Relevant knobs: + +- `--n-backfill` +- `--backfill-cpu-factor` +- `--backfill-mem-factor` +- `--backfill-slowdown-factor` + +`holefill` is the recommended mode for scheduling studies: it +keeps simulated CPU efficiency physically bounded by 100%, tracks +observed runtime improvements from backfilling much better than the +single-factor slowdown model, and still stays simple enough to explain. + +Two modelling choices are worth keeping in mind: + +- foreground tasks are assumed not to slow down due to backfill; +- hole allocation is online and greedy, so enabling backfill may still + change the order in which later tasks become runnable. + +This is intentionally a scheduler-level approximation, not a kernel CPU +sharing model. It is accurate enough for comparative studies while still +remaining easy to reason about and calibrate against real runs. + +### Suggested simulator usage + +For realistic policy comparison, use learned resources and the holefill +backfill model: + +```bash +./o2dpg_schedule_simulator.py \ + --timeframes 1 2 4 5 8 12 20 \ + --update-resources learned.json \ + -f workflow.json \ + --backfill-model holefill +``` + +Use `off` as the baseline and `holefill` as the realistic backfill +comparison. The older `slowdown` mode remains useful as a coarse control +study, but it is no longer the preferred setting for reporting numbers. diff --git a/MC/workflow_runner/o2dpg_runner/__init__.py b/MC/workflow_runner/o2dpg_runner/__init__.py new file mode 100644 index 000000000..0c8a58525 --- /dev/null +++ b/MC/workflow_runner/o2dpg_runner/__init__.py @@ -0,0 +1,11 @@ +""" +o2dpg_runner: modular rewrite of the O2DPG workflow runner. + +Originally started February 2021 by sandro.wenzel@cern.ch as a single-file +prototype (MC/bin/o2dpg_workflow_runner.py). This package is the modular +refactor, keeping full CLI and behavioral compatibility by default while +exposing pluggable scheduling policies, a threaded resource monitor, and +a cleaner testable structure. +""" + +__version__ = "2.0.0" diff --git a/MC/workflow_runner/o2dpg_runner/alienv.py b/MC/workflow_runner/o2dpg_runner/alienv.py new file mode 100644 index 000000000..692e0f077 --- /dev/null +++ b/MC/workflow_runner/o2dpg_runner/alienv.py @@ -0,0 +1,66 @@ +"""Resolving alternative software environments via alienv.""" + +from __future__ import annotations + +import logging +import os +import subprocess +from typing import Dict, Optional + +log = logging.getLogger(__name__) + + +def _load_env_file(path: str) -> Dict[str, str]: + env: Dict[str, str] = {} + with open(path, "r") as f: + for raw in f: + line = raw.strip() + if not line or line.startswith("#"): + continue + if line.startswith("declare -x "): + line = line.replace("declare -x ", "", 1) + if "=" not in line: + env[line.strip()] = "" + else: + k, v = line.split("=", 1) + env[k.strip()] = v.strip('"') + return env + + +def get_alienv_software_environment(packagestring: Optional[str]) -> Dict[str, str]: + """Resolve ``packagestring`` to an env dict. + + Accepts: + - None / '' / 'None' -> empty dict + - a path to a file -> 'export > env.txt' format, parsed + - an alienv spec -> calls /cvmfs/alice.cern.ch/bin/alienv printenv + """ + if not packagestring or packagestring == "None": + return {} + + if os.path.exists(packagestring) and os.path.isfile(packagestring): + log.info("Taking software environment from file %s", packagestring) + return _load_env_file(packagestring) + + cmd = "/cvmfs/alice.cern.ch/bin/alienv printenv " + packagestring + proc = subprocess.Popen( + [cmd], stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True + ) + envstring, err = proc.communicate() + if err: + err_s = err.decode() + if err_s.strip(): + print(err_s) + raise RuntimeError(f"alienv printenv failed for {packagestring}") + + envmap: Dict[str, str] = {} + for t in envstring.decode().split(";"): + if "=" in t: + k, v = t.rstrip().split("=", 1) + envmap[k] = v + elif "export" in t: + tokens = t.split() + if len(tokens) >= 2: + variable = tokens[1] + envmap.setdefault(variable, "") + return envmap diff --git a/MC/workflow_runner/o2dpg_runner/cache.py b/MC/workflow_runner/o2dpg_runner/cache.py new file mode 100644 index 000000000..b7e937f0b --- /dev/null +++ b/MC/workflow_runner/o2dpg_runner/cache.py @@ -0,0 +1,192 @@ +"""Task-completion cache. + +Preserves the O2-taskwrapper-compatible `.log_done` marker file as +the primary source of truth (so that downstream tools and the wrapper +itself continue to work unchanged), and optionally writes a sidecar +`.log_done.json` containing a fingerprint of the inputs that +determine the task's output. + +Cache policies: + off - current behavior: a _done file means "skip" + lenient - read _done.json when present; invalidate only if the + command string changed. Warn on env / software changes. + strict - invalidate on any fingerprint change. + +Fingerprint components: + cmd_hash hash of the task command string + env_hash hash of the allow-listed subset of the task env + software the alienv package string (or '' if default) + needs list of upstream task names + +The sidecar is written best-effort after the _done file exists (so +torn writes don't leave stale fingerprints around). +""" + +from __future__ import annotations + +import hashlib +import json +import logging +import os +from typing import Dict, List, Optional, Tuple + +log = logging.getLogger(__name__) + +# Env vars that actually affect task semantics. Keep this conservative: +# everything else is considered noise (TMPDIR, PWD, terminal colors, etc.). +# Users can override via the workflow by not relying on env and by expressing +# variation through the cmd itself. +SEMANTIC_ENV_KEYS = ( + "ALICE_O2_VERSION", "O2_ROOT", "O2DPG_ROOT", "O2PHYSICS_ROOT", + "NEVENTS", "NWORKERS", "SEED", "INTERACTIONRATE", + "SIMENGINE", "NSIGEVENTS", "NTIMEFRAMES", "GENERATOR", + "ALIDIST_TAG", +) + + +def _hash(*parts: str) -> str: + h = hashlib.sha256() + for p in parts: + h.update(p.encode("utf-8", "replace")) + h.update(b"\x1f") # separator + return h.hexdigest()[:16] + + +def compute_fingerprint( + task: Dict, + alienv_package: str = "", + allow_env_keys: Tuple[str, ...] = SEMANTIC_ENV_KEYS, +) -> Dict[str, str]: + """Compute a fingerprint for a task spec. + + Does NOT depend on upstream task fingerprints -- upstream changes + propagate via the _done/_done.json of upstream tasks (if upstream + was re-run, its _done file was removed and the current task will + see a missing dependency marker and re-run). + """ + cmd = task.get("cmd", "") or "" + env_subset = {} + task_env = task.get("env") or {} + for k in allow_env_keys: + if k in task_env: + env_subset[k] = str(task_env[k]) + env_str = json.dumps(env_subset, sort_keys=True) + needs = sorted(task.get("needs", []) or []) + needs_str = json.dumps(needs) + + return { + "cmd_hash": _hash(cmd), + "env_hash": _hash(env_str), + "software": alienv_package or "", + "needs": needs, + "cmd_preview": cmd[:120], # for human debugging + } + + +def done_path(logfile: str) -> str: + return logfile + "_done" + + +def fingerprint_path(logfile: str) -> str: + return logfile + "_done.json" + + +class TaskCache: + """Policy-aware interface for checking/recording task completion.""" + + def __init__(self, policy: str = "off"): + if policy not in ("off", "lenient", "strict"): + raise ValueError(f"unknown cache policy: {policy}") + self.policy = policy + + def is_done(self, logfile: str, current_fp: Dict[str, str]) -> bool: + """Return True iff the task can be skipped. + + logfile: path like /cwd/taskname.log (we append _done / _done.json) + current_fp: fingerprint dict from compute_fingerprint(task) + """ + dp = done_path(logfile) + if not (os.path.exists(dp) and os.path.isfile(dp)): + return False + + if self.policy == "off": + return True + + fp_path = fingerprint_path(logfile) + if not os.path.exists(fp_path): + # Old run; nothing to compare against. + if self.policy == "strict": + log.info("%s: strict cache policy but no fingerprint -> invalidating", logfile) + self._invalidate(logfile) + return False + log.debug("%s: no fingerprint sidecar; keeping _done (lenient)", logfile) + return True + + try: + with open(fp_path) as f: + prev = json.load(f) + except (OSError, json.JSONDecodeError) as e: + log.warning("%s: fingerprint unreadable (%s); invalidating", fp_path, e) + self._invalidate(logfile) + return False + + cmd_changed = prev.get("cmd_hash") != current_fp["cmd_hash"] + env_changed = prev.get("env_hash") != current_fp["env_hash"] + sw_changed = prev.get("software") != current_fp["software"] + needs_changed = prev.get("needs") != current_fp["needs"] + + if self.policy == "lenient": + if cmd_changed or needs_changed: + log.info("%s: cmd/needs changed -> invalidating (lenient)", logfile) + self._invalidate(logfile) + return False + if env_changed: + log.warning("%s: env fingerprint changed but keeping cache (lenient)", logfile) + if sw_changed: + log.warning("%s: software fingerprint changed but keeping cache (lenient)", + logfile) + return True + + # strict + if cmd_changed or env_changed or sw_changed or needs_changed: + reasons = [] + if cmd_changed: reasons.append("cmd") + if env_changed: reasons.append("env") + if sw_changed: reasons.append("software") + if needs_changed: reasons.append("needs") + log.info("%s: changed (%s) -> invalidating (strict)", logfile, ",".join(reasons)) + self._invalidate(logfile) + return False + return True + + def record(self, logfile: str, current_fp: Dict[str, str]) -> None: + """Write the fingerprint sidecar after the task's _done file exists. + + Best effort: errors are logged but not raised. + """ + if self.policy == "off": + return + dp = done_path(logfile) + if not os.path.exists(dp): + # _done doesn't exist (e.g. skipped in dry-run); nothing to record. + return + try: + with open(fingerprint_path(logfile), "w") as f: + json.dump(current_fp, f, indent=2) + except OSError as e: + log.warning("Could not write fingerprint for %s: %s", logfile, e) + + def _invalidate(self, logfile: str) -> None: + for p in (done_path(logfile), fingerprint_path(logfile)): + try: + if os.path.exists(p): + os.remove(p) + except OSError as e: + log.warning("Could not remove %s: %s", p, e) + + +def remove_done_flag(logfile: str) -> None: + """Explicit invalidation used by --rerun-from.""" + for p in (done_path(logfile), fingerprint_path(logfile)): + if os.path.exists(p) and os.path.isfile(p): + os.remove(p) diff --git a/MC/workflow_runner/o2dpg_runner/cleanup.py b/MC/workflow_runner/o2dpg_runner/cleanup.py new file mode 100644 index 000000000..a5e526ea5 --- /dev/null +++ b/MC/workflow_runner/o2dpg_runner/cleanup.py @@ -0,0 +1,221 @@ +"""Cleanup utilities invoked after a task completes. + +Two concerns: + 1. Early file removal based on a FileIOGraph-derived file dependency map + (--remove-files-early). Files that no task will read/write again are + deleted to keep disc pressure low during large productions. + + 2. Production-mode log archival: .log / .log_done / .log_time files are + appended to a tar archive and removed. Mirrors the prototype's + production_endoftask_hook(). + +Both are side-effecting; errors are logged but don't abort the run. +""" + +from __future__ import annotations + +import json +import logging +import os +import re +import tarfile +from typing import Dict, List, Optional, Set + +log = logging.getLogger(__name__) + +_TF_PATH_RE = re.compile(r"^\./tf(?P\d+)/") + + +def _task_template_for_timeframe(task_name: str, source_tf: int) -> str: + """Turn task_7 from a tf7 observation into task_{tf}. + + Global tasks such as aodmerge, and any explicit cross-timeframe task + references, are left untouched. + """ + suffix = f"_{source_tf}" + if task_name.endswith(suffix): + return f"{task_name[:-len(suffix)]}_{{tf}}" + return task_name + + +def _task_template_from_placeholder(task_name: str) -> str: + return re.sub(r"_X$", "_{tf}", task_name) + + +def _filegraph_expand_timeframes( + data: Dict, + timeframes: Set[int], + target_namelist: List[str], + logger: Optional[logging.Logger] = None, +) -> Dict[str, List[Dict]]: + """Build canonical per-timeframe file templates and replicate per TF. + + The FileIOGraph may have been recorded with one or many timeframes. We + merge all observed timeframe-local entries by normalising ``./tfX/...`` to + ``./tf{tf}/...`` and task suffixes ``_X`` to ``_{tf}``, then instantiate the + merged template for the timeframes of the current workflow. + """ + logger = logger if logger is not None else log + templates: Dict[str, Dict] = {} + source_tfs: Set[int] = set() + + template_report = data.get("file_template_report") or [] + if template_report: + for entry in template_report: + filename = entry.get("file", "") + if filename.startswith("./tfX/"): + file_template = filename.replace("./tfX/", "./tf{tf}/", 1) + elif "./tf{tf}/" in filename: + file_template = filename + else: + continue + + source_tfs.update(int(tf) for tf in entry.get("source_timeframes", [])) + merged = templates.setdefault( + file_template, + { + "file": file_template, + "written_by": set(), + "read_by": set(), + "keep": bool(entry.get("keep", False)), + }, + ) + merged["keep"] = merged["keep"] or bool(entry.get("keep", False)) + for task in entry.get("written_by", []): + merged["written_by"].add(_task_template_from_placeholder(task)) + for task in entry.get("read_by", []): + merged["read_by"].add(_task_template_from_placeholder(task)) + else: + for entry in data.get("file_report", []): + filename = entry.get("file", "") + match = _TF_PATH_RE.match(filename) + if match is None: + continue + + source_tf = int(match.group("tf")) + source_tfs.add(source_tf) + file_template = _TF_PATH_RE.sub("./tf{tf}/", filename, count=1) + merged = templates.setdefault( + file_template, + { + "file": file_template, + "written_by": set(), + "read_by": set(), + "keep": bool(entry.get("keep", False)), + }, + ) + merged["keep"] = merged["keep"] or bool(entry.get("keep", False)) + for task in entry.get("written_by", []): + merged["written_by"].add(_task_template_for_timeframe(task, source_tf)) + for task in entry.get("read_by", []): + merged["read_by"].add(_task_template_for_timeframe(task, source_tf)) + + if not templates: + logger.warning("FileIOGraph contains no ./tfN/ file entries; early removal disabled") + return {} + + logger.info( + "FileIOGraph timeframe template built from observed timeframe(s) %s: %d file pattern(s)", + sorted(source_tfs), + len(templates), + ) + + result: Dict[str, List[Dict]] = {} + for i in timeframes: + if i == -1: + continue + new_entries: List[Dict] = [] + for template in templates.values(): + written_by = sorted(t.format(tf=i) for t in template["written_by"]) + read_by = sorted(t.format(tf=i) for t in template["read_by"]) + expanded = { + "file": template["file"].format(tf=i), + "written_by": written_by, + "read_by": read_by, + } + if template["keep"] or any(w in target_namelist for w in written_by): + expanded["keep"] = True + new_entries.append(expanded) + result[f"timeframe-{i}"] = new_entries + return result + + +class EarlyFileRemover: + """Owns the timeframe-expanded file dependency dict and performs + per-task-completion file deletion.""" + + def __init__( + self, + filegraph_path: str, + timeframes: Set[int], + target_namelist: List[str], + logger: Optional[logging.Logger] = None, + ): + self.log = logger if logger is not None else log + with open(filegraph_path) as f: + data = json.load(f) + self.file_dict = _filegraph_expand_timeframes( + data, timeframes, target_namelist, logger=self.log, + ) + self.timeframes = timeframes + # Pre-build a reverse index: task_name -> list[file_entry dict] (all TFs) + # so completions don't re-scan the whole file map. + self._by_task: Dict[str, List[Dict]] = {} + for entries in self.file_dict.values(): + for e in entries: + for t in e.get("written_by", []): + self._by_task.setdefault(t, []).append(e) + for t in e.get("read_by", []): + self._by_task.setdefault(t, []).append(e) + + def on_task_done(self, taskname: str) -> None: + entries = self._by_task.get(taskname, []) + for entry in entries: + if taskname in entry.get("read_by", []): + entry["read_by"].remove(taskname) + if taskname in entry.get("written_by", []): + entry["written_by"].remove(taskname) + if (not entry.get("read_by") and not entry.get("written_by") + and not entry.get("keep", False)): + self._remove_if_exists(entry["file"]) + + def _remove_if_exists(self, path: str) -> bool: + if os.path.exists(path): + try: + sz = os.path.getsize(path) + os.remove(path) + self.log.info("Removing %s (no longer needed); freed %.2f MB", + path, sz / 1024.0 / 1024.0) + return True + except OSError as e: + self.log.warning("Could not remove %s: %s", path, e) + return False + + +def archive_task_logs(logfile: str, logger: Optional[logging.Logger] = None) -> None: + """Append , _done, _time to a tar archive + and delete the originals. Used in production mode.""" + logger = logger if logger is not None else log + done = logfile + "_done" + timef = logfile + "_time" + try: + tf = tarfile.open(name="pipeline_log_archive.log.tar", mode="a") + except Exception as e: + logger.warning("Could not open log archive: %s", e) + return + try: + for path in (logfile, done, timef): + if os.path.exists(path): + try: + tf.add(path) + except Exception as e: + logger.warning("tar add %s failed: %s", path, e) + finally: + tf.close() + + for path in (logfile, done, timef): + if os.path.exists(path): + try: + os.remove(path) + except OSError as e: + logger.warning("Could not remove %s: %s", path, e) diff --git a/MC/workflow_runner/o2dpg_runner/cli.py b/MC/workflow_runner/o2dpg_runner/cli.py new file mode 100644 index 000000000..79e0cfe46 --- /dev/null +++ b/MC/workflow_runner/o2dpg_runner/cli.py @@ -0,0 +1,443 @@ +"""Command-line entry point. + +Translates argparse -> RunnerConfig, builds loggers, creates the +WorkflowExecutor, and invokes it. All semantics of the original script +are preserved; new flags are additive and default-compatible. +""" + +from __future__ import annotations + +import argparse +import logging +import os +import shutil +import sys +from typing import Optional, Tuple + +import psutil + +from .config import RunnerConfig +from .filegraph import BACKENDS as FILEGRAPH_BACKENDS, FileGraphManager +from .workflow import build_workflow, load_json +from .executor import WorkflowExecutor + +_FORMATTER = logging.Formatter("%(asctime)s %(levelname)s %(message)s") +_IN_SLICE_ENV = "O2DPG_RUNNER_IN_SLICE" + + +def _setup_logger(name: str, logfile: str, level: int = logging.INFO) -> logging.Logger: + handler = logging.FileHandler(logfile, mode="w") + handler.setFormatter(_FORMATTER) + logger = logging.getLogger(name) + logger.setLevel(level) + logger.handlers.clear() + logger.addHandler(handler) + logger.propagate = False + return logger + + +def build_parser() -> argparse.ArgumentParser: + max_system_mem = psutil.virtual_memory().total + default_mem = 0.9 * max_system_mem / 1024.0 / 1024.0 + + p = argparse.ArgumentParser( + description="Parallel execution of an O2-DPG data/job DAG under resource constraints.", + formatter_class=argparse.ArgumentDefaultsHelpFormatter, + ) + p.add_argument("-f", "--workflowfile", required=True) + p.add_argument("-jmax", "--maxjobs", type=int, default=100) + p.add_argument("-k", "--keep-going", action="store_true") + p.add_argument("--dry-run", action="store_true") + p.add_argument("--visualize-workflow", action="store_true") + p.add_argument("--target-labels", nargs="+", default=[]) + p.add_argument("-tt", "--target-tasks", nargs="+", default=["*"]) + p.add_argument("--produce-script", default=None) + p.add_argument("--rerun-from", default=None) + p.add_argument("--list-tasks", action="store_true") + + # Resources + p.add_argument("--update-resources", dest="update_resources", default=None) + p.add_argument("--dynamic-resources", dest="dynamic_resources", action="store_true") + p.add_argument("--optimistic-resources", dest="optimistic_resources", action="store_true") + p.add_argument("--n-backfill", dest="n_backfill", type=int, default=1) + p.add_argument("--backfill-mem-factor", dest="backfill_mem_factor", type=float, default=1.0, + help="memory over-commit allowed for backfill tasks, as a factor of --mem-limit") + p.add_argument("--mem-limit", type=float, default=default_mem, help="in MB") + p.add_argument("--cpu-limit", type=float, default=8) + + # systemd-run slice confinement (replaces the old --cgroup option) + p.add_argument( + "--systemd-run", + dest="systemd_run_spec", + default=None, + metavar="SPEC", + help=( + "Relaunch the whole runner (and all child processes) inside a transient " + "systemd scope unit with resource limits. SPEC is a slash-separated list " + "of key:value pairs. Supported keys: ncpus (number of CPU cores, e.g. 8), " + "mem (memory limit in systemd format, e.g. 16G or 16384M). " + "Either key may be omitted. Examples: \"ncpus:8/mem:16G\", \"ncpus:4\", \"mem:32G\"." + ), + ) + + # Scheduling (new) + p.add_argument("--scheduler-policy", default="timeframe", + choices=["timeframe", "critical-path", "best-fit"]) + p.add_argument("--drop-should-break", action="store_true", + help="In timeframe policy, don't stop scanning on the first " + "non-fitting task (lets light tasks slip past heavy ones).") + + # Monitoring (new) + p.add_argument("--monitor-interval-cpu", type=float, default=1.0) + p.add_argument("--monitor-interval-mem", type=float, default=1.0) + p.add_argument("--monitor-backend", default="psutil", choices=["psutil"]) + + # Cache (new) + p.add_argument("--cache-policy", default="off", + choices=["off", "lenient", "strict"]) + + # Control + p.add_argument("--stdout-on-failure", action="store_true") + p.add_argument("--retry-on-failure", type=int, default=0) + p.add_argument("--no-rootinit-speedup", action="store_true") + p.add_argument("--remove-files-early", type=str, default="") + p.add_argument("--filegraph-backends", type=str, + default=os.getenv("O2DPG_FILEGRAPH_BACKENDS", ""), + help="comma-separated file-IO-graph backends to learn the " + "file dependencies with: " + + ", ".join(sorted(FILEGRAPH_BACKENDS))) + + # Accept-and-ignore for backward compatibility of call sites + # that still pass these flags. They have no effect. + p.add_argument("--webhook", default=None, help=argparse.SUPPRESS) + p.add_argument("--checkpoint-on-failure", default=None, help=argparse.SUPPRESS) + # superseded by --systemd-run; a JDL still passing it must not abort here + p.add_argument("--cgroup", default=None, help=argparse.SUPPRESS) + + # Logging + p.add_argument("--action-logfile", default=None) + p.add_argument("--metric-logfile", default=None) + p.add_argument("--production-mode", action="store_true") + + return p + + +def _args_to_config(ns: argparse.Namespace) -> RunnerConfig: + target_tasks = [f.strip('"').strip("'") for f in ns.target_tasks] + # Extract slice name from spec so the executor can name child scopes. + slice_name: Optional[str] = None + if ns.systemd_run_spec: + try: + _, _, slice_name = _parse_systemd_run_spec(ns.systemd_run_spec) + except ValueError: + pass # error already caught at re-exec time + return RunnerConfig( + workflowfile=ns.workflowfile, + maxjobs=ns.maxjobs, + mem_limit=ns.mem_limit, + cpu_limit=ns.cpu_limit, + n_backfill=ns.n_backfill, + backfill_mem_factor=ns.backfill_mem_factor, + update_resources=ns.update_resources, + dynamic_resources=ns.dynamic_resources, + optimistic_resources=ns.optimistic_resources, + in_systemd_slice=bool(os.environ.get(_IN_SLICE_ENV)), + systemd_run_spec=ns.systemd_run_spec, + systemd_slice_name=slice_name, + scheduler_policy=ns.scheduler_policy, + drop_should_break=ns.drop_should_break, + monitor_interval_cpu=ns.monitor_interval_cpu, + monitor_interval_mem=ns.monitor_interval_mem, + monitor_backend=ns.monitor_backend, + cache_policy=ns.cache_policy, + target_tasks=target_tasks, + target_labels=list(ns.target_labels), + keep_going=ns.keep_going, + dry_run=ns.dry_run, + visualize_workflow=ns.visualize_workflow, + produce_script=ns.produce_script, + rerun_from=ns.rerun_from, + list_tasks=ns.list_tasks, + retry_on_failure=ns.retry_on_failure, + no_rootinit_speedup=ns.no_rootinit_speedup, + remove_files_early=ns.remove_files_early, + filegraph_backends=ns.filegraph_backends, + stdout_on_failure=ns.stdout_on_failure, + production_mode=ns.production_mode, + action_logfile=ns.action_logfile, + metric_logfile=ns.metric_logfile, + ) + + +def _parse_systemd_run_spec(spec: str) -> Tuple[Optional[str], Optional[str], str]: + """Parse "ncpus:N/mem:M/name:S" into (cpu_quota_str, mem_str, slice_name). + + ncpus is given as a number of cores and converted to systemd CPUQuota + format (e.g. 8 cores → "800%"). mem is passed through as-is. + name sets the systemd slice name (default "o2dpg"). Any part may be absent. + """ + cpu_quota: Optional[str] = None + mem: Optional[str] = None + slice_name: str = "o2dpg" + for part in spec.split("/"): + part = part.strip() + if not part: + continue + if ":" not in part: + raise ValueError(f"Expected key:value in --systemd-run spec, got: {part!r}") + key, _, val = part.partition(":") + key = key.strip().lower() + val = val.strip() + if key == "ncpus": + try: + cores = float(val) + except ValueError: + raise ValueError(f"ncpus must be a number, got: {val!r}") + cpu_quota = f"{int(cores * 100)}%" + elif key == "mem": + mem = val + elif key == "name": + slice_name = val + else: + raise ValueError(f"Unknown key in --systemd-run spec: {key!r}. " + f"Supported: ncpus, mem, name") + return cpu_quota, mem, slice_name + + +def _parse_mem_to_bytes(mem_str: str) -> Optional[int]: + """Convert a memory size string (e.g. "16G", "512M") to bytes.""" + suffixes = {"K": 1024, "M": 1024**2, "G": 1024**3, "T": 1024**4} + s = mem_str.strip() + if s and s[-1].upper() in suffixes: + try: + return int(float(s[:-1]) * suffixes[s[-1].upper()]) + except ValueError: + pass + try: + return int(s) + except ValueError: + return None + + +def _apply_slice_cgroup_limits( + cpu_quota: Optional[str], + mem_str: Optional[str], + logger: logging.Logger, +) -> None: + """Write resource limits to the parent slice's cgroup directory. + + Called after re-exec inside the slice. The runner's own scope lives at + .../kaz1.slice/o2dpg-runner-.scope/; one dirname() up is the slice + that covers both the runner and all sibling per-task scopes. + + This is the portable fallback for systemd < 246 which lacks + --slice-property. Writing directly to cpu.max / memory.max works on + any cgroup v2 system where the user owns the cgroup. + """ + cgroup_rel: Optional[str] = None + try: + with open("/proc/self/cgroup") as fh: + for line in fh: + parts = line.strip().split(":", 2) + if len(parts) == 3 and parts[0] == "0": + cgroup_rel = parts[2].lstrip("/") + break + except OSError: + pass + + if not cgroup_rel: + logger.warning("Cannot determine cgroup path; slice limits not applied.") + return + + scope_dir = f"/sys/fs/cgroup/{cgroup_rel}" + slice_dir = os.path.dirname(scope_dir) + + if cpu_quota: + pct = float(cpu_quota.rstrip("%")) + # cgroup cpu.max format: " " + # 100% = 1 core = 100000 usec per 100000 usec period + quota_usec = int(pct * 1000) + cpu_max = os.path.join(slice_dir, "cpu.max") + try: + with open(cpu_max, "w") as fh: + fh.write(f"{quota_usec} 100000\n") + logger.info("Slice CPUQuota=%s applied → %s", cpu_quota, cpu_max) + except OSError as e: + logger.warning("Could not set CPUQuota on slice (%s): %s", cpu_max, e) + + if mem_str: + mem_bytes = _parse_mem_to_bytes(mem_str) + if mem_bytes is not None: + mem_max = os.path.join(slice_dir, "memory.max") + try: + with open(mem_max, "w") as fh: + fh.write(f"{mem_bytes}\n") + logger.info("Slice MemoryMax=%s (%d bytes) applied → %s", + mem_str, mem_bytes, mem_max) + except OSError as e: + logger.warning("Could not set MemoryMax on slice (%s): %s", mem_max, e) + + +def _maybe_reexec_in_slice(ns: argparse.Namespace) -> None: + """If --systemd-run is set and we are not already in the slice, re-exec. + + Uses os.execvp so the current process image is replaced by systemd-run, + which creates a transient scope cgroup and then exec's the runner again. + The child runner sees O2DPG_RUNNER_IN_SLICE=1 and skips this function. + """ + spec = getattr(ns, "systemd_run_spec", None) + if not spec: + return + if os.environ.get(_IN_SLICE_ENV): + return # already inside the slice + + if not shutil.which("systemd-run"): + print( + "Warning: --systemd-run requested but systemd-run not found on PATH; " + "continuing without slice confinement.", + file=sys.stderr, + ) + return + + try: + _, _, slice_name = _parse_systemd_run_spec(spec) + except ValueError as e: + print(f"Error in --systemd-run spec: {e}", file=sys.stderr) + sys.exit(1) + + # Ensure the slice name has the .slice suffix expected by systemd. + systemd_slice = slice_name if slice_name.endswith(".slice") else f"{slice_name}.slice" + unit_name = f"o2dpg-runner-{os.getpid()}.scope" + cmd = ["systemd-run", "--user", "--scope", "--collect", + f"--unit={unit_name}", f"--slice={systemd_slice}"] + # Resource limits are NOT passed here; they are written directly to the + # slice cgroup after re-exec via _apply_slice_cgroup_limits(). + # --property=CPUQuota applies only to the scope (runner), not to the + # sibling task scopes. --slice-property would be correct but requires + # systemd ≥ 246. Direct cgroup writes work on all versions. + cmd += ["--", sys.executable] + sys.argv + + os.environ[_IN_SLICE_ENV] = "1" + try: + os.execvp(cmd[0], cmd) + except OSError as e: + # execvp only returns on failure + del os.environ[_IN_SLICE_ENV] + print( + f"Warning: could not exec systemd-run ({e}); " + "continuing without slice confinement.", + file=sys.stderr, + ) + + +def _maybe_draw_workflow(raw_spec): + try: + from graphviz import Digraph + except ImportError: + print("graphviz not installed; cannot draw workflow") + return + dot = Digraph(comment="MC workflow") + name_to_idx = {} + for i, node in enumerate(raw_spec["stages"]): + name_to_idx[node["name"]] = i + dot.node(str(i), node["name"]) + for node in raw_spec["stages"]: + to_i = name_to_idx[node["name"]] + for r in node.get("needs", []): + if r in name_to_idx: + dot.edge(str(name_to_idx[r]), str(to_i)) + dot.render("workflow.gv") + + +def main(argv=None) -> int: + ns = build_parser().parse_args(argv) + _maybe_reexec_in_slice(ns) # may replace this process; returns only if not re-execing + cfg = _args_to_config(ns) + + # loggers + action_log = cfg.action_logfile or f"pipeline_action_{os.getpid()}.log" + metric_log = cfg.metric_logfile or f"pipeline_metric_{os.getpid()}.log" + action_logger = _setup_logger("pipeline_action_logger", action_log, level=logging.DEBUG) + metric_logger = _setup_logger("pipeline_metric_logger", metric_log) + + for flag in ("webhook", "checkpoint_on_failure", "cgroup"): + if getattr(ns, flag, None): + action_logger.warning("--%s is accepted but has no effect", + flag.replace("_", "-")) + + # also route the package-level log records to the action log + pkg_log = logging.getLogger("o2dpg_runner") + pkg_log.setLevel(logging.INFO) + for h in list(pkg_log.handlers): + pkg_log.removeHandler(h) + pkg_log.propagate = False + for h in action_logger.handlers: + pkg_log.addHandler(h) + + # Apply slice-level cgroup resource limits now that we are inside the + # slice and the action logger is ready to record the outcome. + if cfg.in_systemd_slice and cfg.systemd_run_spec: + try: + cpu_quota, mem_str, _ = _parse_systemd_run_spec(cfg.systemd_run_spec) + _apply_slice_cgroup_limits(cpu_quota, mem_str, action_logger) + except Exception as e: + action_logger.warning("Could not apply slice cgroup limits: %s", e) + + # record meta to the metric log (mirrors prototype) + raw = load_json(cfg.workflowfile) + meta = raw.get("meta", {}) if isinstance(raw, dict) else {} + if not isinstance(meta, dict): + meta = {} + meta.update({ + "cpu_limit": cfg.cpu_limit, + "mem_limit": cfg.mem_limit, + "workflow_file": os.path.abspath(cfg.workflowfile), + "target_task": cfg.target_tasks, + "rerun_from": cfg.rerun_from, + "target_labels": cfg.target_labels, + "scheduler_policy": cfg.scheduler_policy, + "drop_should_break": cfg.drop_should_break, + "cache_policy": cfg.cache_policy, + "systemd_run_spec": cfg.systemd_run_spec, + "in_systemd_slice": cfg.in_systemd_slice, + "monitor_interval_cpu": cfg.monitor_interval_cpu, + "filegraph_backends": cfg.filegraph_backends, + }) + metric_logger.info(meta) + + # visualize if asked (uses raw spec before filtering) + if cfg.visualize_workflow: + _maybe_draw_workflow(raw) + + # build workflow (filters, strips global init, builds DAG) + wf = build_workflow(raw, cfg.target_tasks, cfg.target_labels) + if not wf.stages: + if cfg.target_tasks: + print("Apparently some of the chosen target tasks are not in the workflow") + else: + print("Workflow is empty. Nothing to do") + return 0 + + # Apply global env (as the prototype did at construction time) + for k, v in wf.global_env.items(): + os.environ.setdefault(k, str(v)) + + filegraph = FileGraphManager.from_config( + cfg.filegraph_backends, os.getcwd(), os.getpid(), action_log, action_logger) + filegraph.start() + + rc = 0 + try: + execer = WorkflowExecutor(cfg, wf, action_logger, metric_logger, + filegraph=filegraph) + rc = int(execer.execute()) + finally: + filegraph.stop() + for backend, path in filegraph.analyse().items(): + print(f"FileIOGraph[{backend}] -> {path}") + + return rc + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/MC/workflow_runner/o2dpg_runner/config.py b/MC/workflow_runner/o2dpg_runner/config.py new file mode 100644 index 000000000..3bf9735bb --- /dev/null +++ b/MC/workflow_runner/o2dpg_runner/config.py @@ -0,0 +1,64 @@ +"""Runtime configuration for the runner. + +A dataclass that holds everything the old module-level ``args`` exposed, +so nothing in the code needs to import argparse. Constructed from the +argparse Namespace in cli.py. +""" + +from __future__ import annotations + +from dataclasses import dataclass, field +from typing import List, Optional + + +@dataclass +class RunnerConfig: + # --- required --- + workflowfile: str + + # --- scheduling / resources --- + maxjobs: int = 100 + mem_limit: float = 0.0 # MB; 0 means "auto from psutil" + cpu_limit: float = 8.0 + n_backfill: int = 1 + backfill_mem_factor: float = 1.0 + update_resources: Optional[str] = None + dynamic_resources: bool = False + optimistic_resources: bool = False + in_systemd_slice: bool = False # True when runner was re-exec'd under systemd-run --scope + + # --- new scheduler knobs --- + scheduler_policy: str = "timeframe" # timeframe | critical-path | best-fit + drop_should_break: bool = False # let timeframe policy scan past non-fitting + + # --- systemd-run slice --- + systemd_run_spec: Optional[str] = None # raw "ncpus:N/mem:M/name:S" spec, kept for metric meta + systemd_slice_name: Optional[str] = None # parsed "name:" value; used for child scope names + + # --- new monitor knobs --- + monitor_interval_cpu: float = 1.0 + monitor_interval_mem: float = 1.0 # match prototype cadence; raise for cheaper monitor + monitor_backend: str = "psutil" # psutil | auto (auto reserved for future cgroup backend) + + # --- cache policy (v1 _done.json) --- + cache_policy: str = "off" # off | lenient | strict + + # --- selection / control --- + target_tasks: List[str] = field(default_factory=lambda: ["*"]) + target_labels: List[str] = field(default_factory=list) + keep_going: bool = False + dry_run: bool = False + visualize_workflow: bool = False + produce_script: Optional[str] = None + rerun_from: Optional[str] = None + list_tasks: bool = False + retry_on_failure: int = 0 + no_rootinit_speedup: bool = False + remove_files_early: str = "" + filegraph_backends: str = "" + stdout_on_failure: bool = False + production_mode: bool = False + + # --- logging --- + action_logfile: Optional[str] = None + metric_logfile: Optional[str] = None diff --git a/MC/workflow_runner/o2dpg_runner/executor.py b/MC/workflow_runner/o2dpg_runner/executor.py new file mode 100644 index 000000000..fe5cb87c6 --- /dev/null +++ b/MC/workflow_runner/o2dpg_runner/executor.py @@ -0,0 +1,774 @@ +"""Main control loop. + +Responsibilities: + - boot sequence (global init, ROOT speedup, FAIRMQ socket setup) + - candidate management + - submit / wait / monitor-feedback interplay + - retry, failure handling, rerun-from + - end-of-task hooks (file removal, production archival) + +Split from the prototype's monolithic WorkflowExecutor and freed from +module-level args. +""" + +from __future__ import annotations + +import json +import logging +import os +import platform +import re +import signal +import subprocess +import sys +import threading +import time +import traceback +from dataclasses import dataclass, field +from typing import Dict, List, Optional, Set, Tuple + +import psutil + +from .config import RunnerConfig +from .workflow import Workflow, update_resource_estimates +from .graph import descendants, longest_path_length, kahn_topological_order +from .resources import ResourceManager, ResourceLimitExceeded +from .monitoring import MonitorThread, PsutilBackend, _read_cgroup_v2_dir +from .filegraph import FileGraphManager +from .scheduler import get_policy +from .scheduler.base import SchedulerState +from .scheduler.timeframe import TimeframeFirstPolicy +from .cache import TaskCache, compute_fingerprint, remove_done_flag +from .alienv import get_alienv_software_environment +from .cleanup import EarlyFileRemover, archive_task_logs + +log = logging.getLogger(__name__) + +_UNIT_NAME_RE = re.compile(r"[^a-zA-Z0-9_\-.]") + + +def _unit_name(task_name: str, tid: int) -> str: + """Build a valid systemd unit name for a per-task scope.""" + safe = _UNIT_NAME_RE.sub("-", task_name) + return f"task-{safe}-{tid}.scope" + + +def _start_stderr_drainer(pipe, logger: logging.Logger, tag: str) -> threading.Thread: + """Drain *pipe* line-by-line in a daemon thread, forwarding to *logger*. + + Used to capture systemd-run's own informational messages (e.g. "Running + as unit: ...") and route them to the action log instead of the terminal. + The thread exits naturally when the pipe reaches EOF (process finished). + """ + def _run() -> None: + try: + for raw in pipe: + line = raw.rstrip() if isinstance(raw, str) else raw.rstrip().decode(errors="replace") + if line: + logger.info("[systemd] %s: %s", tag, line) + except Exception: + pass + t = threading.Thread(target=_run, daemon=True, name=f"stderr-{tag}") + t.start() + return t + + +@dataclass +class _TaskRuntime: + """Everything we learn about a task at runtime and need to feed back.""" + logfile: str + fingerprint: Dict[str, str] = field(default_factory=dict) + start_time: float = 0.0 + pid: int = 0 + + +class WorkflowExecutor: + """Construct from a config + workflow; call execute().""" + + def __init__( + self, + config: RunnerConfig, + workflow: Workflow, + action_logger: logging.Logger, + metric_logger: logging.Logger, + filegraph=None, + ): + self.cfg = config + self.filegraph = filegraph or FileGraphManager([], os.getpid(), action_logger) + self.wf = workflow + self.actionlog = action_logger + self.metriclog = metric_logger + + # apply update-resources (before building resource manager) + if config.update_resources: + update_resource_estimates( + workflow, config.update_resources, + logger=action_logger, + ) + + # resource manager + self.rm = ResourceManager( + cpu_limit=config.cpu_limit, + mem_limit=config.mem_limit, + procs_parallel_max=config.maxjobs, + n_backfill_max=config.n_backfill, + backfill_mem_factor=config.backfill_mem_factor, + dynamic_resources=config.dynamic_resources, + optimistic_resources=config.optimistic_resources, + ) + for task in workflow.stages: + try: + rel = float(task["resources"].get("relative_cpu") or 1) + except (TypeError, ValueError): + rel = 1.0 + try: + self.rm.add_task( + name=task["name"], + related_name=self._global_name(task["name"]), + cpu=float(task["resources"]["cpu"]), + cpu_relative=rel, + mem=float(task["resources"]["mem"]), + semaphore_string=task.get("semaphore"), + ) + except ResourceLimitExceeded as e: + print(e, file=sys.stderr) + print("Pass --optimistic-resources to the runner to attempt the run anyway.", + file=sys.stderr) + raise + + # scheduler + if config.scheduler_policy == "timeframe": + self.policy = TimeframeFirstPolicy(drop_should_break=config.drop_should_break) + else: + self.policy = get_policy(config.scheduler_policy) + + # scheduler state: precompute weights once + self.state = self._build_scheduler_state() + + # task cache (covers _done + optional _done.json) + self.cache = TaskCache(policy=config.cache_policy) + # precompute per-task fingerprint so we don't recompute on each check + self._fingerprint_by_tid: Dict[int, Dict[str, str]] = {} + for tid, task in enumerate(workflow.stages): + alienv = task.get("alternative_alienv_package") or "" + self._fingerprint_by_tid[tid] = compute_fingerprint(task, alienv) + + # alternative alienv envs + self.alternative_envs: Dict[int, Dict[str, str]] = {} + self._init_alternative_envs() + + # Compute the global cgroup directory for the aggregate monitor. + # Cgroup monitoring is only meaningful when the runner was launched + # under --systemd-run: the runner's own scope is a leaf node + # (e.g. o2dpg.slice/o2dpg-runner-.scope/) and task scopes are + # siblings. The *parent* slice directory's cpu.stat / memory.current + # then cover the runner + all task scopes. Without --systemd-run the + # runner shares a generic user-session cgroup with unrelated + # processes, so cgroup readings would be misleading; fall back to + # plain psutil monitoring instead. + _global_cgroup: Optional[str] = None + if config.in_systemd_slice: + _runner_cgroup = _read_cgroup_v2_dir(os.getpid()) + if _runner_cgroup: + _global_cgroup = os.path.dirname(_runner_cgroup) + if not os.path.isdir(_global_cgroup): + _global_cgroup = _runner_cgroup # safety fallback + + # monitor + self.monitor = MonitorThread( + cpu_interval=config.monitor_interval_cpu, + mem_interval=config.monitor_interval_mem, + backend=PsutilBackend(), + monitor_disc=bool(os.getenv("MONITOR_DISC_USAGE")), + disc_path=os.getcwd(), + global_cgroup_dir=_global_cgroup, + ) + + # process tracking + self.proc_status: Dict[int, str] = {tid: "ToDo" for tid in range(workflow.n_tasks())} + self.task_runtime: Dict[int, _TaskRuntime] = {} + self.process_list: List[Tuple[int, psutil.Popen]] = [] + self.tids_marked_retry: List[int] = [] + self.retry_counter: List[int] = [0] * workflow.n_tasks() + self.task_retries: List[int] = [ + int(t.get("retry_count", 0)) for t in workflow.stages + ] + + # early file removal + self.file_remover: Optional[EarlyFileRemover] = None + if config.remove_files_early: + try: + self.file_remover = EarlyFileRemover( + config.remove_files_early, + workflow.timeframes, + workflow.full_target_names, + logger=action_logger, + ) + except Exception as e: + log.warning("Could not set up early file removal: %s", e) + + self.start_time: float = 0.0 + self.scheduling_iteration = 0 + self._last_metric_tick: int = -1 # prevents duplicate metric rows per tick + + # signals + signal.signal(signal.SIGINT, self._sighandler) + signal.signal(signal.SIGTERM, self._sighandler) + signal.siginterrupt(signal.SIGINT, False) + signal.siginterrupt(signal.SIGTERM, False) + + # ----- small helpers ----- + @staticmethod + def _global_name(name: str) -> str: + """Strip _ suffix to find sibling group for resource sampling.""" + toks = name.split("_") + if toks and toks[-1].isdigit() and len(toks) > 1: + return "_".join(toks[:-1]) + return name + + def _build_scheduler_state(self) -> SchedulerState: + n = self.wf.n_tasks() + # descendants via memoized iterative DFS (no recursion limit concerns) + desc_cache: Dict[int, Set[int]] = {} + desc_counts = [0] * n + for tid in range(n): + desc_counts[tid] = len(descendants(self.wf.forward_adj, tid, desc_cache)) + + timeframe_of = [t.get("timeframe", -1) for t in self.wf.stages] + tf_weight = [(timeframe_of[t], desc_counts[t]) for t in range(n)] + + cpu = [float(t.get("resources", {}).get("cpu", 1.0)) for t in self.wf.stages] + mem = [float(t.get("resources", {}).get("mem", 0.0)) for t in self.wf.stages] + + # Per-task walltime [s] from learned resources (resources.walltime set + # by update_resource_estimates when --update-resources is given). + # Fall back to cpu as a proxy so behaviour is unchanged without + # learned data. + walltime = [ + float(t.get("resources", {}).get("walltime") or cpu[i]) + for i, t in enumerate(self.wf.stages) + ] + has_walltime = any( + t.get("resources", {}).get("walltime") for t in self.wf.stages + ) + + # Critical path: longest remaining *wall time* to any leaf. + # Using walltime as the node weight gives a true makespan estimate; + # using cpu (the fallback) preserves the original heuristic. + cp_weight = walltime if has_walltime else cpu + topo = kahn_topological_order(n, self.wf.forward_adj, self.wf.indegree) + cp = longest_path_length(self.wf.forward_adj, topo, cp_weight) + + if has_walltime: + self.actionlog.info("Critical path weighted by learned walltime [s]") + + # self-log weights (matches prototype's informational logging) + for tid in range(n): + self.actionlog.info("Score for %s is %s", self.wf.id_to_name[tid], tf_weight[tid]) + + return SchedulerState( + timeframe_of=timeframe_of, + descendants_count=desc_counts, + critical_path=cp, + task_cpu=cpu, + task_mem=mem, + task_walltime=walltime, + timeframe_weight=tf_weight, + ) + + def _init_alternative_envs(self) -> None: + cache: Dict[str, Dict[str, str]] = {} + for tid, task in enumerate(self.wf.stages): + pkg = task.get("alternative_alienv_package") + if not pkg: + continue + if pkg not in cache: + cache[pkg] = get_alienv_software_environment(pkg) + self.alternative_envs[tid] = cache[pkg] + + # ----- task-level helpers ----- + def logfile(self, tid: int) -> str: + task = self.wf.stages[tid] + return os.path.join(task.get("cwd", "."), f"{task['name']}.log") + + def apply_global_env(self, env: Dict[str, str]) -> None: + for k, v in self.wf.global_env.items(): + env.setdefault(k, str(v)) + + # ----- signal handling ----- + def _sighandler(self, signum, frame): + self.actionlog.info("Signal %s caught; terminating children", signum) + try: + self.monitor.stop() + except Exception: + pass + try: + procs = psutil.Process().children(recursive=True) + except psutil.NoSuchProcess: + procs = [] + except (psutil.AccessDenied, PermissionError): + procs = [] + + for p in procs: + try: + p.terminate() + except (psutil.NoSuchProcess, psutil.AccessDenied): + pass + _, alive = psutil.wait_procs(procs, timeout=3) + for p in alive: + try: + p.kill() + except (psutil.NoSuchProcess, psutil.AccessDenied): + pass + sys.exit(1) + + # ----- task submission ----- + def submit(self, tid: int, nice: int) -> Optional[psutil.Popen]: + task = self.wf.stages[tid] + self.actionlog.debug("Submitting %s with nice=%d", task["name"], nice) + cmd = task["cmd"] + workdir = task.get("cwd", ".") + if workdir: + if os.path.exists(workdir) and not os.path.isdir(workdir): + self.actionlog.error("cwd %s exists and is not a directory", workdir) + return None + if not os.path.isdir(workdir): + os.makedirs(workdir, exist_ok=True) + + self.proc_status[tid] = "Running" + + if self.cfg.dry_run: + dry = f"echo ' {self.scheduling_iteration} : would do {task['name']}'" + # psutil.Popen so that the dry-run path also answers .nice() + return psutil.Popen(["/bin/bash", "-c", dry], cwd=workdir) + + env = os.environ.copy() + alt = self.alternative_envs.get(tid) + if alt: + self.actionlog.info("Applying alternative environment to %s", task["name"]) + if alt.get("TERM") is not None: + env = dict(alt) + else: + env.update(alt) + if task.get("env"): + env.update({k: str(v) for k, v in task["env"].items()}) + self.apply_global_env(env) + + if os.environ.get("PIPELINE_RUNNER_DUMP_TASKENVS") is not None: + try: + with open(f"taskenv_{tid}.log", "w") as f: + json.dump(env, f, indent=2) + except OSError as e: + log.warning("could not dump taskenv: %s", e) + + # When the runner is inside a systemd slice, wrap each task in its own + # child scope so per-task cgroup metrics are available alongside psutil. + slice_name = self.cfg.systemd_slice_name + use_scope = self.cfg.in_systemd_slice and bool(slice_name) + if use_scope: + systemd_slice = ( + slice_name if slice_name.endswith(".slice") else f"{slice_name}.slice" + ) + unit = _unit_name(task["name"], tid) + prefix = [ + "systemd-run", "--user", "--scope", "--collect", + "--expand-environment=no", # suppress the $VAR warning; bash handles expansion + f"--unit={unit}", f"--slice={systemd_slice}", "--", + ] + else: + prefix = [] + + # a tracer has to sit inside any systemd scope, or it would only ever + # see systemd-run itself + inner_argv = self.filegraph.wrap(["/bin/bash", "-c", cmd], task["name"], tid) + launch_argv = prefix + inner_argv + + if use_scope: + p = psutil.Popen(launch_argv, cwd=workdir, env=env, stderr=subprocess.PIPE) + _start_stderr_drainer(p.stderr, self.actionlog, task["name"]) + else: + p = psutil.Popen(launch_argv, cwd=workdir, env=env) + try: + p.nice(nice) + except (psutil.NoSuchProcess, psutil.AccessDenied): + self.actionlog.error("Could not renice %d to %d", p.pid, nice) + + rt = _TaskRuntime( + logfile=self.logfile(tid), + fingerprint=self._fingerprint_by_tid[tid], + start_time=time.perf_counter(), + pid=p.pid, + ) + self.task_runtime[tid] = rt + self.monitor.register( + tid, p.pid, task["name"], task.get("labels", []) or [], rt.start_time, + resolve_cgroup=use_scope, + ) + return p + + # ----- skip logic ----- + def ok_to_skip(self, tid: int) -> bool: + return self.cache.is_done(self.logfile(tid), self._fingerprint_by_tid[tid]) + + # ----- candidate scheduling pass ----- + def try_submit_from_candidates( + self, + candidates: List[int], + finished_out: List[int], + ) -> None: + self.scheduling_iteration += 1 + + # skip already-done tasks first (in place) + remaining = [] + for tid in candidates: + if self.ok_to_skip(tid): + finished_out.append(tid) + self.actionlog.info("Skipping %s", self.wf.id_to_name[tid]) + if self.file_remover is not None: + self.file_remover.on_task_done(self.wf.id_to_name[tid]) + else: + remaining.append(tid) + # mutate the list the caller passed in + candidates[:] = remaining + + ordered = self.policy.order(candidates, self.state) + for tid, nice in self.policy.pick_submittable(ordered, self.rm): + self.actionlog.debug("Submitting tid=%d %s (nice=%d)", + tid, self.wf.id_to_name[tid], nice) + p = self.submit(tid, nice) + if p is None: + continue + # pin the nice value the OS actually granted + try: + actual_nice = p.nice() + except (psutil.NoSuchProcess, psutil.AccessDenied): + actual_nice = nice + self.rm.book(tid, actual_nice) + self.process_list.append((tid, p)) + if tid in candidates: + candidates.remove(tid) + + # ----- wait / complete / retry ----- + def wait_for_any( + self, + finished_out: List[int], + failing_out: List[int], + ) -> bool: + """Return True if we should keep waiting (no completion this pass). + + Polls each process via poll(); pulls latest monitor snapshot and + feeds it into the ResourceManager for the dynamic-resources path. + """ + if not self.process_list: + return False + + # Take each monitor tick exactly once. This loop polls faster than the + # monitor fires, and a snapshot recorded twice both inflates the sample + # lists and defeats the "too few samples" guard in sample_resources(). + snapshots = self.monitor.latest() + tick = self.monitor.tick + + if tick != self._last_metric_tick: + self._last_metric_tick = tick + for tid, snap in snapshots.items(): + self.rm.add_monitored(tid, snap.t_delta_ms, + snap.cpu_pct / 100.0, snap.pss_mb) + self.metriclog.info({ + "iter": tick, "name": snap.name, + "cpu": snap.cpu_pct, "uss": snap.uss_mb, "pss": snap.pss_mb, + "nice": snap.nice, "swap": snap.swap_mb, + "label": snap.labels, "disc": snap.disc_mb, + # cgroup-based readings for comparison with psutil (None when + # no per-task scope is active) + "cgroup_cpu": snap.cgroup_cpu_pct, "cgroup_mem": snap.cgroup_mem_mb, + }) + + # cgroup-aggregate slice totals + g_cpu = self.monitor.global_cpu_pct + g_mem = self.monitor.global_mem_mb + if g_cpu is not None or g_mem is not None: + self.metriclog.info({ + "iter": tick, "name": "__cgroup_global__", + "cpu": g_cpu, "uss": None, "pss": g_mem, + "nice": 0, "swap": None, "label": [], "disc": -1, + }) + + # check for completions + newly_done: List[Tuple[int, psutil.Popen, int]] = [] + for tid, p in list(self.process_list): + rc = 0 if self.cfg.dry_run else p.poll() + if rc is None: + continue + newly_done.append((tid, p, rc)) + + failure_detected = False + for tid, p, rc in newly_done: + name = self.wf.id_to_name[tid] + self.actionlog.info("Task pid=%d tid=%d %s finished rc=%d", + p.pid, tid, name, rc) + self.rm.unbook(tid) + self.proc_status[tid] = "Done" + self.monitor.deregister(tid) + self.process_list.remove((tid, p)) + + if rc == 0: + finished_out.append(tid) + # record fingerprint sidecar (best effort) + rt = self.task_runtime.get(tid) + if rt is not None: + self.cache.record(rt.logfile, rt.fingerprint) + if self.file_remover is not None: + self.file_remover.on_task_done(name) + if self.cfg.production_mode: + archive_task_logs(self.logfile(tid), logger=self.actionlog) + else: + print(f"{name} failed ... checking retry") + max_retries = max(self.cfg.retry_on_failure, self.task_retries[tid]) + if self._is_worth_retrying(tid) and self.retry_counter[tid] < max_retries: + self.actionlog.info("Task %s marked for retry", name) + self.tids_marked_retry.append(tid) + self.retry_counter[tid] += 1 + else: + failure_detected = True + failing_out.append(tid) + + if failure_detected and not self.cfg.keep_going: + self.actionlog.info("Stopping due to failure in tids %s", failing_out) + if self.cfg.stdout_on_failure: + self._cat_logfiles(failing_out) + self.stop_and_exit() + + return not finished_out + + def _is_worth_retrying(self, tid: int) -> bool: + """Hook for future log-inspection; currently always True (match prototype).""" + return True + + def _cat_logfiles(self, tids: List[int]) -> None: + for tid in tids: + logf = self.logfile(tid) + if os.path.exists(logf): + print(f" ----> START OF LOGFILE {logf} -----") + try: + with open(logf) as f: + sys.stdout.write(f.read()) + except OSError: + pass + print(f" <---- END OF LOGFILE {logf} -----") + + def stop_and_exit(self) -> None: + for _, p in self.process_list: + try: + p.kill() + except Exception: + pass + self.monitor.stop() + sys.exit(1) + + # ----- boot helpers ----- + def _speedup_root_init(self) -> None: + if platform.system() != "Linux": + return + if os.environ.get("ROOT_LDSYSPATH") and os.environ.get("ROOT_CPPSYSINCL"): + return + if self.cfg.no_rootinit_speedup: + return + try: + cmd = ('LD_DEBUG=libs LD_PRELOAD=DOESNOTEXIST ls /tmp/DOESNOTEXIST 2>&1 | ' + 'grep -m 1 "system search path" | sed \'s/.*=//g\' | ' + 'awk \'//{print $1}\'') + libpath = subprocess.check_output(cmd, shell=True).decode().strip() + if libpath: + os.environ["ROOT_LDSYSPATH"] = libpath + os.environ["CLING_LDSYSPATH"] = libpath + cmd2 = ("LC_ALL=C c++ -xc++ -E -v /dev/null 2>&1 | " + "sed -n '/^#include/,${/^ \\/.*++/{p}}'") + incpath = subprocess.check_output(cmd2, shell=True).decode() + joined = ":".join(line.lstrip() for line in incpath.splitlines()) + if joined: + os.environ["ROOT_CPPSYSINCL"] = joined + os.environ["CLING_CPPSYSINCL"] = joined + except Exception as e: + log.warning("ROOT init speedup failed: %s", e) + + def _execute_global_init_cmd(self) -> bool: + cmd = self.wf.global_init_cmd + if not cmd: + return True + self.actionlog.info("Executing global init cmd: %s", cmd) + p = subprocess.Popen(["/bin/bash", "-c", cmd], + stdout=subprocess.PIPE, stderr=subprocess.PIPE) + stdout, stderr = p.communicate() + if p.returncode == 0: + self.actionlog.info(stdout.decode()) + return True + self.actionlog.error("global init failed: %s", stderr.decode()) + return False + + def _handle_rerun_from(self) -> None: + if not self.cfg.rerun_from: + return + import re + matched = False + for task in self.wf.stages: + if re.match(self.cfg.rerun_from, task["name"]): + matched = True + tid = self.wf.tid(task["name"]) + # remove done flags for tid and all its descendants (iterative) + for d in descendants(self.wf.forward_adj, tid) | {tid}: + name = self.wf.id_to_name[d] + self.actionlog.info("Marking %s for rerun", name) + if not self.cfg.dry_run: + remove_done_flag(self.logfile(d)) + else: + print(f"Would mark {name} as to be done again") + if not matched: + print(f"No task matching {self.cfg.rerun_from} found; refusing to proceed") + sys.exit(1) + + # ----- bash-script emission ----- + def produce_script(self, filename: str) -> None: + topo = kahn_topological_order(self.wf.n_tasks(), + self.wf.forward_adj, + self.wf.indegree) + lines = [ + "#!/usr/bin/env bash\n", + "#THIS FILE IS AUTOGENERATED\n", + "export JOBUTILS_SKIPDONE=ON\n", + "#-- GLOBAL INIT SECTION FROM WORKFLOW --\n", + ] + for k, v in self.wf.global_env.items(): + lines.append(f"export {k}={v}\n") + lines.append("#-- TASKS FROM WORKFLOW --\n") + for tid in topo: + t = self.wf.stages[tid] + workdir = t.get("cwd", ".") + env_pairs = t.get("env") or {} + env_prefix = " ".join(f"{k}={v}" for k, v in env_pairs.items()) + # Subshell so inner `cd` doesn't leak, and local env doesn't pollute. + inner = t["cmd"] + if env_prefix: + inner = f"{env_prefix} {inner}" + lines.append(f"( [ -d {workdir} ] || mkdir -p {workdir}; cd {workdir} && {inner} )\n") + with open(filename, "w") as f: + f.writelines(lines) + + # ----- main loop ----- + def execute(self) -> bool: + self.start_time = time.perf_counter() + psutil.cpu_percent(interval=None) + os.environ["JOBUTILS_SKIPDONE"] = "ON" + self._speedup_root_init() + + if not os.path.isdir("./.tmp"): + os.mkdir("./.tmp") + if os.environ.get("FAIRMQ_IPC_PREFIX") is None: + sp = os.path.join(os.getcwd(), ".tmp") + self.actionlog.info("Setting FAIRMQ_IPC_PREFIX=%s", sp) + os.environ["FAIRMQ_IPC_PREFIX"] = sp + + if self.cfg.list_tasks: + print("List of tasks in this workflow:") + for i, t in enumerate(self.wf.stages): + label_part = t.get("labels", []) + print(f"{t['name']} ({label_part}) ToDo: {not self.ok_to_skip(i)}") + return False + + if self.cfg.produce_script is not None: + self.produce_script(self.cfg.produce_script) + return False + + if not self._execute_global_init_cmd(): + sys.exit(1) + + self._handle_rerun_from() + + # start monitor + self.monitor.start() + + # initial candidates: tasks with no predecessors + candidates = [i for i, d in enumerate(self.wf.indegree) if d == 0] + finishedtasks_set: Set[int] = set() + error_encountered = False + + try: + while True: + finished: List[int] = [] + self.actionlog.debug("candidates: %s", + [(c, self.wf.id_to_name[c]) for c in candidates]) + self.try_submit_from_candidates(candidates, finished) + + if candidates and not self.process_list: + self._noprogress_error() + error_encountered = True + break + + # wait loop + finished_running: List[int] = [] + failing: List[int] = [] + poll_delay = 0.1 # adaptive; grows up to 1s + while self.wait_for_any(finished_running, failing): + if not self.cfg.dry_run: + time.sleep(poll_delay) + poll_delay = min(1.0, poll_delay * 1.5) + else: + time.sleep(0.001) + + finished.extend(finished_running) + finishedtasks_set.update(finished) + + # take failed tasks out of the "finished" accounting + if failing: + error_encountered = True + fs = set(failing) + finished = [x for x in finished if x not in fs] + finishedtasks_set.difference_update(fs) + + # retries go back onto the candidate list + if self.tids_marked_retry: + rs = set(self.tids_marked_retry) + finished = [x for x in finished if x not in rs] + finishedtasks_set.difference_update(rs) + for t in self.tids_marked_retry: + if t not in candidates: + candidates.append(t) + self.tids_marked_retry.clear() + + # new candidates: successors whose all needs are done + for tid in finished: + for succ in self.wf.forward_adj[tid]: + if succ in candidates: + continue + if self.proc_status[succ] != "ToDo": + continue + preds = self.wf.reverse_adj[succ] + if all(p in finishedtasks_set for p in preds): + candidates.append(succ) + + self.actionlog.debug("new candidates %s", candidates) + + if not candidates and not self.process_list: + break + except Exception: + traceback.print_exc() + self._sighandler(0, None) + + self.monitor.stop() + self.monitor.join(timeout=2) + end = time.perf_counter() + msg = "with failures" if error_encountered else "success" + print(f"\n**** Pipeline done {msg} (global_runtime : {end - self.start_time:.3f}s) *****\n") + self.actionlog.debug("global_runtime : %.3fs", end - self.start_time) + return error_encountered + + # ----- error message ----- + def _noprogress_error(self) -> None: + msg = ( + "Scheduler runtime error: cannot make progress although candidates exist.\n\n" + "This typically means a task's estimated resources exceed the configured\n" + "--cpu-limit or --mem-limit. On a 16 GB node, try --mem-limit 20000 (MB);\n" + "the ACTUAL use might be lower than the estimate. Alternatively convert\n" + "the workflow to a linear shell script via --produce-script .sh and\n" + "run that directly.\n" + ) + print(msg, file=sys.stderr) diff --git a/MC/workflow_runner/o2dpg_runner/filegraph.py b/MC/workflow_runner/o2dpg_runner/filegraph.py new file mode 100644 index 000000000..27da41bf1 --- /dev/null +++ b/MC/workflow_runner/o2dpg_runner/filegraph.py @@ -0,0 +1,239 @@ +"""Pluggable observation of task file IO during a pilot run. + +A backend records which task produces and which tasks consume each +intermediate file and writes ``filegraph__.json``, which a +later production run replays with ``--remove-files-early``. Several +backends can be active at once so they can be compared on one run; see +UTILS/FileIOGraph/README.md. +""" + +from __future__ import annotations + +import logging +import os +import shutil +import subprocess +import sys +from typing import Dict, List, Optional + + +def filegraph_dir() -> str: + """The UTILS/FileIOGraph directory holding the monitors and analysers.""" + root = os.getenv("O2DPG_ROOT") + if not root: + root = os.path.abspath(__file__) + while root != "/" and not os.path.isdir( + os.path.join(root, "UTILS", "FileIOGraph")): + root = os.path.dirname(root) + return os.path.join(root, "UTILS", "FileIOGraph") + + +class FileGraphBackend: + """One way of observing which task reads and writes which file.""" + + name = "none" + analyser = "" + #: backends that replace the original fanotify sidecar also write its + #: report under the name the runner has always used + legacy_report = False + + def __init__(self, workdir: str, runner_pid: int, action_log: str, + logger: logging.Logger): + self.workdir = workdir + self.runner_pid = runner_pid + self.action_log = action_log + self.log = logger + + def start(self) -> None: + pass + + def stop(self) -> None: + pass + + def wrap(self, argv: List[str], taskname: str, tid: int) -> List[str]: + """The argv actually used to launch a task.""" + return argv + + # -- result -------------------------------------------------------- + def recorded(self) -> bool: + return False + + def analyser_args(self) -> List[str]: + return [] + + def analyse(self, out_json: str) -> bool: + if not self.recorded(): + return False + argv = [sys.executable, os.path.join(filegraph_dir(), self.analyser), + "--basedir", self.workdir, "-o", out_json] + self.analyser_args() + self.log.info("FileIOGraph analysis: %s", " ".join(argv)) + try: + subprocess.run(argv, check=True) + return True + except (subprocess.CalledProcessError, OSError) as e: + self.log.error("FileIOGraph analysis failed: %s", e) + return False + + +class FanotifyBackend(FileGraphBackend): + """Mount-wide sidecar; needs CAP_SYS_ADMIN on the monitor binary.""" + + name = "fanotify" + analyser = "analyse_FileIO_v2.py" + legacy_report = True + + def __init__(self, workdir, runner_pid, action_log, logger, + exe: Optional[str] = None): + super().__init__(workdir, runner_pid, action_log, logger) + # O2DPG_PRODUCE_FILEGRAPH is the original spelling and still names + # the monitor to run + self.exe = (exe or os.getenv("O2DPG_PRODUCE_FILEGRAPH") + or os.path.join(filegraph_dir(), "monitor_fileaccess_v2.exe")) + self.logfile = f"pipeline_fileaccess_{self.name}_{runner_pid}.log" + self._proc: Optional[subprocess.Popen] = None + self._fh = None + + def start(self) -> None: + if not os.path.exists(self.exe): + self.log.error("filegraph fanotify: no such executable %s", self.exe) + return + env = dict(os.environ, FILEACCESS_MON_ROOTPATH=self.workdir, + MAXMOTHERPID=str(self.runner_pid)) + self._fh = open(self.logfile, "w") + try: + self._proc = subprocess.Popen([self.exe], stdout=self._fh, + stderr=subprocess.STDOUT, env=env) + except OSError as e: + self.log.error("filegraph fanotify: could not start %s: %s", self.exe, e) + self._fh.close() + self._fh = None + return + self.log.info("filegraph fanotify: %s pid=%d -> %s", + self.exe, self._proc.pid, self.logfile) + + def stop(self) -> None: + if self._proc is not None: + self._proc.terminate() + try: + self._proc.wait(timeout=10) + except subprocess.TimeoutExpired: + self._proc.kill() + self._proc = None + if self._fh is not None: + self._fh.close() + self._fh = None + + def recorded(self) -> bool: + return os.path.exists(self.logfile) + + def analyser_args(self) -> List[str]: + return ["--actionFile", self.action_log, "--monitorFile", self.logfile] + + +class StraceBackend(FileGraphBackend): + """Wraps every task in its own strace; attribution is by construction.""" + + name = "strace" + analyser = "analyse_FileIO_strace.py" + #: must stay in step with what analyse_FileIO_strace.py parses + SYSCALLS = "openat,openat2,open,creat,rename,renameat,renameat2" + + def __init__(self, workdir, runner_pid, action_log, logger, + exe: str = "strace"): + super().__init__(workdir, runner_pid, action_log, logger) + self.exe = exe + self.tracedir = os.path.join(workdir, f"strace_{runner_pid}") + self._argv: List[str] = [] + + def start(self) -> None: + if not shutil.which(self.exe): + self.log.error("filegraph strace: %s not found on PATH", self.exe) + return + argv = [self.exe, "-f", "-qq", "-y", + "-e", f"trace={self.SYSCALLS}", "-e", "status=successful"] + # without --seccomp-bpf every read() costs two ptrace stops + if self._seccomp_works(argv): + argv.insert(1, "--seccomp-bpf") + os.makedirs(self.tracedir, exist_ok=True) + self._argv = argv + self.log.info("filegraph strace: %s -> %s", " ".join(argv), self.tracedir) + + def _seccomp_works(self, argv: List[str]) -> bool: + try: + r = subprocess.run(argv[:1] + ["--seccomp-bpf"] + argv[1:] + + ["-o", os.devnull, "true"], + stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, + timeout=60) + return r.returncode == 0 + except (OSError, subprocess.SubprocessError): + return False + + def wrap(self, argv, taskname, tid): + if not self._argv: + return argv + trace = os.path.join(self.tracedir, f"trace_{tid}_{taskname}.log") + return self._argv + ["-o", trace, "--"] + list(argv) + + def recorded(self) -> bool: + return os.path.isdir(self.tracedir) + + def analyser_args(self) -> List[str]: + return ["--straceDir", self.tracedir] + + +BACKENDS = {b.name: b for b in (FanotifyBackend, StraceBackend)} + + +class FileGraphManager: + """Drives zero or more backends over the runner's lifecycle.""" + + def __init__(self, backends: List[FileGraphBackend], runner_pid: int, + logger: logging.Logger): + self.backends = backends + self.runner_pid = runner_pid + self.log = logger + + @classmethod + def from_config(cls, spec: str, workdir: str, runner_pid: int, + action_log: str, logger: logging.Logger) -> "FileGraphManager": + names = [n.strip() for n in spec.split(",") if n.strip()] + if not names and os.getenv("O2DPG_PRODUCE_FILEGRAPH"): + names = ["fanotify"] + backends = [] + for n in names: + factory = BACKENDS.get(n) + if factory is None: + logger.error("unknown filegraph backend %r (known: %s)", + n, ", ".join(sorted(BACKENDS))) + continue + backends.append(factory(workdir, runner_pid, action_log, logger)) + return cls(backends, runner_pid, logger) + + def start(self) -> None: + for b in self.backends: + b.start() + + def stop(self) -> None: + for b in self.backends: + b.stop() + + def wrap(self, argv: List[str], taskname: str, tid: int) -> List[str]: + for b in self.backends: + argv = b.wrap(argv, taskname, tid) + return argv + + def analyse(self) -> Dict[str, str]: + """Produce one report per backend; returns backend name -> path.""" + produced: Dict[str, str] = {} + for b in self.backends: + out = f"filegraph_{b.name}_{self.runner_pid}.json" + if not b.analyse(out): + continue + produced[b.name] = out + if b.legacy_report: + legacy = f"pipeline_fileaccess_report_{self.runner_pid}.json" + try: + shutil.copyfile(out, legacy) + except OSError as e: + self.log.warning("could not write %s: %s", legacy, e) + return produced diff --git a/MC/workflow_runner/o2dpg_runner/graph.py b/MC/workflow_runner/o2dpg_runner/graph.py new file mode 100644 index 000000000..d04184f47 --- /dev/null +++ b/MC/workflow_runner/o2dpg_runner/graph.py @@ -0,0 +1,159 @@ +"""Graph utilities on the task DAG. + +Nothing here knows about workflows, tasks, or resources -- it operates on +integer-indexed adjacency lists only. Pure, testable, no side effects. + +Supersedes the recursive ``findAllTopologicalOrders`` and +``find_all_dependent_tasks`` in the original prototype, both of which had +bugs and needed sys.setrecursionlimit(100000) to survive diamond DAGs. +""" + +from __future__ import annotations + +from collections import defaultdict, deque +from typing import Dict, Iterable, List, Set, Tuple + + +def build_adjacency( + n_nodes: int, edges: Iterable[Tuple[int, int]] +) -> Tuple[List[List[int]], List[List[int]], List[int]]: + """Return (forward_adj, reverse_adj, indegree) for ``n_nodes`` nodes. + + forward_adj[u] lists successors; reverse_adj[v] lists predecessors. + """ + forward: List[List[int]] = [[] for _ in range(n_nodes)] + reverse: List[List[int]] = [[] for _ in range(n_nodes)] + indeg = [0] * n_nodes + for u, v in edges: + forward[u].append(v) + reverse[v].append(u) + indeg[v] += 1 + return forward, reverse, indeg + + +def kahn_topological_order( + n_nodes: int, + forward_adj: List[List[int]], + indegree: List[int], + tiebreak: List[int] = None, +) -> List[int]: + """Deterministic topological order via Kahn's algorithm. + + ``tiebreak`` is an optional per-node integer used to break ties + among ready nodes. Smaller tiebreak value first. If None, the + node index is used (which is also deterministic). + """ + if tiebreak is None: + tiebreak = list(range(n_nodes)) + indeg = list(indegree) + # Use a sorted list as a tiny priority queue; for small DAGs this is + # fine, and ties are broken deterministically. + ready = sorted([n for n in range(n_nodes) if indeg[n] == 0], + key=lambda n: (tiebreak[n], n)) + out: List[int] = [] + # Simple loop; no heap because re-sorting on small ready sets is cheap + # and we want full determinism. + while ready: + u = ready.pop(0) + out.append(u) + for v in forward_adj[u]: + indeg[v] -= 1 + if indeg[v] == 0: + # insert keeping sorted-by-tiebreak + lo, hi = 0, len(ready) + key = (tiebreak[v], v) + while lo < hi: + mid = (lo + hi) // 2 + if (tiebreak[ready[mid]], ready[mid]) < key: + lo = mid + 1 + else: + hi = mid + ready.insert(lo, v) + if len(out) != n_nodes: + raise ValueError("Graph has at least one cycle; topological sort impossible") + return out + + +def descendants( + forward_adj: List[List[int]], source: int, cache: Dict[int, Set[int]] = None +) -> Set[int]: + """All nodes reachable from ``source`` (excluding ``source`` itself), memoized. + + Uses iterative DFS with post-order memoization. Safe on DAGs with + diamonds and deep chains; no recursion limit concerns. + """ + if cache is None: + cache = {} + if source in cache: + return cache[source] + + # iterative post-order: process children first, then combine + order: List[int] = [] + seen: Set[int] = set() + stack: List[Tuple[int, int]] = [(source, 0)] + while stack: + node, child_idx = stack[-1] + kids = forward_adj[node] + if child_idx < len(kids): + stack[-1] = (node, child_idx + 1) + child = kids[child_idx] + if child not in seen and child not in cache: + seen.add(child) + stack.append((child, 0)) + else: + stack.pop() + order.append(node) + + # Now assign cache entries in post-order (children before parents). + for node in order: + if node in cache: + continue + s: Set[int] = set() + for child in forward_adj[node]: + s.add(child) + s |= cache.get(child, set()) + cache[node] = s + return cache[source] + + +def ancestors( + reverse_adj: List[List[int]], sink: int, cache: Dict[int, Set[int]] = None +) -> Set[int]: + """All nodes that can reach ``sink`` (excluding sink itself), memoized.""" + # ancestors in forward graph == descendants in reverse graph + return descendants(reverse_adj, sink, cache) + + +def longest_path_length( + forward_adj: List[List[int]], + topo_order: List[int], + node_weight: List[float], +) -> List[float]: + """Longest-path weight from each node to any leaf (inclusive of node). + + ``topo_order`` must be a valid topological ordering. Uses reverse + traversal and DP. Used by CriticalPathPolicy. + """ + n = len(node_weight) + lp = list(node_weight) # include own weight + for u in reversed(topo_order): + best_child = 0.0 + for v in forward_adj[u]: + if lp[v] > best_child: + best_child = lp[v] + lp[u] = node_weight[u] + best_child + return lp + + +def invert_adj(forward_adj: List[List[int]]) -> List[List[int]]: + """Compute reverse adjacency from forward adjacency.""" + n = len(forward_adj) + rev: List[List[int]] = [[] for _ in range(n)] + for u in range(n): + for v in forward_adj[u]: + rev[v].append(u) + return rev + + +def root_nodes(indegree: List[int]) -> List[int]: + return [i for i, d in enumerate(indegree) if d == 0] diff --git a/MC/workflow_runner/o2dpg_runner/monitoring.py b/MC/workflow_runner/o2dpg_runner/monitoring.py new file mode 100644 index 000000000..f156c6655 --- /dev/null +++ b/MC/workflow_runner/o2dpg_runner/monitoring.py @@ -0,0 +1,505 @@ +"""Resource monitor running in a background thread. + +The main scheduler loop used to poll psutil synchronously at 1 Hz, which +cost 10-20% of one core on realistic workflows. Now the monitor is a +separate thread with independent CPU (cheap, 1 Hz) and MEM (expensive, +0.2 Hz) cadences. The scheduler reads the latest snapshot lock-free via +an atomic dict reference. + +Backend interface: a callable + sample(task_pid_list) -> {pid: {"cpu_pct": float, "pss_mb": float, + "uss_mb": float, "swap_mb": float, + "children": [pid, ...]}} +""" + +from __future__ import annotations + +import logging +import os +import subprocess +import threading +import time +from dataclasses import dataclass, field +from typing import Dict, List, Optional, Tuple + +try: + import psutil + HAVE_PSUTIL = True +except ImportError: + HAVE_PSUTIL = False + +log = logging.getLogger(__name__) + + +@dataclass +class TaskSnapshot: + """Per-task roll-up of the most recent monitor pass.""" + tid: int + name: str + t_delta_ms: int = 0 + cpu_pct: float = 0.0 # 0..100 * n_cores (psutil) + uss_mb: float = 0.0 + pss_mb: float = 0.0 + swap_mb: float = 0.0 + nice: int = 0 + labels: List[str] = field(default_factory=list) + disc_mb: float = -1.0 # global disc usage (same value on all tasks, or -1) + mem_fresh: bool = False # did this snapshot's mem numbers come from a fresh read? + # cgroup-based metrics (None when not in a systemd slice / no per-task scope) + cgroup_cpu_pct: Optional[float] = None # aggregate CPU % from cgroup cpu.stat + cgroup_mem_mb: Optional[float] = None # aggregate memory from cgroup memory.current + + +def _get_child_procs_fallback(base_pid: int) -> List[int]: + """Pure-bash fallback when psutil.Process.children raises AccessDenied.""" + script = r''' + childprocs() { + local parent=$1 + if [ ! "$2" ]; then child_pid_list=""; fi + if [ "$parent" ]; then + child_pid_list="$child_pid_list $parent" + for childpid in $(pgrep -P ${parent}); do + childprocs $childpid "nottoplevel" + done + fi + if [ ! "$2" ]; then echo "${child_pid_list}"; fi + } + ''' + full = script + f"\nchildprocs {base_pid}\n" + out = subprocess.check_output(full, shell=True) + pids: List[int] = [] + for tok in out.decode().split(): + try: + pids.append(int(tok)) + except ValueError: + continue + return pids + + +class PsutilBackend: + """psutil-based monitor backend. Caches per-pid Process objects so that + cpu_percent(interval=None) has a stable 'previous reading' baseline.""" + + def __init__(self): + if not HAVE_PSUTIL: + raise RuntimeError("psutil not available") + self._proc_cache: Dict[int, "psutil.Process"] = {} + # Prime baseline on the manager process so first delta is sensible. + try: + psutil.cpu_percent(interval=None) + except Exception: + pass + + def _get_or_add(self, pid: int) -> Optional["psutil.Process"]: + p = self._proc_cache.get(pid) + if p is not None: + return p + try: + p = psutil.Process(pid) + # Deliberately DO NOT call p.cpu_percent(interval=None) here to prime. + # psutil's rule: the first call after Process() construction returns + # a CPU % relative to process creation -- useful as a "since start" + # figure, not "since last poll". On multi-process tasks that spawn + # children between monitor ticks, priming right before the read + # yields 0 because no time has elapsed. Letting the monitor thread's + # regular cadence handle both calls produces correct deltas from + # tick 2 onward (tick 1 returns the since-creation value, which is + # a reasonable initial estimate anyway). + self._proc_cache[pid] = p + return p + except Exception: + return None + + def forget(self, pid: int) -> None: + self._proc_cache.pop(pid, None) + + def sweep_dead(self) -> int: + """Evict cached Process objects whose underlying PID is gone. + + Called once per monitor pass. Keeps the cache size bounded over + long runs where short-lived DPL/FAIRMQ children come and go. + """ + dead = 0 + for pid in list(self._proc_cache.keys()): + try: + if not self._proc_cache[pid].is_running(): + del self._proc_cache[pid] + dead += 1 + except Exception: + self._proc_cache.pop(pid, None) + dead += 1 + return dead + + def sample( + self, + root_pid: int, + want_mem: bool, + ) -> Tuple[float, float, float, float, int]: + """Return (cpu_pct_sum, pss_mb, uss_mb, swap_mb, nice_of_root). + + Sums over root and all descendants. If want_mem is False the + memory figures are returned as 0 (caller should interpret -> use + last known). + """ + root = self._get_or_add(root_pid) + if root is None: + return 0.0, 0.0, 0.0, 0.0, 0 + + # Enumerate the PIDs of the whole process tree, then look each one + # up in the cache. CRITICAL: psutil.Process.children() returns NEW + # Process objects on every call, which means their cpu_percent() + # baseline is reset each tick -> we'd read 0.0 forever. Always go + # through _get_or_add so baselines persist between ticks. + try: + child_pids = [c.pid for c in root.children(recursive=True)] + except (psutil.NoSuchProcess,): + return 0.0, 0.0, 0.0, 0.0, 0 + except (psutil.AccessDenied, PermissionError): + try: + child_pids = _get_child_procs_fallback(root_pid) + if root_pid in child_pids: + child_pids.remove(root_pid) + except Exception: + child_pids = [] + + procs = [root] + for pid in child_pids: + p = self._get_or_add(pid) + if p is not None: + procs.append(p) + + cpu_sum = 0.0 + pss_sum = 0.0 + uss_sum = 0.0 + swap_sum = 0.0 + for p in procs: + # CPU: cheap + try: + cpu_sum += p.cpu_percent(interval=None) + except (psutil.NoSuchProcess, psutil.AccessDenied): + pass + + if want_mem: + try: + mi = p.memory_full_info() + pss_sum += getattr(mi, "pss", 0) or 0 + uss_sum += getattr(mi, "uss", 0) or 0 + swap_sum += getattr(mi, "swap", 0) or 0 + except (psutil.NoSuchProcess, psutil.AccessDenied): + pass + + # bytes -> MB + pss_mb = pss_sum / 1024.0 / 1024.0 + uss_mb = uss_sum / 1024.0 / 1024.0 + swap_mb = swap_sum / 1024.0 / 1024.0 + + try: + nice = root.nice() + except Exception: + nice = 0 + + return cpu_sum, pss_mb, uss_mb, swap_mb, nice + + +def _read_cgroup_v2_dir(pid: int) -> Optional[str]: + """Return the cgroup v2 directory for *pid*, or None if not on cgroup v2.""" + try: + with open(f"/proc/{pid}/cgroup") as fh: + for line in fh: + parts = line.strip().split(":", 2) + # cgroup v2 unified hierarchy: single entry "0::" + if len(parts) == 3 and parts[0] == "0": + rel = parts[2].lstrip("/") + candidate = f"/sys/fs/cgroup/{rel}" if rel else "/sys/fs/cgroup" + if os.path.isdir(candidate): + return candidate + except OSError: + pass + return None + + +class CgroupV2Monitor: + """Reads aggregate CPU time and memory for one cgroup v2 directory. + + Works with cgroup v2 (unified hierarchy) only — the format used by + systemd on modern Linux. Instantiating this class is always safe; call + ``available`` to test whether a valid cgroup path was found/given before + using ``sample()``. + + If *cgroup_dir* is provided the directory is used directly (for per-task + monitoring of a known scope). Otherwise the caller's own cgroup is + detected from ``/proc/self/cgroup`` (for the global runner-level monitor). + + CPU is computed by differentiating the ``usage_usec`` counter in + ``cpu.stat``. The first ``sample()`` call primes the counter and returns + ``None`` for cpu_pct; subsequent calls give the average utilisation + (as a percentage of one core, same units as psutil's cpu_percent). + + Memory is read from ``memory.current`` (bytes → MB). It counts all + memory mapped by processes in the cgroup including file-backed pages; + shared pages are counted once per cgroup, not per process. For an + apples-to-apples comparison with psutil's PSS metric use the + ``memory.stat`` anon field (not implemented here — ``memory.current`` + is the right proxy for a hard MemoryMax enforcement budget). + """ + + def __init__(self, cgroup_dir: Optional[str] = None) -> None: + if cgroup_dir is not None: + self._cgroup_dir: Optional[str] = cgroup_dir if os.path.isdir(cgroup_dir) else None + else: + self._cgroup_dir = _read_cgroup_v2_dir(os.getpid()) + self._last_usage_usec: Optional[int] = None + self._last_ts: Optional[float] = None + + @property + def available(self) -> bool: + return self._cgroup_dir is not None + + def sample(self) -> Tuple[Optional[float], Optional[float]]: + """Return (cpu_pct, mem_mb). cpu_pct is None on the first call.""" + if not self._cgroup_dir: + return None, None + + cpu_pct: Optional[float] = None + mem_mb: Optional[float] = None + + # --- CPU --- + try: + with open(os.path.join(self._cgroup_dir, "cpu.stat")) as fh: + for line in fh: + if line.startswith("usage_usec"): + usage_usec = int(line.split()[1]) + now = time.monotonic() + if self._last_usage_usec is not None and self._last_ts is not None: + dt = now - self._last_ts + if dt > 0: + d_usec = usage_usec - self._last_usage_usec + # d_usec / (dt * 1e6) is CPU fraction relative to 1 core; + # multiply by 100 to match psutil's cpu_percent scale. + cpu_pct = (d_usec / (dt * 1e6)) * 100.0 + self._last_usage_usec = usage_usec + self._last_ts = now + break + except OSError: + pass + + # --- Memory --- + try: + with open(os.path.join(self._cgroup_dir, "memory.current")) as fh: + mem_mb = int(fh.read().strip()) / 1024.0 / 1024.0 + except OSError: + pass + + return cpu_pct, mem_mb + + +class MonitorThread(threading.Thread): + """Background thread polling a set of (tid, pid) pairs. + + The scheduler registers tasks via register()/deregister(). On each + pass the thread computes the latest snapshot and stores it in + self.snapshots; the scheduler reads atomically. + + CPU and MEM have independent cadences because PSS reads via + /proc//smaps_rollup are ~10x more expensive than cpu_percent. + """ + + def __init__( + self, + cpu_interval: float = 1.0, + mem_interval: float = 5.0, + backend: Optional[PsutilBackend] = None, + monitor_disc: bool = False, + disc_path: str = ".", + global_cgroup_dir: Optional[str] = None, + ): + super().__init__(daemon=True, name="o2dpg-monitor") + self.cpu_interval = cpu_interval + self.mem_interval = mem_interval + self.backend = backend if backend is not None else PsutilBackend() + self.monitor_disc = monitor_disc + self.disc_path = disc_path + + self._lock = threading.Lock() + # registered[tid] = {"pid": int, "name": str, "labels": list, + # "start_time": float} + self._registered: Dict[int, Dict] = {} + self._snapshots: Dict[int, TaskSnapshot] = {} + self._stop_event = threading.Event() + self._last_mem_ts: float = 0.0 + self._last_disc_mb: float = -1.0 + self._last_disc_ts: float = 0.0 + # Tick counter: incremented once per monitor pass (roughly once per + # cpu_interval). Read by the executor when writing metric lines so + # the "iter" field reflects wall-clock ticks like the prototype did. + self.tick: int = 0 + + # Opportunistic cgroup v2 global monitor. Only enabled when the + # caller passes an explicit *slice* directory (i.e. the runner was + # launched under --systemd-run). Without an explicit directory we + # skip cgroup monitoring entirely — the runner's own cgroup outside a + # dedicated slice covers unrelated user-session processes and gives + # misleading aggregates. + # Written only from the monitor thread; read from the executor thread — + # GIL makes bare float/None assignment atomic. + self._cgroup: Optional[CgroupV2Monitor] = ( + CgroupV2Monitor(cgroup_dir=global_cgroup_dir) if global_cgroup_dir else None + ) + self.global_cpu_pct: Optional[float] = None # cgroup-aggregate CPU % + self.global_mem_mb: Optional[float] = None # cgroup-aggregate memory MB + if self._cgroup is not None and self._cgroup.available: + log.info("CgroupV2Monitor global active at %s", self._cgroup._cgroup_dir) + + # ----- registration ----- + def register( + self, + tid: int, + pid: int, + name: str, + labels: List[str], + start_time: float, + resolve_cgroup: bool = False, + ) -> None: + """Register a task for monitoring. + + If *resolve_cgroup* is True the monitor thread will lazily locate the + task's cgroup v2 directory by inspecting the first child process of + *pid* (which is the systemd-run wrapper when per-task scopes are used). + Once resolved a per-task CgroupV2Monitor is created and its readings + are stored in the TaskSnapshot alongside the psutil figures. + """ + with self._lock: + self._registered[tid] = { + "pid": pid, + "name": name, + "labels": labels, + "start_time": start_time, + "resolve_cgroup": resolve_cgroup, + "cgroup_monitor": None, # filled lazily by _one_pass + } + + def deregister(self, tid: int) -> None: + with self._lock: + entry = self._registered.pop(tid, None) + if entry is not None: + self.backend.forget(entry["pid"]) + + # ----- snapshot access ----- + def latest(self) -> Dict[int, TaskSnapshot]: + """Return a shallow copy of current snapshots.""" + with self._lock: + return dict(self._snapshots) + + def latest_for(self, tid: int) -> Optional[TaskSnapshot]: + with self._lock: + return self._snapshots.get(tid) + + # ----- control ----- + def stop(self) -> None: + self._stop_event.set() + + # ----- loop ----- + def _disc_usage_mb(self) -> float: + try: + out = subprocess.check_output(["du", "-sb", self.disc_path], text=True) + return int(out.split()[0]) / 1024.0 / 1024.0 + except Exception: + return -1.0 + + def _one_pass(self, now: float) -> None: + self.tick += 1 + + # cgroup global totals (cheap reads, always done every pass) + if self._cgroup is not None and self._cgroup.available: + g_cpu, g_mem = self._cgroup.sample() + if g_cpu is not None: + self.global_cpu_pct = g_cpu + if g_mem is not None: + self.global_mem_mb = g_mem + + want_mem = (now - self._last_mem_ts) >= self.mem_interval + if want_mem: + self._last_mem_ts = now + + if self.monitor_disc and (now - self._last_disc_ts) >= self.mem_interval: + self._last_disc_mb = self._disc_usage_mb() + self._last_disc_ts = now + + # snapshot registrations under lock, release for the slow work + with self._lock: + registered_copy = dict(self._registered) + + new_snaps: Dict[int, TaskSnapshot] = {} + for tid, info in registered_copy.items(): + pid = info["pid"] + cpu_pct, pss_mb, uss_mb, swap_mb, nice = self.backend.sample(pid, want_mem) + # fall back to previous mem reading if not a mem-interval tick + prev = self._snapshots.get(tid) + if not want_mem and prev is not None: + pss_mb = prev.pss_mb + uss_mb = prev.uss_mb + swap_mb = prev.swap_mb + + # --- per-task cgroup monitoring --- + # Lazy resolution: when the task runs inside a systemd scope, its + # direct child of the systemd-run wrapper PID lands in that scope's + # cgroup. We probe once per pass until the child appears. + if info.get("resolve_cgroup") and info.get("cgroup_monitor") is None: + try: + p_obj = self.backend._get_or_add(pid) + if p_obj is not None: + children = p_obj.children() + if children: + cgroup_dir = _read_cgroup_v2_dir(children[0].pid) + if cgroup_dir: + info["cgroup_monitor"] = CgroupV2Monitor(cgroup_dir=cgroup_dir) + log.info("Per-task cgroup resolved: tid=%d %s → %s", + tid, info["name"], cgroup_dir) + except Exception: + pass + + cgroup_cpu: Optional[float] = None + cgroup_mem: Optional[float] = None + cm: Optional[CgroupV2Monitor] = info.get("cgroup_monitor") + if cm is not None and cm.available: + cgroup_cpu, cgroup_mem = cm.sample() + # cgroup_cpu is None on the very first sample() call (no prior + # baseline yet) and whenever the scope's cpu.stat is unreadable + # (e.g. after --collect removes the finished scope). In both + # cases we leave it as None rather than carrying forward a stale + # value — one tick with None is preferable to a wrong number. + # ------------------------------------------------------- + + t_delta_ms = int((now - info["start_time"]) * 1000) + new_snaps[tid] = TaskSnapshot( + tid=tid, name=info["name"], + t_delta_ms=t_delta_ms, cpu_pct=cpu_pct, + uss_mb=uss_mb, pss_mb=pss_mb, swap_mb=swap_mb, + nice=nice, labels=info["labels"], + disc_mb=self._last_disc_mb, + mem_fresh=want_mem, + cgroup_cpu_pct=cgroup_cpu, + cgroup_mem_mb=cgroup_mem, + ) + + with self._lock: + self._snapshots = new_snaps + + # housekeeping: evict dead cached Process objects once per pass + try: + self.backend.sweep_dead() + except Exception: + pass + + def run(self) -> None: + log.debug("Monitor thread started (cpu=%.2fs, mem=%.2fs)", + self.cpu_interval, self.mem_interval) + while not self._stop_event.is_set(): + now = time.perf_counter() + try: + self._one_pass(now) + except Exception: + log.exception("Monitor pass failed (continuing)") + # Sleep at the CPU cadence; MEM is gated by its own interval. + self._stop_event.wait(self.cpu_interval) + log.debug("Monitor thread exiting") diff --git a/MC/workflow_runner/o2dpg_runner/resources.py b/MC/workflow_runner/o2dpg_runner/resources.py new file mode 100644 index 000000000..79e604b98 --- /dev/null +++ b/MC/workflow_runner/o2dpg_runner/resources.py @@ -0,0 +1,362 @@ +"""Resource management: per-task estimates, global budget, semaphores. + +Structurally equivalent to the prototype's TaskResources / ResourceManager, +with three changes: + 1. is_within_limits() actually checks MEM against mem_limit (not CPU). + 2. No module-level args dependency; n_backfill is passed in. + 3. Booking/unbooking is explicit about which bucket (default / backfill). + +Sibling-sampling for --dynamic-resources still runs inside unbook(), same +ordering as before. +""" + +from __future__ import annotations + +import logging +import os +from dataclasses import dataclass, field +from typing import Dict, Iterator, List, Optional, Tuple + +log = logging.getLogger(__name__) + + +class Semaphore: + """A named mutual-exclusion flag shared by a group of tasks. + + Deliberately not threading.Semaphore -- this is a logical gate checked + by the scheduler, not a concurrency primitive. + """ + __slots__ = ("locked",) + + def __init__(self): + self.locked = False + + def lock(self): + self.locked = True + + def unlock(self): + self.locked = False + + +@dataclass +class ResourceBoundaries: + cpu_limit: float + mem_limit: float + dynamic_resources: bool = False + optimistic_resources: bool = False + + +class TaskResources: + """Resource accounting for a single task.""" + + def __init__( + self, + tid: int, + name: str, + cpu: float, + cpu_relative: Optional[float], + mem: float, + boundaries: ResourceBoundaries, + ): + self.tid = tid + self.name = name + # originals (never mutated) + self.cpu_assigned_original = cpu + self.mem_assigned_original = mem + self.cpu_relative = cpu_relative if cpu_relative else 1.0 + # transient assignments (may be updated by sampling) + self.cpu_assigned = cpu + self.mem_assigned = mem + self.boundaries = boundaries + # sampled (after a sibling finished) + self.cpu_sampled: Optional[float] = None + self.mem_sampled: Optional[float] = None + # live monitor feed + self.time_collect: List[float] = [] + self.cpu_collect: List[float] = [] + self.mem_collect: List[float] = [] + # siblings (same "global" task name) + self.related_tasks: Optional[List["TaskResources"]] = None + self.semaphore: Optional[Semaphore] = None + self.nice_value: Optional[int] = None + self.booked = False + + # ----- helpers ----- + @property + def is_done(self) -> bool: + return bool(self.time_collect) and not self.booked + + def is_within_limits(self) -> bool: + """Check the current assignment against global boundaries.""" + ok_cpu = self.cpu_assigned <= self.boundaries.cpu_limit + ok_mem = self.mem_assigned <= self.boundaries.mem_limit + if not ok_cpu: + log.warning("CPU of %s exceeds limit: %.2f > %.2f", + self.name, self.cpu_assigned, self.boundaries.cpu_limit) + if not ok_mem: + log.warning("MEM of %s exceeds limit: %.2f > %.2f", + self.name, self.mem_assigned, self.boundaries.mem_limit) + return ok_cpu and ok_mem + + def limit_resources(self, cpu_limit: float = None, mem_limit: float = None) -> None: + if cpu_limit is None: + cpu_limit = self.boundaries.cpu_limit + if mem_limit is None: + mem_limit = self.boundaries.mem_limit + self.cpu_assigned = min(self.cpu_assigned, cpu_limit) + self.mem_assigned = min(self.mem_assigned, mem_limit) + + def add_sample(self, time_passed: float, cpu_fraction: float, mem_mb: float) -> None: + """Record a monitor sample.""" + self.time_collect.append(time_passed) + self.cpu_collect.append(cpu_fraction) + self.mem_collect.append(mem_mb) + + def sample_resources(self) -> None: + """Compute CPU/MEM sample and propagate to un-started siblings.""" + if not self.is_done: + return + + if len(self.time_collect) < 3: + self.cpu_sampled = self.cpu_assigned + self.mem_sampled = self.mem_assigned + log.debug("Task %s: not enough samples (<3); using assigned as sampled", + self.name) + else: + # Weighted mean of CPU over sample intervals, skipping the first + # (cpu_percent(interval=None) first reading is meaningless). + deltas = [self.time_collect[i + 1] - self.time_collect[i] + for i in range(len(self.time_collect) - 1)] + tot = sum(deltas) or 1.0 + cpu_integral = sum( + c * dt for c, dt in zip(self.cpu_collect[1:], deltas) if c >= 0 + ) + self.cpu_sampled = cpu_integral / tot + self.mem_sampled = max(self.mem_collect) + + if self.related_tasks is None: + return + + # aggregate over finished siblings + mem_agg = 0.0 + cpu_list: List[float] = [] + for sib in self.related_tasks: + if sib.is_done and sib.cpu_sampled is not None and sib.mem_sampled is not None: + mem_agg = max(mem_agg, sib.mem_sampled) + cpu_list.append(sib.cpu_sampled) + if not cpu_list: + return + cpu_agg = sum(cpu_list) / len(cpu_list) + + if cpu_agg > self.boundaries.cpu_limit: + log.warning("Sampled CPU (%.2f) exceeds limit (%.2f)", + cpu_agg, self.boundaries.cpu_limit) + elif cpu_agg <= 0: + # a zero reading is missing information, not a task that needs no + # CPU; handing it on would let every sibling be admitted at once + log.debug("Sampled CPU<=0 for %s; reverting to assigned", self.name) + cpu_agg = self.cpu_assigned + + if mem_agg > self.boundaries.mem_limit: + log.warning("Sampled MEM (%.2f) exceeds limit (%.2f)", + mem_agg, self.boundaries.mem_limit) + elif mem_agg <= 0: + log.debug("Sampled MEM<=0 for %s; reverting to assigned", self.name) + mem_agg = self.mem_assigned + + for sib in self.related_tasks: + if sib.is_done or sib.booked: + continue + sib.cpu_assigned = cpu_agg * sib.cpu_relative + sib.mem_assigned = mem_agg + sib.limit_resources() + + +class ResourceManager: + """Central accounting: who is booked, which bucket, what's left.""" + + def __init__( + self, + cpu_limit: float, + mem_limit: float, + procs_parallel_max: int = 100, + n_backfill_max: int = 1, + backfill_cpu_factor: float = 1.5, + backfill_mem_factor: float = 1.0, + dynamic_resources: bool = False, + optimistic_resources: bool = False, + ): + self.boundaries = ResourceBoundaries( + cpu_limit, mem_limit, dynamic_resources, optimistic_resources + ) + self.resources: List[TaskResources] = [] + self._related_by_name: Dict[str, List[TaskResources]] = {} + self._semaphores: Dict[str, Semaphore] = {} + + # default-priority bucket + self.cpu_booked = 0.0 + self.mem_booked = 0.0 + self.n_procs = 0 + + # backfill (niced) bucket + self.cpu_booked_backfill = 0.0 + self.mem_booked_backfill = 0.0 + self.n_procs_backfill = 0 + + self.procs_parallel_max = procs_parallel_max + self.n_backfill_max = n_backfill_max + self.backfill_cpu_factor = backfill_cpu_factor + self.backfill_mem_factor = backfill_mem_factor + + try: + self.nice_default = os.nice(0) + except (AttributeError, OSError): + self.nice_default = 0 + self.nice_backfill = self.nice_default + 19 + + # ----- registration ----- + def add_task( + self, + name: str, + related_name: Optional[str], + cpu: float, + cpu_relative: Optional[float], + mem: float, + semaphore_string: Optional[str] = None, + ) -> TaskResources: + res = TaskResources( + len(self.resources), name, cpu, cpu_relative, mem, self.boundaries + ) + if not res.is_within_limits() and not self.boundaries.optimistic_resources: + raise ResourceLimitExceeded( + f"Task {name} exceeds resource boundaries " + f"(cpu={cpu}/{self.boundaries.cpu_limit}, " + f"mem={mem}/{self.boundaries.mem_limit}). " + f"Use --optimistic-resources to attempt anyway." + ) + res.limit_resources() + self.resources.append(res) + + if semaphore_string: + if semaphore_string not in self._semaphores: + self._semaphores[semaphore_string] = Semaphore() + res.semaphore = self._semaphores[semaphore_string] + + if related_name: + bucket = self._related_by_name.setdefault(related_name, []) + bucket.append(res) + res.related_tasks = bucket + + return res + + # ----- monitor hook ----- + def add_monitored(self, tid: int, t_delta: float, cpu_fraction: float, mem_mb: float) -> None: + self.resources[tid].add_sample(t_delta, cpu_fraction, mem_mb) + + # ----- booking ----- + def book(self, tid: int, nice_value: int) -> None: + res = self.resources[tid] + # Prior check is expected to have set nice_value; if not, force backfill. + if res.nice_value is None: + log.warning("Task %d booked without prior ok_to_submit check; forcing backfill", + tid) + nice_value = self.nice_backfill + + res.nice_value = nice_value + res.booked = True + if res.semaphore is not None: + res.semaphore.lock() + if nice_value != self.nice_default: + self.n_procs_backfill += 1 + self.cpu_booked_backfill += res.cpu_assigned + self.mem_booked_backfill += res.mem_assigned + else: + self.n_procs += 1 + self.cpu_booked += res.cpu_assigned + self.mem_booked += res.mem_assigned + + def unbook(self, tid: int) -> None: + res = self.resources[tid] + res.booked = False + if self.boundaries.dynamic_resources: + res.sample_resources() + if res.semaphore is not None: + res.semaphore.unlock() + if res.nice_value != self.nice_default: + self.cpu_booked_backfill -= res.cpu_assigned + self.mem_booked_backfill -= res.mem_assigned + self.n_procs_backfill -= 1 + if self.n_procs_backfill <= 0: + self.cpu_booked_backfill = 0.0 + self.mem_booked_backfill = 0.0 + else: + self.n_procs -= 1 + self.cpu_booked -= res.cpu_assigned + self.mem_booked -= res.mem_assigned + if self.n_procs <= 0: + self.cpu_booked = 0.0 + self.mem_booked = 0.0 + + # ----- queries ----- + def total_procs(self) -> int: + return self.n_procs + self.n_procs_backfill + + def at_proc_cap(self) -> bool: + return self.total_procs() >= self.procs_parallel_max + + def cpu_free_default(self) -> float: + return self.boundaries.cpu_limit - self.cpu_booked + + def mem_free_default(self) -> float: + return self.boundaries.mem_limit - self.mem_booked - self.mem_booked_backfill + + def fits_default(self, res: TaskResources) -> bool: + return ( + self.cpu_booked + res.cpu_assigned <= self.boundaries.cpu_limit + and self.mem_booked + self.mem_booked_backfill + res.mem_assigned <= self.boundaries.mem_limit + ) + + def fits_backfill( + self, + res: TaskResources, + cpu_factor: float = None, + mem_factor: float = None, + ) -> bool: + if cpu_factor is None: + cpu_factor = self.backfill_cpu_factor + if mem_factor is None: + mem_factor = self.backfill_mem_factor + if self.n_procs_backfill >= self.n_backfill_max: + return False + # don't backfill with huge tasks (originals: avoid tasks too close to limit) + if res.cpu_assigned > 0.9 * self.boundaries.cpu_limit: + return False + # mem per core sanity: don't launch something whose mem is huge relative to + # the CPU budget (original heuristic: mem/cpu_limit >= 1900). + if self.boundaries.cpu_limit > 0 and res.mem_assigned / self.boundaries.cpu_limit >= 1900: + return False + + ok_cpu = (self.cpu_booked_backfill + res.cpu_assigned + <= self.boundaries.cpu_limit) + ok_cpu = ok_cpu and ( + self.cpu_booked + self.cpu_booked_backfill + res.cpu_assigned + <= cpu_factor * self.boundaries.cpu_limit + ) + ok_mem = ( + self.mem_booked + self.mem_booked_backfill + res.mem_assigned + <= mem_factor * self.boundaries.mem_limit + ) + return ok_cpu and ok_mem + + def can_be_submitted_at_all(self, res: TaskResources) -> bool: + """True if a task is not blocked by its semaphore and is not already booked.""" + if res.booked: + return False + if res.semaphore is not None and res.semaphore.locked: + return False + return True + + +class ResourceLimitExceeded(Exception): + """Raised when a task's declared resources exceed the global boundaries + and --optimistic-resources was not given.""" diff --git a/MC/workflow_runner/o2dpg_runner/scheduler/__init__.py b/MC/workflow_runner/o2dpg_runner/scheduler/__init__.py new file mode 100644 index 000000000..0386a93ea --- /dev/null +++ b/MC/workflow_runner/o2dpg_runner/scheduler/__init__.py @@ -0,0 +1,22 @@ +from .base import SchedulerPolicy, SchedulerState +from .timeframe import TimeframeFirstPolicy +from .critical_path import CriticalPathPolicy +from .best_fit import BestFitBackfillPolicy + + +def get_policy(name: str) -> SchedulerPolicy: + name = name.lower().strip() + if name in ("timeframe", "tf", "legacy"): + return TimeframeFirstPolicy() + if name in ("critical-path", "cp"): + return CriticalPathPolicy() + if name in ("best-fit", "bf"): + return BestFitBackfillPolicy() + raise ValueError(f"unknown scheduler policy: {name}") + + +__all__ = [ + "SchedulerPolicy", "SchedulerState", + "TimeframeFirstPolicy", "CriticalPathPolicy", "BestFitBackfillPolicy", + "get_policy", +] diff --git a/MC/workflow_runner/o2dpg_runner/scheduler/base.py b/MC/workflow_runner/o2dpg_runner/scheduler/base.py new file mode 100644 index 000000000..9ea3fea36 --- /dev/null +++ b/MC/workflow_runner/o2dpg_runner/scheduler/base.py @@ -0,0 +1,62 @@ +"""Pluggable scheduler policy interface. + +A SchedulerPolicy has two responsibilities: + 1. order(candidates, state) -> ordered list of tids + (how to prioritize among runnable tasks) + 2. pick_submittable(ordered, resource_manager) -> iterator of (tid, nice) + (which of the ordered tasks actually fit in the remaining budget, + at what nice level, subject to policy-specific packing rules) + +Resources-aware bookkeeping (what's currently booked, what the limits +are) lives in ResourceManager. Policies only read it; they don't mutate +it. The executor calls rm.book() after picking a task. +""" + +from __future__ import annotations + +from dataclasses import dataclass, field +from typing import Iterator, List, Tuple, TYPE_CHECKING + +if TYPE_CHECKING: + from ..resources import ResourceManager + + +@dataclass +class SchedulerState: + """Everything a policy might want to know about the current run. + + Kept lightweight: policies that don't need a field just ignore it. + """ + # Static data + timeframe_of: List[int] = field(default_factory=list) # tid -> timeframe + descendants_count: List[int] = field(default_factory=list) # |desc(tid)| + critical_path: List[float] = field(default_factory=list) # longest_path_length weighted by walltime (or cpu fallback) + task_cpu: List[float] = field(default_factory=list) + task_mem: List[float] = field(default_factory=list) + task_walltime: List[float] = field(default_factory=list) # per-task walltime [s]; 0 if unknown + # Derived weight tuples (static, cached by executor) + # timeframe_weight[tid] = (timeframe, -num_descendants) + timeframe_weight: List[Tuple[int, int]] = field(default_factory=list) + + +class SchedulerPolicy: + """Base class. Subclasses override order() and/or pick_submittable().""" + + name = "base" + + def order(self, candidates: List[int], state: SchedulerState) -> List[int]: + """Return candidates ordered by policy preference (best first).""" + raise NotImplementedError + + def pick_submittable( + self, + ordered: List[int], + rm: "ResourceManager", + ) -> Iterator[Tuple[int, int]]: + """Yield (tid, nice_value) for tasks that fit now. + + Removes nothing; the executor is responsible for calling rm.book() + and rebuilding candidate lists. pick_submittable is a pure + read-only view onto rm's current state. + """ + raise NotImplementedError diff --git a/MC/workflow_runner/o2dpg_runner/scheduler/best_fit.py b/MC/workflow_runner/o2dpg_runner/scheduler/best_fit.py new file mode 100644 index 000000000..a6546ac9d --- /dev/null +++ b/MC/workflow_runner/o2dpg_runner/scheduler/best_fit.py @@ -0,0 +1,104 @@ +"""Best-fit bin-packing scheduler. + +Instead of following the ordered list linearly, it picks the candidate +that maximizes a fitness score given the current remaining budget. +Iterates until nothing fits. The backfill pass uses the same idea. + +Fitness = critical_path_weight * packing_tightness, where: + - critical_path_weight is state.critical_path[tid] — remaining walltime + on the longest path (walltime-weighted when learned data is available, + cpu-weighted otherwise). This is the "hybrid CP + packing" heuristic: + prefer tasks on the longest critical path, but among those that fit + similarly, pick the one that uses capacity most tightly. + - packing_tightness = max(c/cpu_free, m/mem_free) — dominant-resource + utilisation fraction. A task scores high when it fills at least one + resource bin well; unlike 1/max(ratio), this is not dominated by a + resource with extreme slack (e.g. 60 GB mem_limit with tasks using 1 GB). + +Default ordering is still critical-path (good baseline); pick_submittable +re-ranks on the fly within the fitting set. +""" + +from __future__ import annotations + +from typing import Iterator, List, Optional, Tuple + +from .base import SchedulerPolicy, SchedulerState +from .critical_path import CriticalPathPolicy +from ..resources import ResourceManager + + +class BestFitBackfillPolicy(SchedulerPolicy): + name = "best-fit" + + def __init__(self): + self._ordering = CriticalPathPolicy() + self._state: Optional[SchedulerState] = None + + def order(self, candidates: List[int], state: SchedulerState) -> List[int]: + self._state = state + return self._ordering.order(candidates, state) + + @staticmethod + def _fitness(res, state: SchedulerState, cpu_free: float, mem_free: float) -> float: + """Higher is better; negative if it doesn't fit.""" + c = max(res.cpu_assigned, 0.01) + m = max(res.mem_assigned, 1.0) + cpu_ratio = cpu_free / c + mem_ratio = mem_free / m + if cpu_ratio < 1 or mem_ratio < 1: + return -1.0 + # Dominant-resource utilisation: fraction of the most-used resource. + # max(c/cpu_free, m/mem_free) = max(1/cpu_ratio, 1/mem_ratio). + # Higher means the task fills at least one resource bin well. + # This is correct in both constrained and resource-ample (serial) modes: + # unlike 1/max(ratio), it is not dominated by a resource with extreme slack. + tightness = max(c / cpu_free, m / mem_free) + # CP weight: remaining walltime on the longest path from this task. + # Rewards placing tasks that unblock the most remaining work first. + # Falls back to descendants_count+1 when no critical_path available. + cp = state.critical_path[res.tid] if state.critical_path else 0.0 + cp_weight = cp if cp > 0 else (state.descendants_count[res.tid] + 1 if state.descendants_count else 1) + return cp_weight * tightness + + def pick_submittable( + self, ordered: List[int], rm: ResourceManager + ) -> Iterator[Tuple[int, int]]: + if rm.at_proc_cap(): + return + + state = self._state + assert state is not None, "order() must be called before pick_submittable()" + + # --- default-nice: iterative best-fit --- + pool = list(ordered) + while pool and not rm.at_proc_cap(): + cpu_free = rm.cpu_free_default() + mem_free = rm.mem_free_default() + best_tid = -1 + best_score = -1.0 + for tid in pool: + res = rm.resources[tid] + if not rm.can_be_submitted_at_all(res): + continue + s = self._fitness(res, state, cpu_free, mem_free) + if s > best_score: + best_score = s + best_tid = tid + if best_tid < 0 or best_score < 0: + break + pool.remove(best_tid) + res = rm.resources[best_tid] + res.nice_value = rm.nice_default + yield best_tid, rm.nice_default + + # --- backfill pass --- + if rm.at_proc_cap(): + return + for tid in pool: + res = rm.resources[tid] + if not rm.can_be_submitted_at_all(res): + continue + if rm.fits_backfill(res): + res.nice_value = rm.nice_backfill + yield tid, rm.nice_backfill diff --git a/MC/workflow_runner/o2dpg_runner/scheduler/critical_path.py b/MC/workflow_runner/o2dpg_runner/scheduler/critical_path.py new file mode 100644 index 000000000..d07a6b748 --- /dev/null +++ b/MC/workflow_runner/o2dpg_runner/scheduler/critical_path.py @@ -0,0 +1,60 @@ +"""Critical-path-first scheduler. + +Sorts candidates by state.critical_path[tid] — the longest remaining +path weight to any leaf. The weight is walltime [s] when --update-resources +has been used to inject learned lifetime data (resources.walltime per task); +it falls back to cpu cores otherwise. Both give a valid makespan proxy; +the walltime variant is strictly more accurate for multithreaded tasks. + +Submit discipline: scan ordered list, submit everything that fits at default +nice, then do a second backfill pass at elevated nice. No should_break — +there is no reason to stop scanning once we have committed to CP ordering. +""" + +from __future__ import annotations + +from typing import Iterator, List, Tuple + +from .base import SchedulerPolicy, SchedulerState +from ..resources import ResourceManager + + +class CriticalPathPolicy(SchedulerPolicy): + name = "critical-path" + + def order(self, candidates: List[int], state: SchedulerState) -> List[int]: + cp = state.critical_path + tfw = state.timeframe_weight + # primary: longest path (largest first); tie-break: timeframe, tid + return sorted( + candidates, + key=lambda t: (-cp[t] if cp else 0, tfw[t][0], t), + ) + + def pick_submittable( + self, ordered: List[int], rm: ResourceManager + ) -> Iterator[Tuple[int, int]]: + if rm.at_proc_cap(): + return + skipped: List[int] = [] + for tid in ordered: + if rm.at_proc_cap(): + return + res = rm.resources[tid] + if not rm.can_be_submitted_at_all(res): + continue + if rm.fits_default(res): + res.nice_value = rm.nice_default + yield tid, rm.nice_default + else: + skipped.append(tid) + + if rm.at_proc_cap(): + return + for tid in skipped: + res = rm.resources[tid] + if not rm.can_be_submitted_at_all(res): + continue + if rm.fits_backfill(res): + res.nice_value = rm.nice_backfill + yield tid, rm.nice_backfill diff --git a/MC/workflow_runner/o2dpg_runner/scheduler/timeframe.py b/MC/workflow_runner/o2dpg_runner/scheduler/timeframe.py new file mode 100644 index 000000000..22df6acd2 --- /dev/null +++ b/MC/workflow_runner/o2dpg_runner/scheduler/timeframe.py @@ -0,0 +1,81 @@ +"""Timeframe-first scheduler: exact behavior of the prototype. + +Order: + (timeframe, -num_descendants) # small TF first, then most-connected tasks + +Submit: + - First pass: default nice. Scan in order; on first non-fitting task, BREAK + (this is the legacy behavior; it can block light tasks behind a heavy one). + - Second pass: backfill nice. Scan remaining in order; no break on miss. + +This is the default policy so a bare-minimum invocation reproduces the +prototype's scheduling decisions. +""" + +from __future__ import annotations + +import logging +from typing import Iterator, List, Tuple + +from .base import SchedulerPolicy, SchedulerState +from ..resources import ResourceManager + +log = logging.getLogger(__name__) + + +class TimeframeFirstPolicy(SchedulerPolicy): + name = "timeframe" + + def __init__(self, drop_should_break: bool = False): + # When True, the default pass does not break on a non-fitting task + # but keeps scanning, so a light task can slip past a heavy one. + self.drop_should_break = drop_should_break + + def order(self, candidates: List[int], state: SchedulerState) -> List[int]: + # sort prefers small timeframe, then more descendants + return sorted( + candidates, + key=lambda t: (state.timeframe_weight[t][0], -state.timeframe_weight[t][1]), + ) + + def pick_submittable( + self, ordered: List[int], rm: ResourceManager + ) -> Iterator[Tuple[int, int]]: + if rm.at_proc_cap(): + return + + # --- default-nice pass --- + skipped_for_backfill: List[int] = [] + for tid in ordered: + if rm.at_proc_cap(): + return + res = rm.resources[tid] + if not rm.can_be_submitted_at_all(res): + continue + if rm.fits_default(res): + res.nice_value = rm.nice_default + yield tid, rm.nice_default + else: + if self.drop_should_break: + skipped_for_backfill.append(tid) + continue + # legacy behavior: the first non-fit breaks the default pass + skipped_for_backfill.extend( + ordered[ordered.index(tid):] + ) + break + + # --- backfill pass --- + if rm.at_proc_cap(): + return + seen = set() + for tid in skipped_for_backfill: + if tid in seen: + continue + seen.add(tid) + res = rm.resources[tid] + if not rm.can_be_submitted_at_all(res): + continue + if rm.fits_backfill(res): + res.nice_value = rm.nice_backfill + yield tid, rm.nice_backfill diff --git a/MC/workflow_runner/o2dpg_runner/tests/__init__.py b/MC/workflow_runner/o2dpg_runner/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/MC/workflow_runner/o2dpg_runner/tests/fixtures/tiny_workflow.json b/MC/workflow_runner/o2dpg_runner/tests/fixtures/tiny_workflow.json new file mode 100644 index 000000000..251cd5e04 --- /dev/null +++ b/MC/workflow_runner/o2dpg_runner/tests/fixtures/tiny_workflow.json @@ -0,0 +1,104 @@ +{ + "stages": [ + { + "name": "__global_init_task__", + "cmd": "NO-COMMAND", + "needs": [], + "cwd": "./", + "timeframe": -1, + "labels": [], + "resources": {"cpu": 1, "mem": 100, "relative_cpu": null}, + "env": {"FOO": "bar"} + }, + { + "name": "bkg", + "cmd": "echo bkg > out.dat", + "needs": [], + "cwd": "./", + "timeframe": -1, + "labels": ["SIM"], + "resources": {"cpu": 2, "mem": 500, "relative_cpu": null} + }, + { + "name": "sgnsim_1", + "cmd": "echo sgnsim_1", + "needs": ["bkg"], + "cwd": "./tf1", + "timeframe": 1, + "labels": ["SIM"], + "resources": {"cpu": 4, "mem": 2000, "relative_cpu": 1.0} + }, + { + "name": "digi_1", + "cmd": "echo digi_1", + "needs": ["sgnsim_1"], + "cwd": "./tf1", + "timeframe": 1, + "labels": ["DIGI"], + "resources": {"cpu": 2, "mem": 1000, "relative_cpu": 1.0} + }, + { + "name": "reco_1", + "cmd": "echo reco_1", + "needs": ["digi_1"], + "cwd": "./tf1", + "timeframe": 1, + "labels": ["RECO"], + "resources": {"cpu": 4, "mem": 1500, "relative_cpu": 1.0} + }, + { + "name": "qc_1", + "cmd": "echo qc_1", + "needs": ["reco_1"], + "cwd": "./tf1", + "timeframe": 1, + "labels": ["QC"], + "resources": {"cpu": 1, "mem": 500, "relative_cpu": 1.0} + }, + { + "name": "sgnsim_2", + "cmd": "echo sgnsim_2", + "needs": ["bkg"], + "cwd": "./tf2", + "timeframe": 2, + "labels": ["SIM"], + "resources": {"cpu": 4, "mem": 2000, "relative_cpu": 1.0} + }, + { + "name": "digi_2", + "cmd": "echo digi_2", + "needs": ["sgnsim_2"], + "cwd": "./tf2", + "timeframe": 2, + "labels": ["DIGI"], + "resources": {"cpu": 2, "mem": 1000, "relative_cpu": 1.0} + }, + { + "name": "reco_2", + "cmd": "echo reco_2", + "needs": ["digi_2"], + "cwd": "./tf2", + "timeframe": 2, + "labels": ["RECO"], + "resources": {"cpu": 4, "mem": 1500, "relative_cpu": 1.0} + }, + { + "name": "qc_2", + "cmd": "echo qc_2", + "needs": ["reco_2"], + "cwd": "./tf2", + "timeframe": 2, + "labels": ["QC"], + "resources": {"cpu": 1, "mem": 500, "relative_cpu": 1.0} + }, + { + "name": "aod", + "cmd": "echo aod", + "needs": ["reco_1", "reco_2"], + "cwd": "./", + "timeframe": -1, + "labels": ["AOD"], + "resources": {"cpu": 2, "mem": 800, "relative_cpu": 1.0} + } + ] +} diff --git a/MC/workflow_runner/o2dpg_runner/tests/test_cache.py b/MC/workflow_runner/o2dpg_runner/tests/test_cache.py new file mode 100644 index 000000000..3dd68cfb7 --- /dev/null +++ b/MC/workflow_runner/o2dpg_runner/tests/test_cache.py @@ -0,0 +1,119 @@ +import json +import os + +import pytest + +from o2dpg_runner.cache import ( + TaskCache, compute_fingerprint, done_path, fingerprint_path, + remove_done_flag, +) + + +def _make_task(cmd="echo hi", needs=None, env=None): + return { + "name": "t", + "cmd": cmd, + "needs": needs or [], + "env": env or {}, + } + + +def _mkdone(tmpdir, logname="t.log"): + logfile = os.path.join(str(tmpdir), logname) + open(done_path(logfile), "w").close() + return logfile + + +def test_fingerprint_stable(): + t = _make_task("cmd1", needs=["a", "b"]) + f1 = compute_fingerprint(t) + f2 = compute_fingerprint(t) + assert f1 == f2 + + +def test_fingerprint_changes_on_cmd(): + f1 = compute_fingerprint(_make_task("cmd1")) + f2 = compute_fingerprint(_make_task("cmd2")) + assert f1["cmd_hash"] != f2["cmd_hash"] + + +def test_fingerprint_needs_order_insensitive(): + f1 = compute_fingerprint(_make_task(needs=["a", "b"])) + f2 = compute_fingerprint(_make_task(needs=["b", "a"])) + assert f1["needs"] == f2["needs"] + + +def test_cache_off_skips_if_done(tmp_path): + logfile = _mkdone(tmp_path) + c = TaskCache("off") + fp = compute_fingerprint(_make_task()) + assert c.is_done(logfile, fp) is True + + +def test_cache_off_does_not_skip_without_done(tmp_path): + logfile = os.path.join(str(tmp_path), "t.log") + c = TaskCache("off") + fp = compute_fingerprint(_make_task()) + assert c.is_done(logfile, fp) is False + + +def test_cache_lenient_keeps_when_no_sidecar(tmp_path): + logfile = _mkdone(tmp_path) + c = TaskCache("lenient") + fp = compute_fingerprint(_make_task()) + assert c.is_done(logfile, fp) is True + + +def test_cache_strict_invalidates_without_sidecar(tmp_path): + logfile = _mkdone(tmp_path) + c = TaskCache("strict") + fp = compute_fingerprint(_make_task()) + assert c.is_done(logfile, fp) is False + # also cleared the _done file + assert not os.path.exists(done_path(logfile)) + + +def test_cache_lenient_invalidates_on_cmd_change(tmp_path): + logfile = _mkdone(tmp_path) + c = TaskCache("lenient") + old_fp = compute_fingerprint(_make_task("old_cmd")) + c.record(logfile, old_fp) + new_fp = compute_fingerprint(_make_task("new_cmd")) + assert c.is_done(logfile, new_fp) is False + assert not os.path.exists(done_path(logfile)) + + +def test_cache_lenient_tolerates_env_change(tmp_path, caplog): + logfile = _mkdone(tmp_path) + c = TaskCache("lenient") + old_fp = compute_fingerprint(_make_task(env={"ALICE_O2_VERSION": "v1"})) + c.record(logfile, old_fp) + new_fp = compute_fingerprint(_make_task(env={"ALICE_O2_VERSION": "v2"})) + assert c.is_done(logfile, new_fp) is True + + +def test_cache_strict_invalidates_on_env_change(tmp_path): + logfile = _mkdone(tmp_path) + c = TaskCache("strict") + old_fp = compute_fingerprint(_make_task(env={"ALICE_O2_VERSION": "v1"})) + c.record(logfile, old_fp) + new_fp = compute_fingerprint(_make_task(env={"ALICE_O2_VERSION": "v2"})) + assert c.is_done(logfile, new_fp) is False + + +def test_cache_record_off_does_nothing(tmp_path): + logfile = _mkdone(tmp_path) + c = TaskCache("off") + fp = compute_fingerprint(_make_task()) + c.record(logfile, fp) + assert not os.path.exists(fingerprint_path(logfile)) + + +def test_remove_done_flag_removes_both(tmp_path): + logfile = _mkdone(tmp_path) + fp_path = fingerprint_path(logfile) + with open(fp_path, "w") as f: + f.write("{}") + remove_done_flag(logfile) + assert not os.path.exists(done_path(logfile)) + assert not os.path.exists(fp_path) diff --git a/MC/workflow_runner/o2dpg_runner/tests/test_executor_e2e.py b/MC/workflow_runner/o2dpg_runner/tests/test_executor_e2e.py new file mode 100644 index 000000000..fc081a685 --- /dev/null +++ b/MC/workflow_runner/o2dpg_runner/tests/test_executor_e2e.py @@ -0,0 +1,216 @@ +"""End-to-end executor tests using the tiny fixture workflow. + +The commands are plain `echo` calls, so no CVMFS / O2 software needed. +We invoke the CLI's main() the same way the shell entry point would. +""" + +import json +import logging +import os +import shutil +import sys +from types import SimpleNamespace + +import pytest + +from o2dpg_runner.config import RunnerConfig +from o2dpg_runner.workflow import build_workflow, load_json +from o2dpg_runner.executor import WorkflowExecutor + +FIXTURE = os.path.join(os.path.dirname(__file__), "fixtures", "tiny_workflow.json") + + +def _make_logger(name, path): + lg = logging.getLogger(name) + lg.handlers.clear() + lg.addHandler(logging.FileHandler(path)) + lg.setLevel(logging.INFO) + lg.propagate = False + return lg + + +def _prep_workflow_in_tmp(tmp_path): + """Copy the fixture into tmp_path and wrap every cmd with a _done marker, + since the stock O2 taskwrapper isn't available in tests.""" + raw = load_json(FIXTURE) + # Patch each command so that it writes the expected _done file after success. + for t in raw["stages"]: + if t["name"] == "__global_init_task__": + continue + cwd = t.get("cwd", "./") + name = t["name"] + # emulate the taskwrapper: run the cmd, then write /.log_done + t["cmd"] = f'({t["cmd"]}) > {name}.log 2>&1 && touch {name}.log_done' + fixture_path = tmp_path / "wf.json" + fixture_path.write_text(json.dumps(raw)) + return str(fixture_path) + + +def _make_executor(tmp_path, cfg_overrides=None): + os.chdir(str(tmp_path)) + wf_path = _prep_workflow_in_tmp(tmp_path) + cfg = RunnerConfig( + workflowfile=wf_path, + cpu_limit=8, + mem_limit=16000, + maxjobs=100, + monitor_interval_cpu=0.2, + monitor_interval_mem=0.5, + ) + if cfg_overrides: + for k, v in cfg_overrides.items(): + setattr(cfg, k, v) + raw = load_json(wf_path) + wf = build_workflow(raw, cfg.target_tasks, cfg.target_labels) + action_logger = _make_logger("a", str(tmp_path / "act.log")) + metric_logger = _make_logger("m", str(tmp_path / "met.log")) + return WorkflowExecutor(cfg, wf, action_logger, metric_logger) + + +def _run(tmp_path, cfg_overrides=None): + exe = _make_executor(tmp_path, cfg_overrides) + return exe.execute(), exe.wf, tmp_path + + +class _FakeProc: + pid = 4242 + + def poll(self): + return None + + def kill(self): + pass + + +class _FakeMonitor: + """One task, one snapshot, and a tick the test advances by hand.""" + + def __init__(self): + self.tick = 1 + self.global_cpu_pct = None + self.global_mem_mb = None + self.snap = SimpleNamespace( + tid=0, name="t", t_delta_ms=1000, cpu_pct=100.0, uss_mb=1.0, + pss_mb=2.0, swap_mb=0.0, nice=0, labels=[], disc_mb=-1, + cgroup_cpu_pct=None, cgroup_mem_mb=None) + + def latest(self): + return {0: self.snap} + + +def test_a_monitor_tick_is_recorded_once_however_often_it_is_polled(tmp_path): + """The wait loop polls faster than the monitor fires. Recording the same + snapshot twice defeats the 'too few samples' guard in sample_resources().""" + exe = _make_executor(tmp_path) + exe.monitor = _FakeMonitor() + exe.process_list = [(0, _FakeProc())] + + for _ in range(5): + exe.wait_for_any([], []) + assert len(exe.rm.resources[0].time_collect) == 1 + + exe.monitor.tick = 2 + exe.monitor.snap.t_delta_ms = 2000 + exe.wait_for_any([], []) + assert exe.rm.resources[0].time_collect == [1000, 2000] + + +def test_executor_runs_all_tasks(tmp_path): + rc, wf, path = _run(tmp_path) + assert rc is False # no errors + # every task should have produced a _done file + for t in wf.stages: + done_file = path / (t.get("cwd", ".") or ".") / f"{t['name']}.log_done" + assert done_file.exists(), f"missing _done for {t['name']}" + + +def test_executor_respects_target_filter(tmp_path): + rc, wf, path = _run(tmp_path, {"target_tasks": ["qc_1"]}) + assert rc is False + # only bkg, sgnsim_1, digi_1, reco_1, qc_1 should have run + expected = {"bkg", "sgnsim_1", "digi_1", "reco_1", "qc_1"} + got = {t["name"] for t in wf.stages} + assert got == expected + + +def test_executor_skips_done_tasks_on_rerun(tmp_path): + # first run + rc1, _, _ = _run(tmp_path) + assert rc1 is False + + # second run: stages unchanged -> every task should be skipped via _done. + os.chdir(str(tmp_path)) + wf_path = str(tmp_path / "wf.json") + cfg = RunnerConfig( + workflowfile=wf_path, + cpu_limit=8, mem_limit=16000, + ) + raw = load_json(wf_path) + wf = build_workflow(raw, cfg.target_tasks, cfg.target_labels) + act = _make_logger("a2", str(tmp_path / "act2.log")) + met = _make_logger("m2", str(tmp_path / "met2.log")) + exe = WorkflowExecutor(cfg, wf, act, met) + # All tasks should appear skippable up front. + for tid in range(wf.n_tasks()): + assert exe.ok_to_skip(tid), f"{wf.id_to_name[tid]} should be skippable" + + +def test_executor_critical_path_policy(tmp_path): + rc, wf, path = _run(tmp_path, {"scheduler_policy": "critical-path"}) + assert rc is False + assert all((path / (t.get("cwd", ".") or ".") / f"{t['name']}.log_done").exists() + for t in wf.stages) + + +def test_executor_best_fit_policy(tmp_path): + rc, wf, path = _run(tmp_path, {"scheduler_policy": "best-fit"}) + assert rc is False + assert all((path / (t.get("cwd", ".") or ".") / f"{t['name']}.log_done").exists() + for t in wf.stages) + + +def test_executor_drop_should_break(tmp_path): + rc, wf, path = _run(tmp_path, {"drop_should_break": True}) + assert rc is False + + +def test_executor_produce_script(tmp_path): + os.chdir(str(tmp_path)) + wf_path = _prep_workflow_in_tmp(tmp_path) + cfg = RunnerConfig( + workflowfile=wf_path, + cpu_limit=8, mem_limit=16000, + produce_script=str(tmp_path / "run.sh"), + ) + raw = load_json(wf_path) + wf = build_workflow(raw, cfg.target_tasks, cfg.target_labels) + act = _make_logger("a3", str(tmp_path / "act3.log")) + met = _make_logger("m3", str(tmp_path / "met3.log")) + exe = WorkflowExecutor(cfg, wf, act, met) + exe.execute() + text = open(str(tmp_path / "run.sh")).read() + assert "bkg" in text + assert "aod" in text + assert "#!/usr/bin/env bash" in text + + +def test_executor_dry_run(tmp_path): + rc, wf, path = _run(tmp_path, {"dry_run": True}) + assert rc is False + # dry run should NOT create _done files + any_done = any((path / (t.get("cwd", ".") or ".") / f"{t['name']}.log_done").exists() + for t in wf.stages) + assert not any_done + + +def test_executor_cache_lenient_end_to_end(tmp_path): + # first run with lenient cache + rc1, _, _ = _run(tmp_path, {"cache_policy": "lenient"}) + assert rc1 is False + # fingerprint sidecars should exist + for t in ["bkg", "qc_1", "aod"]: + # find cwd + cwd = next(s["cwd"] for s in load_json(str(tmp_path / "wf.json"))["stages"] + if s["name"] == t) + assert os.path.exists(str(tmp_path / cwd / f"{t}.log_done")) + assert os.path.exists(str(tmp_path / cwd / f"{t}.log_done.json")) diff --git a/MC/workflow_runner/o2dpg_runner/tests/test_graph.py b/MC/workflow_runner/o2dpg_runner/tests/test_graph.py new file mode 100644 index 000000000..f0e285d51 --- /dev/null +++ b/MC/workflow_runner/o2dpg_runner/tests/test_graph.py @@ -0,0 +1,90 @@ +import pytest + +from o2dpg_runner.graph import ( + build_adjacency, kahn_topological_order, descendants, ancestors, + longest_path_length, invert_adj, root_nodes, +) + + +def test_adjacency_basic(): + # 0 -> 1 -> 2 + # \-> 3 + fwd, rev, ind = build_adjacency(4, [(0, 1), (1, 2), (1, 3)]) + assert fwd == [[1], [2, 3], [], []] + assert rev == [[], [0], [1], [1]] + assert ind == [0, 1, 1, 1] + + +def test_kahn_simple_chain(): + fwd, _, ind = build_adjacency(3, [(0, 1), (1, 2)]) + assert kahn_topological_order(3, fwd, ind) == [0, 1, 2] + + +def test_kahn_diamond(): + # 0 -> 1, 0 -> 2, 1 -> 3, 2 -> 3 + fwd, _, ind = build_adjacency(4, [(0, 1), (0, 2), (1, 3), (2, 3)]) + order = kahn_topological_order(4, fwd, ind) + assert order[0] == 0 + assert order[-1] == 3 + assert set(order[1:3]) == {1, 2} + + +def test_kahn_detects_cycle(): + fwd, _, ind = build_adjacency(3, [(0, 1), (1, 2), (2, 0)]) + with pytest.raises(ValueError): + kahn_topological_order(3, fwd, ind) + + +def test_descendants_simple(): + # 0 -> 1 -> 2 + fwd, _, _ = build_adjacency(3, [(0, 1), (1, 2)]) + assert descendants(fwd, 0) == {1, 2} + assert descendants(fwd, 1) == {2} + assert descendants(fwd, 2) == set() + + +def test_descendants_diamond_memoized(): + fwd, _, _ = build_adjacency(4, [(0, 1), (0, 2), (1, 3), (2, 3)]) + cache = {} + assert descendants(fwd, 0, cache) == {1, 2, 3} + # call again - cache hit should return same result + assert descendants(fwd, 0, cache) == {1, 2, 3} + assert descendants(fwd, 1, cache) == {3} + + +def test_ancestors(): + fwd, rev, _ = build_adjacency(4, [(0, 1), (0, 2), (1, 3), (2, 3)]) + assert ancestors(rev, 3) == {0, 1, 2} + assert ancestors(rev, 0) == set() + + +def test_invert_adj(): + fwd, _, _ = build_adjacency(4, [(0, 1), (0, 2), (1, 3)]) + rev = invert_adj(fwd) + assert rev == [[], [0], [0], [1]] + + +def test_root_nodes(): + _, _, ind = build_adjacency(4, [(0, 2), (1, 2), (2, 3)]) + assert root_nodes(ind) == [0, 1] + + +def test_longest_path(): + # 0(w=1) -> 1(w=2) -> 3(w=4) + # 0(w=1) -> 2(w=10) -> 3(w=4) + fwd, _, ind = build_adjacency(4, [(0, 1), (0, 2), (1, 3), (2, 3)]) + topo = kahn_topological_order(4, fwd, ind) + lp = longest_path_length(fwd, topo, [1.0, 2.0, 10.0, 4.0]) + # longest from 0: 0 -> 2 -> 3 = 1+10+4 = 15 + assert lp[0] == 15.0 + assert lp[3] == 4.0 + assert lp[2] == 14.0 + + +def test_descendants_deep_chain_no_recursion_limit(): + # Chain of 2000 nodes -- would blow prototype's recursion limit + N = 2000 + edges = [(i, i + 1) for i in range(N - 1)] + fwd, _, _ = build_adjacency(N, edges) + result = descendants(fwd, 0) + assert len(result) == N - 1 diff --git a/MC/workflow_runner/o2dpg_runner/tests/test_resources.py b/MC/workflow_runner/o2dpg_runner/tests/test_resources.py new file mode 100644 index 000000000..83c58fafc --- /dev/null +++ b/MC/workflow_runner/o2dpg_runner/tests/test_resources.py @@ -0,0 +1,164 @@ +import pytest + +from o2dpg_runner.resources import ( + ResourceManager, ResourceLimitExceeded, Semaphore, TaskResources, + ResourceBoundaries, +) + + +def _make_rm(cpu=8.0, mem=16000.0, **kw): + return ResourceManager(cpu_limit=cpu, mem_limit=mem, **kw) + + +def test_add_task_within_limits(): + rm = _make_rm() + rm.add_task("a", None, cpu=2, cpu_relative=1, mem=1000) + assert len(rm.resources) == 1 + assert rm.resources[0].name == "a" + + +def test_add_task_exceeds_limits_raises(): + rm = _make_rm(cpu=4) + with pytest.raises(ResourceLimitExceeded): + rm.add_task("big", None, cpu=8, cpu_relative=1, mem=100) + + +def test_is_within_limits_checks_mem_correctly(): + """Regression: prototype compared CPU to mem_limit by mistake.""" + rm = _make_rm(cpu=100, mem=1000, optimistic_resources=True) + # CPU under limit, MEM over limit -> should be caught + res = rm.add_task("t", None, cpu=1, cpu_relative=1, mem=5000) + # After add_task's limit_resources, mem is capped + assert res.mem_assigned == 1000 + + +def test_book_and_unbook_default(): + rm = _make_rm() + rm.add_task("a", None, 2, 1, 1000) + res = rm.resources[0] + res.nice_value = rm.nice_default + rm.book(0, rm.nice_default) + assert rm.n_procs == 1 + assert rm.cpu_booked == 2 + assert rm.mem_booked == 1000 + rm.unbook(0) + assert rm.n_procs == 0 + assert rm.cpu_booked == 0 + assert rm.mem_booked == 0 + + +def test_book_and_unbook_backfill(): + rm = _make_rm() + rm.add_task("a", None, 2, 1, 1000) + res = rm.resources[0] + res.nice_value = rm.nice_backfill + rm.book(0, rm.nice_backfill) + assert rm.n_procs_backfill == 1 + assert rm.n_procs == 0 + rm.unbook(0) + assert rm.n_procs_backfill == 0 + + +def test_fits_default(): + rm = _make_rm(cpu=4, mem=4000) + rm.add_task("a", None, 2, 1, 1000) + rm.add_task("b", None, 3, 1, 1000) + rm.resources[0].nice_value = rm.nice_default + rm.book(0, rm.nice_default) + # now 2 cpu / 1000 mem booked; b (3 cpu) doesn't fit + assert not rm.fits_default(rm.resources[1]) + + +def test_fits_backfill_rejects_too_big(): + rm = _make_rm(cpu=4, mem=4000) + rm.add_task("big", None, 4, 1, 1000) # equals cpu_limit -> 100% > 90% + rm.resources[0].limit_resources() # ensure assigned=cpu_limit + rm.resources[0].cpu_assigned = 4 # at 90% threshold + assert not rm.fits_backfill(rm.resources[0]) + + +def test_semaphore_blocks_duplicate(): + rm = _make_rm() + rm.add_task("a", None, 1, 1, 100, semaphore_string="S") + rm.add_task("b", None, 1, 1, 100, semaphore_string="S") + assert rm.resources[0].semaphore is rm.resources[1].semaphore + rm.resources[0].nice_value = rm.nice_default + rm.book(0, rm.nice_default) + assert not rm.can_be_submitted_at_all(rm.resources[1]) + rm.unbook(0) + assert rm.can_be_submitted_at_all(rm.resources[1]) + + +def test_related_tasks_share_bucket(): + rm = _make_rm() + rm.add_task("sgnsim_1", "sgnsim", 2, 1, 1000) + rm.add_task("sgnsim_2", "sgnsim", 2, 1, 1000) + assert rm.resources[0].related_tasks is rm.resources[1].related_tasks + + +def test_dynamic_sampling_propagates(): + rm = ResourceManager(cpu_limit=8, mem_limit=16000, dynamic_resources=True) + rm.add_task("t_1", "t", 2, 1, 1000) + rm.add_task("t_2", "t", 2, 1, 1000) + # Feed monitor samples for t_1 + for i in range(5): + rm.add_monitored(0, i * 1.0, cpu_fraction=1.5, mem_mb=800.0) + rm.resources[0].nice_value = rm.nice_default + rm.book(0, rm.nice_default) + rm.unbook(0) # this triggers sampling + propagation + # t_2 should now have an adjusted assignment based on observed sample + assert rm.resources[1].cpu_assigned > 0 + # The sampled CPU was ~1.5; t_2's cpu_assigned should reflect that ballpark + assert rm.resources[1].mem_assigned == pytest.approx(800.0, abs=1) + + +def test_a_zero_cpu_sample_leaves_siblings_alone(): + """A task seen only through its psutil baseline reads 0.0 cores. Passing + that on would make every sibling look free and admit them all at once.""" + manager = ResourceManager(cpu_limit=8, mem_limit=16000, dynamic_resources=True) + manager.add_task("t_1", "t", 2, 1, 1000) + manager.add_task("t_2", "t", 2, 1, 1000) + for i in range(5): + manager.add_monitored(0, i * 1.0, cpu_fraction=0.0, mem_mb=800.0) + manager.resources[0].nice_value = manager.nice_default + manager.book(0, manager.nice_default) + manager.unbook(0) + assert manager.resources[1].cpu_assigned == pytest.approx(2.0) + + +def test_at_proc_cap(): + rm = _make_rm() + rm.procs_parallel_max = 2 + assert not rm.at_proc_cap() + for i in range(2): + rm.add_task(f"t{i}", None, 1, 1, 100) + rm.resources[i].nice_value = rm.nice_default + rm.book(i, rm.nice_default) + assert rm.at_proc_cap() + + +def test_default_gate_counts_memory_booked_in_backfill(): + """Memory held by a backfill task must count against the default-tier gate.""" + rm = _make_rm(cpu=8, mem=16000) + rm.add_task("a", None, 1, 1, 11000) + rm.add_task("b", None, 1, 1, 11000) + rm.resources[0].nice_value = rm.nice_backfill + rm.book(0, rm.nice_backfill) + assert rm.mem_booked == 0 and rm.mem_booked_backfill == 11000 + assert not rm.fits_default(rm.resources[1]) + assert rm.mem_free_default() == 5000 + + +def test_backfill_mem_factor_default_does_not_overcommit_memory(): + rm = _make_rm(cpu=8, mem=16000) + rm.add_task("a", None, 1, 1, 9000) + rm.add_task("b", None, 1, 1, 9000) + rm.resources[0].nice_value = rm.nice_default + rm.book(0, rm.nice_default) + assert not rm.fits_backfill(rm.resources[1]) + rm15 = _make_rm(cpu=8, mem=16000, backfill_mem_factor=1.5) + rm15.add_task("a", None, 1, 1, 9000) + rm15.add_task("b", None, 1, 1, 9000) + rm15.resources[0].nice_value = rm15.nice_default + rm15.book(0, rm15.nice_default) + assert rm15.fits_backfill(rm15.resources[1]) diff --git a/MC/workflow_runner/o2dpg_runner/tests/test_scheduler.py b/MC/workflow_runner/o2dpg_runner/tests/test_scheduler.py new file mode 100644 index 000000000..720f87ae4 --- /dev/null +++ b/MC/workflow_runner/o2dpg_runner/tests/test_scheduler.py @@ -0,0 +1,179 @@ +import pytest + +from o2dpg_runner.resources import ResourceManager +from o2dpg_runner.scheduler import ( + TimeframeFirstPolicy, CriticalPathPolicy, BestFitBackfillPolicy, get_policy, +) +from o2dpg_runner.scheduler.base import SchedulerState + + +def _setup(n=4, cpu_limit=8.0, mem_limit=16000.0): + """Create an RM with n tasks: all 2 cpu / 1000 mem.""" + rm = ResourceManager(cpu_limit=cpu_limit, mem_limit=mem_limit, n_backfill_max=1) + for i in range(n): + rm.add_task(f"t{i}", None, cpu=2, cpu_relative=1, mem=1000) + return rm + + +def _make_state(n, tf=None, desc=None, cp=None): + tf = tf if tf is not None else [0] * n + desc = desc if desc is not None else [0] * n + cp = cp if cp is not None else [1.0] * n + return SchedulerState( + timeframe_of=tf, + descendants_count=desc, + critical_path=cp, + task_cpu=[2.0] * n, + task_mem=[1000.0] * n, + timeframe_weight=[(tf[i], desc[i]) for i in range(n)], + ) + + +def test_timeframe_policy_order(): + s = _make_state(4, tf=[1, 0, 1, 0], desc=[5, 2, 10, 1]) + p = TimeframeFirstPolicy() + # timeframe 0 first, then within tf more descendants first + order = p.order([0, 1, 2, 3], s) + # tf 0: tasks 1 (desc=2) and 3 (desc=1) -> 1 before 3 + # tf 1: tasks 0 (desc=5) and 2 (desc=10) -> 2 before 0 + assert order == [1, 3, 2, 0] + + +def test_timeframe_policy_submit_default_fits_all(): + rm = _setup(n=4) # 4 * 2cpu = 8 = cpu_limit + s = _make_state(4) + p = TimeframeFirstPolicy() + picked = _drain(p.pick_submittable([0, 1, 2, 3], rm), rm) + assert len(picked) == 4 + assert all(nice == rm.nice_default for _, nice in picked) + + +def _drain(picks, rm): + """Drain a pick_submittable generator, booking each pick as the + executor would (so subsequent fits_default checks see it).""" + out = [] + for tid, nice in picks: + rm.book(tid, nice) + out.append((tid, nice)) + return out + + +def test_timeframe_policy_should_break_legacy(): + """Prototype behavior: the first non-fitting task breaks the default + pass entirely; following tasks only get a shot in the backfill pass + (which itself may be blocked once an earlier task consumed its budget).""" + rm = ResourceManager(cpu_limit=4, mem_limit=16000, n_backfill_max=4) + rm.add_task("small1", None, cpu=2, cpu_relative=1, mem=100) + rm.add_task("big", None, cpu=3, cpu_relative=1, mem=100) + rm.add_task("small2", None, cpu=2, cpu_relative=1, mem=100) + p = TimeframeFirstPolicy(drop_should_break=False) + picks = _drain(p.pick_submittable([0, 1, 2], rm), rm) + nice_for = {tid: n for tid, n in picks} + # small1 fits default + assert nice_for[0] == rm.nice_default + # small2, which WOULD have fit default (2+2=4), did not go default -- + # that's the legacy bug/feature. It's either backfill or not scheduled. + assert nice_for.get(2) != rm.nice_default + + +def test_timeframe_policy_drop_unblocks_small2_at_default(tmp_path=None): + """Contrast: with drop_should_break=True, small2 gets default priority.""" + rm = ResourceManager(cpu_limit=4, mem_limit=16000, n_backfill_max=4) + rm.add_task("small1", None, cpu=2, cpu_relative=1, mem=100) + rm.add_task("big", None, cpu=3, cpu_relative=1, mem=100) + rm.add_task("small2", None, cpu=2, cpu_relative=1, mem=100) + p = TimeframeFirstPolicy(drop_should_break=True) + picks = _drain(p.pick_submittable([0, 1, 2], rm), rm) + nice_for = {tid: n for tid, n in picks} + # small1 and small2 both at default -- the poster's headline result + assert nice_for[0] == rm.nice_default + assert nice_for[2] == rm.nice_default + + +def test_timeframe_policy_drop_should_break(): + """With drop_should_break=True, light tasks can slip past heavy ones + in the default pass.""" + rm = ResourceManager(cpu_limit=4, mem_limit=16000, n_backfill_max=4) + rm.add_task("small1", None, cpu=2, cpu_relative=1, mem=100) + rm.add_task("big", None, cpu=3, cpu_relative=1, mem=100) + rm.add_task("small2", None, cpu=2, cpu_relative=1, mem=100) + s = _make_state(3) + p = TimeframeFirstPolicy(drop_should_break=True) + picks = _drain(p.pick_submittable([0, 1, 2], rm), rm) + nice_for = {tid: n for tid, n in picks} + # small1 fits default; big doesn't (2+3=5>4); small2 fits default (2+2=4) + assert nice_for.get(0) == rm.nice_default + assert nice_for.get(2) == rm.nice_default + + +def test_critical_path_policy_order(): + # node 2 has the highest critical path -> should come first + s = _make_state(3, cp=[5.0, 3.0, 10.0]) + p = CriticalPathPolicy() + assert p.order([0, 1, 2], s) == [2, 0, 1] + + +def test_best_fit_policy_fills_budget(): + # cpu_limit=10; tasks: 3,3,4 -> best-fit should pack all three + rm = ResourceManager(cpu_limit=10, mem_limit=16000, n_backfill_max=1) + rm.add_task("a", None, cpu=3, cpu_relative=1, mem=100) + rm.add_task("b", None, cpu=3, cpu_relative=1, mem=100) + rm.add_task("c", None, cpu=4, cpu_relative=1, mem=100) + s = _make_state(3, cp=[1.0, 1.0, 1.0], desc=[0, 0, 0]) + p = BestFitBackfillPolicy() + ordered = p.order([0, 1, 2], s) + picks = _drain(p.pick_submittable(ordered, rm), rm) + assert len(picks) == 3 + assert all(n == rm.nice_default for _, n in picks) + # verify that with overfilled budget, best-fit DOES stop + rm2 = ResourceManager(cpu_limit=5, mem_limit=16000, n_backfill_max=0) + rm2.add_task("a", None, cpu=3, cpu_relative=1, mem=100) + rm2.add_task("b", None, cpu=3, cpu_relative=1, mem=100) + ordered2 = p.order([0, 1], _make_state(2)) + picks2 = _drain(p.pick_submittable(ordered2, rm2), rm2) + assert len(picks2) == 1 # only one fits the cpu=5 budget + + +def test_get_policy_names(): + assert isinstance(get_policy("timeframe"), TimeframeFirstPolicy) + assert isinstance(get_policy("critical-path"), CriticalPathPolicy) + assert isinstance(get_policy("best-fit"), BestFitBackfillPolicy) + with pytest.raises(ValueError): + get_policy("nonsense") + + +def test_semaphore_prevents_concurrent_submit(): + rm = ResourceManager(cpu_limit=8, mem_limit=16000, n_backfill_max=1) + rm.add_task("a", None, 1, 1, 100, semaphore_string="S") + rm.add_task("b", None, 1, 1, 100, semaphore_string="S") + s = _make_state(2) + # book a directly + rm.resources[0].nice_value = rm.nice_default + rm.book(0, rm.nice_default) + p = TimeframeFirstPolicy() + picks = list(p.pick_submittable([1], rm)) + assert picks == [] + + +def test_n_backfill_cap(): + rm = ResourceManager(cpu_limit=2, mem_limit=16000, n_backfill_max=1) + rm.add_task("a", None, 1, 1, 100) + rm.add_task("b", None, 1, 1, 100) + rm.add_task("c", None, 1, 1, 100) + # fill default with a+b + for i in (0, 1): + rm.resources[i].nice_value = rm.nice_default + rm.book(i, rm.nice_default) + assert rm.cpu_booked == 2 + # c doesn't fit default; backfill allowed? n_backfill_max=1 + p = TimeframeFirstPolicy() + picks = list(p.pick_submittable([2], rm)) + # one backfill slot, c gets it + assert len(picks) == 1 + assert picks[0][1] == rm.nice_backfill + rm.resources[2].nice_value = rm.nice_backfill + rm.book(2, rm.nice_backfill) + # next candidate tries to backfill but cap reached + rm.add_task("d", None, 1, 1, 100) + picks2 = list(p.pick_submittable([3], rm)) + assert picks2 == [] diff --git a/MC/workflow_runner/o2dpg_runner/tests/test_simulator.py b/MC/workflow_runner/o2dpg_runner/tests/test_simulator.py new file mode 100644 index 000000000..727bb394f --- /dev/null +++ b/MC/workflow_runner/o2dpg_runner/tests/test_simulator.py @@ -0,0 +1,131 @@ +import os +import sys + +import pytest + +from o2dpg_runner.workflow import build_workflow + +BIN_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..")) +if BIN_DIR not in sys.path: + sys.path.insert(0, BIN_DIR) + +import o2dpg_schedule_simulator as sim + + +def _wf(stages): + return build_workflow({"stages": stages}, ["*"], []) + + +def _task(name, cpu, mem=100.0, walltime=1.0, needs=None, timeframe=1): + return { + "name": name, + "needs": list(needs or []), + "cmd": "true", + "cwd": ".", + "timeframe": timeframe, + "labels": [], + "resources": {"cpu": cpu, "mem": mem, "walltime": walltime}, + } + + +def test_simulator_uses_amdahl_override_in_critical_path_state(): + wf = _wf([ + _task("a", cpu=1, walltime=1.0), + _task("b", cpu=1, walltime=5.0), + ]) + model = sim.AmdahlModel( + t_serial=20.0, + t_parallel_tot=0.0, + n_ref=1, + cpu_mean_ref=1.0, + min_workers=1, + max_workers=1, + ) + result = sim.simulate( + wf, + "critical-path", + cpu_limit=8.0, + mem_limit=1000.0, + amdahl_models={"a": model}, + worker_assignment={"a": 1}, + maxjobs=1, + ) + assert result.tasks[0].name == "a" + + +def test_simulator_keeps_tid_mapping_when_task_exceeds_limits(): + wf = _wf([ + _task("too_big", cpu=20, walltime=3.0), + _task("ok", cpu=1, walltime=2.0), + ]) + result = sim.simulate( + wf, + "timeframe", + cpu_limit=4.0, + mem_limit=1000.0, + maxjobs=1, + ) + assert [t.name for t in result.tasks] == ["ok"] + assert result.deadlocked_tids == [0] + + +def test_simulator_backfill_slowdown_marks_and_slows_backfill_tasks(): + wf = _wf([ + _task("small1", cpu=2, walltime=3.0), + _task("big", cpu=3, walltime=8.0), + _task("small2", cpu=2, walltime=3.0), + ]) + result = sim.simulate( + wf, + "timeframe", + cpu_limit=4.0, + mem_limit=1000.0, + backfill_model="slowdown", + n_backfill=1, + backfill_slowdown_factor=1.25, + ) + by_name = {t.name: t for t in result.tasks} + assert "big" in by_name + assert by_name["big"].start == pytest.approx(0.0) + compute_wt = 8.0 * 1.25 + assert by_name["big"].walltime == pytest.approx(compute_wt + 0.1) + # cpu is the average over the whole slot, so the idle overhead dilutes it; + # cpu * walltime is the CPU-seconds actually spent + assert by_name["big"].cpu == pytest.approx( + 3.0 / 1.25 * compute_wt / (compute_wt + 0.1)) + assert by_name["big"].cpu_booked == pytest.approx(3.0) + assert result.cpu_utilization(4.0) <= 1.0 + + +def test_simulator_holefill_uses_only_foreground_hole(): + wf = _wf([ + _task("fg", cpu=2, walltime=4.0), + _task("bf", cpu=3, walltime=2.0), + ]) + result = sim.simulate( + wf, + "timeframe", + cpu_limit=4.0, + mem_limit=1000.0, + backfill_model="holefill", + n_backfill=1, + task_overhead=0.0, + ) + by_name = {t.name: t for t in result.tasks} + assert by_name["fg"].walltime == pytest.approx(4.0) + assert by_name["bf"].walltime == pytest.approx(3.0) + assert by_name["bf"].cpu == pytest.approx(2.0) + assert by_name["bf"].cpu_booked == pytest.approx(3.0) + assert result.cpu_utilization(4.0) == pytest.approx(0.875) + + +def test_amdahl_model_rejects_negative_components(): + with pytest.raises(ValueError): + sim.AmdahlModel.from_dict( + { + "t_serial": -1.0, + "t_parallel_tot": 5.0, + "n_ref": 4, + "cpu_mean_ref": 4.0, + } + ) diff --git a/MC/workflow_runner/o2dpg_runner/tests/test_workflow.py b/MC/workflow_runner/o2dpg_runner/tests/test_workflow.py new file mode 100644 index 000000000..db912673b --- /dev/null +++ b/MC/workflow_runner/o2dpg_runner/tests/test_workflow.py @@ -0,0 +1,117 @@ +import json +import os + +import pytest + +from o2dpg_runner.workflow import ( + load_json, extract_global_init, filter_workflow, build_workflow, + update_resource_estimates, +) + +FIXTURE = os.path.join(os.path.dirname(__file__), "fixtures", "tiny_workflow.json") + + +def _load(): + return load_json(FIXTURE) + + +def test_extract_global_init(): + spec = _load() + env, cmd = extract_global_init(spec) + assert env == {"FOO": "bar"} + assert cmd is None # cmd was "NO-COMMAND" + # __global_init_task__ removed from stages + assert spec["stages"][0]["name"] != "__global_init_task__" + assert spec["stages"][0]["name"] == "bkg" + + +def test_filter_workflow_all(): + spec = _load() + extract_global_init(spec) + filtered, targets = filter_workflow(spec, ["*"], []) + assert targets == [] # "*" means no target list + assert len(filtered["stages"]) == len(spec["stages"]) + + +def test_filter_workflow_by_target(): + spec = _load() + extract_global_init(spec) + filtered, targets = filter_workflow(spec, ["digi_1"], []) + names = [t["name"] for t in filtered["stages"]] + # digi_1 + its needs bkg, sgnsim_1 + assert "digi_1" in names + assert "sgnsim_1" in names + assert "bkg" in names + assert "reco_1" not in names + assert "aod" not in names + assert targets == ["digi_1"] + + +def test_filter_workflow_by_label(): + spec = _load() + extract_global_init(spec) + filtered, targets = filter_workflow(spec, ["*"], ["QC"]) + # "*" with label filter narrows, and pulls in all deps + names = [t["name"] for t in filtered["stages"]] + assert "qc_1" in names + assert "qc_2" in names + # deps pulled in + for need in ("reco_1", "reco_2", "digi_1", "digi_2", "sgnsim_1", "sgnsim_2", "bkg"): + assert need in names + + +def test_filter_workflow_regex_target(): + spec = _load() + extract_global_init(spec) + filtered, targets = filter_workflow(spec, ["^qc_.*"], []) + names = [t["name"] for t in filtered["stages"]] + assert set(targets) == {"qc_1", "qc_2"} + assert "aod" not in names + + +def test_filter_does_not_alias_input(): + """Regression: the prototype did `transformedworkflowspec = workflowspec` + and then mutated .stages, aliasing the caller's dict. Ensure we don't.""" + spec = _load() + extract_global_init(spec) + original_len = len(spec["stages"]) + filtered, _ = filter_workflow(spec, ["qc_1"], []) + # filtered should be smaller, spec unchanged + assert len(filtered["stages"]) < original_len + assert len(spec["stages"]) == original_len + + +def test_build_workflow_end_to_end(): + raw = _load() + wf = build_workflow(raw, ["*"], []) + # 10 tasks after removing global init + assert wf.n_tasks() == 10 + # indegrees: bkg has 0; aod has 2 (reco_1, reco_2) + bkg_tid = wf.tid("bkg") + aod_tid = wf.tid("aod") + assert wf.indegree[bkg_tid] == 0 + assert wf.indegree[aod_tid] == 2 + # timeframes: -1, 1, 2 + assert wf.timeframes == {-1, 1, 2} + + +def test_update_resource_estimates(tmp_path): + raw = _load() + wf = build_workflow(raw, ["*"], []) + # synthesize a learned-estimates JSON keyed by "global" task name + est = { + "sgnsim": {"pss": {"max": 3000}, "cpu": {"mean": 3.5}}, + "digi": {"pss": {"max": 1200}, "cpu": {"mean": 1.8}}, + } + p = tmp_path / "res.json" + p.write_text(json.dumps(est)) + update_resource_estimates(wf, str(p)) + + for name in ("sgnsim_1", "sgnsim_2"): + t = wf.stages[wf.tid(name)] + assert t["resources"]["mem"] == 3000 + assert t["resources"]["cpu"] == 3.5 + for name in ("digi_1", "digi_2"): + t = wf.stages[wf.tid(name)] + assert t["resources"]["mem"] == 1200 + assert t["resources"]["cpu"] == 1.8 diff --git a/MC/workflow_runner/o2dpg_runner/workflow.py b/MC/workflow_runner/o2dpg_runner/workflow.py new file mode 100644 index 000000000..7c2322f8d --- /dev/null +++ b/MC/workflow_runner/o2dpg_runner/workflow.py @@ -0,0 +1,411 @@ +"""Workflow loading, filtering, and DAG construction. + +The workflow JSON schema is unchanged from the original. Key fields per +stage: + - name: str + - needs: list[str] (names of upstream stages) + - cmd: str + - cwd: str + - timeframe: int (-1 for global stages) + - labels: list[str] + - resources: {cpu, mem, relative_cpu} + - semaphore: str (optional) + - retry_count: int (optional) + - alternative_alienv_package: str (optional) + - env: dict (optional) + +One stage may be the synthetic ``__global_init_task__`` at index 0, +holding global env and an optional init cmd; it is stripped from the +DAG during loading. +""" + +from __future__ import annotations + +import copy +import json +import logging +import math +import re +from dataclasses import dataclass, field +from typing import Any, Dict, List, Optional, Set, Tuple + +from .graph import build_adjacency + +log = logging.getLogger(__name__) + + +@dataclass +class Workflow: + """In-memory representation of a workflow after filtering. + + Tasks are stored as a list of dicts (the raw JSON objects) and indexed + by integer ``tid``. The forward/reverse adjacency and derived quantities + are computed once at construction time. + """ + stages: List[Dict[str, Any]] + global_env: Dict[str, str] = field(default_factory=dict) + global_init_cmd: Optional[str] = None + full_target_names: List[str] = field(default_factory=list) + + # Derived + name_to_id: Dict[str, int] = field(default_factory=dict) + id_to_name: List[str] = field(default_factory=list) + forward_adj: List[List[int]] = field(default_factory=list) + reverse_adj: List[List[int]] = field(default_factory=list) + indegree: List[int] = field(default_factory=list) + timeframes: Set[int] = field(default_factory=set) + + def __post_init__(self): + self._rebuild_indices() + + def _rebuild_indices(self): + self.name_to_id = {s["name"]: i for i, s in enumerate(self.stages)} + self.id_to_name = [s["name"] for s in self.stages] + edges: List[Tuple[int, int]] = [] + for i, s in enumerate(self.stages): + for n in s.get("needs", []): + if n in self.name_to_id: + edges.append((self.name_to_id[n], i)) + self.forward_adj, self.reverse_adj, self.indegree = build_adjacency( + len(self.stages), edges + ) + self.timeframes = {s.get("timeframe", -1) for s in self.stages} + + def tid(self, name: str) -> int: + return self.name_to_id[name] + + def name(self, tid: int) -> str: + return self.id_to_name[tid] + + def n_tasks(self) -> int: + return len(self.stages) + + +def load_json(path: str) -> Dict[str, Any]: + with open(path) as fp: + return json.load(fp) + + +def extract_global_init(raw_spec: Dict[str, Any]) -> Tuple[Dict[str, str], Optional[str]]: + """Pull out the synthetic __global_init_task__ if present. + + Mutates raw_spec['stages'] in place (removes the init stage). + """ + env: Dict[str, str] = {} + init_cmd: Optional[str] = None + stages = raw_spec.get("stages", []) + if stages and stages[0].get("name") == "__global_init_task__": + init = stages[0] + env_in = init.get("env") + if env_in: + env = {k: str(v) for k, v in env_in.items()} + cmd = init.get("cmd") + if cmd and cmd != "NO-COMMAND": + init_cmd = cmd + del stages[0] + return env, init_cmd + + +def filter_workflow( + raw_spec: Dict[str, Any], + targets: List[str], + target_labels: List[str], +) -> Tuple[Dict[str, Any], List[str]]: + """Filter the raw spec down to tasks matching target selectors. + + Returns (new_spec, full_target_names). When no filter is requested, + returns (raw_spec, []). The returned spec is always a fresh top-level + dict (no aliasing bug like in the prototype), but the per-stage dicts + are shared. + """ + stages = raw_spec.get("stages", []) + if not targets: + return {**raw_spec, "stages": list(stages)}, [] + if not target_labels and len(targets) == 1 and targets[0] == "*": + return {**raw_spec, "stages": list(stages)}, [] + + name_to_idx = {t["name"]: i for i, t in enumerate(stages)} + + def task_matches(name: str) -> bool: + for f in targets: + if f == "*": + return True + if re.match(f, name) is not None: + return True + return False + + def task_matches_labels(t: Dict[str, Any]) -> bool: + if not target_labels: + return True + for lbl in t.get("labels", []): + if lbl in target_labels: + return True + return False + + # Memoized canBeDone using iterative traversal. + ok_cache: Dict[str, bool] = {} + + def can_be_done(name: str) -> bool: + if name in ok_cache: + return ok_cache[name] + idx = name_to_idx.get(name) + if idx is None: + ok_cache[name] = False + return False + # iterative post-order DFS + order: List[str] = [] + seen: Set[str] = {name} + stack: List[Tuple[str, int]] = [(name, 0)] + while stack: + cur, ci = stack[-1] + needs = stages[name_to_idx[cur]].get("needs", []) if cur in name_to_idx else [] + if ci < len(needs): + stack[-1] = (cur, ci + 1) + child = needs[ci] + if child not in seen and child not in ok_cache: + if child not in name_to_idx: + ok_cache[child] = False + else: + seen.add(child) + stack.append((child, 0)) + else: + stack.pop() + order.append(cur) + for cur in order: + if cur in ok_cache: + continue + idx2 = name_to_idx.get(cur) + if idx2 is None: + ok_cache[cur] = False + continue + ok = all(ok_cache.get(r, False) for r in stages[idx2].get("needs", [])) + ok_cache[cur] = ok + if not ok: + log.info("Disabling target %s due to unsatisfied requirements", cur) + return ok_cache[name] + + full_target_list = [ + t for t in stages + if task_matches(t["name"]) and task_matches_labels(t) and can_be_done(t["name"]) + ] + full_target_names = [t["name"] for t in full_target_list] + + # Collect all upstream requirements (iterative, deduped). + needed: Set[str] = set(full_target_names) + stack2 = list(full_target_names) + while stack2: + cur = stack2.pop() + idx = name_to_idx.get(cur) + if idx is None: + continue + for r in stages[idx].get("needs", []): + if r not in needed: + needed.add(r) + stack2.append(r) + + new_stages = [t for t in stages if t["name"] in needed] + new_spec = {**raw_spec, "stages": new_stages} + return new_spec, full_target_names + + +def build_workflow( + raw_spec: Dict[str, Any], + targets: List[str], + target_labels: List[str], +) -> Workflow: + """End-to-end: strip global-init, filter, build DAG.""" + # Operate on a shallow copy at top level so we don't mutate caller's dict. + spec = {**raw_spec, "stages": list(raw_spec.get("stages", []))} + # extract_global_init mutates spec['stages'] + env, init_cmd = extract_global_init(spec) + filtered, target_names = filter_workflow(spec, targets, target_labels) + wf = Workflow( + stages=filtered["stages"], + global_env=env, + global_init_cmd=init_cmd, + full_target_names=target_names, + ) + return wf + + +def update_resource_estimates( + workflow: Workflow, + resource_json_path: str, + logger=None, +) -> None: + """Apply learned resource estimates from a JSON file. + + The JSON is produced by o2dpg_sim_metrics.py json-stat and is keyed on + the "global" task name (i.e. with the _ suffix stripped). + + MEM is taken from pss.max (peak proportional set size). + CPU is taken from cpu.mean (average cores used during the task). + + Note on relative_cpu: the workflow JSON carries a relative_cpu field + that historically scaled a "max" CPU estimate down to an "expected" + usage. When injecting *measured* cpu.mean values that scaling must NOT + be applied again — the measurement already reflects actual usage. + relative_cpu remains untouched and continues to be used by the dynamic- + resources sampler (resources.py) for sibling reassignment, which is + correct behaviour: the sampler scales a freshly-observed aggregate back + to an expected per-task assignment. + """ + _log = logger if logger is not None else log + _log.info("Applying learned resource estimates from: %s", resource_json_path) + + with open(resource_json_path) as fp: + resource_dict = json.load(fp) + + # Remove the metadata key so task lookup doesn't match it. + resource_dict.pop("count", None) + + n_stages = len(workflow.stages) + n_updated = 0 + missing_base_names: set = set() + + for task in workflow.stages: + tf = task.get("timeframe", -1) + name = task["name"] + global_name = "_".join(name.split("_")[:-1]) if tf >= 1 else name + + if global_name not in resource_dict: + missing_base_names.add(global_name) + continue + + new_res = resource_dict[global_name] + task_updated = False + + walltime = new_res.get("lifetime", {}).get("mean") + if walltime is not None: + # Store even when walltime=0 (sub-10ms tasks that GNU time rounds + # to zero). The simulator clamps to a 1ms minimum so zero is + # handled gracefully; the fallback (cpu * factor) is always worse. + task["resources"]["walltime"] = float(walltime) + _log.info(" WALLTIME %-40s %.3f s", name, float(walltime)) + task_updated = True + + new_mem = new_res.get("pss", {}).get("max") + if new_mem is not None: + old_mem = task["resources"]["mem"] + task["resources"]["mem"] = new_mem + _log.info(" MEM %-40s %.1f MB -> %.1f MB", name, float(old_mem), new_mem) + task_updated = True + + new_cpu = new_res.get("cpu", {}).get("mean") + if new_cpu is not None: + old_cpu = task["resources"]["cpu"] + uses_dynamic_workers = "O2DPG_DYNAMIC_NWORKER_OVERWRITE" in task.get("cmd", "") + + if uses_dynamic_workers: + # Round cpu.mean to the nearest integer worker count and use + # that value for BOTH the scheduler's cpu booking and the + # actual NWORKERS setting. This keeps them consistent: the + # task will run with n_workers processes each using ~1 core, + # so total cpu ≈ n_workers = what we book. + n_workers = max(1, round(new_cpu)) + task["resources"]["cpu"] = float(n_workers) + if not isinstance(task.get("env"), dict): + task["env"] = {} + task["env"]["O2DPG_DYNAMIC_NWORKER_OVERWRITE"] = str(n_workers) + _log.info( + " CPU+NWORKERS %-36s cpu.mean=%.2f -> %d workers, %.0f cores booked", + name, new_cpu, n_workers, float(n_workers), + ) + else: + # No dynamic worker override: book cpu.mean directly. + # Do NOT apply relative_cpu scaling — the measurement already + # reflects actual usage. + task["resources"]["cpu"] = new_cpu + _log.info(" CPU %-40s %.3f cores -> %.3f cores", + name, float(old_cpu), new_cpu) + task_updated = True + + if task_updated: + n_updated += 1 + + if missing_base_names: + _log.info(" No learned data for: %s", ", ".join(sorted(missing_base_names))) + _log.info( + "Resource update done: %d/%d task stages updated (%d base name(s) not in learned data).", + n_updated, n_stages, len(missing_base_names), + ) + + +def replicate_workflow_for_timeframes(raw_spec: Dict[str, Any], M: int) -> Dict[str, Any]: + """Return a synthetic M-timeframe workflow derived from *raw_spec*. + + The original workflow may have N timeframes. This function: + 1. Detects per-TF stages (``timeframe >= 1``) and uses the + lowest-numbered timeframe as the canonical template. + 2. Instantiates the template for TF=1..M. + 3. Updates global stage dependencies so they reference exactly TF=1..M. + + Works for both M < N (shrink) and M > N (expand). + The returned dict shares no mutable state with *raw_spec*. + """ + stages = raw_spec.get("stages", []) + per_tf = [s for s in stages if s.get("timeframe", -1) >= 1] + global_stgs = [s for s in stages if s.get("timeframe", -1) < 1] + + if not per_tf: + return raw_spec # no per-TF template structure detected + + original_tf_set = {s["timeframe"] for s in per_tf} + min_tf = min(original_tf_set) + template_stages = [s for s in per_tf if s.get("timeframe") == min_tf] + template_names = {s["name"] for s in template_stages} + all_per_tf_names = {s["name"] for s in per_tf} + + def _base(name: str, tf: int) -> str: + sfx = f"_{tf}" + return name[:-len(sfx)] if name.endswith(sfx) else name + + # Replicate per-TF tasks for i = 1 .. M. + new_per_tf: List[Dict[str, Any]] = [] + for i in range(1, M + 1): + for tmpl in template_stages: + s = copy.deepcopy(tmpl) + base = _base(s["name"], min_tf) + s["name"] = f"{base}_{i}" + s["timeframe"] = i + new_needs: List[str] = [] + for need in tmpl.get("needs", []): + if need in template_names: + new_needs.append(f"{_base(need, min_tf)}_{i}") + else: + new_needs.append(need) + s["needs"] = new_needs + new_per_tf.append(s) + + # Update global stages: replace all per-TF deps with the full 1..M set, + # expanding each unique base name exactly once (deduplicates cross-TF refs). + new_global: List[Dict[str, Any]] = [] + for gstage in global_stgs: + s = copy.deepcopy(gstage) + new_needs_g: List[str] = [] + seen: Set[str] = set() + expanded_bases: Set[str] = set() + for need in gstage.get("needs", []): + if need not in all_per_tf_names: + if need not in seen: + new_needs_g.append(need) + seen.add(need) + continue + # Identify the base name (strip whichever TF suffix this entry has). + base = need + for otf in original_tf_set: + if need.endswith(f"_{otf}"): + base = need[:-len(f"_{otf}")] + break + if base in expanded_bases: + continue + expanded_bases.add(base) + for j in range(1, M + 1): + n = f"{base}_{j}" + if n not in seen: + new_needs_g.append(n) + seen.add(n) + s["needs"] = new_needs_g + new_global.append(s) + + return {**raw_spec, "stages": new_per_tf + new_global} diff --git a/MC/workflow_runner/o2dpg_schedule_simulator.py b/MC/workflow_runner/o2dpg_schedule_simulator.py new file mode 100755 index 000000000..016dc82b2 --- /dev/null +++ b/MC/workflow_runner/o2dpg_schedule_simulator.py @@ -0,0 +1,1268 @@ +#!/usr/bin/env python3 +"""Discrete-event simulator for the O2DPG workflow scheduler. + +Loads a workflow (and optionally applies learned resources), then +simulates scheduling under one or more policies in microseconds — +no processes are spawned. + +Assumptions (matching a kernel-enforced hard CPU limit): + - No nice / backfill tier: all tasks are submitted at the default + nice level against a single hard CPU budget. + - Memory is also a hard limit. + - Tasks run for exactly their `resources.walltime` seconds (set by + --update-resources). If walltime is absent, cpu * --walltime-per-core + is used as a proxy. + - Task parallelism is limited only by cpu_limit and mem_limit, not + by --maxjobs (the simulator sets the process cap to infinity). + +Usage examples +-------------- +Compare all three policies with learned resources: + + o2dpg_schedule_simulator.py \\ + -f workflow.json \\ + --update-resources learned.json \\ + --cpu-limit 8 --mem-limit 16384 \\ + --policies timeframe critical-path best-fit + +Single policy, verbose per-task schedule: + + o2dpg_schedule_simulator.py \\ + -f workflow.json --update-resources learned.json \\ + --cpu-limit 8 --mem-limit 16384 \\ + --policies critical-path --verbose + +JSON output for downstream analysis: + + o2dpg_schedule_simulator.py ... --output sim.json +""" + +from __future__ import annotations + +import argparse +import copy +import json +import math +import os +import random +import statistics +import sys +from dataclasses import dataclass, field +from typing import Dict, List, Optional, Set, Tuple + +_here = os.path.dirname(os.path.abspath(__file__)) +if _here not in sys.path: + sys.path.insert(0, _here) + +from o2dpg_runner.workflow import ( + build_workflow, + load_json, + replicate_workflow_for_timeframes, + update_resource_estimates, +) +from o2dpg_runner.resources import ResourceManager, ResourceLimitExceeded +from o2dpg_runner.scheduler import get_policy +from o2dpg_runner.scheduler.base import SchedulerState +from o2dpg_runner.scheduler.timeframe import TimeframeFirstPolicy +from o2dpg_runner.graph import descendants, longest_path_length, kahn_topological_order + + +# --------------------------------------------------------------------------- +# Data classes +# --------------------------------------------------------------------------- + +@dataclass +class SimTask: + tid: int + name: str + start: float # wall seconds from t=0 + finish: float + cpu: float # effective average cores consumed over [start, finish] + cpu_booked: float # cores booked for scheduler admission + mem: float # MB booked + walltime: float # finish - start + + +@dataclass +class _RunningBackfill: + task: SimTask + nominal_work: float + remaining_work: float + launch_seq: int + overhead_until: Optional[float] = None + + +@dataclass +class SimResult: + policy: str + makespan: float # total wall seconds + tasks: List[SimTask] = field(default_factory=list) + deadlocked_tids: List[int] = field(default_factory=list) + + def cpu_utilization(self, cpu_limit: float) -> float: + """Mean CPU utilisation as a fraction of cpu_limit.""" + if self.makespan <= 0 or cpu_limit <= 0: + return 0.0 + total = sum(t.cpu * t.walltime for t in self.tasks) + return total / (self.makespan * cpu_limit) + + def peak_mem_mb(self) -> float: + """Peak concurrent memory usage in MB (sweep-line).""" + events: List[Tuple[float, float]] = [] + for t in self.tasks: + events.append((t.start, +t.mem)) + events.append((t.finish, -t.mem)) + events.sort() + peak = cur = 0.0 + for _, delta in events: + cur += delta + peak = max(peak, cur) + return peak + + def to_dict(self) -> dict: + return { + "policy": self.policy, + "makespan_s": round(self.makespan, 3), + "tasks": [ + { + "tid": t.tid, "name": t.name, + "start": round(t.start, 3), "finish": round(t.finish, 3), + "cpu": round(t.cpu, 3), "cpu_booked": round(t.cpu_booked, 3), + "mem": round(t.mem, 1), + "walltime": round(t.walltime, 3), + } + for t in sorted(self.tasks, key=lambda x: x.start) + ], + "deadlocked_tids": self.deadlocked_tids, + } + + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- + +def _global_name(name: str) -> str: + toks = name.split("_") + if toks and toks[-1].isdigit() and len(toks) > 1: + return "_".join(toks[:-1]) + return name + + +def _task_walltime(task: dict, cpu_fallback_factor: float) -> float: + """Return walltime [s] for a task, falling back to cpu * factor.""" + wt = task.get("resources", {}).get("walltime") + if wt is not None: + try: + return max(1e-3, float(wt)) + except (TypeError, ValueError): + pass + cpu = float(task.get("resources", {}).get("cpu", 1.0)) + return max(1e-3, cpu * cpu_fallback_factor) + + +_LEARN_ALL: Set[str] = frozenset({"cpu", "mem", "lifetime"}) + + +def _apply_learned_fields(workflow, learned: Dict, fields: Set[str]) -> int: + """Patch workflow stages in-place with a subset of learned resource fields. + + *fields* is a subset of ``{"cpu", "mem", "lifetime"}``. Only the named + dimensions are written; the rest keep the values from workflow.json. + Returns the count of stages that received at least one update. + """ + n_updated = 0 + for task in workflow.stages: + tf = task.get("timeframe", -1) + name = task["name"] + gname = "_".join(name.split("_")[:-1]) if tf >= 1 else name + data = learned.get(gname) + if not isinstance(data, dict): + continue + updated = False + if "lifetime" in fields: + wt = data.get("lifetime", {}).get("mean") + if wt is not None: + task["resources"]["walltime"] = float(wt) + updated = True + if "mem" in fields: + mem = data.get("pss", {}).get("max") + if mem is not None: + task["resources"]["mem"] = float(mem) + updated = True + if "cpu" in fields: + cpu = data.get("cpu", {}).get("mean") + if cpu is not None: + task["resources"]["cpu"] = float(cpu) + updated = True + if updated: + n_updated += 1 + return n_updated + + +@dataclass +class AmdahlModel: + """Amdahl scaling model derived from a single measurement point. + + walltime(n) = t_serial + t_parallel_tot / n + + t_serial and t_parallel_tot are solved from: + walltime_ref = t_serial + t_parallel_tot / n_ref + cpu_mean_ref = (t_serial + t_parallel_tot) / walltime_ref + """ + t_serial: float + t_parallel_tot: float + n_ref: int + cpu_mean_ref: float + min_workers: int = 1 + max_workers: int = 1 + + def walltime(self, n: int) -> float: + return max(1e-3, self.t_serial + self.t_parallel_tot / max(1, n)) + + @property + def worker_range(self) -> List[int]: + return list(range(self.min_workers, self.max_workers + 1)) + + @classmethod + def from_dict(cls, d: dict) -> "AmdahlModel": + model = cls( + t_serial=float(d["t_serial"]), + t_parallel_tot=float(d["t_parallel_tot"]), + n_ref=int(d["n_ref"]), + cpu_mean_ref=float(d["cpu_mean_ref"]), + min_workers=int(d.get("min_workers", 1)), + max_workers=int(d.get("max_workers", d["n_ref"])), + ) + if model.t_serial < 0 or model.t_parallel_tot < 0: + raise ValueError("Amdahl model has negative serial/parallel component") + if model.n_ref < 1 or model.min_workers < 1 or model.max_workers < model.min_workers: + raise ValueError("Amdahl model has invalid worker bounds") + return model + + +def _sample_walltime(mean: float, std: float, rng: random.Random) -> float: + """Draw a walltime sample from a log-normal distribution. + + Log-normal is a natural model for walltime: always positive, right-skewed + (occasional slow outliers). When std=0 the mean is returned unchanged. + """ + if std <= 1e-9 or mean <= 1e-9: + return max(1e-3, mean) + cv = std / mean + sigma2 = math.log(1.0 + cv * cv) + mu = math.log(mean) - 0.5 * sigma2 + return max(1e-3, rng.lognormvariate(mu, math.sqrt(sigma2))) + + +def _build_rm( + workflow, + cpu_limit: float, + mem_limit: float, + cpu_overrides: Optional[Dict[int, float]] = None, + n_backfill_max: int = 0, + backfill_cpu_factor: float = 1.5, + backfill_mem_factor: float = 1.0, + maxjobs: int = 10_000, +) -> Tuple[ResourceManager, Set[int]]: + """Fresh ResourceManager with no backfill tier and unlimited job slots. + + *cpu_overrides* maps tid → cpu to override resources.cpu for specific + tasks (used by the worker-count optimizer). + """ + rm = ResourceManager( + cpu_limit=cpu_limit, + mem_limit=mem_limit, + procs_parallel_max=maxjobs, + n_backfill_max=n_backfill_max, + backfill_cpu_factor=backfill_cpu_factor, + backfill_mem_factor=backfill_mem_factor, + dynamic_resources=False, + optimistic_resources=True, + ) + impossible_tids: Set[int] = set() + for i, task in enumerate(workflow.stages): + rel = None + try: + rv = task["resources"].get("relative_cpu") + rel = float(rv) if rv is not None else None + except (TypeError, ValueError): + pass + cpu = float(task["resources"]["cpu"]) + if cpu_overrides and i in cpu_overrides: + cpu = cpu_overrides[i] + mem = float(task["resources"]["mem"]) + if cpu > cpu_limit or mem > mem_limit: + impossible_tids.add(i) + try: + rm.add_task( + name=task["name"], + related_name=_global_name(task["name"]), + cpu=cpu, + cpu_relative=rel, + mem=mem, + semaphore_string=task.get("semaphore"), + ) + except ResourceLimitExceeded as e: + print(f" WARNING: task {task['name']} exceeds limits and will never run: {e}", + file=sys.stderr) + if impossible_tids: + names = [workflow.stages[i]["name"] for i in sorted(impossible_tids)] + print( + " WARNING: tasks exceed the hard simulator limits and will remain unscheduled: " + f"{names[:5]}{'...' if len(names) > 5 else ''}", + file=sys.stderr, + ) + return rm, impossible_tids + + +def _build_state( + workflow, + cpu_fallback_factor: float, + cpu_overrides: Optional[Dict[int, float]] = None, + walltime_overrides: Optional[Dict[int, float]] = None, +) -> SchedulerState: + n = workflow.n_tasks() + desc_cache: Dict = {} + desc_counts = [len(descendants(workflow.forward_adj, tid, desc_cache)) + for tid in range(n)] + + timeframe_of = [t.get("timeframe", -1) for t in workflow.stages] + tf_weight = [(timeframe_of[i], desc_counts[i]) for i in range(n)] + + cpu = [float(t.get("resources", {}).get("cpu", 1.0)) for t in workflow.stages] + if cpu_overrides: + for tid, value in cpu_overrides.items(): + cpu[tid] = float(value) + mem = [float(t.get("resources", {}).get("mem", 0.0)) for t in workflow.stages] + walltime = [_task_walltime(t, cpu_fallback_factor) for t in workflow.stages] + if walltime_overrides: + for tid, value in walltime_overrides.items(): + walltime[tid] = float(value) + + has_walltime = any(t.get("resources", {}).get("walltime") for t in workflow.stages) + cp_weight = walltime if has_walltime else cpu + topo = kahn_topological_order(n, workflow.forward_adj, workflow.indegree) + cp = longest_path_length(workflow.forward_adj, topo, cp_weight) + + return SchedulerState( + timeframe_of=timeframe_of, + descendants_count=desc_counts, + critical_path=cp, + task_cpu=cpu, + task_mem=mem, + task_walltime=walltime, + timeframe_weight=tf_weight, + ) + + +# --------------------------------------------------------------------------- +# Core simulation +# --------------------------------------------------------------------------- + +def simulate( + workflow, + policy_name: str, + cpu_limit: float, + mem_limit: float, + cpu_fallback_factor: float = 10.0, + task_overhead: float = 0.1, + walltime_stds: Optional[Dict[str, float]] = None, + cv_fallback: float = 0.15, + rng: Optional[random.Random] = None, + amdahl_models: Optional[Dict[str, "AmdahlModel"]] = None, + worker_assignment: Optional[Dict[str, int]] = None, + backfill_model: str = "off", + n_backfill: int = 1, + backfill_cpu_factor: float = 1.5, + backfill_mem_factor: float = 1.0, + backfill_slowdown_factor: float = 1.15, + maxjobs: int = 10_000, +) -> SimResult: + """Run one discrete-event simulation; return SimResult. + + When *rng* is provided, each task's walltime is sampled from + log-normal(mean, std). The std is taken from *walltime_stds* when + available; otherwise *cv_fallback* × mean is used as a noise floor. + + When *amdahl_models* and *worker_assignment* are both provided, walltime + and cpu booking for scalable tasks are derived from the Amdahl model at + the assigned worker count rather than from the workflow resources. + """ + mean_walltimes = [_task_walltime(t, cpu_fallback_factor) for t in workflow.stages] + + # Apply Amdahl model overrides for scalable tasks. + cpu_overrides: Dict[int, float] = {} + walltime_overrides: Dict[int, float] = {} + if amdahl_models and worker_assignment: + for i, task in enumerate(workflow.stages): + base = _global_name(task["name"]) + model = amdahl_models.get(base) + n = worker_assignment.get(base) + if model is not None and n is not None: + mean_walltimes[i] = model.walltime(n) + walltime_overrides[i] = mean_walltimes[i] + cpu_overrides[i] = float(n) + + state = _build_state( + workflow, + cpu_fallback_factor, + cpu_overrides=cpu_overrides or None, + walltime_overrides=walltime_overrides or None, + ) + + # Sample walltimes for this simulation run. + if rng is not None: + walltimes = [] + for i, task in enumerate(workflow.stages): + mean_wt = mean_walltimes[i] + base = _global_name(task["name"]) + std_wt = (walltime_stds or {}).get(base, 0.0) + if std_wt <= 0 and cv_fallback > 0: + std_wt = mean_wt * cv_fallback + walltimes.append(_sample_walltime(mean_wt, std_wt, rng)) + else: + walltimes = mean_walltimes + + if policy_name == "timeframe": + policy = TimeframeFirstPolicy(drop_should_break=False) + else: + policy = get_policy(policy_name) + + use_backfill = backfill_model in ("structural", "slowdown", "holefill") + rm, impossible_tids = _build_rm( + workflow, + cpu_limit, + mem_limit, + cpu_overrides=cpu_overrides or None, + n_backfill_max=n_backfill if use_backfill else 0, + backfill_cpu_factor=backfill_cpu_factor, + backfill_mem_factor=backfill_mem_factor, + maxjobs=maxjobs, + ) + + n = workflow.n_tasks() + proc_status = ["ToDo"] * n + candidates: List[int] = [ + i for i in range(n) if workflow.indegree[i] == 0 and i not in impossible_tids + ] + finished: Set[int] = set() + running: List[Tuple[int, float]] = [] # (tid, finish_time) + result = SimResult(policy=policy_name, makespan=0.0) + t = 0.0 + + if backfill_model == "holefill": + running_fg: List[Tuple[int, float]] = [] + running_bf: Dict[int, _RunningBackfill] = {} + launch_seq = 0 + + for _guard in range(n * n + 1): + ordered = policy.order(candidates, state) + for tid, nice in policy.pick_submittable(ordered, rm): + if nice != rm.nice_default and rm.cpu_free_default() <= 1e-9: + continue + rm.book(tid, nice) + candidates.remove(tid) + proc_status[tid] = "Running" + res = rm.resources[tid] + if nice == rm.nice_default: + compute_wt = walltimes[tid] + wt = compute_wt + task_overhead + finish = t + wt + fg_cpu = res.cpu_assigned * (compute_wt / wt) if wt > 0 else 0.0 + task = SimTask( + tid=tid, + name=workflow.id_to_name[tid], + start=t, + finish=finish, + cpu=fg_cpu, + cpu_booked=res.cpu_assigned, + mem=res.mem_assigned, + walltime=wt, + ) + running_fg.append((tid, finish)) + result.tasks.append(task) + else: + launch_seq += 1 + task = SimTask( + tid=tid, + name=workflow.id_to_name[tid], + start=t, + finish=t, + cpu=0.0, + cpu_booked=res.cpu_assigned, + mem=res.mem_assigned, + walltime=0.0, + ) + nominal_work = res.cpu_assigned * walltimes[tid] + running_bf[tid] = _RunningBackfill( + task=task, + nominal_work=nominal_work, + remaining_work=nominal_work, + launch_seq=launch_seq, + ) + result.tasks.append(task) + + if not running_fg and not running_bf: + break + + next_fg = min((ft for _, ft in running_fg), default=float("inf")) + cpu_fg = sum( + next(task.cpu_booked for task in result.tasks if task.tid == tid) + for tid, _ in running_fg + ) + hole_cpu = max(0.0, cpu_limit - cpu_fg) + remaining_hole = hole_cpu + bf_alloc: Dict[int, float] = {} + for tid, rb in sorted(running_bf.items(), key=lambda item: item[1].launch_seq): + if rb.overhead_until is not None: + bf_alloc[tid] = 0.0 + continue + alloc = min(rb.task.cpu_booked, remaining_hole) + bf_alloc[tid] = alloc + remaining_hole -= alloc + + next_bf = float("inf") + for tid, rb in running_bf.items(): + if rb.overhead_until is not None: + next_bf = min(next_bf, rb.overhead_until) + else: + alloc = bf_alloc.get(tid, 0.0) + if alloc > 1e-12: + next_bf = min(next_bf, t + rb.remaining_work / alloc) + + next_t = min(next_fg, next_bf) + if not math.isfinite(next_t): + break + dt = max(0.0, next_t - t) + for tid, rb in running_bf.items(): + if rb.overhead_until is None: + alloc = bf_alloc.get(tid, 0.0) + if alloc > 0.0: + rb.remaining_work = max(0.0, rb.remaining_work - alloc * dt) + t = next_t + + new_running_fg: List[Tuple[int, float]] = [] + for tid, ft in running_fg: + if abs(ft - t) < 1e-9: + rm.unbook(tid) + proc_status[tid] = "Done" + finished.add(tid) + for succ in workflow.forward_adj[tid]: + if proc_status[succ] == "ToDo": + if succ in impossible_tids: + continue + if all(p in finished for p in workflow.reverse_adj[succ]): + candidates.append(succ) + else: + new_running_fg.append((tid, ft)) + running_fg = new_running_fg + + done_bf: List[int] = [] + for tid, rb in running_bf.items(): + if rb.overhead_until is not None: + if abs(rb.overhead_until - t) < 1e-9: + done_bf.append(tid) + continue + if rb.remaining_work <= 1e-9: + if task_overhead > 0: + rb.overhead_until = t + task_overhead + else: + done_bf.append(tid) + + for tid in done_bf: + rb = running_bf.pop(tid) + rb.task.finish = t + rb.task.walltime = max(1e-9, rb.task.finish - rb.task.start) + rb.task.cpu = rb.nominal_work / rb.task.walltime + rm.unbook(tid) + proc_status[tid] = "Done" + finished.add(tid) + for succ in workflow.forward_adj[tid]: + if proc_status[succ] == "ToDo": + if succ in impossible_tids: + continue + if all(p in finished for p in workflow.reverse_adj[succ]): + candidates.append(succ) + + if not candidates and not running_fg and not running_bf: + break + else: + print(f" WARNING [{policy_name}]: simulation hit guard limit — possible deadlock", + file=sys.stderr) + + result.makespan = t + result.deadlocked_tids = [ + i for i, s in enumerate(proc_status) if s == "ToDo" + ] + if result.deadlocked_tids: + names = [workflow.id_to_name[i] for i in result.deadlocked_tids] + print(f" WARNING [{policy_name}]: {len(names)} tasks never scheduled " + f"(resource limits too tight?): {names[:5]}{'...' if len(names) > 5 else ''}", + file=sys.stderr) + return result + + for _guard in range(n * n + 1): # at most n rounds to completion + # --- schedule everything that fits right now --- + ordered = policy.order(candidates, state) + for tid, nice in policy.pick_submittable(ordered, rm): + # book immediately so subsequent picks in this pass see the + # updated resource availability (mirrors executor behaviour) + rm.book(tid, nice) + slowdown = 1.0 + if backfill_model == "slowdown" and nice != rm.nice_default: + slowdown = backfill_slowdown_factor + compute_wt = walltimes[tid] * slowdown # time spent doing actual work + wt = compute_wt + task_overhead # total slot duration (incl. idle overhead) + finish = t + wt + running.append((tid, finish)) + candidates.remove(tid) + proc_status[tid] = "Running" + res = rm.resources[tid] + # Overhead is idle time (process start/stop, alienv load, I/O flush). + # Average CPU over the full slot = booked_cpu × compute_fraction only. + effective_cpu = res.cpu_assigned / slowdown * (compute_wt / wt) if wt > 0 else 0.0 + result.tasks.append(SimTask( + tid=tid, + name=workflow.id_to_name[tid], + start=t, + finish=finish, + cpu=effective_cpu, + cpu_booked=res.cpu_assigned, + mem=res.mem_assigned, + walltime=wt, + )) + + if not running: + break + + # --- advance to next task completion --- + next_t = min(ft for _, ft in running) + t = next_t + + # --- complete all tasks finishing at t (within float tolerance) --- + still_running: List[Tuple[int, float]] = [] + for tid, ft in running: + if abs(ft - t) < 1e-9: + rm.unbook(tid) + proc_status[tid] = "Done" + finished.add(tid) + for succ in workflow.forward_adj[tid]: + if proc_status[succ] == "ToDo": + if succ in impossible_tids: + continue + if all(p in finished for p in workflow.reverse_adj[succ]): + candidates.append(succ) + else: + still_running.append((tid, ft)) + running = still_running + + if not candidates and not running: + break + else: + print(f" WARNING [{policy_name}]: simulation hit guard limit — possible deadlock", + file=sys.stderr) + + result.makespan = t + result.deadlocked_tids = [ + i for i, s in enumerate(proc_status) if s == "ToDo" + ] + if result.deadlocked_tids: + names = [workflow.id_to_name[i] for i in result.deadlocked_tids] + print(f" WARNING [{policy_name}]: {len(names)} tasks never scheduled " + f"(resource limits too tight?): {names[:5]}{'...' if len(names) > 5 else ''}", + file=sys.stderr) + return result + + +# --------------------------------------------------------------------------- +# Presentation +# --------------------------------------------------------------------------- + +def _fmt_time(s: float) -> str: + return f"{s:.1f}s" + + +def print_summary( + results_by_policy: Dict[str, List[SimResult]], + cpu_limit: float, + n_samples: int, +) -> None: + w = 16 + stoch = n_samples > 1 + mk_hdr = f"{'Makespan (mean±std)':>22}" if stoch else f"{'Makespan':>10}" + cpu_hdr = f"{'CPU util (mean±std)':>20}" if stoch else f"{'CPU util':>9}" + mem_hdr = f"{'Peak mem (mean±std)':>22}" if stoch else f"{'Peak mem':>10}" + header = f"{'Policy':<{w}} {mk_hdr} {cpu_hdr} {mem_hdr} {'Tasks':>6}" + print() + print(header) + print("-" * len(header)) + for policy, runs in results_by_policy.items(): + makespans = [r.makespan for r in runs] + mean_mk = statistics.mean(makespans) + util_pct = statistics.mean(r.cpu_utilization(cpu_limit) * 100 for r in runs) + peak = statistics.mean(r.peak_mem_mb() for r in runs) + n_tasks = runs[0].tasks.__len__() if runs else 0 + if stoch: + std_mk = statistics.stdev(makespans) if len(makespans) > 1 else 0.0 + std_util = statistics.stdev(r.cpu_utilization(cpu_limit)*100 for r in runs) if len(runs)>1 else 0.0 + std_peak = statistics.stdev(r.peak_mem_mb() for r in runs) if len(runs)>1 else 0.0 + mk_str = f"{_fmt_time(mean_mk)} ± {_fmt_time(std_mk)}" + print(f"{policy:<{w}} {mk_str:>22} {util_pct:>7.1f}±{std_util:.1f}% {peak:>8.0f}±{std_peak:.0f}MB {n_tasks:>6}") + else: + print(f"{policy:<{w}} {_fmt_time(mean_mk):>10} {util_pct:>8.1f}% " + f"{peak:>9.0f}MB {n_tasks:>6}") + print() + + +def print_verbose(result: SimResult) -> None: + print(f"\n--- Schedule: {result.policy} ---") + prev_t = -1.0 + for task in sorted(result.tasks, key=lambda x: (x.start, x.name)): + if abs(task.start - prev_t) > 1e-9: + print(f" t={_fmt_time(task.start)}") + prev_t = task.start + print(f" START {task.name:<40} " + f"cpu={task.cpu:.1f}" + + (f" ({task.cpu_booked:.1f} booked)" if abs(task.cpu - task.cpu_booked) > 1e-9 else "") + + f" mem={task.mem:.0f}MB " + f"dur={_fmt_time(task.walltime)}") + print(f" Makespan: {_fmt_time(result.makespan)}") + + +def _print_sweep_table( + sweep_results: "List[Tuple]", # (M, results_by_policy, n_stages, worker_assignment|None) + cpu_limit: float, + n_samples: int, +) -> None: + """Print a compact table summarising a timeframe-sweep simulation run.""" + stoch = n_samples > 1 + + # Collect all scalable task names that appear in any worker assignment. + scalable_names: List[str] = [] + seen_names: "Set[str]" = set() + for _, _, _, wa in sweep_results: + if wa: + for name in sorted(wa): + if name not in seen_names: + scalable_names.append(name) + seen_names.add(name) + + # Build header with optional per-task worker columns. + worker_cols = " ".join(f"{n[:12]:>12}" for n in scalable_names) + cpu_col_hdr = f"{'CPU util(±std)':>14}" if stoch else f"{'CPU util':>9}" + hdr = (f" {'M':>4} {'Policy':<16} {'N tasks':>7} " + f"{'Makespan':>12} {cpu_col_hdr} {'Peak mem':>9}" + + (f" {worker_cols}" if scalable_names else "")) + cpu_col_w = 14 if stoch else 9 + print("\nTimeframe sweep results:") + print(hdr) + print(" " + "-" * (len(hdr) - 2)) + + for M, results_by_policy, n_stages, worker_assignment in sweep_results: + m_str = str(M) if M is not None else "orig" + for policy, runs in results_by_policy.items(): + makespans = [r.makespan for r in runs] + mean_mk = statistics.mean(makespans) + util_pct = statistics.mean(r.cpu_utilization(cpu_limit) * 100 for r in runs) + peak = statistics.mean(r.peak_mem_mb() for r in runs) + if stoch and len(runs) > 1: + std_mk = statistics.stdev(makespans) + mk_str = f"{_fmt_time(mean_mk)}±{_fmt_time(std_mk)}" + std_util = statistics.stdev(r.cpu_utilization(cpu_limit) * 100 for r in runs) + util_str = f"{util_pct:.1f}±{std_util:.1f}%" + else: + mk_str = _fmt_time(mean_mk) + util_str = f"{util_pct:.1f}%" + row = (f" {m_str:>4} {policy:<16} {n_stages:>7} " + f"{mk_str:>12} {util_str:>{cpu_col_w}} {peak:>7.0f}MB") + if scalable_names and worker_assignment: + wvals = " ".join( + f"{worker_assignment.get(n, '-'):>12}" + for n in scalable_names + ) + row += f" {wvals}" + print(row) + print() + + +# --------------------------------------------------------------------------- +# CLI +# --------------------------------------------------------------------------- + +def optimize_workers( + workflow, + policy_name: str, + cpu_limit: float, + mem_limit: float, + amdahl_models: Dict[str, AmdahlModel], + cpu_fallback_factor: float = 10.0, + task_overhead: float = 0.1, + n_eval_samples: int = 3, + rng_seed: int = 0, + backfill_model: str = "off", + n_backfill: int = 1, + backfill_cpu_factor: float = 1.5, + backfill_mem_factor: float = 1.0, + backfill_slowdown_factor: float = 1.15, + maxjobs: int = 10_000, +) -> Tuple[Dict[str, int], float]: + """Coordinate-descent search for the best worker assignment. + + For each scalable task, iterates over its valid worker range (from the + Amdahl model) and picks the count that minimises mean makespan while + holding all other tasks fixed. Repeats until no improvement is found. + + Returns (best_assignment, best_makespan_s). + """ + # Start from the current assignment implied by each model's cpu_mean_ref + # (what update_resource_estimates already set via round(cpu_mean)). + assignment: Dict[str, int] = { + name: max(model.min_workers, + min(model.max_workers, max(1, round(model.cpu_mean_ref)))) + for name, model in amdahl_models.items() + } + + walltime_stds: Dict[str, float] = {} # no learned std for optimizer runs + + def _evaluate(asgn: Dict[str, int]) -> float: + makespans = [] + for s in range(n_eval_samples): + rng = random.Random(rng_seed + s) if n_eval_samples > 1 else None + r = simulate( + workflow, policy_name, cpu_limit, mem_limit, + cpu_fallback_factor=cpu_fallback_factor, + task_overhead=task_overhead, + walltime_stds=walltime_stds, + cv_fallback=0.1, + rng=rng, + amdahl_models=amdahl_models, + worker_assignment=asgn, + backfill_model=backfill_model, + n_backfill=n_backfill, + backfill_cpu_factor=backfill_cpu_factor, + backfill_mem_factor=backfill_mem_factor, + backfill_slowdown_factor=backfill_slowdown_factor, + maxjobs=maxjobs, + ) + makespans.append(r.makespan) + return statistics.mean(makespans) + + best_score = _evaluate(assignment) + improved = True + while improved: + improved = False + for name, model in amdahl_models.items(): + best_n = assignment[name] + for n in model.worker_range: + if n == best_n: + continue + trial = dict(assignment) + trial[name] = n + score = _evaluate(trial) + if score < best_score - 0.1: # 0.1 s improvement threshold + best_score = score + best_n = n + improved = True + assignment[name] = best_n + return assignment, best_score + + +def build_parser() -> argparse.ArgumentParser: + p = argparse.ArgumentParser( + description="Simulate O2DPG workflow scheduling without running tasks.", + formatter_class=argparse.ArgumentDefaultsHelpFormatter, + ) + p.add_argument("-f", "--workflowfile", required=True) + p.add_argument("--update-resources", dest="update_resources", default=None, + metavar="JSON[:FIELDS]", + help="Apply learned resources from JSON (same file as " + "--update-resources in the runner). Enables walltime-based " + "critical path. Optionally restrict which dimensions are " + "patched with a colon-separated field list chosen from " + "{cpu,mem,lifetime}. Example: learned.json:lifetime applies " + "only walltimes, keeping cpu/mem from workflow.json.") + p.add_argument("--cpu-limit", type=float, default=8.0) + p.add_argument("--mem-limit", type=float, default=60000.0, help="in MB") + p.add_argument("--policies", nargs="+", + default=["timeframe", "critical-path", "best-fit"], + choices=["timeframe", "critical-path", "best-fit"]) + p.add_argument("-tt", "--target-tasks", nargs="+", default=["*"]) + p.add_argument("--target-labels", nargs="+", default=[]) + p.add_argument("--walltime-per-core", type=float, default=10.0, metavar="S", + help="Fallback walltime per CPU core [s] when no learned " + "walltime is available.") + p.add_argument("--task-overhead", type=float, default=0.1, metavar="S", + help="Per-task idle overhead [s] added to every task's slot " + "duration (models alienv load, process startup, I/O flush, " + "and scheduler reaction time). This time contributes zero " + "CPU to the utilisation numerator, so larger values lower " + "both predicted makespan and CPU efficiency. " + "Default 0.1 s is conservative; calibration against " + "measurements typically gives 5–7 s for production " + "ALICE MC workflows.") + p.add_argument("--backfill-model", default="off", + choices=["off", "structural", "slowdown", "holefill"], + help="Backfill approximation used by the simulator. " + "'structural' replays the runner's second admission lane; " + "'slowdown' adds a fitted walltime penalty to backfill tasks; " + "'holefill' lets backfill tasks consume only the CPU left idle " + "by foreground tasks, slowing them proportionally.") + p.add_argument("--n-backfill", type=int, default=1, metavar="N", + help="Maximum concurrent backfill tasks when backfill simulation is enabled.") + p.add_argument("--backfill-cpu-factor", type=float, default=1.5, metavar="X", + help="Total CPU oversubscription factor allowed for backfill admission.") + p.add_argument("--backfill-mem-factor", type=float, default=1.0, metavar="X", + help="Total memory oversubscription factor allowed for backfill admission.") + p.add_argument("--backfill-slowdown-factor", type=float, default=1.15, metavar="X", + help="Walltime multiplier applied to backfill tasks in " + "--backfill-model slowdown.") + p.add_argument("--samples", type=int, default=1, metavar="N", + help="Number of Monte Carlo samples for stochastic simulation. " + "When >1, walltime for each task is drawn from a log-normal " + "distribution parameterised by lifetime.mean and lifetime.std " + "from the learned JSON. Output shows mean ± std of makespan.") + p.add_argument("--cv-fallback", type=float, default=0.15, metavar="CV", + help="Coefficient of variation (std/mean) used as a noise floor " + "for tasks whose learned walltime std is zero or absent " + "(single-TF runs, old learned files, etc.). " + "0 disables fallback and makes those tasks deterministic.") + p.add_argument("-j", "--maxjobs", type=int, default=0, metavar="N", + help="Maximum concurrent tasks. -1 = serial mode (1 task at a time): " + "reproduces the learning-run conditions (-jmax 1) and makes the " + "optimizer prefer maximum workers per task. 0 = unlimited (default). " + "N > 0 = at most N concurrent tasks.") + p.add_argument("--optimize-workers", action="store_true", + help="Run coordinate-descent optimizer to find the best worker " + "assignment for scalable tasks. Requires --update-resources " + "with a learned.json that contains 'amdahl' blocks (produced " + "by json-stat --workflow workflow.json).") + p.add_argument("--opt-eval-samples", type=int, default=3, metavar="N", + help="Simulator evaluations per candidate during optimization " + "(more = less noise, slower).") + p.add_argument("--write-optimized", default=None, metavar="FILE", + help="After --optimize-workers, write a copy of the learned JSON " + "with updated lifetime.mean and cpu.mean for scalable tasks. " + "Pass this file to --update-resources in the runner to apply " + "optimized worker counts. When multiple --policies are given " + "the policy with the best (lowest) optimized makespan is used.") + p.add_argument("--verbose", action="store_true", + help="Print per-task schedule for each policy.") + p.add_argument("--output", default=None, metavar="FILE", + help="Write results as JSON to FILE.") + p.add_argument("--timeframes", type=int, nargs="+", default=None, metavar="M", + help="Simulate with M timeframes instead of the workflow's original count. " + "Detects the per-TF template structure automatically and replicates it. " + "Pass multiple values for a sweep (e.g. --timeframes 1 2 5 10 20) to " + "produce a table of makespan and CPU utilisation vs timeframe count.") + return p + + +def main(argv=None) -> int: + ns = build_parser().parse_args(argv) + + raw = load_json(ns.workflowfile) + target_tasks = [t.strip('"').strip("'") for t in ns.target_tasks] + + # ── Parse --update-resources path[:field,field,...] ─────────────────────── + ur_path: Optional[str] = None + ur_fields: Optional[Set[str]] = None # None = all three fields + if ns.update_resources: + _parts = ns.update_resources.split(":", 1) + ur_path = _parts[0] + if len(_parts) > 1: + _raw_fields = {f.strip().lower() for f in _parts[1].split(",")} + _bad = _raw_fields - _LEARN_ALL + if _bad: + print(f"ERROR: unknown field(s) in --update-resources specifier: " + f"{sorted(_bad)}. Valid: cpu, mem, lifetime", file=sys.stderr) + return 1 + ur_fields = _raw_fields + + # ── Load learned JSON once (independent of timeframe count) ────────────── + walltime_stds: Dict[str, float] = {} + learned_full: Dict = {} + amdahl_models: Dict[str, AmdahlModel] = {} + + if ur_path: + _active = ur_fields if ur_fields is not None else _LEARN_ALL + _fields_str = ", ".join(sorted(_active)) + print(f"Applying learned resources from {ur_path}" + + (f" (fields: {_fields_str})" if ur_fields is not None else "") + + " ...") + with open(ur_path) as fh: + learned_full = json.load(fh) + apply_lifetime = "lifetime" in _active + for name, data in learned_full.items(): + if name == "count": + continue + if ns.samples > 1 and apply_lifetime: + std = data.get("lifetime", {}).get("std", 0.0) or 0.0 + if std > 0: + walltime_stds[name] = float(std) + if isinstance(data, dict) and "amdahl" in data: + try: + amdahl_models[name] = AmdahlModel.from_dict(data["amdahl"]) + except (KeyError, ValueError): + pass + if amdahl_models: + print(f" Amdahl models loaded for {len(amdahl_models)} scalable task(s): " + f"{', '.join(sorted(amdahl_models))}") + else: + print(f"No learned resources; using cpu * {ns.walltime_per_core}s as walltime proxy.") + + stochastic = ns.samples > 1 + if stochastic: + n_with_std = len(walltime_stds) + print(f" Stochastic: {n_with_std} tasks use learned std, " + f"cv_fallback={ns.cv_fallback:.2f} for the rest.") + if ns.backfill_model != "off": + print( + "Backfill simulation: " + f"model={ns.backfill_model}, n_backfill={ns.n_backfill}, " + f"cpu_factor={ns.backfill_cpu_factor}, mem_factor={ns.backfill_mem_factor}, " + + ( + f"slowdown={ns.backfill_slowdown_factor:.2f}x\n" + if ns.backfill_model == "slowdown" + else "foreground-hole driven\n" + if ns.backfill_model == "holefill" + else "\n" + ) + ) + + # Verbose Amdahl model summary (shown once, outside the sweep loop). + if amdahl_models and ns.verbose: + print() + for name, model in sorted(amdahl_models.items()): + n_cur = max(model.min_workers, + min(model.max_workers, max(1, round(model.cpu_mean_ref)))) + print(f" Amdahl: {name} " + f"(n_ref={model.n_ref}, cpu_mean={model.cpu_mean_ref:.2f}, " + f"t_serial={model.t_serial:.1f}s, " + f"t_parallel_tot={model.t_parallel_tot:.1f}s)") + print(f" {'n':>4} {'walltime':>10} {'Δ vs current':>14}") + wt_cur = model.walltime(n_cur) + for n in model.worker_range: + wt = model.walltime(n) + marker = " ← current" if n == n_cur else "" + print(f" {n:>4} {_fmt_time(wt):>10} " + f"{wt - wt_cur:>+12.1f}s{marker}") + print() + + # Resolve maxjobs — must come before optimizer and policy loops. + # -j -1 → serial + n_ref workers -j 1 → serial + round(cpu_mean) workers + # -j 0 → unlimited (default) -j N → at most N concurrent tasks + serial_mode = ns.maxjobs == -1 + procs_limit = 1 if (serial_mode or ns.maxjobs == 1) else (10_000 if ns.maxjobs <= 0 else ns.maxjobs) + if serial_mode: + print("Serial mode (-j -1): reproducing learning-run conditions " + "(1 task at a time, n_ref workers per scalable task).") + + # Default worker assignment: Amdahl-based, same for all M values in a sweep. + default_worker_assignment: Optional[Dict[str, int]] = None + if amdahl_models: + if serial_mode: + default_worker_assignment = {name: m.n_ref for name, m in amdahl_models.items()} + parts = [f"{n}: {w}w (n_ref)" for n, w in sorted(default_worker_assignment.items())] + else: + default_worker_assignment = { + name: max(m.min_workers, min(m.max_workers, max(1, round(m.cpu_mean_ref)))) + for name, m in amdahl_models.items() + } + parts = [f"{n}: {w}w" for n, w in sorted(default_worker_assignment.items())] + print(f"Worker counts for simulation: {', '.join(parts)}\n") + + # ── Timeframe sweep ─────────────────────────────────────────────────────── + tf_list: List[Optional[int]] = sorted(set(ns.timeframes)) if ns.timeframes else [None] + sweep_mode = len(tf_list) > 1 + if sweep_mode: + print(f"Timeframe sweep: M={tf_list} policies={ns.policies} " + f"cpu_limit={ns.cpu_limit} mem_limit={ns.mem_limit} MB\n") + + all_sweep_results: List[Tuple] = [] # (M, results_by_policy, n_stages, worker_assignment) + last_opt_results: List[Tuple[str, Dict[str, int], float]] = [] + + for M in tf_list: + if sweep_mode: + print(f" M={M} ...", end=" ", flush=True) + + cur_raw = replicate_workflow_for_timeframes(raw, M) if M is not None else raw + wf = build_workflow(cur_raw, target_tasks, ns.target_labels) + if not wf.stages: + print(f"Workflow is empty after filtering (M={M}).") + continue + + if ur_path: + if ur_fields is None: + update_resource_estimates(wf, ur_path) + else: + _apply_learned_fields(wf, learned_full, ur_fields) + has_wt = sum(1 for t in wf.stages if t.get("resources", {}).get("walltime")) + if not sweep_mode: + print(f" {has_wt}/{len(wf.stages)} tasks have learned walltime.") + if stochastic: + n_fallback = len(wf.stages) - len(walltime_stds) + print(f" Stochastic: {len(walltime_stds)} tasks use learned std, " + f"{n_fallback} use cv_fallback={ns.cv_fallback:.2f}.") + if not sweep_mode: + print(f"Workflow: {len(wf.stages)} tasks, cpu_limit={ns.cpu_limit}, " + f"mem_limit={ns.mem_limit} MB, samples={ns.samples}" + + (" (stochastic)" if stochastic else " (deterministic)") + "\n") + + # Worker-count optimizer (--optimize-workers): runs per-M so that the + # simulated workflow matches the actual task count. + cur_worker_assignment = default_worker_assignment + opt_results: List[Tuple[str, Dict[str, int], float]] = [] + if ns.optimize_workers: + if not amdahl_models: + print("WARNING: --optimize-workers requires Amdahl models in learned.json. " + "Re-run json-stat with --workflow workflow.json first.", file=sys.stderr) + else: + if not sweep_mode: + print(f"\nOptimizing worker assignment over " + f"{len(amdahl_models)} scalable task(s)...") + for policy_name in ns.policies: + best_asgn, best_mk = optimize_workers( + wf, policy_name, ns.cpu_limit, ns.mem_limit, + amdahl_models=amdahl_models, + cpu_fallback_factor=ns.walltime_per_core, + task_overhead=ns.task_overhead, + n_eval_samples=ns.opt_eval_samples, + backfill_model=ns.backfill_model, + n_backfill=ns.n_backfill, + backfill_cpu_factor=ns.backfill_cpu_factor, + backfill_mem_factor=ns.backfill_mem_factor, + backfill_slowdown_factor=ns.backfill_slowdown_factor, + maxjobs=procs_limit, + ) + opt_results.append((policy_name, best_asgn, best_mk)) + if not sweep_mode: + print(f"\n [{policy_name}] best makespan: {_fmt_time(best_mk)}") + print(f" {'Task':<35} {'n_ref':>6} {'current':>8} {'→ opt':>6} " + f"{'wt(current)':>12} {'wt(opt)':>10} {'Δwt':>8}") + print(f" {'-'*85}") + for name, model in sorted(amdahl_models.items()): + n_cur = max(model.min_workers, + min(model.max_workers, max(1, round(model.cpu_mean_ref)))) + n_opt = best_asgn[name] + wt_cur = model.walltime(n_cur) + wt_opt = model.walltime(n_opt) + delta = wt_opt - wt_cur + changed = "←" if n_opt != n_cur else "" + print(f" {name:<35} {model.n_ref:>6} {n_cur:>8} {n_opt:>6} " + f"{_fmt_time(wt_cur):>12} {_fmt_time(wt_opt):>10} " + f"{delta:>+7.1f}s {changed}") + if not sweep_mode: + print() + _, best_asgn_opt, _ = min(opt_results, key=lambda x: x[2]) + cur_worker_assignment = best_asgn_opt + last_opt_results = opt_results + + # ── Run simulations ─────────────────────────────────────────────────── + results_by_policy: Dict[str, List[SimResult]] = {} + for policy_name in ns.policies: + runs: List[SimResult] = [] + for s in range(ns.samples): + rng = random.Random(s) if stochastic else None + r = simulate( + wf, policy_name, + cpu_limit=ns.cpu_limit, + mem_limit=ns.mem_limit, + cpu_fallback_factor=ns.walltime_per_core, + task_overhead=ns.task_overhead, + walltime_stds=walltime_stds, + cv_fallback=ns.cv_fallback, + rng=rng, + amdahl_models=amdahl_models if amdahl_models else None, + worker_assignment=cur_worker_assignment, + backfill_model=ns.backfill_model, + n_backfill=ns.n_backfill, + backfill_cpu_factor=ns.backfill_cpu_factor, + backfill_mem_factor=ns.backfill_mem_factor, + backfill_slowdown_factor=ns.backfill_slowdown_factor, + maxjobs=procs_limit, + ) + runs.append(r) + results_by_policy[policy_name] = runs + if not sweep_mode: + makespans = [r.makespan for r in runs] + summary = _fmt_time(statistics.mean(makespans)) + if stochastic: + summary += f" ± {_fmt_time(statistics.stdev(makespans))}" + print(f" {policy_name:<16} makespan={summary}") + + all_sweep_results.append((M, results_by_policy, len(wf.stages), cur_worker_assignment)) + + if sweep_mode: + best_mk = min( + statistics.mean(r.makespan for r in runs) + for runs in results_by_policy.values() + ) + print(f"{len(wf.stages)} tasks, best makespan={_fmt_time(best_mk)}") + + if not all_sweep_results: + return 1 + + # ── Summary ─────────────────────────────────────────────────────────────── + if sweep_mode: + _print_sweep_table(all_sweep_results, ns.cpu_limit, ns.samples) + else: + _, results_by_policy, _, _ = all_sweep_results[0] + print_summary(results_by_policy, ns.cpu_limit, ns.samples) + if ns.verbose: + for policy_name, runs in results_by_policy.items(): + print_verbose(runs[0]) + + # ── Write optimized learned.json ────────────────────────────────────────── + if ns.write_optimized and last_opt_results and learned_full: + if sweep_mode: + print(f"NOTE: --write-optimized in sweep mode writes the optimisation " + f"result from the last M={tf_list[-1]}.") + best_policy, best_asgn, best_mk = min(last_opt_results, key=lambda x: x[2]) + if len(ns.policies) > 1: + print(f"Writing optimized resources: policy '{best_policy}' " + f"selected (best makespan {_fmt_time(best_mk)}).") + else: + print(f"Writing optimized resources (makespan {_fmt_time(best_mk)}).") + out_learned = copy.deepcopy(learned_full) + n_changed = 0 + for name, model in amdahl_models.items(): + if name not in out_learned: + continue + n_opt = best_asgn[name] + n_cur = max(model.min_workers, + min(model.max_workers, max(1, round(model.cpu_mean_ref)))) + if "lifetime" not in out_learned[name]: + out_learned[name]["lifetime"] = {} + out_learned[name]["lifetime"]["mean"] = round(model.walltime(n_opt), 3) + if "cpu" not in out_learned[name]: + out_learned[name]["cpu"] = {} + out_learned[name]["cpu"]["mean"] = float(n_opt) + if n_opt != n_cur: + n_changed += 1 + with open(ns.write_optimized, "w") as fh: + json.dump(out_learned, fh, indent=2) + print(f" Wrote {ns.write_optimized} " + f"({len(amdahl_models)} scalable tasks updated, " + f"{n_changed} changed from current assignment).") + + # ── JSON output ─────────────────────────────────────────────────────────── + if ns.output: + out_list = [] + for M, results_by_policy, n_stages, _ in all_sweep_results: + for policy_name, runs in results_by_policy.items(): + for i, r in enumerate(runs): + d = r.to_dict() + d["sample"] = i + d["n_stages"] = n_stages + if M is not None: + d["timeframes"] = M + out_list.append(d) + with open(ns.output, "w") as fh: + json.dump(out_list, fh, indent=2) + print(f"Results written to {ns.output}") + + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/MC/workflow_runner/o2dpg_workflow_runner.py b/MC/workflow_runner/o2dpg_workflow_runner.py new file mode 100755 index 000000000..70c56a4ba --- /dev/null +++ b/MC/workflow_runner/o2dpg_workflow_runner.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +"""Entry point preserving the legacy filename MC/bin/o2dpg_workflow_runner.py. + +All behavior lives in the o2dpg_runner package next to this script. +""" + +import os +import sys + +# Make sure we can import the o2dpg_runner package that sits next to us. +_here = os.path.dirname(os.path.realpath(__file__)) +if _here not in sys.path: + sys.path.insert(0, _here) + +from o2dpg_runner.cli import main + +if __name__ == "__main__": + sys.exit(main()) diff --git a/RelVal/PlotOverlays.C b/RelVal/PlotOverlays.C deleted file mode 100644 index f73557544..000000000 --- a/RelVal/PlotOverlays.C +++ /dev/null @@ -1,326 +0,0 @@ -#include -#include -#include - - - -void findRangeNotEmpty1D(TH1* h, double& min, double& max) -{ - auto axis = h->GetXaxis(); - min = axis->GetBinLowEdge(1); - max = axis->GetBinUpEdge(axis->GetNbins()); - for (int i = 1; i <= axis->GetNbins(); i++) { - if (h->GetBinContent(i) != 0) { - min = axis->GetBinLowEdge(i); - break; - } - } - for (int i = axis->GetNbins(); i >= 1 ; i--) { - if (h->GetBinContent(i) != 0) { - max = axis->GetBinUpEdge(i); - break; - } - } -} - - -TH1* makeFrameFromHistograms(TPad& pad, std::vector const& histograms, bool& shouldBeLog) -{ - // make a frame to fit all histograms - // propose log10 scale in case some integrals differ by more than 2 orders of magnitude - auto integralRef = histograms[0]->Integral(); - shouldBeLog = false; - auto minY = histograms[0]->GetMinimum(0); - double maxY = histograms[0]->GetMaximum(); - - double minX; - double maxX; - findRangeNotEmpty1D(histograms[0], minX, maxX); - - // find minima and maxima - for (int i = 1; i < histograms.size(); i++) { - minY = std::min(histograms[i]->GetMinimum(0), minY); - maxY = std::max(histograms[i]->GetMaximum(), maxY); - - double minXNext; - double maxXNext; - findRangeNotEmpty1D(histograms[i], minXNext, maxXNext); - minX = std::min(minX, minXNext); - maxX = std::max(maxX, maxXNext); - - auto integral = histograms[i]->Integral(); - if ((integralRef > 0 && integral / integralRef > 100) || (integral > 0 && integralRef / integral > 100)) { - // decide whether to do a log plot - shouldBeLog = true; - } - } - - // finalise the y-axis limits - if (shouldBeLog) { - auto margin = std::log10(maxY / minY); - minY = minY / std::pow(10, margin * 0.1); - maxY = maxY * std::pow(10, margin * 0.3);; - } else { - auto margin = 0.1 * (maxY - minY); - maxY += 3 * margin; - minY -= std::max(0., margin); - } - - if (histograms[0]->GetXaxis()->IsAlphanumeric()) { - auto alphanumericFrame = (TH1*)histograms[0]->Clone(); - alphanumericFrame->Reset("ICEMS"); - return alphanumericFrame; - } - - return pad.DrawFrame(minX, minY, maxX, maxY); -} - -// overlay 1D histograms -void overlay1D(std::vector hVec, std::vector labelVec, TLegend* additionalLegend, std::string const& outputDir) -{ - TCanvas c("overlay", "", 800, 800); - c.cd(); - - TPad nominalPad("nominalPad", "nominalPad", 0, 0.3, 1., 1.); - nominalPad.SetBottomMargin(0); - TPad ratioPad("ratioPad", "ratioPad", 0, 0.05, 1. ,0.32); - ratioPad.SetTopMargin(0); - ratioPad.SetBottomMargin(0.2); - - nominalPad.Draw(); - ratioPad.Draw(); - - const int colors[7] = {kRed + 2, kBlue - 4, kGreen + 3, kMagenta + 1, kCyan + 2, kOrange + 5, kYellow - 6}; - const int linestyles[6] = {1, 10, 2, 9, 8, 7}; - - TLegend legendOverlay(0.65, 0.7, 0.9, 0.9); - legendOverlay.SetFillStyle(0); - legendOverlay.SetBorderSize(0); - - bool logY{}; - nominalPad.cd(); - auto frame = makeFrameFromHistograms(nominalPad, hVec, logY); - frame->SetTitle(hVec[0]->GetTitle()); - auto yAxis = frame->GetYaxis(); - yAxis->ChangeLabel(1, -1, -1, -1, -1, -1, " "); - yAxis->SetTitleFont(43); - yAxis->SetTitleSize(20); - yAxis->SetLabelFont(43); - yAxis->SetLabelSize(20); - yAxis->SetTitle(hVec[0]->GetYaxis()->GetTitle()); - auto xAxis = frame->GetXaxis(); - xAxis->SetLabelFont(43); - xAxis->SetLabelSize(0); - - - std::vector ratios; - - std::string emptyText; - for (int i = 0; i < hVec.size(); i++) { - auto& h = hVec[i]; - - h->SetStats(0); - h->SetLineStyle(linestyles[i % 6]); - h->SetLineWidth(1); - h->SetLineColor(colors[i % 7]); - - if (i > 0) { - // no ratio for the first histogram (which would simply be 1) - TH1* hRatio = (TH1*)h->Clone(); - hRatio->Divide(h, hVec[0], 1.0, 1.0, "B"); // error option? - ratios.push_back(hRatio); - } - - legendOverlay.AddEntry(h, labelVec[i].c_str()); - - h->Draw("same E hist"); - if (h->GetEntries() == 0) { - emptyText += labelVec[i] + ", "; - } - } - - if (logY) { - nominalPad.SetLogy(); - } - - if (additionalLegend) { - additionalLegend->SetBorderSize(0); - additionalLegend->SetFillStyle(0); - // To reposition the legend we need to: Draw, Update, set new coordinates, Modified - additionalLegend->Draw("same"); - nominalPad.Update(); - additionalLegend->SetX1NDC(0.15); - additionalLegend->SetY1NDC(0.7); - additionalLegend->SetX2NDC(0.4); - additionalLegend->SetY2NDC(0.9); - nominalPad.Modified(); - } - legendOverlay.Draw("same"); - - if (!emptyText.empty()) { - emptyText.pop_back(); - emptyText.pop_back(); - emptyText = std::string("EMPTY: ") + emptyText; - TText *t1 = new TText(0.2, 0.5, emptyText.c_str()); - t1->SetNDC(); - t1->Draw(); - } - - ratioPad.cd(); - frame = makeFrameFromHistograms(ratioPad, ratios, logY); - yAxis = frame->GetYaxis(); - yAxis->SetTitleFont(43); - yAxis->SetTitleSize(20); - yAxis->SetLabelFont(43); - yAxis->SetLabelSize(20); - yAxis->SetTitle("ratio"); - - xAxis = frame->GetXaxis(); - xAxis->SetTitleFont(43); - xAxis->SetTitleSize(20); - xAxis->SetLabelFont(43); - xAxis->SetLabelSize(20); - xAxis->SetTitle(hVec[0]->GetXaxis()->GetTitle()); - - for (int i = 0; i < ratios.size(); i++) { - auto& h = ratios[i]; - h->Draw("same"); - } - - if (logY) { - ratioPad.SetLogy(); - } - - auto savePath = outputDir + "/" + hVec[0]->GetName() + ".png"; - c.SaveAs(savePath.c_str()); - c.Close(); -} - -// overlay 2D histograms -void overlay2D(std::vector hVec1, std::vector labelVec, TLegend* legend, std::string const& outputDir) -{ - std::vector hVec; - for (auto h : hVec1){ - hVec.push_back(dynamic_cast(h)); - } - int nHistos = hVec.size(); - - TCanvas c("overlay", "", 2400, 800 * (nHistos-1)); - c.Divide(3, nHistos - 1); - c.cd(1); - hVec[0]->SetTitle(hVec[0]->GetTitle() + TString("(" + labelVec[0] + ")")); - hVec[0]->SetStats(0); - hVec[0]->Draw("colz"); - - if (hVec[0]->GetEntries() == 0) { - TText *t1 = new TText(0.5, 0.5, "EMPTY"); - t1->SetNDC(); - t1->Draw(); - } - - for (int i = 1; i < nHistos; i++){ - auto hDiv = (TH2*)hVec[i]->Clone(Form("%s_ratio", hVec[i]->GetName())); - hDiv->SetTitle(hVec[i]->GetTitle() + TString("(" + labelVec[i] + "/"+labelVec[0]+")")); - hDiv->SetStats(0); - hDiv->Divide(hVec[0]); - hVec[i]->SetTitle(hVec[i]->GetTitle() + TString("(" + labelVec[i] + ")")); - hVec[i]->SetStats(0); - - c.cd(i * 3 - 1); - hVec[i]->Draw("colz"); - if (hVec[i]->GetEntries() == 0) { - TText *t1 = new TText(0.5, 0.5, "EMPTY"); - t1->SetNDC(); - t1->Draw(); - } - - c.cd(i * 3); - hDiv->Draw("colz"); - } - - if (legend){ - c.cd(3); - legend->SetTextSize(0.03); - legend->SetTextFont(62); - legend->Draw("same"); - gPad->Update(); - legend->SetX1NDC(0.4); - legend->SetY1NDC(0.7); - legend->SetX2NDC(0.89); - legend->SetY2NDC(0.89); - gPad->Modified(); - } - - auto savePath = outputDir + "/" + hVec[0]->GetName() + ".png"; - c.SaveAs(savePath.c_str()); - c.Close(); -} - -// entry point for overlay plots from ReleaseValidation.C -void PlotOverlayAndRatio(std::vector hVec, std::vector labelVec, std::string outputDir = "overlayPlots", TLegend* legendMetrics = nullptr) -{ - if (!std::filesystem::exists(outputDir)) { - std::filesystem::create_directory(outputDir); - } - - bool is2D = false; - for (auto h : hVec){ - if (dynamic_cast(h)){ - std::cerr << "Cannot yet overlay 3D histograms\nSkipping " << h->GetName() << "\n"; - return; - } - if (dynamic_cast(h)){ - is2D = true; - } - } - - if (is2D){ - overlay2D(hVec, labelVec, legendMetrics, outputDir); - } - else { - overlay1D(hVec, labelVec, legendMetrics, outputDir); - } -} - -// entry point for plotting only overlays -void PlotOverlays(initializer_list fileNames_list, initializer_list labelVec_list, std::string outputDir = "overlayPlots"){ - std::vector fileNames(fileNames_list); - std::vector labelVec(labelVec_list); - - if (fileNames.size() > labelVec.size()){ - for (int i=labelVec.size();i files; - for (auto& fileName : fileNames){ - TFile* thisFile = TFile::Open(fileName.c_str(), "READ"); - files.push_back(thisFile); - } - - TIter next(files[0]->GetListOfKeys()); - TKey* key{}; - while ((key = static_cast(next()))) { - std::vector hVec; - hVec.push_back(static_cast(key->ReadObj())); - auto oname = key->GetName(); - bool foundAll = true; - for (int i=1; i(files[i]->Get(oname)); - if (!hNew) { - // That could still happen in case we compare either comletely different file by accident or something has been changed/added/removed - foundAll = false; - std::cerr << "ERROR: Histogram " << oname << " not found in file " << fileNames[i].c_str() << "\n"; - } - hVec.push_back(hNew); - } - if (foundAll){ - PlotOverlayAndRatio(hVec, labelVec, outputDir); - } - else { - std::cerr << "ERROR: Histogram " << oname << " not found in all files\n"; - return; - } - } -} diff --git a/RelVal/README.md b/RelVal/README.md index e50a897fc..680f9a4db 100644 --- a/RelVal/README.md +++ b/RelVal/README.md @@ -1,159 +1,3 @@ # O2DPG ReleaseValidation (RelVal) -The RelVal is specifically designed to compare 2 sets of QC objects. However, it is also possible to compare ROOT files that contain other objects such as histograms (`TH1`) or also `TTree`s: -* ROOT histograms (deriving from `TH1`) -* ROOT `TProfile` -* ROOT `TEfficiency` -* O2 `o2::quality_control::core::MonitorObjectCollection` -* O2 `o2::quality_control::core::MonitorObject` -* ROOT `TTree` (Here the algorithm does its best to extract as many TLeafs as possible which works when they can be drawn with `TTree::Draw`.) - -Objects from compared files are extracted recursively and so all objects in sub-directories are compared. - -The convention is, that only those objects that have the exact same path are compared to one another so the 2 ROOT files must have the same structure. Note though, that all possible pairs are compared. If there are singular objects in one or the other file, they will be safely ignored. - -At the end of this README are some examples for QC RelVal. - -## Definitions - -### Metric -A metric is a way to compare 2 corresponding objects and assign a number to that comparison. There are currently 3 definitions: -1. `chi2`: Chi2 test of compared histograms (see also the [ROOT documentation](https://root.cern.ch/doc/master/classTH1.html#ab7d63c7c177ccbf879b5dc31f2311b27)), -1. `kolmogorov`: shape comparison using Kolmogorov test (see also the [ROOT documentation](https://root.cern.ch/doc/master/classTH1.html#aeadcf087afe6ba203bcde124cfabbee4)), -1. `num_entries`: relative difference in the number of entries. -So for each pair of histograms there can be multiple metrics. - -### Test -A test is the comparison of a computed metric to certain limits (upper,lower). How these limits came about is the property of such a test. For instance, a simple **threshold** test, where lower is better, would mean to have limits of `(, -infty)`. -There can hence be multiple tests for one metric. - -### Interpretation -A test can be assigned an interpretation. There are -1. `GOOD` if a metric passes a test, -1. `WARNING`: if a **non-critical** metric fails a test, -1. `NONCRIT_NC` if the objects could not be compared e.g. due to different binning or axis ranges **and** if the metric is considered **non-critical**, -1. `CRIT_NC` if the histograms could not be compared e.g. due to different binning or axis ranges **and** if the metric is considered **critical**, -1. `BAD` if a test of a amtric fails that is considered **critical** -1. `UNKNOWN` used for instance when a test might have been defined but no metric was passed to be tested. - -## Usage - -The [Python script](o2dpg_release_validation.py) is the entrypoint of the RelVal and it has multiple sub-commands. - -The full help message of this script can be seen by typing -```bash -${O2DPG_ROOT}/RelVal/o2dpg_release_validation.py [] --help -``` -The wrapper includes 3 different sub-commands for now -1. `rel-val` to steer the RelVal, -1. `inspect` to print histograms of specified severity (if any), -1. `compare` to compare the results of 2 RelVal runs, -1. `print` simply print object names, metric names or test names line-by-line to the command line; convenient to further digest the output, -1. `influx` to convert the summary into a format that can be understood by and sent to an InfluxDB instance. - -Each sub-command can be run with `--help` to see all options/flags. - -### `rel-val` - -If you would like to compare 2 files (or sets of files), simply run -```bash -${O2DPG_ROOT}/RelVal/o2dpg_release_validation.py rel-val -i -j \ - [--include-dirs ] -``` -It will run the full release validation, dumps plots and further artifacts in the directory `rel_val` and prints a result summary in the terminal. -Via the optional `--include-patterns` a list of patterns can be passed so that only those ROOT sub-directories are taken into consideration which contain at least on of those patters, **Note** though, that regular expressions cannot (yet) be used. - -For the comparison of 2 sets of files this is always the first necessary step and of the most important outputs produced is `rel_val/Summary.json` which contains all the test results. It can be used for further and also more in-depth studies as mentioned in the following. - -There are also various plots created during the RelVal run. For each compared file there are -* overlay plots, 1D and 2D (to be found in the sub directory `overlayPlots`), -* 2D plots summarising the interpretations in a grid (called `SummaryTest.png`), -* pie charts showing the fraction of interpretations per metric (and potentially per test, if there are multiple), -* 1D plots showing the computed value and test means per metric (and potentially per test, if there are multiple). - - -### `inspect` -This command requires that a `rel-val` was run previously which produced a `/Summary.json`. - -Imagine you would like to change or experiment with some settings, e.g. you would like to only take objects with certain names into account or only enable certain metrics etc. These things you like to see reflected in the summary as well as in the produced plots. -This is possible with -```bash -${O2DPG_ROOT}/RelVal/o2dpg_release_validation.py inspect --path \ - [--include-patterns ] [--exclude-patterns ] \ - [--enable-metric ] [--disable-metric ] \ - [--interpretations ] \ - [--critical ] \ - [--output|-o ] -``` -All of those options, except for `--include-patterns` and `--exclude-patterns` also work with the `rel-val` command. -The output will by default be written to `rel_val_inspect`. All plots which are produced by the `rel-val` command are produced again for a potential given sub-set depending on the given options. Only the overlay plots are not produced again. - -**NOTE** that with `inspect` the original overlay plots satisfying your selection criteria (e.g. `--include-patters` or `--interpretations`) are also copied over to the target directory. - -**Other additional optional arguments** -* `--use-values-as-thresholds []`: By passing a set of summaries that where produced from `rel-val`, the computed metric values can be used as **new** thresholds. To decide how to combine the values for multiple metrics referring to the same object, the option `--combine-thresholds mean|extreme` can be used. Also, an additional relative margin can be added for each metric with `--margin-threshold `; this argument must be repeated for if it should be used for multiple metrics. -* `--regions []`: This computes means and standard deviations for each metric from previously computed values. The corresponding test is passed, if the value lies around the mean within the standard deviations. The deviation from the mean is also given as number-of-sigmas in the summary grid. -* `rel-val -i -j --no-extract` runs RelVal on **flat** ROOT files that have only histogram objects in them. - -### `print` -This command has the same optional arguments as the `inspect` command. But the only thing it does is writing some information line-by-line. For instance, to get the object names that were flagged `BAD` by the `chi2` metric, do -```bash -${O2DPG_ROOT}/RelVal/o2dpg_release_validation.py print --path --enable-metric chi2 --interpretations BAD -``` -If no RelVal was run but one would like to know the available metrics, one can check with -```bash -${O2DPG_ROOT}/RelVal/o2dpg_release_validation.py print --metric-names -``` - -### `influx` - -To convert the final output to something that can be digested by InfluxDB, use -```bash -${O2DPG_ROOT}/RelVal/o2dpg_release_validation.py influx --dir [--tags k1=v1 k2=v2 ...] [--table-name ] -``` -When the `--tags` argument is specified, these are injected as TAGS for InfluxDB in addition. The table name can also be specified explicitly; if not given, it defaults to `O2DPG_MC_ReleaseValidation`. - -## RelVal for QC (examples) - -### Comparing data with MC - -There is an ongoing effort to unify the names of QC objects inside MC and data QC files. Some are already unified and the following command would run comparison of those. However, others are not yet unified and will not be considered in the comparison. - -MC QC objects are usually distributed over multiple files while those from data are all contained in one single file. It is possible to directly compare them with -```bash -${O2DPG_ROOT}/RelVal/o2dpg_release_validation.py rel-val -i ${MC_PRODUCTION}/QC/*.root -j ${DATA_PRODUCTION}/QC.root [--include-dirs ] -``` - -## Run for QC -This is a simple guide to run RelVal for QC. - -### If you are interested in all QC plots -To have everything and to use this as a starting point for deeper inspections, first run -```bash -${O2DPG_ROOT}/RelVal/o2dpg_release_validation.py rel-val -i QC_file_1.root -j QC_file_2.root -o rel_val_all [--labels meaningfulLabel1 meaningfulLabel2] -``` -Now, there is of course a lot but from now on you are fully flexible. - -In order to get some insight into a specific detector, say ITS, run -```bash -${O2DPG_ROOT}/RelVal/o2dpg_release_validation.py inspect --path rel_val_all --include-patterns "^ITS_" -o rel_val_ITS -``` -This will only print pie charts and summaries for ITS and also copies all overlay plots related to ITS to your target directory `rel_val_ITS`. - -The `inspect` command is much faster now since no new plots are generated and metrics do not have to be recomputed. It simply filters the results according to your criteria. However, what can be re-evaluated are the computed values against new thresholds. - -### If you are only interested in some ROOT sub-directories to begin with -If you only want to study for instance the ITS and CPV and there is no interest at this point to study any other detector, run -```bash -${O2DPG_ROOT}/RelVal/o2dpg_release_validation.py rel-val -i QC_file_1.root -j QC_file_2.root -o rel_val_all --include-dirs ITS CPV [--labels meaningfulLabel1 meaningfulLabel2] -``` -From here on, you can use the `inspect` command as usual. But there will never be detectors other than ITS and CPV. - -## Expert section - -### Adding a new metric -A new metric can be added in [ReleaseValidationMetrics.C](ReleaseValidationMetrics.C) by extending the function `void initialiseMetrics(MetricRunner& metricRunner)`. - -## Future plans - -* Store a JSON/JSONs on CCDB for central derivation of more refined thresholds or regions. +Please find the detailed documentation at [https://aliceo2group.github.io/simulation/docs/relval/](https://aliceo2group.github.io/simulation/docs/relval/). diff --git a/RelVal/config/QC/async/ITS_include_patterns_default.txt b/RelVal/config/QC/async/ITS_include_patterns_default.txt new file mode 100644 index 000000000..f8a391236 --- /dev/null +++ b/RelVal/config/QC/async/ITS_include_patterns_default.txt @@ -0,0 +1,16 @@ +# note that all "/" have to be replaced by "_" +# for data and MC +ITS_Tracks_PhiDistribution$ +ITS_Tracks_NClusters$ +ITS_Tracks_VertexZ$ +ITS_Tracks_VertexRvsZ$ +ITS_Tracks_VertexCoordinates$ +ITS_Clusters_Layer0_AverageClusterSizeSummary$ +ITS_Clusters_Layer3_AverageClusterSizeSummary$ +# only MC +ITS_TracksMc_efficiency_eta_ratioFromTEfficiency$ +ITS_TracksMc_efficiency_phi_ratioFromTEfficiency$ +ITS_TracksMc_efficiency_pt_ratioFromTEfficiency$ +ITS_TracksMc_faketrack_eta_ratioFromTEfficiency$ +ITS_TracksMc_faketrack_phi_ratioFromTEfficiency$ +ITS_TracksMc_faketrack_pt_ratioFromTEfficiency$ diff --git a/RelVal/config/QC/async/MFT_include_patterns_default.txt b/RelVal/config/QC/async/MFT_include_patterns_default.txt new file mode 100644 index 000000000..a2b013ea1 --- /dev/null +++ b/RelVal/config/QC/async/MFT_include_patterns_default.txt @@ -0,0 +1,9 @@ +# note that all "/" have to be replaced by "_" +MFT_Tracks_mMFTTrackEta_5_MinClusters$ +MFT_Tracks_mMFTTrackPhi_5_MinClusters$ +MFT_Tracks_mMFTTrackEta_6_MinClusters$ +MFT_Tracks_mMFTTrackPhi_6_MinClusters$ +MFT_Tracks_mMFTTrackInvQPt$ +MFT_Tracks_mMFTTrackNumberOfClusters$ +MFT_Tracks_mMFTTrackROFSize$ +MFT_Clusters_mClustersROFSize$ diff --git a/RelVal/config/QC/async/TOF_include_patterns_default.txt b/RelVal/config/QC/async/TOF_include_patterns_default.txt new file mode 100644 index 000000000..2a9204335 --- /dev/null +++ b/RelVal/config/QC/async/TOF_include_patterns_default.txt @@ -0,0 +1,11 @@ +# note that all "/" have to be replaced by "_" +TOF_Digits_DecodingErrors$ +TOF_Digits_Multiplicity_Integrated$ +TOF_MatchTrAll_mTOFChi2ITSTPC-ITSTPCTRD$ +TOF_MatchTrAll_mTOFChi2TPC$ +TOF_MatchTrAll_mTOFChi2TPCTRD$ +TOF_MatchTrAll_mEffPt_ITSTPC-ITSTPCTRD_ratioFromTEfficiency$ +TOF_PID_EvTimeTOF$ +TOF_PID_DeltaBCTOFFT0$ +TOF_PID_DeltatPi_Pt_ITSTPC_t0TOF$ +TOF_PID_DeltatPi_Pt_ITSTPCTRD_t0TOF$ diff --git a/RelVal/config/QC/async/TPC_include_patterns_default.txt b/RelVal/config/QC/async/TPC_include_patterns_default.txt new file mode 100644 index 000000000..4472729e5 --- /dev/null +++ b/RelVal/config/QC/async/TPC_include_patterns_default.txt @@ -0,0 +1,13 @@ +# note that all "/" have to be replaced by "_" +TPC_Tracks_hPhiAside$ +TPC_Tracks_hPhiCside$ +TPC_Tracks_hEta$ +TPC_Tracks_hNClustersAfterCuts$ +TPC_Tracks_hQOverPt$ +TPC_Tracks_hDCAr_A_Pos$ +TPC_Tracks_hDCAr_C_Pos$ +TPC_Tracks_hDCArVsEtaPos$ +TPC_Tracks_h2DNClustersPhiAside$ +TPC_Tracks_h2DNClustersPhiCside$ +TPC_PID_hdEdxTotMIP_TPC$ +TPC_PID_hdEdxTotVsP_TPC$ diff --git a/RelVal/o2dpg_overlay_plots.py b/RelVal/o2dpg_overlay_plots.py index 8f9bae311..12682ca0b 100755 --- a/RelVal/o2dpg_overlay_plots.py +++ b/RelVal/o2dpg_overlay_plots.py @@ -2,10 +2,9 @@ import sys import argparse -from os import environ, makedirs, remove -from os.path import join, exists, abspath -from subprocess import Popen, PIPE, STDOUT -from shlex import split +import importlib.util +from os import environ +from os.path import join # make sure O2DPG + O2 is loaded O2DPG_ROOT=environ.get('O2DPG_ROOT') @@ -14,91 +13,54 @@ print('ERROR: This needs O2DPG loaded') sys.exit(1) -ROOT_MACRO_EXTRACT=join(O2DPG_ROOT, "RelVal", "ExtractAndFlatten.C") -ROOT_MACRO_OVERLAYS=join(O2DPG_ROOT, "RelVal", "PlotOverlays.C") - -FLATTENED_FILE_NAME = "newfile" - -def run_macro(cmd, log_file, output_dir): - p = Popen(split(cmd), cwd=output_dir, stdout=PIPE, stderr=STDOUT, universal_newlines=True) - log_file = open(log_file, 'a') - for line in p.stdout: - log_file.write(line) - p.wait() - log_file.close() - -def call_extract_and_flatten(inputs, output_dir): - output_dir = abspath(output_dir) - if not exists(output_dir): - makedirs(output_dir) - log_file_extract = join(abspath(output_dir), "extract_and_flatten.log") - counter = 0 - firstfile = join(output_dir,FLATTENED_FILE_NAME+str(1)+".root") - for f in inputs: - counter += 1 - print(f"Extraction of objects from {f}") - f = abspath(f) - newfile = join(output_dir,FLATTENED_FILE_NAME+str(counter)+".root") - if exists(newfile): - remove(newfile) - cmd = "" - if counter==1: - cmd = f"\\(\\\"{f}\\\",\\\"{newfile}\\\",\\\"\\\",\\\"\\\"\\)" - else: - cmd = f"\\(\\\"{f}\\\",\\\"{newfile}\\\",\\\"{firstfile}\\\",\\\"\\\"\\)" - cmd = f"root -l -b -q {ROOT_MACRO_EXTRACT}{cmd}" - run_macro(cmd, log_file_extract, output_dir) - return 0 +spec = importlib.util.spec_from_file_location("o2dpg_release_validation", join(O2DPG_ROOT, "RelVal", 'o2dpg_release_validation.py')) +o2dpg_release_validation = importlib.util.module_from_spec(spec) +spec.loader.exec_module(o2dpg_release_validation) +sys.modules["o2dpg_release_validation"] = o2dpg_release_validation +from o2dpg_release_validation import extract_and_flatten -def call_plot_overlays(inputs, output_dir, labels, no_extract): - nInput = len(inputs) - output_dir = abspath(output_dir) - if not exists(output_dir): - makedirs(output_dir) - cmd = f"\\({{" - for i in range(nInput): - if not no_extract: - f = join(output_dir,FLATTENED_FILE_NAME+str(i+1)+".root") - else: - f = abspath(inputs[i]) - cmd = cmd + f"\\\"{f}\\\"," - cmd = cmd[:-1] - cmd = cmd + f"}},{{" - if labels: - for l in labels: - cmd = cmd + f"\\\"{l}\\\"," - cmd = cmd[:-1] - cmd = cmd + f"}},\\\"{output_dir}\\\"\\)" - cmd = f"root -l -b -q {ROOT_MACRO_OVERLAYS}{cmd}" - log_file_extract = join(output_dir, "plot_overlays.log") - print("Make the plots") - run_macro(cmd, log_file_extract, output_dir) - return 0 +spec = importlib.util.spec_from_file_location("o2dpg_release_validation_plot", join(O2DPG_ROOT, "RelVal", "utils", 'o2dpg_release_validation_plot.py')) +o2dpg_release_validation_plot = importlib.util.module_from_spec(spec) +spec.loader.exec_module(o2dpg_release_validation_plot) +sys.modules["o2dpg_release_validation_plot"] = o2dpg_release_validation_plot +from o2dpg_release_validation_plot import plot_overlays_no_rel_val + + +def run(args): + + if not args.labels: + args.labels = [f"label_{i}" for i, _ in enumerate(args.input)] + + if len(args.labels) != len(args.input): + print("ERROR: Number of input files and labels is different, must be the same") + return 1 + + out_configs = [] + ref_file = None + for i, (input_file, label) in enumerate(zip(args.inputs, args.labels)): + + _, config = extract_and_flatten(input_file, args.output, label, prefix=i, reference_extracted=ref_file) + if not config: + print(f"ERROR: Problem with input file {input_file}, cannot extract") + return 1 + + if not ref_file: + ref_file = config["path"] + + out_configs.append(config) + + plot_overlays_no_rel_val(out_configs, args.output) -def clean_up(nInput, output_dir): - for i in range(nInput): - f = join(output_dir,FLATTENED_FILE_NAME+str(i+1)+".root") - remove(f) - print(f"Remove {f}") return 0 + def main(): parser = argparse.ArgumentParser() parser.add_argument("-i", "--input", nargs="*", help="list of ROOT files", required=True) parser.add_argument("-o", "--output", help="output directory", default="overlayPlots") parser.add_argument("-l", "--labels", nargs="*", help="plot labels") - parser.add_argument("--clean", help="delete newfile.root files after macro has finished", action="store_true") - parser.add_argument("--no-extract", dest="no_extract", help="no extraction but immediately expect histograms present for comparison", action="store_true") - args = parser.parse_args() - if not args.no_extract: - call_extract_and_flatten(args.input, args.output) - call_plot_overlays(args.input, args.output, args.labels, args.no_extract) - if args.clean: - if not args.no_extract: - clean_up(len(args.input), args.output) - else: - print("Nothing to clean up.") - return 0 + return run(parser.parse_args()) + if __name__ == "__main__": sys.exit(main()) diff --git a/RelVal/o2dpg_release_validation.py b/RelVal/o2dpg_release_validation.py index adccdc982..5904a020a 100755 --- a/RelVal/o2dpg_release_validation.py +++ b/RelVal/o2dpg_release_validation.py @@ -11,9 +11,11 @@ import sys import argparse import importlib.util -from os import environ, makedirs, remove, rename +from os import environ, makedirs, remove from os.path import join, abspath, exists, dirname, basename, isfile -from shutil import copy, rmtree +import json + +import numpy as np # make sure O2DPG + O2 is loaded O2DPG_ROOT=environ.get('O2DPG_ROOT') @@ -24,72 +26,37 @@ O2DPG_ROOT = environ.get("O2DPG_ROOT") -spec = importlib.util.spec_from_file_location("o2dpg_release_validation_variables", join(O2DPG_ROOT, "RelVal", 'o2dpg_release_validation_variables.py')) +spec = importlib.util.spec_from_file_location("o2dpg_release_validation_variables", join(O2DPG_ROOT, "RelVal", "utils", 'o2dpg_release_validation_variables.py')) o2dpg_release_validation_variables = importlib.util.module_from_spec(spec) spec.loader.exec_module(o2dpg_release_validation_variables) sys.modules["o2dpg_release_validation_variables"] = o2dpg_release_validation_variables import o2dpg_release_validation_variables as variables -spec = importlib.util.spec_from_file_location("o2dpg_release_validation_utils", join(O2DPG_ROOT, "RelVal", 'o2dpg_release_validation_utils.py')) +spec = importlib.util.spec_from_file_location("o2dpg_release_validation_utils", join(O2DPG_ROOT, "RelVal", "utils", 'o2dpg_release_validation_utils.py')) o2dpg_release_validation_utils = importlib.util.module_from_spec(spec) spec.loader.exec_module(o2dpg_release_validation_utils) sys.modules["o2dpg_release_validation_utils"] = o2dpg_release_validation_utils -from o2dpg_release_validation_utils import * +import o2dpg_release_validation_utils as utils -spec = importlib.util.spec_from_file_location("o2dpg_release_validation_plot", join(O2DPG_ROOT, "RelVal", 'o2dpg_release_validation_plot.py')) +spec = importlib.util.spec_from_file_location("o2dpg_release_validation_plot", join(O2DPG_ROOT, "RelVal", "utils", 'o2dpg_release_validation_plot.py')) o2dpg_release_validation_plot = importlib.util.module_from_spec(spec) spec.loader.exec_module(o2dpg_release_validation_plot) sys.modules["o2dpg_release_validation_plot"] = o2dpg_release_validation_plot -from o2dpg_release_validation_plot import plot_pie_charts, plot_summary_grid, plot_compare_summaries +from o2dpg_release_validation_plot import plot_pie_charts, plot_summary_grid, plot_compare_summaries, plot_overlays, plot_value_histograms -ROOT_MACRO_EXTRACT=join(O2DPG_ROOT, "RelVal", "ExtractAndFlatten.C") -ROOT_MACRO_RELVAL=join(O2DPG_ROOT, "RelVal", "ReleaseValidation.C") -ROOT_MACRO_METRICS=join(O2DPG_ROOT, "RelVal", "ReleaseValidationMetrics.C") +ROOT_MACRO_EXTRACT=join(O2DPG_ROOT, "RelVal", "utils", "ExtractAndFlatten.C") +ROOT_MACRO_RELVAL=join(O2DPG_ROOT, "RelVal", "utils", "ReleaseValidation.C") +ROOT_MACRO_METRICS=join(O2DPG_ROOT, "RelVal", "utils", "ReleaseValidationMetrics.C") from ROOT import gROOT gROOT.SetBatch() -def copy_overlays(rel_val, input_dir, output_dir): - """ - copy overlay plots in this summary from the input directory to the output directory - """ - input_dir = abspath(input_dir) - output_dir = abspath(output_dir) - - if not exists(input_dir): - print(f"ERROR: Input directory {input_dir} does not exist") - return 1 - - inOutSame = input_dir == output_dir - - input_dir_new = input_dir + "_tmp" - if inOutSame: - # move input directory - rename(input_dir, input_dir_new) - input_dir = input_dir_new - - if not exists(output_dir): - makedirs(output_dir) - - object_names, _ = rel_val.get_result_per_metric_and_test() - object_names = list(set(object_names)) - - ret = 0 - for object_name in object_names: - filename=join(input_dir, f"{object_name}.png") - if exists(filename): - copy(filename, output_dir) - else: - print(f"File {filename} not found.") - ret = 1 - - if inOutSame: - rmtree(input_dir) - - return ret +############################################# +# Helper functions only used in this script # +############################################# def metrics_from_root(): """ @@ -99,7 +66,7 @@ def metrics_from_root(): if exists(log_file_name): remove(log_file_name) cmd = f"root -l -b -q {ROOT_MACRO_METRICS}" - ret = run_macro(cmd, log_file_name) + ret = utils.run_macro(cmd, log_file_name) if ret > 0: return ret @@ -114,13 +81,31 @@ def metrics_from_root(): current_metric = None return 0 -def extract(input_filenames, target_filename, include_file_directories="", add_if_exists=False, reference_extracted=""): + +def load_from_meta_json(json_path): + """ + Load a meta JSON file and return dictionary + """ + if not exists(json_path): + return None + + with open(json_path, "r") as f: + try: + return json.load(f) + except (json.decoder.JSONDecodeError, UnicodeDecodeError): + pass + return None + + +def extract_and_flatten_impl(input_filenames, target_filename, include_file_directories=None, add_if_exists=False, reference_extracted="", json_extracted=""): """ Wrap the extraction of objects to be compared Will be extracted (from TH1, QC objects, TTree etc.), converted to TH1 and put into a flat ROOT file structure. Args: + input_filenames: list + list of input filenames to extract objects from target_filename: str path to file where extracted objects should be saved include_file_directories: iterable or "" (default: "") @@ -130,13 +115,35 @@ def extract(input_filenames, target_filename, include_file_directories="", add_i reference_extracted: str is used in case of the extraction of TTrees in which case the x-axis binning will be set according to that reference to make objects comparable. + json_extracted: str + the path to where the JSON file with the info of "what has been extracted where" will be saved + Returns: - bool - True in case of success, False otherwise + bool: True in case of success, False otherwise """ + def get_files_from_list(list_filename): + """ + Quick helper to extract filenames from what is listed in a given file + """ + collect_files = [] + with open(list_filename, "r") as f: + for line in f: + line = line.strip() + if not line: + continue + collect_files.append(line) + return collect_files include_file_directories = ",".join(include_file_directories) if include_file_directories else "" + if len(input_filenames) == 1 and input_filenames[0][0] == "@": + # if there is only one filename and it starts with "@", assume that it contains the paths of the actual files that should be extracted + read_files_from = input_filenames[0][1:] + input_filenames = get_files_from_list(read_files_from) + if not input_filenames: + print(f"ERROR: Apparently {read_files_from} contains no files to be extracted.") + return False + if exists(target_filename) and not add_if_exists: # this file will otherwise be updated if it exists remove(target_filename) @@ -151,9 +158,9 @@ def extract(input_filenames, target_filename, include_file_directories="", add_i for f in input_filenames: f = abspath(f) print(f" {f}") - cmd = f"\\(\\\"{f}\\\",\\\"{target_filename}\\\",\\\"{reference_extracted}\\\",\\\"{include_file_directories}\\\"\\)" + cmd = f"\\(\\\"{f}\\\",\\\"{target_filename}\\\",\\\"{reference_extracted}\\\",\\\"{include_file_directories}\\\",\\\"{json_extracted}\\\"\\)" cmd = f"root -l -b -q {ROOT_MACRO_EXTRACT}{cmd}" - ret = run_macro(cmd, log_file_name, cwd) + ret = utils.run_macro(cmd, log_file_name, cwd) if ret != 0: print(f"ERROR: Extracting from file {f} failed. Please check logfile {abspath(join(cwd, log_file_name))}") return False @@ -161,7 +168,42 @@ def extract(input_filenames, target_filename, include_file_directories="", add_i return True -def rel_val_root(files1, files2, include_root_dirs, add_to_previous, metrics_enabled, metrics_disabled, label1, label2, output_dir, no_extract=False): +def extract_and_flatten(files, output, label, include_directories=None, add_if_exists=False, prefix=None, reference_extracted=""): + """ + Extract from input files to a flat ROOT file + + Returns the path to a meta JSON and that JSON file loaded as dictionary + """ + + if len(files) == 1: + d = load_from_meta_json(files[0]) + if d is not None: + return files[0], d + + if not exists(output): + makedirs(output) + + json_out = f"{prefix}_extracted.json" if prefix else "extracted.json" + root_out = f"{prefix}_extracted.root" if prefix else "extracted.root" + json_out = abspath(join(output, json_out)) + root_out = abspath(join(output, root_out)) + + if not extract_and_flatten_impl(files, root_out, include_file_directories=include_directories, add_if_exists=add_if_exists, reference_extracted=reference_extracted, json_extracted=json_out): + return None, None + + d = None + with open(json_out, "r") as f: + d = json.load(f) + + d["label"] = label + + with open(json_out, "w") as f: + json.dump(d, f, indent=2) + + return json_out, d + + +def rel_val_root(d1, d2, metrics_enabled, metrics_disabled, output_dir): """ RelVal for 2 ROOT files, simply a wrapper around ReleaseValidation.C macro @@ -189,35 +231,22 @@ def rel_val_root(files1, files2, include_root_dirs, add_to_previous, metrics_ena in case of success, return the path to the JSON with computed metrics None otherwise """ - print("==> Process and compare 2 sets of files <==") - - # prepare the output directory - if not exists(output_dir): - makedirs(output_dir) - log_file_rel_val = join(abspath(output_dir), "rel_val.log") - # flat ROOT files to extract to and read from during RelVal; make absolute paths so we don't confuse ourselves when running e.g. ROOT macros in different directories - file_1 = abspath(join(output_dir, "extracted_objects_1.root")) - file_2 = abspath(join(output_dir, "extracted_objects_2.root")) + print("==> Process and compare 2 sets of files <==") - if no_extract: - # in this case we expect the input files to be what we would otherwise extract first - if len(files1) != 1 or len(files2) != 1: - print(f"ERROR: --no-extract option was passed and expecting list of files to be of length 1 each. However, received lengths of {len(files1)} and {len(files2)}") - return 1 - file_1 = abspath(files1[0]) - file_2 = abspath(files2[0]) - elif not extract(files1, file_1, include_root_dirs, add_to_previous) or not extract(files2, file_2, include_root_dirs, add_to_previous, reference_extracted=file_1): - return None + file_1 = d1["path"] + file_2 = d2["path"] # RelVal on flattened files metrics_enabled = ";".join(metrics_enabled) if metrics_enabled else "" metrics_disabled = ";".join(metrics_disabled) if metrics_disabled else "" - cmd = f"\\(\\\"{file_1}\\\",\\\"{file_2}\\\",\\\"{metrics_enabled}\\\",\\\"{metrics_disabled}\\\",\\\"{label1}\\\",\\\"{label2}\\\"\\)" + cmd = f"\\(\\\"{file_1}\\\",\\\"{file_2}\\\",\\\"{metrics_enabled}\\\",\\\"{metrics_disabled}\\\"\\)" cmd = f"root -l -b -q {ROOT_MACRO_RELVAL}{cmd}" + output_dir = abspath(output_dir) + log_file_rel_val = join(output_dir, "rel_val.log") print("Running RelVal on extracted objects") - ret = run_macro(cmd, log_file_rel_val, cwd=output_dir) + ret = utils.run_macro(cmd, log_file_rel_val, cwd=output_dir) # This comes from the ROOT macro json_path = join(output_dir, "RelVal.json") @@ -246,7 +275,7 @@ def load_rel_val(json_path, include_patterns=None, exclude_patterns=None, enable Returns RelVal """ - rel_val = RelVal() + rel_val = utils.RelVal() rel_val.set_object_name_patterns(include_patterns, exclude_patterns) rel_val.enable_metrics(enable_metrics) rel_val.disable_metrics(disable_metrics) @@ -254,14 +283,14 @@ def load_rel_val(json_path, include_patterns=None, exclude_patterns=None, enable return rel_val -def initialise_evaluator(rel_val, thresholds, thresholds_default, thresholds_margins, thresholds_combine, regions): +def initialise_evaluator(rel_val, thresholds_paths, thresholds_default, thresholds_margins, thresholds_combine, regions_paths): """ Wrapper to create an evaluator Args: rel_val: RelVal the RelVal object that should potentially be tested and is used to derive default threshold - thresholds: iterable or None + thresholds_paths: iterable or None if not None, iterable of string as the paths to RelVal JSONs thresholds_defaults: iterable of 2-tuples or None assign a default threshold value (tuple[1]) to a metric name (tuple[0]) @@ -269,29 +298,44 @@ def initialise_evaluator(rel_val, thresholds, thresholds_default, thresholds_mar add a margin given in percent (tuple[1]) to a threshold value of a metric name (tuple[0]) thresholds_combine: str either "mean" or "extreme", how threshold values extracted from argument thresholds should be combined - regions: iterable or None + regions_paths: iterable or None if not None, iterable of string as the paths to RelVal JSONs Returns: Evaluator """ - evaluator = Evaluator() + evaluator = utils.Evaluator() # initialise to run tests on proper mean +- std - if regions: - rel_val_regions = get_summaries_or_from_file(regions) - initialise_regions(evaluator, rel_val_regions) + if regions_paths: + regions = utils.get_paths_or_from_file(regions_paths) + rel_val_regions = utils.RelVal() + rel_val_regions.load(regions) + utils.initialise_regions(evaluator, rel_val_regions) # initialise to run tests on thresholds thresholds_default = {metric_name: float(value) for metric_name, value in thresholds_default} if thresholds_default else None rel_val_thresholds = None - if thresholds: + if thresholds_paths: thresholds_margins = {metric_name: float(value) for metric_name, value in thresholds_margins} if thresholds_margins else None - rel_val_thresholds = get_summaries_or_from_file(thresholds) - initialise_thresholds(evaluator, rel_val, rel_val_thresholds, thresholds_default, thresholds_margins, thresholds_combine) + thresholds_paths = utils.get_paths_or_from_file(thresholds_paths) + rel_val_thresholds = utils.RelVal() + rel_val_thresholds.load(thresholds_paths) + utils.initialise_thresholds(evaluator, rel_val, rel_val_thresholds, thresholds_default, thresholds_margins, thresholds_combine) evaluator.initialise() return evaluator +################################################################### +# Functions that are called after command line has been processed # +################################################################### + + +def only_extract(args): + if not extract_and_flatten(args.input, args.output, None, args.label, args.reference)[0]: + # checking one of the return values for None + return 1 + return 0 + def rel_val(args): """ @@ -301,7 +345,7 @@ def rel_val(args): """ def interpret_results(result, metric): """ - Taking in a result and the metric it was derived from, assign an interpretation + Taking in a result and the corresponding metric it was derived from and assign an interpretation """ is_critical = args.is_critical is None or metric.name in args.is_critical if not metric.comparable and is_critical: @@ -310,13 +354,13 @@ def interpret_results(result, metric): if not metric.comparable: result.interpretation = variables.REL_VAL_INTERPRETATION_NONCRIT_NC return - if result.result_flag == Result.FLAG_UNKNOWN: + if result.result_flag == utils.Result.FLAG_UNKNOWN: result.interpretation = variables.REL_VAL_INTERPRETATION_UNKNOWN return - if result.result_flag == Result.FLAG_PASSED: + if result.result_flag == utils.Result.FLAG_PASSED: result.interpretation = variables.REL_VAL_INTERPRETATION_GOOD return - if result.result_flag == Result.FLAG_FAILED and is_critical: + if result.result_flag == utils.Result.FLAG_FAILED and is_critical: result.interpretation = variables.REL_VAL_INTERPRETATION_BAD return result.interpretation = variables.REL_VAL_INTERPRETATION_WARNING @@ -326,30 +370,43 @@ def interpret_results(result, metric): need_apply = False is_inspect = False + dict_1 = None + dict_2 = None if hasattr(args, "json_path"): # this comes from the inspect command is_inspect = True - json_path = get_summary_path(args.json_path) + json_path = utils.get_summary_path(args.json_path) annotations = None include_patterns, exclude_patterns = (args.include_patterns, args.exclude_patterns) else: # in this case, new input ROOT files were provided and we need to apply all our tests need_apply = True + # always take everything include_patterns, exclude_patterns = (None, None) if args.add: print(f"NOTE: Extracted objects will be added to existing ones in case there was already a RelVal at {args.output}.\n") - json_path = rel_val_root(args.input1, args.input2, args.include_dirs, args.add, args.enable_metric, args.disable_metric, args.labels[0], args.labels[1], args.output, args.no_extract) + + # each extraction will leave us with a JSON + json_path_1, dict_1 = extract_and_flatten(args.input1, args.output, args.labels[0], args.include_dirs, args.add, prefix="1", reference_extracted=None) + if not json_path_1: + return 1 + json_path_2, dict_2 = extract_and_flatten(args.input2, args.output, args.labels[1], args.include_dirs, args.add, prefix="2", reference_extracted=dict_1["path"]) + if not json_path_2: + return 1 + json_path = rel_val_root(dict_1, dict_2, args.enable_metric, args.disable_metric, args.output) if json_path is None: + print("ERROR: Problem during RelVal") return 1 - annotations = {"batch_i": [abspath(p) for p in args.input1], - "batch_j": [abspath(p) for p in args.input2]} + annotations = {"json_path_1": json_path_1, + "json_path_2": json_path_2} + # now loading and constructing a RelVal object rel_val = load_rel_val(json_path, include_patterns, exclude_patterns, args.enable_metric, args.disable_metric) if need_apply or args.use_values_as_thresholds or args.default_threshold or args.regions: evaluator = initialise_evaluator(rel_val, args.use_values_as_thresholds, args.default_threshold, args.margin_threshold, args.combine_thresholds, args.regions) - rel_val.apply(evaluator) - # assign interpretations to the results we got + rel_val.apply_evaluator(evaluator) + # assign interpretations to the results we got rel_val.interpret(interpret_results) def filter_on_interpretations(result): @@ -363,15 +420,27 @@ def filter_on_interpretations(result): # if this comes from inspecting, there will be the annotations from the rel-val before that ==> re-write it rel_val.write(join(args.output, "Summary.json"), annotations=annotations or rel_val.annotations[0]) - if is_inspect: - copy_overlays(rel_val, join(dirname(json_path), "overlayPlots"), join(args.output, "overlayPlots")) + utils.print_summary(rel_val, variables.REL_VAL_SEVERITIES, long=args.print_long) if not args.no_plot: + print("Now plotting...") # plot various different figures for user inspection plot_pie_charts(rel_val, variables.REL_VAL_SEVERITIES, variables.REL_VAL_SEVERITY_COLOR_MAP, args.output) plot_compare_summaries((rel_val,), args.output) plot_summary_grid(rel_val, variables.REL_VAL_SEVERITIES, variables.REL_VAL_SEVERITY_COLOR_MAP, args.output) - print_summary(rel_val, variables.REL_VAL_SEVERITIES, long=args.print_long) + plot_value_histograms(rel_val, args.output) + + if is_inspect: + if annotations_inspect := rel_val.annotations: + annotations_inspect = annotations_inspect[0] + dict_1 = load_from_meta_json(annotations_inspect["json_path_1"]) + dict_2 = load_from_meta_json(annotations_inspect["json_path_2"]) + + if dict_1 and dict_2: + overlay_plots_out = join(args.output, "overlayPlots") + if not exists(overlay_plots_out): + makedirs(overlay_plots_out) + plot_overlays(rel_val, dict_1, dict_2, overlay_plots_out) return 0 @@ -387,8 +456,8 @@ def compare(args): output_dir = args.output # load - rel_val1 = load_rel_val(get_summary_path(args.input1[0]), args.include_patterns, args.exclude_patterns, args.enable_metric, args.disable_metric) - rel_val2 = load_rel_val(get_summary_path(args.input2[0]), args.include_patterns, args.exclude_patterns, args.enable_metric, args.disable_metric) + rel_val1 = load_rel_val(utils.get_summary_path(args.input1[0]), args.include_patterns, args.exclude_patterns, args.enable_metric, args.disable_metric) + rel_val2 = load_rel_val(utils.get_summary_path(args.input2[0]), args.include_patterns, args.exclude_patterns, args.enable_metric, args.disable_metric) # get the test and metric names they have in common test_names = np.intersect1d(rel_val1.known_test_names, rel_val2.known_test_names) @@ -404,8 +473,8 @@ def compare(args): if args.interpretations and interpretation not in args.interpretations: continue # object names of Results matching an interpretation - object_names_interpretation1 = object_names1[count_interpretations(results1, interpretation)] - object_names_interpretation2 = object_names2[count_interpretations(results2, interpretation)] + object_names_interpretation1 = object_names1[utils.count_interpretations(results1, interpretation)] + object_names_interpretation2 = object_names2[utils.count_interpretations(results2, interpretation)] # elements in 1 that are not in 2... only_in1 = np.setdiff1d(object_names_interpretation1, object_names_interpretation2) # ...and the other way round @@ -433,7 +502,7 @@ def influx(args): """ Create an influxDB metrics file """ - rel_val = load_rel_val(get_summary_path(args.path)) + rel_val = load_rel_val(utils.get_summary_path(args.path)) output_path = args.path if isfile(args.path) else join(args.path, "influxDB.dat") table_name = "O2DPG_MC_ReleaseValidation" @@ -480,7 +549,7 @@ def print_simple(args): return 0 return metrics_from_root() - rel_val = load_rel_val(get_summary_path(args.path), args.include_patterns, args.exclude_patterns, args.enable_metric, args.disable_metric) + rel_val = load_rel_val(utils.get_summary_path(args.path), args.include_patterns, args.exclude_patterns, args.enable_metric, args.disable_metric) def filter_on_interpretations(result): # only consider those results that match a flag requested by the user @@ -508,14 +577,18 @@ def print_header(): print(f"\n{'#' * 25}\n#{' ' * 23}#\n# RUN ReleaseValidation #\n#{' ' * 23}#\n{'#' * 25}\n") -# we define the parser here +################################################################ +# define the parser globally so that it could even be imported # +################################################################ +# common parser for digesting input files COMMON_FILE_PARSER = argparse.ArgumentParser(add_help=False) COMMON_FILE_PARSER.add_argument("-i", "--input1", nargs="*", help="EITHER first set of input files for comparison OR first input directory from simulation for comparison", required=True) COMMON_FILE_PARSER.add_argument("-j", "--input2", nargs="*", help="EITHER second set of input files for comparison OR second input directory from simulation for comparison", required=True) COMMON_FILE_PARSER.add_argument("--labels", nargs=2, help="labels you want to appear in the plot legends in case of overlay plots from batches -i and -j", default=("batch_i", "batch_j")) COMMON_FILE_PARSER.add_argument("--no-extract", dest="no_extract", action="store_true", help="no extraction but immediately expect histograms present for comparison") +# common parser digesting options related to thresholds COMMON_THRESHOLD_PARSER = argparse.ArgumentParser(add_help=False) COMMON_THRESHOLD_PARSER.add_argument("--regions", help="Use calculated regions to test status") COMMON_THRESHOLD_PARSER.add_argument("--default-threshold", dest="default_threshold", action="append", nargs=2) @@ -523,41 +596,53 @@ def print_header(): COMMON_THRESHOLD_PARSER.add_argument("--combine-thresholds", dest="combine_thresholds", choices=["mean", "extreme"], help="Arithmetic mean or extreme value is chosen as threshold", default="mean") COMMON_THRESHOLD_PARSER.add_argument("--margin-threshold", dest="margin_threshold", action="append", nargs=2) +# common parser to digest metric options COMMON_METRIC_PARSER = argparse.ArgumentParser(add_help=False) COMMON_METRIC_PARSER.add_argument("--enable-metric", dest="enable_metric", nargs="*") COMMON_METRIC_PARSER.add_argument("--disable-metric", dest="disable_metric", nargs="*") +# common parser to digest object name patterns COMMON_PATTERN_PARSER = argparse.ArgumentParser(add_help=False) COMMON_PATTERN_PARSER.add_argument("--include-patterns", dest="include_patterns", nargs="*", help="include objects whose name includes at least one of the given patterns (takes precedence)") COMMON_PATTERN_PARSER.add_argument("--exclude-patterns", dest="exclude_patterns", nargs="*", help="exclude objects whose name includes at least one of the given patterns") +# common parser to digest options related to interpretations COMMON_FLAGS_PARSER = argparse.ArgumentParser(add_help=False) COMMON_FLAGS_PARSER.add_argument("--interpretations", nargs="*", help="extract all objects which have at least one test with this severity flag", choices=list(variables.REL_VAL_SEVERITY_MAP.keys())) COMMON_FLAGS_PARSER.add_argument("--is-critical", dest="is_critical", nargs="*", help="set names of metrics that are assumed to be critical") +# common parser to handle verbosity COMMON_VERBOSITY_PARSER = argparse.ArgumentParser(add_help=False) COMMON_VERBOSITY_PARSER.add_argument("--print-long", dest="print_long", action="store_true", help="enhance verbosity") COMMON_VERBOSITY_PARSER.add_argument("--no-plot", dest="no_plot", action="store_true", help="suppress plotting") +# The main parser PARSER = argparse.ArgumentParser(description='Wrapping ReleaseValidation macro') + +# Use various sub-parsers SUB_PARSERS = PARSER.add_subparsers(dest="command") + +# rel-val REL_VAL_PARSER = SUB_PARSERS.add_parser("rel-val", parents=[COMMON_FILE_PARSER, COMMON_METRIC_PARSER, COMMON_THRESHOLD_PARSER, COMMON_FLAGS_PARSER, COMMON_VERBOSITY_PARSER]) REL_VAL_PARSER.add_argument("--include-dirs", dest="include_dirs", nargs="*", help="only include desired directories inside ROOT file; note that each pattern is assumed to start in the top-directory (at the moment no regex or *)") REL_VAL_PARSER.add_argument("--add", action="store_true", help="If given and there is already a RelVal in the output directory, extracted objects will be added to the existing ones") REL_VAL_PARSER.add_argument("--output", "-o", help="output directory", default="rel_val") REL_VAL_PARSER.set_defaults(func=rel_val) +# inspect INSPECT_PARSER = SUB_PARSERS.add_parser("inspect", parents=[COMMON_THRESHOLD_PARSER, COMMON_METRIC_PARSER, COMMON_PATTERN_PARSER, COMMON_FLAGS_PARSER, COMMON_VERBOSITY_PARSER]) INSPECT_PARSER.add_argument("--path", dest="json_path", help="either complete file path to a Summary.json or directory where one of the former is expected to be", required=True) INSPECT_PARSER.add_argument("--output", "-o", help="output directory", default="rel_val_inspect") INSPECT_PARSER.set_defaults(func=rel_val) +# compare COMPARE_PARSER = SUB_PARSERS.add_parser("compare", parents=[COMMON_FILE_PARSER, COMMON_PATTERN_PARSER, COMMON_METRIC_PARSER, COMMON_VERBOSITY_PARSER, COMMON_FLAGS_PARSER]) COMPARE_PARSER.add_argument("--output", "-o", help="output directory", default="rel_val_comparison") COMPARE_PARSER.add_argument("--difference", action="store_true", help="plot histograms with different severity") COMPARE_PARSER.add_argument("--plot", action="store_true", help="plot value and threshold comparisons of RelVals") COMPARE_PARSER.set_defaults(func=compare) +# influx INFLUX_PARSER = SUB_PARSERS.add_parser("influx") INFLUX_PARSER.add_argument("--path", help="directory where ReleaseValidation was run", required=True) INFLUX_PARSER.add_argument("--tags", nargs="*", help="tags to be added for influx, list of key=value") @@ -565,6 +650,7 @@ def print_header(): INFLUX_PARSER.add_argument("--output", "-o", help="output path; if not given, a file influxDB.dat is places inside the RelVal directory") INFLUX_PARSER.set_defaults(func=influx) +# print PRINT_PARSER = SUB_PARSERS.add_parser("print", parents=[COMMON_METRIC_PARSER, COMMON_PATTERN_PARSER, COMMON_FLAGS_PARSER]) PRINT_PARSER.add_argument("--path", help="either complete file path to a Summary.json or directory where one of the former is expected to be") PRINT_PARSER.add_argument("--metric-names", dest="metric_names", action="store_true") @@ -572,6 +658,16 @@ def print_header(): PRINT_PARSER.add_argument("--object-names", dest="object_names", action="store_true") PRINT_PARSER.set_defaults(func=print_simple) +# extract +EXTRACT_PARSER = SUB_PARSERS.add_parser("extract", parents=[COMMON_VERBOSITY_PARSER]) +EXTRACT_PARSER.add_argument("--input", nargs="*", help="Set of input files to be extracted", required=True) +EXTRACT_PARSER.add_argument("--output", "-o", help="output directory", default="rel_val_extracted") +EXTRACT_PARSER.add_argument("--prefix", "-p", help="prefix to prepend to output files") +EXTRACT_PARSER.add_argument("--label", "-l", help="label to be assigned", required=True) +EXTRACT_PARSER.add_argument("--reference", "-r", help="path to a reference extraction file (useful to have same binning when TTrees are extracted)") +EXTRACT_PARSER.set_defaults(func=only_extract) + + def main(): """entry point when run directly from command line""" args = PARSER.parse_args() diff --git a/RelVal/run/run_data_rel_val.sh b/RelVal/run/run_data_rel_val.sh new file mode 100755 index 000000000..9f33f91da --- /dev/null +++ b/RelVal/run/run_data_rel_val.sh @@ -0,0 +1,132 @@ +#!/bin/bash + +if [[ -z ${O2DPG_ROOT+x} ]] ; then + echo "O2DPG is not loaded, probably other packages are missing as well in this environment." + exit 1 +fi + +DATE=$(date '+%Y-%m-%d_%H-%M') +OUTPUT=rel_val_${DATE} +LOGFILE=${OUTPUT}/"rel_val.log" + +function wait_for_jobs() +{ + local n_parallel=${1:-5} + local sleep_time=${2:-2} + while true + do + n_packing=$(jobs | grep "include-patterns" | grep "Running" | wc -l ) + if (( $n_packing >= ${n_parallel} )) + then + sleep $sleep_time + else + break + fi + done +} + +rel_val_qc() +{ + local file1=${1} + local file2=${2} + local label1=${3} + local label2=${4} + local output=${OUTPUT}/QC + local output_all=${output}/all + local output_all_bad=${output_all}_BAD + local output_det="${output}/det" + rm -r ${output} 2>/dev/null + + echo "Full RelVal to ${output_all}" | tee -a ${LOGFILE} + ${O2DPG_ROOT}/RelVal/o2dpg_release_validation.py rel-val -i ${file1} -j ${file2} -o ${output_all} --labels ${label1} ${label2} 2>&1 | tee -a ${LOGFILE} + echo "Extract BAD from ${output_all} and write to ${output_all_bad}" | tee -a ${LOGFILE} + ${O2DPG_ROOT}/RelVal/o2dpg_release_validation.py inspect --path ${output_all} --output ${output_all_bad} --interpretations BAD >> ${LOGFILE} 2>&1 + echo "RelVal per detector..." | tee -a ${LOGFILE} + for det in CPV EMC FDD FT0 FV0 GLO ITS MCH MFT MID PHS TOF TPC TRD ZDC ; do + echo "...for ${det} to ${output_det}_${det}, checking for include pattern int_${det}_ ; the latter might need to be changed depending on the internal file structure of the QC ROOT file" | tee -a ${LOGFILE} + ${O2DPG_ROOT}/RelVal/o2dpg_release_validation.py inspect --path ${output_all} --output ${output_det}_${det} --include-patterns "int_${det}_" >> ${LOGFILE} 2>&1 & + wait_for_jobs 3 + done + wait_for_jobs 1 +} + +rel_val_aod() +{ + local file1=${1} + local file2=${2} + local label1=${3} + local label2=${4} + local output=${OUTPUT}/AOD + local output_all=${output}/all + local output_all_bad=${output_all}_BAD + rm -r ${output} 2>/dev/null + + echo "Full RelVal to ${output_all}" | tee -a ${LOGFILE} + ${O2DPG_ROOT}/RelVal/o2dpg_release_validation.py rel-val -i ${file1} -j ${file2} -o ${output_all} --labels ${label1} ${label2} 2>&1 | tee -a ${LOGFILE} + echo "Extract BAD from ${output_all} and write to ${output_all_bad}" | tee -a ${LOGFILE} + ${O2DPG_ROOT}/RelVal/o2dpg_release_validation.py inspect --path ${output_all} --output ${output_all_bad} --interpretations BAD 2>&1 | tee -a ${LOGFILE} +} + +print_help() +{ + echo "Usage:" + echo "run_data_rel_val.sh [--qc ] [--aod ] [--labels ]" +} + +# Files and labels +AOD1= +AOD2= +QC1= +QC2= +LABEL1="label1" +LABEL2="label2" + +while [[ $# -gt 0 ]]; do + key="$1" + + case $key in + --qc) + shift + QC1=${1} + shift + QC2=${1} + shift + ;; + --aod) + shift + AOD1=${1} + shift + AOD2=${1} + shift + ;; + --labels) + shift + LABEL1=${1} + shift + LABEL2=${1} + shift + ;; + --help|-h) + print_help + exit 0 + ;; + *) + echo "ERROR: Unknown argument ${1}" + print_help + exit 1 + ;; + esac +done + +QC_RET=0 +AOD_RET=0 + +mkdir ${OUTPUT} 2>/dev/null +echo "Do RelVal and output to ${OUTPUT}" | tee -a ${LOGFILE} + +[[ "${QC1}" != "" && "${QC2}" != "" ]] && { rel_val_qc ${QC1} ${QC2} ${LABEL1} ${LABEL2} ; QC_RET=${?} ; } || { echo "No QC RelVal" | tee -a ${LOGFILE} ; } +[[ "${AOD1}" != "" && "${AOD2}" != "" ]] && { rel_val_aod ${AOD1} ${AOD2} ${LABEL1} ${LABEL2} ; AOD_RET=${?} ; } || { echo "No AOD RelVal" | tee -a ${LOGFILE} ; } + +RET=$((QC_RET + AOD_RET)) +echo "Exit with ${RET}" | tee -a ${LOGFILE} +exit ${RET} diff --git a/RelVal/ExtractAndFlatten.C b/RelVal/utils/ExtractAndFlatten.C similarity index 70% rename from RelVal/ExtractAndFlatten.C rename to RelVal/utils/ExtractAndFlatten.C index 09de2c645..11a00dc8e 100644 --- a/RelVal/ExtractAndFlatten.C +++ b/RelVal/utils/ExtractAndFlatten.C @@ -1,10 +1,10 @@ -void ExtractAndFlattenDirectory(TDirectory* inDir, TDirectory* outDir, std::string const& basedOnTree = "", std::string const& currentPrefix = "", std::vector* includeDirs = nullptr); -void ExtractTree(TTree* tree, TDirectory* outDir, std::string const& basedOnTree = "", std::string const& currentPrefix = ""); -void ExtractFromMonitorObjectCollection(o2::quality_control::core::MonitorObjectCollection* o2MonObjColl, TDirectory* outDir, std::string const& currentPrefix = ""); -void WriteHisto(TH1* obj, TDirectory* outDir, std::string const& currentPrefix = ""); -void WriteTEfficiency(TEfficiency* obj, TDirectory* outDir, std::string const& currentPrefix = ""); -void WriteToDirectory(TH1* histo, TDirectory* dir, std::string const& prefix = ""); -bool WriteObject(TObject* o, TDirectory* outDir, std::string const& currentPrefix = ""); +void ExtractAndFlattenDirectory(TDirectory* inDir, TDirectory* outDir, std::vector& collectNames, std::string const& basedOnTree = "", std::string const& currentPrefix = "", std::vector* includeDirs = nullptr); +void ExtractTree(TTree* tree, TDirectory* outDir, std::vector& collectNames, std::string const& basedOnTree = "", std::string const& currentPrefix = ""); +void ExtractFromMonitorObjectCollection(o2::quality_control::core::MonitorObjectCollection* o2MonObjColl, TDirectory* outDir, std::vector& collectNames, std::string const& currentPrefix = ""); +void WriteHisto(TH1* obj, TDirectory* outDir, std::vector& collectNames, std::string const& currentPrefix = ""); +void WriteTEfficiency(TEfficiency* obj, TDirectory* outDir, std::vector& collectNames, std::string const& currentPrefix = ""); +void WriteToDirectory(TH1* histo, TDirectory* dir, std::vector& collectNames, std::string const& prefix = ""); +bool WriteObject(TObject* o, TDirectory* outDir, std::vector& collectNames, std::string const& currentPrefix = ""); // use this potentially to write histograms from TTree::Draw to TDirectory* BUFFER_DIR = nullptr; @@ -19,7 +19,7 @@ bool checkFileOpen(TFile* file) // outputFilename: Where to store histograms of flattened output // basedOnTree: This is in principle only needed for TTrees to determine the x-axis range and binning -int ExtractAndFlatten(std::string const& filename, std::string const& outputFilename, std::string const& basedOnTree = "", std::string const& includeDirsString = "") +int ExtractAndFlatten(std::string const& filename, std::string const& outputFilename, std::string const& basedOnTree = "", std::string const& includeDirsString = "", std::string const& outJson = "") { gROOT->SetBatch(); @@ -52,17 +52,36 @@ int ExtractAndFlatten(std::string const& filename, std::string const& outputFile return 1; } TFile extractedFile(outputFilename.c_str(), "UPDATE"); - ExtractAndFlattenDirectory(&inFile, &extractedFile, basedOnTree, "", includeDirs); + // collect the names so that we can dump them to a JSON file afterwards + std::vector collectNames; + ExtractAndFlattenDirectory(&inFile, &extractedFile, collectNames, basedOnTree, "", includeDirs); inFile.Close(); extractedFile.Close(); + if (!outJson.empty()) { + std::ofstream jsonout(outJson.c_str()); + jsonout << "{\n" << " \"path\": " << std::filesystem::absolute(outputFilename) << ","; + jsonout << "\n" << " \"objects\": [\n"; + int mapIndex = 0; + int mapSize = collectNames.size(); + for (auto& name : collectNames) { + jsonout << "\"" << name << "\""; + if (++mapIndex < mapSize) { + // this puts a comma except for the very last entry + jsonout << ",\n"; + } + } + jsonout << "\n ]\n}"; + jsonout.close(); + } return 0; } // writing a TObject to a TDirectory -void WriteToDirectory(TH1* histo, TDirectory* dir, std::string const& prefix) +void WriteToDirectory(TH1* histo, TDirectory* dir, std::vector& collectNames, std::string const& prefix) { std::string name = prefix + histo->GetName(); + collectNames.push_back(name); histo->SetName(name.c_str()); auto hasObject = (TH1*)dir->Get(name.c_str()); @@ -117,7 +136,7 @@ bool checkIncludePath(std::string thisPath, std::vector*& includeDi } // Read from a given input directory and write everything found there (including sub directories) to a flat output directory -void ExtractAndFlattenDirectory(TDirectory* inDir, TDirectory* outDir, std::string const& basedOnTree, std::string const& currentPrefix, std::vector* includeDirs) +void ExtractAndFlattenDirectory(TDirectory* inDir, TDirectory* outDir, std::vector& collectNames, std::string const& basedOnTree, std::string const& currentPrefix, std::vector* includeDirs) { if (!checkIncludePath(inDir->GetPath(), includeDirs)) { @@ -129,27 +148,27 @@ void ExtractAndFlattenDirectory(TDirectory* inDir, TDirectory* outDir, std::stri auto obj = key->ReadObj(); if (auto nextInDir = dynamic_cast(obj)) { // recursively scan TDirectory - ExtractAndFlattenDirectory(nextInDir, outDir, basedOnTree, currentPrefix + nextInDir->GetName() + "_", includeDirs); + ExtractAndFlattenDirectory(nextInDir, outDir, collectNames, basedOnTree, currentPrefix + nextInDir->GetName() + "_", includeDirs); } else if (auto qcMonitorCollection = dynamic_cast(obj)) { auto qcMonPath = std::string(inDir->GetPath()) + "/" + qcMonitorCollection->GetName(); auto includeDirsCache = includeDirs; if (!checkIncludePath(qcMonPath, includeDirsCache)) { continue; } - ExtractFromMonitorObjectCollection(qcMonitorCollection, outDir, currentPrefix); + ExtractFromMonitorObjectCollection(qcMonitorCollection, outDir, collectNames, currentPrefix); } else if (auto tree = dynamic_cast(obj)) { - ExtractTree(tree, outDir, basedOnTree, currentPrefix); + ExtractTree(tree, outDir, collectNames, basedOnTree, currentPrefix); } else { - if (!WriteObject(obj, outDir, currentPrefix)) { - std::cerr << "Cannot handle object " << obj->GetName() << " which is of class " << key->GetClassName() << "\n"; + if (!WriteObject(obj, outDir, collectNames, currentPrefix)) { + std::cerr << "Cannot handle object of class " << key->GetClassName() << "\n"; } } } } -void ExtractTree(TTree* tree, TDirectory* outDir, std::string const& basedOnTree, std::string const& currentPrefix) +void ExtractTree(TTree* tree, TDirectory* outDir, std::vector& collectNames, std::string const& basedOnTree, std::string const& currentPrefix) { - const std::vector acceptedLeafTypes{"char", "int", "float", "double"}; + const std::vector acceptedLeafTypes{"char", "int", "float", "double", "short", "long"}; TIter next(tree->GetListOfLeaves()); std::vector leafNames; TLeaf* obj = nullptr; @@ -211,18 +230,18 @@ void ExtractTree(TTree* tree, TDirectory* outDir, std::string const& basedOnTree std::cerr << "WARNING: Cannot draw TLeaf " << ln << "\n"; continue; } - WriteObject(currentHist, outDir); + WriteObject(currentHist, outDir, collectNames); } BUFFER_DIR->Clear(); } // extract everything from a o2::quality_control::core::MonitorObjectCollection object -void ExtractFromMonitorObjectCollection(o2::quality_control::core::MonitorObjectCollection* o2MonObjColl, TDirectory* outDir, std::string const& currentPrefix) +void ExtractFromMonitorObjectCollection(o2::quality_control::core::MonitorObjectCollection* o2MonObjColl, TDirectory* outDir, std::vector& collectNames, std::string const& currentPrefix) { std::cout << "--- Process o2 Monitor Object Collection " << o2MonObjColl->GetName() << " ---\n"; int nProcessed{}; for (int j = 0; j < o2MonObjColl->GetEntries(); j++) { - if (WriteObject(o2MonObjColl->At(j), outDir, currentPrefix + o2MonObjColl->GetName() + "_")) { + if (WriteObject(o2MonObjColl->At(j), outDir, collectNames, currentPrefix + o2MonObjColl->GetName() + "_")) { nProcessed++; } } @@ -240,18 +259,22 @@ void adjustName(TObject* o) } // decide which concrete function to call to write the given object -bool WriteObject(TObject* o, TDirectory* outDir, std::string const& currentPrefix) +bool WriteObject(TObject* o, TDirectory* outDir, std::vector& collectNames, std::string const& currentPrefix) { + if (!o) { + std::cerr << "WARNING: Cannot process object, nullptr received.\n"; + return false; + } if (auto monObj = dynamic_cast(o)) { - return WriteObject(monObj->getObject(), outDir, currentPrefix); + return WriteObject(monObj->getObject(), outDir, collectNames, currentPrefix); } adjustName(o); if (auto eff = dynamic_cast(o)) { - WriteTEfficiency(eff, outDir, currentPrefix); + WriteTEfficiency(eff, outDir, collectNames, currentPrefix); return true; } if (auto hist = dynamic_cast(o)) { - WriteHisto(hist, outDir, currentPrefix); + WriteHisto(hist, outDir, collectNames, currentPrefix); return true; } std::cerr << "WARNING: Cannot process object " << o->GetName() << "\n"; @@ -259,13 +282,13 @@ bool WriteObject(TObject* o, TDirectory* outDir, std::string const& currentPrefi } // Implementation to write a TH1 -void WriteHisto(TH1* hA, TDirectory* outDir, std::string const& currentPrefix) +void WriteHisto(TH1* hA, TDirectory* outDir, std::vector& collectNames, std::string const& currentPrefix) { - WriteToDirectory(hA, outDir, currentPrefix); + WriteToDirectory(hA, outDir, collectNames, currentPrefix); } // Implementation to extract TH1 from TEfficieny and write them -void WriteTEfficiency(TEfficiency* hEff, TDirectory* outDir, std::string const& currentPrefix) +void WriteTEfficiency(TEfficiency* hEff, TDirectory* outDir, std::vector& collectNames, std::string const& currentPrefix) { // should I further develop that? // separate numerator and denominator of the efficiency. // NOTE These have no directory assigned -> GOOD @@ -281,9 +304,9 @@ void WriteTEfficiency(TEfficiency* hEff, TDirectory* outDir, std::string const& hEffWrite->SetTitle(Form("%s", hEff->GetTitle())); hEffWrite->Divide(hEffNumerator, hEffDenominator, 1., 1., "B"); - WriteToDirectory(hEffNumerator, outDir, currentPrefix); - WriteToDirectory(hEffDenominator, outDir, currentPrefix); - WriteToDirectory(hEffWrite, outDir, currentPrefix); + WriteToDirectory(hEffNumerator, outDir, collectNames, currentPrefix); + WriteToDirectory(hEffDenominator, outDir, collectNames, currentPrefix); + WriteToDirectory(hEffWrite, outDir, collectNames, currentPrefix); delete hEffNumerator; delete hEffDenominator; diff --git a/RelVal/ReleaseValidation.C b/RelVal/utils/ReleaseValidation.C similarity index 91% rename from RelVal/ReleaseValidation.C rename to RelVal/utils/ReleaseValidation.C index f66edbe47..cbebc9eae 100644 --- a/RelVal/ReleaseValidation.C +++ b/RelVal/utils/ReleaseValidation.C @@ -3,7 +3,6 @@ #include #include #include -#include "PlotOverlays.C" #include "ReleaseValidationMetrics.C" // define a global epsilon @@ -53,7 +52,7 @@ int isEmptyHisto(TH1* h) // 6) select if files have to be taken from the grid or not // 7) choose if specific critic plots have to be saved in a second .pdf file -int ReleaseValidation(std::string const& filename1, std::string const& filename2, std::string const& withMetrics="", std::string const& withoutMetrics="", std::string const& labelA="batch_i", std::string const& labelB="batch_j") +int ReleaseValidation(std::string const& filename1, std::string const& filename2, std::string const& withMetrics="", std::string const& withoutMetrics="") { gROOT->SetBatch(); @@ -125,23 +124,7 @@ int ReleaseValidation(std::string const& filename1, std::string const& filename2 auto ncCode = CheckComparable(hA, hB); auto areComparable = NCCodes::isComparable(ncCode); - TLegend legendMetricsOverlayPlot(0.6, 0.6, 0.9, 0.8); - legendMetricsOverlayPlot.SetBorderSize(1); - legendMetricsOverlayPlot.SetFillStyle(0); - - std::vector metricResults; - metricRunner.evaluate(hA, hB, ncCode, metricResults); - for (auto& metricResult : metricResults) { - if (metricResult.comparable) { - legendMetricsOverlayPlot.AddEntry((TObject*)nullptr, Form("%s = %f", metricResult.name.c_str(), metricResult.value), ""); - } - } - - if (isEmptyHisto(hA) == 2 || isEmptyHisto(hB) == 2) { - std::cerr << "WARNING: Cannot draw objects due to the fact that all entries are in under- or overflow bins\n"; - continue; - } - PlotOverlayAndRatio({hA, hB}, {labelA, labelB}, "overlayPlots", &legendMetricsOverlayPlot); + metricRunner.evaluate(hA, hB, ncCode); nComparisons++; } @@ -155,6 +138,9 @@ int ReleaseValidation(std::string const& filename1, std::string const& filename2 WriteToJsonFromMap(metricRunner); + extractedFile1.Close(); + extractedFile2.Close(); + return 0; } diff --git a/RelVal/ReleaseValidationMetrics.C b/RelVal/utils/ReleaseValidationMetrics.C similarity index 97% rename from RelVal/ReleaseValidationMetrics.C rename to RelVal/utils/ReleaseValidationMetrics.C index 04c7f0a9b..40cff7d7c 100644 --- a/RelVal/ReleaseValidationMetrics.C +++ b/RelVal/utils/ReleaseValidationMetrics.C @@ -130,16 +130,14 @@ struct MetricRunner } } - void evaluate(TH1* hA, TH1* hB, NCCodes::CODE code, std::vector& metricResultsReturn) + void evaluate(TH1* hA, TH1* hB, NCCodes::CODE code) { - metricResultsReturn.clear(); for (auto& metric : metricsEnabled) { if (!metric) { // here is a nullptr so it is not active continue; } metricResults.push_back(metric->evaluate(hA, hB, code)); - metricResultsReturn.push_back(metricResults.back()); } } diff --git a/RelVal/o2dpg_release_validation_plot.py b/RelVal/utils/o2dpg_release_validation_plot.py similarity index 60% rename from RelVal/o2dpg_release_validation_plot.py rename to RelVal/utils/o2dpg_release_validation_plot.py index 38d788382..562f5446d 100755 --- a/RelVal/o2dpg_release_validation_plot.py +++ b/RelVal/utils/o2dpg_release_validation_plot.py @@ -11,26 +11,41 @@ import matplotlib.pyplot as plt from matplotlib.colors import LinearSegmentedColormap import seaborn +from scipy.stats import iqr O2DPG_ROOT = environ.get("O2DPG_ROOT") -spec = importlib.util.spec_from_file_location("o2dpg_release_validation_utils", join(O2DPG_ROOT, "RelVal", '.', 'o2dpg_release_validation_utils.py')) +spec = importlib.util.spec_from_file_location("o2dpg_release_validation_utils", join(O2DPG_ROOT, "RelVal", "utils", '.', 'o2dpg_release_validation_utils.py')) o2dpg_release_validation_utils = importlib.util.module_from_spec(spec) spec.loader.exec_module(o2dpg_release_validation_utils) sys.modules["o2dpg_release_validation_utils"] = o2dpg_release_validation_utils from o2dpg_release_validation_utils import count_interpretations +spec = importlib.util.spec_from_file_location("o2dpg_release_validation_plot_root", join(O2DPG_ROOT, "RelVal", "utils", '.', 'o2dpg_release_validation_plot_root.py')) +o2dpg_release_validation_plot_root = importlib.util.module_from_spec(spec) +spec.loader.exec_module(o2dpg_release_validation_plot_root) +sys.modules["o2dpg_release_validation_plot_root"] = o2dpg_release_validation_plot_root +from o2dpg_release_validation_plot_root import plot_overlays_root, plot_overlays_root_no_rel_val + def plot_pie_charts(rel_val, interpretations, interpretation_colors, out_dir, title="", get_figure=False): + """ + Plot pie charts per metric and test + Each pie chart shows the ratio of given interpretations + """ print("==> Plot pie charts <==") for metric_name, test_name in product(rel_val.known_metrics, rel_val.known_test_names): figure, ax = plt.subplots(figsize=(20, 20)) - colors = [] + # collect counts of interpretations, their colours and labels counts = [] + colors = [] labels = [] object_names, results = rel_val.get_result_per_metric_and_test(metric_name, test_name) + if not len(object_names): + continue + for interpretation in interpretations: n_objects = len(object_names[count_interpretations(results, interpretation)]) if not n_objects: @@ -51,7 +66,47 @@ def plot_pie_charts(rel_val, interpretations, interpretation_colors, out_dir, ti plt.close(figure) +def plot_value_histograms(rel_val, out_dir, title="values histogram", get_figure=False): + """ + Plot a histogram of metric values + """ + + print("==> Plot value histograms <==") + for metric_name in rel_val.known_metrics: + figure, ax = plt.subplots(figsize=(20, 20)) + values = [] + for _, _, metric in zip(*rel_val.get_metrics(metric_name=metric_name)): + if not metric.comparable: + continue + values.append(metric.value) + + if not values: + continue + + ax.set_xlabel(metric_name, fontsize=20) + ax.set_ylabel("counts", fontsize=20) + ax.hist(values, bins=100) + ax.tick_params("both", labelsize=20) + figure.tight_layout() + + figure.suptitle(f"{title} (metric: {metric_name})", fontsize=40) + save_path = join(out_dir, f"histogram_values_{metric_name}.png") + figure.savefig(save_path) + if get_figure: + return figure + plt.close(figure) + + def plot_summary_grid(rel_val, interpretations, interpretation_colors, output_dir, get_figure=False): + """ + Plot a summary grid per test. + + horizontal axis: metric names + vertical axis: object names + + Each cell is coloured according to an interpretation. + In addition, the cells contain the computed metric values + """ print("==> Plot summary grid <==") @@ -65,12 +120,15 @@ def plot_summary_grid(rel_val, interpretations, interpretation_colors, output_di for nt in range(rel_val.number_of_tests): metric_names, object_names, results_matrix = rel_val.get_result_matrix_objects_metrics(nt) - arr = np.full(results_matrix.shape, 0, dtype=int) + # make an array where each interpretation is mapped to a numerical value + arr_interpretation = np.full(results_matrix.shape, 0, dtype=int) + # collect annotations for each cell arr_annot = np.full(results_matrix.shape, "", dtype=object) + # iterate over the cells and set values and annotations it = np.nditer(results_matrix, flags=['multi_index', "refs_ok"]) for _ in it: result = results_matrix[it.multi_index] - arr[it.multi_index] = interpretation_name_to_number[result.interpretation] + arr_interpretation[it.multi_index] = interpretation_name_to_number[result.interpretation] if result.value is not None: annot = f"{result.value:.3f} (mean: {result.mean:.3f})" if result.n_sigmas is not None: @@ -80,8 +138,9 @@ def plot_summary_grid(rel_val, interpretations, interpretation_colors, output_di arr_annot[it.multi_index] = annot + #now comes the plotting figure, ax = plt.subplots(figsize=(20, 20)) - seaborn.heatmap(arr, ax=ax, cmap=cmap, vmin=-0.5, vmax=len(interpretations) - 0.5, yticklabels=object_names, xticklabels=metric_names, linewidths=0.5, annot=arr_annot, fmt="") + seaborn.heatmap(arr_interpretation, ax=ax, cmap=cmap, vmin=-0.5, vmax=len(interpretations) - 0.5, yticklabels=object_names, xticklabels=metric_names, linewidths=0.5, annot=arr_annot, fmt="") cbar = ax.collections[0].colorbar cbar.set_ticks(range(len(colors))) cbar.set_ticklabels(interpretations) @@ -102,9 +161,11 @@ def plot_summary_grid(rel_val, interpretations, interpretation_colors, output_di def plot_compare_summaries(rel_vals, out_dir, *, labels=None, get_figure=False): """ - if labels is given, it needs to have the same length as summaries + Plot the metric values for each object. """ + print("==> Plot metric values <==") + figures = [] if not labels: @@ -118,14 +179,18 @@ def plot_compare_summaries(rel_vals, out_dir, *, labels=None, get_figure=False): for metric_name, test_name in product(metric_names, test_names): figure, ax = plt.subplots(figsize=(20, 20)) + plot_this = False for rel_val, label in zip(rel_vals, labels): object_names, results = rel_val.get_result_per_metric_and_test(metric_name, test_name) values = [result.value for result in results] means = [result.mean for result in results] if not values: continue + plot_this = True ax.plot(object_names, values, label=f"values_{label}") ax.plot(object_names, means, label=f"test_means_{label}") + if not plot_this: + continue ax.legend(loc="best", fontsize=20) ax.tick_params("both", labelsize=20) ax.tick_params("x", rotation=90) @@ -138,3 +203,19 @@ def plot_compare_summaries(rel_vals, out_dir, *, labels=None, get_figure=False): plt.close(figure) if get_figure: return figures + + +def plot_overlays(rel_val, file_config_map1, file_config_map2, out_dir, plot_regex=None): + """ + Wrapper around ROOT overlay plotting + """ + print("==> Plot overlays <==") + plot_overlays_root(rel_val, file_config_map1, file_config_map2, out_dir, plot_regex) + + +def plot_overlays_no_rel_val(file_configs, out_dir): + """ + Wrapper around ROOT plotting when no RelVal object is given + """ + print("==> Plot overlays <==") + plot_overlays_root_no_rel_val(file_configs, out_dir) diff --git a/RelVal/utils/o2dpg_release_validation_plot_root.py b/RelVal/utils/o2dpg_release_validation_plot_root.py new file mode 100755 index 000000000..98bd20ae3 --- /dev/null +++ b/RelVal/utils/o2dpg_release_validation_plot_root.py @@ -0,0 +1,313 @@ +#!/usr/bin/env python3 +# +# Definition common functionality + +from os.path import join, exists +from os import remove +from math import log10, pow +import re + +from ctypes import c_char_p +from ROOT import gSystem, TFile, TCanvas, TPad, TLegend, TH2, TH3, TText, TPaveText, kWhite, kRed, kBlue, kGreen, kMagenta, kCyan, kOrange, kYellow, TProfile + + +def style_histograms(histograms): + if isinstance(histograms[0], (TH2, TH3)): + return + colors = (kRed + 2, kBlue - 4, kGreen + 3, kMagenta + 1, kCyan + 2, kOrange + 5, kYellow - 6) + linestyles = (1, 10, 2, 9, 8, 7) + + for i, h in enumerate(histograms): + h.SetLineStyle(linestyles[i % len(linestyles)]) + h.SetLineColor(colors[i % len(colors)]) + h.SetLineWidth(1) + +def findRangeNotEmpty1D(histogram): + axis = histogram.GetXaxis() + minX = axis.GetBinLowEdge(1) + maxX = axis.GetBinUpEdge(axis.GetNbins()) + + for i in range(1, axis.GetNbins() + 1): + # go from left to right to find the first non-empty bin + if histogram.GetBinContent(i) != 0: + minX = axis.GetBinLowEdge(i) + break + + for i in range(axis.GetNbins(), 0, -1): + # go from right to left to find the last non-empty bin + if histogram.GetBinContent(i) != 0: + maxX = axis.GetBinUpEdge(i) + break + + return minX, maxX + + +def adjust_axis_text(axis, label_size, title_size): + axis.SetLabelFont(43) + axis.SetTitleFont(43) + axis.SetLabelSize(label_size) + axis.SetTitleSize(title_size) + + +def make_frame(pad, histograms): + integralRef = histograms[0].Integral() + shouldBeLog = False + minY = histograms[0].GetMinimum(0) + maxY = histograms[0].GetMaximum() + + minX, maxX = findRangeNotEmpty1D(histograms[0]) + + # find minima and maxima + for h in histograms[1:]: + minY = min(h.GetMinimum(0), minY) + maxY = max(h.GetMaximum(), maxY) + + minXNext, maxXNext = findRangeNotEmpty1D(h) + minX = min(minX, minXNext) + maxX = max(maxX, maxXNext) + + integral = h.Integral() + if (integralRef > 0 and integral / integralRef > 100) or (integral > 0 and integralRef / integral > 100): + # decide whether to do a log plot + shouldBeLog = True + + # finalise the y-axis limits + if shouldBeLog: + margin = log10(maxY / minY) + minY = minY / pow(10, margin * 0.1) + maxY = maxY * pow(10, margin * 0.3) + else: + margin = 0.2 * (maxY - minY) + maxY += 3 * margin + minY -= max(0., margin) + + if histograms[0].GetXaxis().IsAlphanumeric(): + alphanumericFrame = histograms[0].Clone() + alphanumericFrame.Reset("ICEMS") + return alphanumericFrame, shouldBeLog + + frame = pad.DrawFrame(minX, minY, maxX, maxY, histograms[0].GetYaxis().GetTitle()) + + return frame, shouldBeLog + + +def plot_single_overlay_1d(histograms, more_objects, out_path, *args): + + ratios = [] + denominator = histograms[0] + if isinstance(denominator, TProfile): + return + for h in histograms[1:]: + ratio = h.Clone() + ratio.SetDirectory(0) + ratio.Divide(h, denominator, 1, 1, "B") + ratios.append(ratio) + + c = TCanvas("overlay", "", 800, 800) + c.cd() + + nominalPad = TPad("nominalPad", "nominalPad", 0, 0.3, 1., 1.) + nominalPad.SetBottomMargin(0) + ratioPad = TPad("ratioPad", "ratioPad", 0, 0.05, 1. ,0.32) + ratioPad.SetTopMargin(0) + ratioPad.SetBottomMargin(0.2) + + nominalPad.Draw() + ratioPad.Draw() + + nominalPad.cd() + nominalFrame, logY = make_frame(nominalPad, histograms) + yAxis = nominalFrame.GetYaxis() + yAxis.ChangeLabel(1, -1, -1, -1, -1, -1, " ") + + adjust_axis_text(yAxis, 20, 20) + adjust_axis_text(nominalFrame.GetXaxis(), 0, 0) + nominalFrame.Draw("*") + for h in histograms: + h.Draw("same E hist") + for mo in more_objects: + mo.Draw("same") + + if logY: + nominalPad.SetLogy() + + ratioPad.cd() + ratioFrame, logY = make_frame(ratioPad, ratios) + axis = ratioFrame.GetXaxis() + axis.SetTitle(histograms[0].GetXaxis().GetTitle()) + adjust_axis_text(axis, 20, 20) + axis = ratioFrame.GetYaxis() + axis.SetTitle("ratio") + adjust_axis_text(axis, 20, 20) + + ratioFrame.Draw("*") + for ratio in ratios: + ratio.Draw("same") + if logY: + ratioFrame.SetLogy() + + c.SaveAs(out_path) + c.Close() + + +def plot_single_overlay_2d(histograms, more_objects, out_path, labels=None): + + n_histograms = len(histograms) + if not labels: + labels = [f"label_{i}" for i in range(n_histograms)] + + c = TCanvas("overlay", "", 2400, 800 * (n_histograms-1)) + c.Divide(3, n_histograms - 1) + c.cd(1) + histograms[0].SetTitle(histograms[0].GetTitle() + f"({labels[0]})") + histograms[0].SetStats(0); + histograms[0].Draw("colz"); + + keep_elements = [] + if histograms[0].GetEntries() == 0: + t1 = TText(0.5, 0.5, "EMPTY") + keep_elements.append(t1) + t1.SetNDC() + t1.Draw() + + ratios = [] + + for i, h in enumerate(histograms[1:], start=1): + ratio = h.Clone() + ratio.SetDirectory(0) + ratios.append(ratio) + ratio.SetTitle(f"{h.GetTitle()} ({labels[i]} / {labels[0]})") + ratio.SetStats(0) + ratio.Divide(histograms[0]) + h.SetStats(0) + h.SetTitle(f"{h.GetTitle()} ({labels[i]})") + + c.cd(i * 3 - 1) + h.Draw("colz") + if h.GetEntries() == 0: + t1 = TText(0.5, 0.5, "EMPTY") + t1.SetNDC() + t1.Draw() + keep_elements.append(t1) + + c.cd(i * 3) + ratio.Draw("colz") + + c.cd(3) + for mo in more_objects: + mo.Draw("same") + + c.SaveAs(out_path) + c.Close() + + +def plot_overlays_root(rel_val, file_config_map1, file_config_map2, out_dir, plot_regex=None): + + + file1 = TFile(file_config_map1["path"], "READ") + file2 = TFile(file_config_map2["path"], "READ") + + label1 = file_config_map1["label"] + label2 = file_config_map2["label"] + + plot_log_file = join(out_dir, "overlay_plotting.log") + if exists(plot_log_file): + remove(plot_log_file) + + for object_name, metrics, results in rel_val.yield_metrics_results_per_object(): + if plot_regex is not None and not re.search(object_name, plot_regex): + continue + + metric_legend_entries = {} + for metric, result in zip(metrics, results): + if metric.name not in metric_legend_entries: + value = "NONE" if not metric.comparable else f"{metric.value}" + metric_legend_entries[metric.name] = value + if result is None: + continue + metric_legend_entries[metric.name] += f", {result.interpretation}" + + h1 = file1.Get(object_name) + h2 = file2.Get(object_name) + + more_objects = [] + plot_func = plot_single_overlay_2d + metrics_box = TPaveText(0.15, 0.7, 0.4, 0.9, "brNDC") + metrics_box.SetTextFont(43) + metrics_box.SetTextSize(20) + metrics_box.SetBorderSize(0) + more_objects.append(metrics_box) + if not isinstance(h1, (TH2, TH3)): + plot_func = plot_single_overlay_1d + metrics_box.SetFillStyle(0) + style_histograms([h1, h2]) + legend_labels = TLegend(0.65, 0.7, 0.9, 0.9) + legend_labels.SetFillStyle(0) + legend_labels.SetBorderSize(0) + legend_labels.SetTextFont(43) + legend_labels.SetTextSize(20) + legend_labels.AddEntry(h1, label1) + legend_labels.AddEntry(h2, label2) + more_objects.append(legend_labels) + else: + metrics_box.SetFillColor(kWhite) + + legend_metrics = TLegend(0.15, 0.7, 0.4, 0.9) + legend_metrics.SetBorderSize(0) + legend_metrics.SetFillStyle(0) + for key, value in metric_legend_entries.items(): + metrics_box.AddText(f"{key} = {value}") + + out_path = join(out_dir, f"{object_name}.png") + gSystem.RedirectOutput(join(out_dir, "overlay_plotting.log"), "a") + plot_func([h1, h2], more_objects, out_path, [label1, label2]) + gSystem.RedirectOutput(c_char_p(0)) + + print(f"INFO: Log file for overlay plotting at {plot_log_file}") + + +def plot_overlays_root_no_rel_val(file_configs, out_dir): + gSystem.RedirectOutput(join(out_dir, "overlay_plotting.log"), "w") + + all_names = [] + labels = [] + files = [] + names_per_file = [] + for fc in file_configs: + all_names.extend(fc["objects"]) + labels.append(fc["label"]) + names_per_file.append(fc["objects"]) + files.append(TFile(fc["path"], "READ")) + + all_names = list(set(all_names)) + for name in all_names: + histograms = [] + current_labels = [] + + for object_names, label, f in zip(labels, files): + if name not in object_names: + continue + histograms.append(f.Get(name)) + current_labels.append(label) + + if not histograms: + continue + + more_objects = [] + plot_func = plot_single_overlay_2d + if not isinstance(histograms[0], (TH2, TH3)): + plot_func = plot_single_overlay_1d + style_histograms(histograms) + legend_labels = TLegend(0.65, 0.7, 0.9, 0.9) + legend_labels.SetFillStyle(0) + legend_labels.SetBorderSize(0) + legend_labels.SetTextFont(43) + legend_labels.SetTextSize(20) + for h, cl in zip(histograms, current_labels): + legend_labels.AddEntry(h, cl) + more_objects.append(legend_labels) + + out_path = join(out_dir, f"{name}.png") + plot_func(histograms, more_objects, out_path, labels) + + gSystem.RedirectOutput(c_char_p(0)) diff --git a/RelVal/o2dpg_release_validation_utils.py b/RelVal/utils/o2dpg_release_validation_utils.py similarity index 70% rename from RelVal/o2dpg_release_validation_utils.py rename to RelVal/utils/o2dpg_release_validation_utils.py index 235b6116f..2607ab69e 100755 --- a/RelVal/o2dpg_release_validation_utils.py +++ b/RelVal/utils/o2dpg_release_validation_utils.py @@ -3,7 +3,9 @@ # Definition of common functionality import re -from os.path import join, exists, isdir +from os.path import join, exists, isdir, abspath +from os import makedirs, rename +from shutil import rmtree, copy from itertools import product from subprocess import Popen, PIPE, STDOUT from shlex import split @@ -11,20 +13,6 @@ import numpy as np -def remove_outliers(data, m=6.): - """ - Helper to remove outliers from a list of floats - """ - if not data: - return None, None - data = np.array(data) - d = np.abs(data - np.median(data)) - mdev = np.median(d) - s = d / (mdev if mdev else 1.) - print(s) - return data[s < m], data[s >= m] - - def default_evaluation(limits): """ Return a lambda f(value) -> bool @@ -34,10 +22,10 @@ def default_evaluation(limits): if limits[0] is None and limits[1] is None: return lambda x: None if limits[0] is not None and limits[1] is None: - return lambda x: x > limits[0] + return lambda x: x >= limits[0] if limits[0] is None and limits[1] is not None: - return lambda x: x < limits[1] - return lambda x: limits[0] < x < limits[1] + return lambda x: x <= limits[1] + return lambda x: limits[0] <= x <= limits[1] def compute_limits(mean, std): @@ -106,6 +94,9 @@ def __init__(self, object_name=None, name=None, value=None, proposed_threshold=N if in_dict is not None: self.from_dict(in_dict) + def __eq__(self, other): + return self.object_name == other.object_name and self.name == other.name + def as_dict(self): return {"object_name": self.object_name, "metric_name": self.name, @@ -190,36 +181,6 @@ def initialise(self): self.metric_names = np.array(self.metric_names, dtype=str) self.test_names = np.array(self.test_names, dtype=str) self.tests = np.array(self.tests, dtype=TestLimits) - - # fill up tests - # The following guarantees that we have all metrics and all tests for the object names - # NOTE Probably there is a more elegant way?! - test_names_known = np.unique(self.test_names) - metric_names_known = np.unique(self.metric_names) - object_names_known = np.unique(self.object_names) - - object_names_to_add = [] - metric_names_to_add = [] - test_names_to_add = [] - - for object_name, metric_name in product(object_names_known, metric_names_known): - mask = (self.object_names == object_name) & (self.metric_names == metric_name) - if not np.any(mask): - object_names_to_add.extend([object_name] * len(test_names_known)) - metric_names_to_add.extend([metric_name] * len(test_names_known)) - test_names_to_add.extend(test_names_known) - continue - present_test_names = self.test_names[mask] - test_names_not_present = test_names_known[~np.isin(present_test_names, test_names_known)] - test_names_to_add.extend(test_names_not_present) - metric_names_to_add.extend([metric_name] * len(test_names_not_present)) - object_names_to_add.extend([object_name] * len(test_names_not_present)) - - self.object_names = np.array(np.append(self.object_names, object_names_to_add)) - self.metric_names = np.array(np.append(self.metric_names, metric_names_to_add)) - self.test_names = np.array(np.append(self.test_names, test_names_to_add)) - self.tests = np.array(np.append(self.tests, [TestLimits(tnta) for tnta in test_names_to_add])) - self.mask_any = np.full(self.test_names.shape, True) def test(self, metrics): @@ -253,42 +214,51 @@ class RelVal: def __init__(self): # metric names that should be considered (if empty, all) self.include_metrics = [] + # metric names that should be excluded, takes precedence over self.include_metrics self.exclude_metrics = [] - # lists of regex to include/exclude objects by name + # lists of regex to include/exclude objects by their names self.include_patterns = None self.exclude_patterns = None - # collecting everything we have; all of the following will have the same length in the end + # collecting everything we have; the following three members will have the same length self.object_names = None self.metric_names = None # metric objects self.metrics = None - # object and metric names known to this RelVal + # unique object and metric names self.known_objects = None self.known_metrics = None - # collecting all results; all of the following will have the same length in the end + # collecting all results; the following three members will have the same length self.results = None - # indices to refer to self.object_names, self.metric_names and self.metrics + # each index refers to the corresponding object in self.object_names, self.metric_names and self.metrics self.results_to_metrics_idx = None + + # unique list of test names self.known_test_names = None # to store some annotations self.annotations = None - def enable_metrics(self, metrics): - if not metrics: + def enable_metrics(self, metric_names): + """ + Enable a list of metrics given their names + """ + if not metric_names: return - for metric in metrics: + for metric in metric_names: if metric in self.include_metrics: continue self.include_metrics.append(metric) - def disable_metrics(self, metrics): - if not metrics: + def disable_metrics(self, metric_names): + """ + Disable a list of metrics given their names + """ + if not metric_names: return - for metric in metrics: + for metric in metric_names: if metric in self.exclude_metrics: continue self.exclude_metrics.append(metric) @@ -310,8 +280,21 @@ def set_object_name_patterns(self, include_patterns, exclude_patterns): def load_this_patterns(patterns): if not patterns or not patterns[0].startswith("@"): return patterns - with open(patterns[0][1:], "r") as f: - return f.read().splitlines() + + patterns_from_file = [] + filename = patterns[0][1:] + if not exists(filename): + print(f"WARNING: Pattern file {filename} does not exist, not extracting any patterns!") + return + with open(filename, "r") as f: + for line in f: + line = line.strip() + # remove all comments; allows for inline comments or entire comment lines), then take the first token + line = line.split("#")[0].strip() + if not line: + continue + patterns_from_file.append(line) + return patterns_from_file self.include_patterns = load_this_patterns(include_patterns) self.exclude_patterns = load_this_patterns(exclude_patterns) @@ -367,29 +350,25 @@ def add_result(self, metric_idx, result): self.results_to_metrics_idx.append(metric_idx) self.results.append(result) - def load(self, summaries_to_test): + def get_metric_checking_dict(self, in_dict): + """ + Check if that metric is already known + """ + if self.metrics is None: + return None, None - self.annotations = [] - self.object_names = [] - self.metric_names = [] - self.metrics = [] - self.results_to_metrics_idx = [] - self.results = [] + metric = Metric(in_dict=in_dict) - for summary_to_test in summaries_to_test: - summary_to_test = self.read(summary_to_test) - if annotations := summary_to_test.get(RelVal.KEY_ANNOTATIONS, None): - self.annotations.append(annotations) - for line in summary_to_test[RelVal.KEY_OBJECTS]: - metric = Metric(in_dict=line) - if not self.add_metric(metric): - continue + for idx, search_metric in enumerate(self.metrics): + if metric == search_metric: + return idx, search_metric - if "result_name" in line: - # NOTE We could think about not duplicating metrics. - # Because there is the same metric for each of the corresponding test results - self.add_result(len(self.metrics) - 1, Result(in_dict=line)) + return None, metric + def to_numpy(self): + """ + Convert everything that is a list to numpy for faster querying later on + """ self.known_objects = np.unique(self.object_names) self.known_metrics = np.unique(self.metric_names) @@ -402,8 +381,42 @@ def load(self, summaries_to_test): self.results_to_metrics_idx = np.array(self.results_to_metrics_idx, dtype=int) if self.results else None self.test_names_results = np.array([r.name for r in self.results]) if self.results else None self.known_test_names = np.unique(self.test_names_results) if self.results else None - self.result_filter_mask = np.full(self.known_test_names.shape, True) if self.results else None self.results = np.array(self.results, dtype=Result) if self.results else None + self.result_filter_mask = np.full(self.results.shape, True) if self.results is not None else None + + def load(self, summaries_to_test): + """ + Loads and populates this object from a dictionary + """ + self.annotations = [] + self.object_names = [] + self.metric_names = [] + self.metrics = [] + self.results_to_metrics_idx = [] + self.results = [] + + for summary_to_test in summaries_to_test: + # loop over the list of dictionaries given + summary_to_test = self.read(summary_to_test) + if annotations := summary_to_test.get(RelVal.KEY_ANNOTATIONS, None): + self.annotations.append(annotations) + for line in summary_to_test[RelVal.KEY_OBJECTS]: + # each list object corresponds to and object with a certain test result + # first of all we check if that metric is already loaded + idx, metric = self.get_metric_checking_dict(line) + if idx is None: + # in this case, this metric is new + idx = len(self.metrics) + if not self.add_metric(metric): + # only attempt to add if that metric is not yet there + continue + + if "result_name" in line: + # add this result; the result will be mapped to the metric it is based on via the index + self.add_result(idx, Result(in_dict=line)) + + # convert everything that was a list before to numpy objects + self.to_numpy() def get_metrics(self, object_name=None, metric_name=None): """ @@ -422,7 +435,7 @@ def get_metrics(self, object_name=None, metric_name=None): mask = mask & (self.any_mask if metric_name is None else np.isin(self.metric_names, metric_name)) return self.object_names[mask], self.metric_names[mask], self.metrics[mask] - def apply(self, evaluator): + def apply_evaluator(self, evaluator): """ Apply loaded tests """ @@ -438,20 +451,32 @@ def apply(self, evaluator): self.results_to_metrics_idx, self.results = evaluator.test(self.metrics) self.test_names_results = np.array([r.name for r in self.results]) self.known_test_names = np.unique(self.test_names_results) - self.result_filter_mask = np.full(self.known_test_names.shape, True) + self.result_filter_mask = np.full(self.results.shape, True) def interpret(self, interpret_func): + """ + Add an interpretation to the Result objects based on a function given by the user + """ for metric_idx, result in zip(self.results_to_metrics_idx, self.results): interpret_func(result, self.metrics[metric_idx]) def filter_results(self, filter_func): + """ + Construct a mask to filter results without losing any of them + """ if self.results is None: return self.result_filter_mask = [filter_func(result) for result in self.results] def query_results(self, query_func=None): + """ + Query Result objects based on a function given by the user + + Return matching Result objects along with names + """ mask = np.array([query_func is None or query_func(result) for result in enumerate(self.results)]) - mask = mask & self.result_filter_mask + if self.result_filter_mask is not None: + mask = mask & self.result_filter_mask idx = self.results_to_metrics_idx[mask] return np.take(self.object_names, idx), np.take(self.metric_names, idx), self.test_names_results[idx], self.results[idx] @@ -474,15 +499,28 @@ def get_metric_name(self, idx): return self.known_metrics[idx] def get_result_per_metric_and_test(self, metric_index_or_name=None, test_index_or_name=None): + """ + Return Result objects that belong to given metric or test + """ test_name = test_index_or_name if (isinstance(test_index_or_name, str) or test_index_or_name is None) else self.known_test_names[test_index_or_name] metric_name = metric_index_or_name if (isinstance(metric_index_or_name, str) or metric_index_or_name is None) else self.known_metrics[metric_index_or_name] metric_idx = np.argwhere(self.metric_names == metric_name) if metric_name is not None else self.results_to_metrics_idx - mask = np.isin(self.results_to_metrics_idx, metric_idx) & self.result_filter_mask + mask = np.isin(self.results_to_metrics_idx, metric_idx) + if self.result_filter_mask is not None: + mask = mask & self.result_filter_mask if test_name is not None: mask = mask & (self.test_names_results == test_name) return np.take(self.object_names, self.results_to_metrics_idx[mask]), self.results[mask] def get_result_matrix_objects_metrics(self, test_index): + """ + Return a matrix of Result objects + + vertical axis: object names + horizontal axis: metric names + + in addition return metric and object names so the user knows what she gets + """ mask = self.test_names_results == (self.known_test_names[test_index]) idx = self.results_to_metrics_idx[mask] results = self.results[mask] @@ -496,11 +534,38 @@ def get_result_matrix_objects_metrics(self, test_index): return metric_names, object_names, np.reshape(results[idx], (len(object_names), len(metric_names))) + def yield_metrics_results_per_object(self): + """ + One-by-one return metrics and results of objects + """ + results = None + if self.results is not None: + if self.result_filter_mask is not None: + mask = self.result_filter_mask + else: + mask = np.full(self.results.shape, True) + idx = self.results_to_metrics_idx[mask] + object_names = np.take(self.object_names, idx) + metrics = np.take(self.metrics, idx) + results = self.results[mask] + else: + object_names = self.object_names + metrics = self.metrics + + for object_name in np.unique(object_names): + mask = object_names == object_name + yield_metrics = metrics[mask] + yield_results = results[mask] if results is not None else np.array([None] * len(yield_metrics)) + yield object_name, yield_metrics, yield_results + def write(self, filepath, annotations=None): + """ + Write everything to a JSON file + Structure corresponds to what ROOT's RelVal returns so in turn it can be used to construct a RelVal object again + """ all_objects = [] - # TODO return one flat dictionary not a nested one def make_dict_include_results(object_name, metric, result): return {RelVal.KEY_OBJECT_NAME: object_name} | metric.as_dict() | result.as_dict() @@ -510,7 +575,7 @@ def make_dict_exclude_results(object_name, metric, *args): if self.results is None: object_names = self.object_names metrics = self.metrics - results = np.empty(metric.shape, dtype=bool) + results = np.empty(metrics.shape, dtype=bool) make_dict = make_dict_exclude_results else: object_names = np.take(self.object_names, self.results_to_metrics_idx) @@ -525,23 +590,29 @@ def make_dict_exclude_results(object_name, metric, *args): RelVal.KEY_ANNOTATIONS: annotations} with open(filepath, "w") as f: - json.dump(final_dict, f) + json.dump(final_dict, f, indent=2) -def get_summaries_or_from_file(in_objects): - - if len(in_objects) == 1 and in_objects[0].startswith("@"): - with open(in_objects[0][1:], "r") as f: +def get_paths_or_from_file(paths): + """ + Either simply return the paths or extract them from a text file + """ + if len(paths) == 1 and paths[0].startswith("@"): + with open(paths[0][1:], "r") as f: return f.read().splitlines() - return in_objects + return paths def initialise_thresholds(evaluator, rel_val, rel_val_thresholds, thresholds_default, thresholds_margin, thresholds_combine="mean"): - + """ + Add thresholds to the Evaluator as one test case + """ # The default thresholds will be derived and set for all the objects and metrics that we find in the RelVal to test _, _, metrics = rel_val.get_metrics() for metric in metrics: + # get the default thresholds for each metric proposed_threshold = thresholds_default.get(metric.name, metric.proposed_threshold) if thresholds_default else metric.proposed_threshold + # depending on what's better (lower/greater), set the std boundaries std = (None, 0) if metric.lower_is_better else (0, None) evaluator.add_limits(metric.object_name, metric.name, TestLimits("threshold_default", proposed_threshold, std)) @@ -551,56 +622,74 @@ def initialise_thresholds(evaluator, rel_val, rel_val_thresholds, thresholds_def for object_name in rel_val_thresholds.known_objects: for metric_name in rel_val_thresholds.known_metrics: + # get metric for given objects by name _, _, metrics = rel_val_thresholds.get_metrics((object_name,), (metric_name,)) + if not np.any(metrics): continue + # collect all values from all metrics for this object values = [m.value for m in metrics if m.comparable] + # check what is better, lower or greater lower_is_better = metrics[0].lower_is_better factor = 1 if lower_is_better else -1 if not values: + evaluator.add_limits(object_name, metric_name, TestLimits("threshold_user")) continue if thresholds_combine == "mean": + # combine the values, by default take the mean as the threshold mean_central = np.mean(values) else: + # otherwise take the extremum mean_central = factor * max([factor * v for v in values]) + margin = thresholds_margin[metric_name] * mean_central if thresholds_margin and metric_name in thresholds_margin else 0 + # put together the std limits and add the TestLimits to the Evaluator if factor > 0: low = None - up = (1 + thresholds_margin[metric_name]) * mean_central + up = margin else: up = None - low = (1 - thresholds_margin) * mean_central + low = margin evaluator.add_limits(object_name, metric_name, TestLimits("threshold_user", mean_central, (low, up))) -def initialise_regions(evaluator, regions): - rel_val_regions = RelVal() - rel_val_regions.load(regions) +def initialise_regions(evaluator, rel_val_regions): + """ + Add regions to the Evaluator as test case + """ + # Loop through everything for object_name in rel_val_regions.known_objects: for metric_name in rel_val_regions.known_metrics: _, _, metrics = rel_val_regions.get_metrics((object_name,), (metric_name,)) + # get all the metric values for the given object and a particular metric values = [m.value for m in metrics if m.comparable] + # extract some properties of the metrics that need to be known proposed_threshold = metrics[0].proposed_threshold lower_is_better = metrics[0].lower_is_better + # a list of metric values where outliers are removed values_central = [] + # a list of metric values with only outliers values_outlier = [] for v in values: diff = v - proposed_threshold if (diff < 0 and lower_is_better) or (diff > 0 and not lower_is_better): - # if the value is below and lower is better (or the other way round), then accept it + # if the value is below and lower is better (or the other way round), then accept it because it is definitely better than even the proposed threshold values_central.append(v) continue if diff != 0: + # check how far off the calculated difference is from the proposed value diff = abs(proposed_threshold / diff) if diff < 0.1: - # this means we accept up to an order of magnitude + # this means we accept up to an order of magnitude, this is hence an outlier values_outlier.append(v) continue + # if this is reached, the value is worse than the proposed threshold but only by less than one order of magnitude values_central.append(v) + # now get the means of this region with their std for both central and outliers mean_central = np.mean(values_central) std_central = np.std(values_central) if np.any(values_outlier): @@ -609,16 +698,22 @@ def initialise_regions(evaluator, regions): else: mean_outlier = None std_outlier = None + # add these mean and std values as two different test limits evaluator.add_limits(object_name, metric_name, TestLimits("regions_tight", mean_central, (std_central, std_central))) evaluator.add_limits(object_name, metric_name, TestLimits("regions_loose", mean_outlier, (std_outlier, std_outlier))) def run_macro(cmd, log_file, cwd=None): + """ + Wrapper to run a command line + """ p = Popen(split(cmd), cwd=cwd, stdout=PIPE, stderr=STDOUT, universal_newlines=True) + # open a logfile and write to it line by line log_file = open(log_file, 'a') for line in p.stdout: log_file.write(line) p.wait() + # when done, close the logfile and return the cmd's return code log_file.close() return p.returncode @@ -651,9 +746,54 @@ def print_summary(rel_val, interpretations, long=False): def get_summary_path(path): + """ + Get the full path to Summary.json + + If a directory is given, look for the file inside + """ if isdir(path): path = join(path, "Summary.json") if exists(path): return path print(f"ERROR: Cannot neither find {path}.") return None + + +def copy_overlays(rel_val, input_dir, output_dir): + """ + copy overlay plots in this summary from the input directory to the output directory + """ + input_dir = abspath(input_dir) + output_dir = abspath(output_dir) + + if not exists(input_dir): + print(f"ERROR: Input directory {input_dir} does not exist") + return 1 + + in_out_same = input_dir == output_dir + + input_dir_new = input_dir + "_tmp" + if in_out_same: + # move input directory + rename(input_dir, input_dir_new) + input_dir = input_dir_new + + if not exists(output_dir): + makedirs(output_dir) + + object_names, _ = rel_val.get_result_per_metric_and_test() + object_names = list(set(object_names)) + + ret = 0 + for object_name in object_names: + filename=join(input_dir, f"{object_name}.png") + if exists(filename): + copy(filename, output_dir) + else: + print(f"File {filename} not found.") + ret = 1 + + if in_out_same: + rmtree(input_dir) + + return ret diff --git a/RelVal/o2dpg_release_validation_variables.py b/RelVal/utils/o2dpg_release_validation_variables.py similarity index 100% rename from RelVal/o2dpg_release_validation_variables.py rename to RelVal/utils/o2dpg_release_validation_variables.py diff --git a/UTILS/FileIOGraph/README.md b/UTILS/FileIOGraph/README.md new file mode 100644 index 000000000..8450e09af --- /dev/null +++ b/UTILS/FileIOGraph/README.md @@ -0,0 +1,97 @@ +# FileIOGraph — learning which task produces and which tasks consume each file + +An O2DPG Monte Carlo workflow declares task dependencies, but not which +file each task reads or writes. The tools here observe a pilot run and +write that missing relation down, so a production run can + +(a) verify the data paths, and +(b) delete an intermediate file the moment its last consumer is finished + (`o2dpg_workflow_runner.py --remove-files-early`). + +## Backends + +The observation is pluggable: + +```bash +ALIEN_O2DPG_WORKFLOW_RUNNER=new $O2DPG_ROOT/MC/bin/o2_dpg_workflow_runner.py \ + -f workflow.json --filegraph-backends fanotify +``` + +`--filegraph-backends` exists only in the runner under +`MC/workflow_runner`, hence the environment variable. + +Each named backend writes `filegraph__.json`; fanotify also +writes `pipeline_fileaccess_report_.json`, the name the runner has +always used. Naming several at once is how they are compared. + +| Backend | How it observes | How it attributes | Privilege | +|---|---|---|---| +| `fanotify` | `monitor_fileaccess_v2.exe`, a mount-wide sidecar | walks `/proc/` up to the runner, after the event | `CAP_SYS_ADMIN` | +| `strace` | each task command wrapped in its own `strace` | by construction, from the trace file it lands in | none | + +`O2DPG_PRODUCE_FILEGRAPH=` still selects fanotify and names the +monitor to run. + +### fanotify + +The original. It needs `CAP_SYS_ADMIN`, granted per machine with +`setcap cap_sys_admin+ep monitor_fileaccess_v2.exe`, so a pilot run is +only possible where somebody with root has prepared the binary — and file +capabilities do not survive a `nosuid` mount, so the copy distributed +through CVMFS can never carry one. + +Unprivileged fanotify does not help: since Linux 5.13 `fanotify_init()` +needs no `CAP_SYS_ADMIN`, but such a group may not use `FAN_MARK_MOUNT` +and does not receive the pid, and the pid is the whole attribution +mechanism. + +Two further properties are worth knowing. A mount-wide mark sees +everything, so in a two-timeframe pilot only 4 301 of 563 611 records were +inside the working directory. And because the process chain is resolved +from `/proc` after the event, an access made by a process that has already +exited yields the chain `;0` and is dropped. + +### strace + +Wraps each task command, so attribution needs no inference. It sees the +`open` itself rather than the close, and `-y` gives the path the kernel +resolved the descriptor to, so relative paths, `chdir` and directory +descriptors all come out right. + +`--seccomp-bpf` is what makes this affordable; the backend probes for it +and uses it when present. Without it every `read()` costs two ptrace +stops: on a read-heavy task, 22.5 s against a 0.95 s baseline, versus +1.00 s with it. The residual is about 59 µs per traced `open`. + +## Checking one backend against another + +```bash +python3 compare_reports.py --reference filegraph_fanotify_123.json \ + --candidate filegraph_strace_123.json +``` + +`EXACT` means edge for edge, `SAFE` that the candidate is a superset, +`UNSAFE` that it misses an edge the reference has. The asymmetry is the +point — an extra reader only keeps a file on disc longer, a missing one +deletes a file a later task still needs. + +`tests/equivalence_test.py` runs a synthetic workflow whose graph is known +by construction under several backends at once and grades all of them, in +seconds and without any ALICE software: + +```bash +python3 tests/equivalence_test.py --backends strace --reference fanotify \ + --ntf 8 --cpu-limit 8 --sleep 0.05 +``` + +Drop `--reference fanotify` where no privileged monitor exists; the +synthetic workflow still carries its own analytic truth. + +Offline tests: `python3 -m unittest discover -s tests -t tests`. + +## Building + +```bash +g++ monitor_fileaccess_v2.cpp -O2 -o monitor_fileaccess_v2.exe +sudo setcap cap_sys_admin+ep monitor_fileaccess_v2.exe # fanotify only +``` diff --git a/UTILS/FileIOGraph/analyse_FileIO.py b/UTILS/FileIOGraph/analyse_FileIO.py new file mode 100755 index 000000000..f2e35c736 --- /dev/null +++ b/UTILS/FileIOGraph/analyse_FileIO.py @@ -0,0 +1,201 @@ +#!/usr/bin/env python3 + +# This is a python script which analyses +# a report from a "fanotify" file access report +# convoluted with task information from an O2DPG MC workflow. +# The tool produces: +# - a json report +# - optionally a graphviz visualization of file and task dependencies + +import argparse +import re +import json + +try: + from graphviz import Digraph + havegraphviz=True +except ImportError: + havegraphviz=False + +parser = argparse.ArgumentParser(description='Produce O2DPG MC file dependency reports') + +# the run-number of data taking or default if unanchored +parser.add_argument('--actionFile', type=str, help="O2DPG pipeline runner action file") +parser.add_argument('--monitorFile', type=str, help="monitoring file provided by fanotify tool. See O2DPG/UTILS/FileIOGraph.") +parser.add_argument('--basedir', default="/", type=str, help="O2DPG workflow dir") +parser.add_argument('--file-filters', nargs='+', default=[r'.*'], help="Filters (regular expressions) to select files (default all = '.*')") +parser.add_argument('--graphviz', type=str, help="Produce a graphviz plot") +parser.add_argument('-o','--output', type=str, help="Output JSON report") + +args = parser.parse_args() + +# what do we need to do +# (a) - parse action File for mapping of O2DPG task name to PID +# ---> fills pid_to_task + task_to_pid + +# Define the pattern using regular expressions +pid_to_O2DPGtask = {} +O2DPGtask_to_pid = {} + +pattern = re.compile(r'.*INFO Task (\d+).*:(\w+) finished with status 0') +# Open the action file and process each line +with open(args.actionFile, 'r') as file: + for line in file: + # Try to match the pattern in each line + match = pattern.match(line) + + # If a match is found, extract the information + if match: + task_number = match.group(1) + task_name = match.group(2) + + pid_to_O2DPGtask[task_number] = task_name + O2DPGtask_to_pid[task_name] = task_number + + +# (b) - parse monitor file for mapping from files to processes and operation +# ---> fills the following structures: +task_reads = { tname : set() for tname in O2DPGtask_to_pid } +task_writes = { tname : set() for tname in O2DPGtask_to_pid } +file_written_task = {} +file_consumed_task = {} + +pattern = re.compile(r'"?([^"]+)"?,((?:read|write)),(.*)') +basedir_pattern = re.compile("^" + args.basedir) +# neglecting some framework file names +file_exclude_filter = re.compile(r'(.*)\.log(.*)|(ccdb/log)|(.*)dpl-config\.json') + +# construct user-filter regular expressions +file_filter_re = [ re.compile(l) for l in args.file_filters ] + +with open(args.monitorFile, 'r') as file: + for line in file: + # Try to match the pattern in each line + match = pattern.match(line) + if match: + file_name = match.group(1) + mode = match.group(2) + pids = match.group(3).split(";") + + # see if matches the workdir + if not basedir_pattern.match(file_name): + continue + + # remove basedir from file_name + file_name = file_name.replace(args.basedir + '/', "./", 1) + + # implement file name filter + if file_exclude_filter.match(file_name): + continue + + # look if file matches one of the user provided filters + file_matches = False + for r in file_filter_re: + if r.match(file_name): + file_matches = True + break + + if not file_matches: + continue + + if file_consumed_task.get(file_name) == None: + file_consumed_task[file_name] = set() + if file_written_task.get(file_name) == None: + file_written_task[file_name] = set() + + for p in pids: + if p in pid_to_O2DPGtask: + task = pid_to_O2DPGtask.get(p) + if mode == 'read': + task_reads.get(task).add(file_name) + file_consumed_task[file_name].add(task) + + if mode == 'write': + task_writes.get(task).add(file_name) + file_written_task[file_name].add(task) + + +# draws the graph of files and tasks +def draw_graph(graphviz_filename): + if not havegraphviz: + print('graphviz not installed, cannot draw workflow') + return + + dot = Digraph(comment='O2DPG file - task network') + + ccdbfilter = re.compile('ccdb(.*)/snapshot.root') + + nametoindex={} + index=0 + + allfiles = set(file_written_task.keys()) | set(file_consumed_task.keys()) + normalfiles = [ s for s in allfiles if not ccdbfilter.match(s) ] + ccdbfiles = [ (s, ccdbfilter.match(s).group(1)) for s in allfiles if ccdbfilter.match(s) ] + + with dot.subgraph(name='CCDB') as ccdbpartition: + ccdbpartition.attr(color = 'blue') + for f in ccdbfiles: + nametoindex[f[0]] = index + ccdbpartition.node(str(index), f[1], color = 'blue') + index = index + 1 + + with dot.subgraph(name='normal') as normalpartition: + normalpartition.attr(color = 'black') + for f in normalfiles: + nametoindex[f] = index + normalpartition.node(str(index), f, color = 'red') + index = index + 1 + for t in O2DPGtask_to_pid: + nametoindex[t] = index + normalpartition.node(str(index), t, shape = 'box', color = 'green', style = 'filled' ) + index = index + 1 + + # edges (arrows between files and tasks) + for node in file_consumed_task: + # node is a file (source) + sourceindex = nametoindex[node] + for task in file_consumed_task[node]: + toindex = nametoindex[task] + dot.edge(str(sourceindex), str(toindex)) + + # edges (arrows between files and tasks) + for node in file_written_task: + # node is a file (target) + toindex = nametoindex[node] + for task in file_written_task[node]: + sourceindex = nametoindex[task] + dot.edge(str(sourceindex), str(toindex)) + + dot.render(graphviz_filename, format='pdf') + dot.render(graphviz_filename, format='gv') + +def write_json_report(json_file_name): + # produce a JSON report of file dependencies + all_filenames = set(file_written_task.keys()) | set(file_consumed_task.keys()) + file_written_task_tr = [ + { + "file" : k, + "written_by" : list(file_written_task.get(k, [])), + "read_by" : list(file_consumed_task.get(k, [])) + } + for k in all_filenames + ] + + tasks_output = [ + { + "task" : t, + "writes" : list(task_writes.get(t,[])), + "reads" : list(task_reads.get(t,[])) + } + for t in O2DPGtask_to_pid + ] + + # Write the dictionary to a JSON file + with open(json_file_name, 'w') as json_file: + json.dump({ "file_report" : file_written_task_tr, "task_report" : tasks_output }, json_file, indent=2) + +if args.graphviz: + draw_graph(args.graphviz) + +if args.output: + write_json_report(args.output) \ No newline at end of file diff --git a/UTILS/FileIOGraph/analyse_FileIO_strace.py b/UTILS/FileIOGraph/analyse_FileIO_strace.py new file mode 100755 index 000000000..2ea49e99e --- /dev/null +++ b/UTILS/FileIOGraph/analyse_FileIO_strace.py @@ -0,0 +1,152 @@ +#!/usr/bin/env python3 +"""Turn per-task strace logs into the file-task dependency report. + +The runner wraps each task in its own strace, so the trace file name says +which task made the call and nothing has to be attributed after the fact. +Traces are taken with -y, so the path on the *return* value is the one the +kernel resolved -- which is what makes relative paths, chdir and a +non-AT_FDCWD directory descriptor all come out right. +""" +from __future__ import annotations + +import argparse +import os +import re +import sys +from typing import Dict, List, Set, Tuple + +from filegraph_report import ( + add_common_arguments, basedir_prefix, emit, keep_file, relative_to_basedir, +) + +#: trace__.log, written by the runner's strace backend +TRACEFILE_RE = re.compile(r'^trace_(?P\d+)_(?P.+)\.log$') + +OPEN_CALLS = ("openat", "openat2", "open", "creat") +#: longest first, or 'openat' would match the head of 'openat2' +_OPEN_ALT = "|".join(sorted(OPEN_CALLS, key=len, reverse=True)) + +_OPEN_RE = re.compile( + rf'(?:^|\s)(?:{_OPEN_ALT})\((?P.*?)\)\s*=\s*\d+<(?P[^>]*)>') +_RENAME_RE = re.compile(r'(?:^|\s)rename(?:at2?)?\((?P.*?)\)\s*=\s*0') +_UNFINISHED_RE = re.compile(r'^(?P\d+)\s+(?P\w+)\((?P.*?)\d+)\s+<\.\.\.\s+(?P\w+)\s+resumed>(?P.*)$') +_RESULT_RE = re.compile(r'=\s*\d+<(?P[^>]*)>') +_QUOTED_RE = re.compile(r'"((?:[^"\\]|\\.)*)"') + +#: opendir reaches the kernel as an open with O_DIRECTORY; fanotify reports +#: directories only with FAN_ONDIR, so they are in neither graph +_DIRECTORY = "O_DIRECTORY" +_WRITE_FLAGS = ("O_WRONLY", "O_RDWR", "O_CREAT", "O_TRUNC", "O_APPEND") + + +def _kind(args: str) -> str: + return "write" if any(f in args for f in _WRITE_FLAGS) else "read" + + +def parse_trace(path: str) -> Tuple[Set[Tuple[str, str]], int]: + """Distinct (absolute path, read|write) pairs for one task, and the + number of calls they were distilled from.""" + seen: Set[Tuple[str, str]] = set() + calls = 0 + # strace splits a call around another process's entry; the flags are on + # the half that was interrupted + pending: Dict[Tuple[str, str], str] = {} + + with open(path, errors="replace") as fh: + for line in fh: + if "" in line: + m = _RESUMED_RE.match(line) + if m is None or m.group("call") not in OPEN_CALLS: + continue + res = _RESULT_RE.search(m.group("rest")) + if res is None or not res.group("path"): + continue + args = (pending.pop((m.group("pid"), m.group("call")), "") + + m.group("rest").split("=", 1)[0]) + calls += 1 + if _DIRECTORY not in args: + seen.add((res.group("path"), _kind(args))) + continue + + m = _OPEN_RE.search(line) + if m is not None: + calls += 1 + if m.group("path") and _DIRECTORY not in m.group("args"): + seen.add((m.group("path"), _kind(m.group("args")))) + continue + + if "rename" in line: + m = _RENAME_RE.search(line) + if m is None: + continue + names = _QUOTED_RE.findall(m.group("args")) + if names: + calls += 1 + seen.add((names[-1], "write")) # the destination is produced here + + return seen, calls + + +def collect(tracedir: str, basedir: str, file_filters): + written: Dict[str, Set[str]] = {} + read: Dict[str, Set[str]] = {} + tasks: List[str] = [] + prefix = basedir_prefix(basedir) + stats = {"traces": 0, "calls": 0, "kept": 0} + + for name in sorted(os.listdir(tracedir)): + m = TRACEFILE_RE.match(name) + if m is None: + continue + task = m.group("task") + tasks.append(task) + stats["traces"] += 1 + + accesses, calls = parse_trace(os.path.join(tracedir, name)) + stats["calls"] += calls + for target, kind in accesses: + rel = relative_to_basedir(target, prefix) + if rel is None or not keep_file(rel, file_filters): + continue + stats["kept"] += 1 + (written if kind == "write" else read).setdefault(rel, set()).add(task) + + return written, read, sorted(set(tasks)), stats + + +def build_parser() -> argparse.ArgumentParser: + p = argparse.ArgumentParser( + description=__doc__, + formatter_class=argparse.RawDescriptionHelpFormatter) + p.add_argument("--straceDir", required=True, + help="directory of trace__.log files") + add_common_arguments(p) + return p + + +def main(argv=None) -> int: + a = build_parser().parse_args(argv) + if not os.path.isdir(a.straceDir): + print(f"no such directory: {a.straceDir}", file=sys.stderr) + return 2 + + written, read, tasks, stats = collect( + a.straceDir, a.basedir, [re.compile(f) for f in a.file_filters]) + print(f"strace: {stats['traces']} task trace(s), {stats['calls']} call(s), " + f"{stats['kept']} access(es) kept under {a.basedir}") + if not stats["traces"]: + print(f"WARNING: no trace_*.log in {a.straceDir}", file=sys.stderr) + + emit(a, written, read, tasks) + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/UTILS/FileIOGraph/analyse_FileIO_v2.py b/UTILS/FileIOGraph/analyse_FileIO_v2.py new file mode 100644 index 000000000..67951f183 --- /dev/null +++ b/UTILS/FileIOGraph/analyse_FileIO_v2.py @@ -0,0 +1,138 @@ +#!/usr/bin/env python3 +"""Turn a fanotify file-access log plus the runner action log into the +file-task dependency report. + +Attribution comes from the process chain the monitor records: an access is +a task's if any pid in the chain is the pid the action log gives that task. +Both action-log formats are accepted: + + Old runner: "... INFO Task : finished with status 0" + New runner: "... INFO Task pid= tid= finished rc=0" +""" +from __future__ import annotations + +import argparse +import re +import sys +from typing import Dict, List, Set, Tuple + +from filegraph_report import ( + add_common_arguments, basedir_prefix, emit, keep_file, relative_to_basedir, +) + + +# ── action-log patterns ─────────────────────────────────────────────────────── + +# Old runner: "... INFO Task : finished with status 0" +_PAT_OLD = re.compile(r'.*INFO Task (\d+)[^:]*:(\w+) finished with status 0') +# New runner: "... INFO Task pid= tid= finished rc=0" +_PAT_NEW = re.compile(r'.*INFO Task pid=(\d+) tid=\d+ (\S+) finished rc=0') + + +def parse_action_log(path: str) -> Tuple[Dict[str, str], Dict[str, str]]: + """Parse task-PID associations from an action log. + + Returns (pid_to_task, task_to_pid). Only successfully completed tasks + are included (rc=0 / status 0) so failed retries don't pollute the map. + """ + pid_to_task: Dict[str, str] = {} + task_to_pid: Dict[str, str] = {} + with open(path) as fh: + for line in fh: + m = _PAT_OLD.match(line) or _PAT_NEW.match(line) + if m: + pid, name = m.group(1), m.group(2) + pid_to_task[pid] = name + task_to_pid[name] = pid + return pid_to_task, task_to_pid + + +# ── monitor-log parsing ─────────────────────────────────────────────────────── + +_RECORD_RE = re.compile(r'"?([^"]+)"?,(read|write),(.*)') + + +def parse_monitor_log( + path: str, + pid_to_task: Dict[str, str], + basedir: str, + file_filters: List[re.Pattern], +) -> Tuple[Dict[str, Set[str]], Dict[str, Set[str]]]: + """Parse the fanotify raw log and map files to the tasks that touched them. + + Returns (file_written_by, file_read_by) where each value is a set of + task names. Only files inside *basedir* that pass *file_filters* and + are not excluded by the built-in exclude pattern are included. + + A file access is attributed to a task when any PID in the process-chain + column of the monitor log appears in *pid_to_task*. This works for both + direct children and children-of-children of the task process because the + fanotify monitor records the full ancestor chain up to the root PID. + """ + file_written: Dict[str, Set[str]] = {} + file_read: Dict[str, Set[str]] = {} + prefix = basedir_prefix(basedir) + + with open(path) as fh: + for line in fh: + m = _RECORD_RE.match(line) + if not m: + continue + fname, mode, chain = m.group(1), m.group(2), m.group(3) + + rel = relative_to_basedir(fname, prefix) + if rel is None or not keep_file(rel, file_filters): + continue + + for pid in chain.split(";"): + task = pid_to_task.get(pid) + if task is None: + continue + if mode == "write": + file_written.setdefault(rel, set()).add(task) + else: + file_read.setdefault(rel, set()).add(task) + + return file_written, file_read + + +# ── CLI ─────────────────────────────────────────────────────────────────────── + +def build_parser() -> argparse.ArgumentParser: + p = argparse.ArgumentParser( + description=__doc__, + formatter_class=argparse.RawDescriptionHelpFormatter, + ) + p.add_argument("--actionFile", required=True, + help="O2DPG pipeline runner action log") + p.add_argument("--monitorFile", required=True, + help="fanotify raw log from monitor_fileaccess_v2.exe") + add_common_arguments(p) + return p + + +def main(argv=None) -> None: + args = build_parser().parse_args(argv) + + file_filters = [re.compile(f) for f in args.file_filters] + + pid_to_task, task_to_pid = parse_action_log(args.actionFile) + if not pid_to_task: + print( + f"WARNING: no task completions found in {args.actionFile}.\n" + "Check that the action log is from a completed run and that\n" + "its format matches either the old or new O2DPG runner.", + file=sys.stderr, + ) + else: + print(f"Action log: {len(pid_to_task)} completed task(s) found") + + file_written, file_read = parse_monitor_log( + args.monitorFile, pid_to_task, args.basedir, file_filters, + ) + + emit(args, file_written, file_read, sorted(task_to_pid)) + + +if __name__ == "__main__": + main() diff --git a/UTILS/FileIOGraph/compare_reports.py b/UTILS/FileIOGraph/compare_reports.py new file mode 100755 index 000000000..588abae78 --- /dev/null +++ b/UTILS/FileIOGraph/compare_reports.py @@ -0,0 +1,156 @@ +#!/usr/bin/env python3 +"""Grade one file-graph report against another. + + EXACT candidate and reference agree edge for edge + SAFE candidate is a superset: nothing missing, some extra + UNSAFE candidate misses an edge the reference has + +The asymmetry is the point: the runner deletes a file once every task +listed against it has finished, so a missing edge deletes a file a later +task still reads, while an extra one only keeps it on disc longer. +""" +from __future__ import annotations + +import argparse +import json +import sys +from typing import Dict, List, Set, Tuple + +SECTIONS = ("file_report", "file_template_report") +Edges = Dict[Tuple[str, str], Set[str]] + + +def _edges(report: Dict, section: str) -> Edges: + """(file, 'written_by'|'read_by') -> set of task names.""" + out: Edges = {} + for entry in report.get(section) or []: + f = entry.get("file") + if not f: + continue + for kind in ("written_by", "read_by"): + out[(f, kind)] = set(entry.get(kind) or []) + return out + + +def _diff(a: Edges, b: Edges) -> List[Tuple[str, str, List[str]]]: + """Edges present in a and not in b, as (file, kind, tasks).""" + out = [] + for (f, kind), tasks in a.items(): + extra = tasks - b.get((f, kind), set()) + if extra: + out.append((f, kind, sorted(extra))) + return sorted(out) + + +class SectionDiff: + def __init__(self, section: str, ref: Dict, cand: Dict): + self.section = section + re_, ce = _edges(ref, section), _edges(cand, section) + self.ref_files = {f for f, _ in re_} + self.cand_files = {f for f, _ in ce} + self.only_ref = sorted(self.ref_files - self.cand_files) + self.only_cand = sorted(self.cand_files - self.ref_files) + self.missing = _diff(re_, ce) + self.extra = _diff(ce, re_) + self.n_ref_edges = sum(len(v) for v in re_.values()) + self.n_cand_edges = sum(len(v) for v in ce.values()) + self.n_missing = sum(len(t) for _, _, t in self.missing) + self.n_extra = sum(len(t) for _, _, t in self.extra) + + @property + def verdict(self) -> str: + if self.n_missing: + return "UNSAFE" + return "SAFE" if self.n_extra else "EXACT" + + @property + def recall(self) -> float: + if not self.n_ref_edges: + return 1.0 + return (self.n_ref_edges - self.n_missing) / self.n_ref_edges + + def report(self, limit: int) -> str: + def block(header, items, fmt): + if not items: + return [] + out = [f" {header} ({len(items)}):"] + out += [f" {fmt(i)}" for i in items[:limit]] + if len(items) > limit: + out.append(f" ... {len(items) - limit} more") + return out + + lines = [ + f"[{self.section}]", + f" files reference={len(self.ref_files)} " + f"candidate={len(self.cand_files)} only-ref={len(self.only_ref)} " + f"only-cand={len(self.only_cand)}", + f" edges reference={self.n_ref_edges} " + f"candidate={self.n_cand_edges} missing={self.n_missing} " + f"extra={self.n_extra} recall={self.recall * 100:.2f}%", + f" verdict {self.verdict}", + ] + lines += block("files the candidate never saw", self.only_ref, str) + lines += block("MISSING edges", self.missing, + lambda e: f"- {e[0]} {e[1]}: {', '.join(e[2])}") + lines += block("extra edges, harmless", self.extra, + lambda e: f"+ {e[0]} {e[1]}: {', '.join(e[2])}") + return "\n".join(lines) + + +def compare(ref: Dict, cand: Dict, limit: int = 20) -> Tuple[str, Dict]: + diffs = [SectionDiff(s, ref, cand) for s in SECTIONS] + verdicts = [d.verdict for d in diffs] + overall = ("UNSAFE" if "UNSAFE" in verdicts + else "SAFE" if "SAFE" in verdicts else "EXACT") + summary = { + "verdict": overall, + "sections": { + d.section: { + "verdict": d.verdict, + "reference_edges": d.n_ref_edges, + "candidate_edges": d.n_cand_edges, + "missing_edges": d.n_missing, + "extra_edges": d.n_extra, + "recall": d.recall, + "files_only_in_reference": d.only_ref, + "files_only_in_candidate": d.only_cand, + "missing": [list(e) for e in d.missing], + "extra": [list(e) for e in d.extra], + } for d in diffs + }, + } + return "\n".join(d.report(limit) for d in diffs), summary + + +def main(argv=None) -> int: + ap = argparse.ArgumentParser( + description=__doc__, + formatter_class=argparse.RawDescriptionHelpFormatter) + ap.add_argument("--reference", required=True, help="the report to be reproduced") + ap.add_argument("--candidate", required=True, help="the report under test") + ap.add_argument("--limit", type=int, default=20, help="differences to print per class") + ap.add_argument("--json", default=None, help="also write the full diff here") + ap.add_argument("--allow", choices=["EXACT", "SAFE"], default="SAFE", + help="worst verdict that still exits 0 (default: SAFE)") + a = ap.parse_args(argv) + + with open(a.reference) as f: + ref = json.load(f) + with open(a.candidate) as f: + cand = json.load(f) + + text, summary = compare(ref, cand, a.limit) + print(f"reference: {a.reference}") + print(f"candidate: {a.candidate}") + print(text) + print(f"\nOVERALL: {summary['verdict']}") + if a.json: + with open(a.json, "w") as f: + json.dump(summary, f, indent=2) + print(f"wrote {a.json}") + + return 0 if summary["verdict"] in ("EXACT", a.allow) else 1 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/UTILS/FileIOGraph/filegraph_report.py b/UTILS/FileIOGraph/filegraph_report.py new file mode 100644 index 000000000..952110771 --- /dev/null +++ b/UTILS/FileIOGraph/filegraph_report.py @@ -0,0 +1,173 @@ +"""Build the file-task dependency report from two file->tasks maps. + +Holds the exclusion rules, the ./tfN -> ./tfX templating, the JSON schema +that --remove-files-early reads back, and the graphviz rendering. +""" +from __future__ import annotations + +import json +import re +import sys +from typing import Dict, Optional, Sequence, Set + +#: log files, the CCDB log and the DPL config are noise, not data flow +EXCLUDE_RE = re.compile(r'(.*\.log.*|ccdb/log|.*dpl-config\.json)') +TF_PATH_RE = re.compile(r'^\./tf(?P\d+)/') + + +def basedir_prefix(basedir: str) -> str: + return basedir.rstrip("/") + "/" + + +def relative_to_basedir(fname: str, prefix: str) -> Optional[str]: + """'./x/y' for a path under the prefix from basedir_prefix(), else None.""" + if not fname.startswith(prefix): + return None + return "./" + fname[len(prefix):] + + +def keep_file(rel: str, file_filters: Sequence) -> bool: + if EXCLUDE_RE.match(rel): + return False + return any(r.match(rel) for r in file_filters) + + +def task_template_for_timeframe(task_name: str, source_tf: int) -> str: + suffix = f"_{source_tf}" + if task_name.endswith(suffix): + return f"{task_name[:-len(suffix)]}_X" + return task_name + + +def build_report(file_written: Dict[str, Set[str]], + file_read: Dict[str, Set[str]], + tasks: Sequence[str]) -> Dict: + """Assemble the JSON document from the two file->tasks maps.""" + all_files = sorted(set(file_written) | set(file_read)) + file_report = [ + { + "file": f, + "written_by": sorted(file_written.get(f, set())), + "read_by": sorted(file_read.get(f, set())), + } + for f in all_files + ] + + templates: Dict[str, Dict] = {} + for entry in file_report: + match = TF_PATH_RE.match(entry["file"]) + if match is None: + continue + source_tf = int(match.group("tf")) + merged = templates.setdefault( + TF_PATH_RE.sub("./tfX/", entry["file"], count=1), + {"written_by": set(), "read_by": set(), "source_timeframes": set()}, + ) + merged["source_timeframes"].add(source_tf) + for kind in ("written_by", "read_by"): + for task in entry[kind]: + merged[kind].add(task_template_for_timeframe(task, source_tf)) + + file_template_report = [ + { + "file": f, + "written_by": sorted(v["written_by"]), + "read_by": sorted(v["read_by"]), + "source_timeframes": sorted(v["source_timeframes"]), + } + for f, v in sorted(templates.items()) + ] + + task_reads: Dict[str, Set[str]] = {} + task_writes: Dict[str, Set[str]] = {} + for f, ts in file_read.items(): + for t in ts: + task_reads.setdefault(t, set()).add(f) + for f, ts in file_written.items(): + for t in ts: + task_writes.setdefault(t, set()).add(f) + task_report = [ + { + "task": t, + "writes": sorted(task_writes.get(t, set())), + "reads": sorted(task_reads.get(t, set())), + } + for t in sorted(tasks) + ] + + return { + "file_report": file_report, + "file_template_report": file_template_report, + "task_report": task_report, + } + + +def draw_graph(filename: str, file_written: Dict[str, Set[str]], + file_read: Dict[str, Set[str]], tasks: Sequence[str]) -> None: + try: + from graphviz import Digraph + except ImportError: + print("graphviz not installed, skipping graph", file=sys.stderr) + return + + ccdb_re = re.compile(r"ccdb(.*)/snapshot\.root") + dot = Digraph(comment="O2DPG file-task network") + idx: Dict[str, int] = {} + + # CCDB snapshots are labelled by object path; a real workflow pulls + # dozens and the full names swamp the picture + ccdb, normal = [], [] + for f in set(file_written) | set(file_read): + m = ccdb_re.match(f) + (ccdb if m else normal).append((f, m.group(1) if m else f)) + + with dot.subgraph(name="CCDB") as sg: + sg.attr(color="blue") + for f, label in ccdb: + idx[f] = len(idx) + sg.node(str(idx[f]), label, color="blue") + + with dot.subgraph(name="normal") as sg: + sg.attr(color="black") + for f, label in normal: + idx[f] = len(idx) + sg.node(str(idx[f]), label, color="red") + for t in tasks: + idx[t] = len(idx) + sg.node(str(idx[t]), t, shape="box", color="green", style="filled") + + for f, ts in file_read.items(): + for t in ts: + dot.edge(str(idx[f]), str(idx[t])) + for f, ts in file_written.items(): + for t in ts: + dot.edge(str(idx[t]), str(idx[f])) + + dot.render(filename, format="pdf") + dot.render(filename, format="gv") + print(f"Wrote {filename}.pdf and {filename}.gv") + + +def add_common_arguments(parser) -> None: + parser.add_argument("--basedir", default="/", + help="Workflow working directory (default: /)") + parser.add_argument("--file-filters", nargs="+", default=[r".*"], + help="Regex filters to select file paths (default: all)") + parser.add_argument("--graphviz", default=None, + help="also render the file/task network with this " + "base filename") + parser.add_argument("-o", "--output", required=True, + help="Output JSON report path") + + +def emit(args, file_written: Dict[str, Set[str]], file_read: Dict[str, Set[str]], + tasks: Sequence[str]) -> None: + """Render and write what add_common_arguments() asked for.""" + if args.graphviz: + draw_graph(args.graphviz, file_written, file_read, tasks) + doc = build_report(file_written, file_read, tasks) + with open(args.output, "w") as fh: + json.dump(doc, fh, indent=2) + print(f"Wrote {args.output}: {len(doc['file_report'])} file(s) referenced, " + f"{len(doc['file_template_report'])} timeframe file template(s), " + f"{len(doc['task_report'])} task(s) mapped") diff --git a/UTILS/FileIOGraph/monitor_fileaccess.cpp b/UTILS/FileIOGraph/monitor_fileaccess.cpp new file mode 100644 index 000000000..9c7519c6b --- /dev/null +++ b/UTILS/FileIOGraph/monitor_fileaccess.cpp @@ -0,0 +1,188 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#define CHK(expr, errcode) \ + if ((expr) == errcode) \ + perror(#expr), exit(EXIT_FAILURE) + +#include +#include +#include +#include +#include +#include + +#define MAXBUF (BUFSIZ * 2) + +int getppid(int pid) +{ + int ppid; + char buf[MAXBUF]; + char procname[32]; // Holds /proc/4294967296/status\0 + FILE* fp; + + snprintf(procname, sizeof(procname), "/proc/%u/status", pid); + fp = fopen(procname, "r"); + if (fp != NULL) { + size_t ret = fread(buf, sizeof(char), MAXBUF - 1, fp); + if (!ret) { + return 0; + } else { + buf[ret++] = '\0'; // Terminate it. + } + fclose(fp); + } + char* ppid_loc = strstr(buf, "\nPPid:"); + if (ppid_loc) { + int ret = sscanf(ppid_loc, "\nPPid:%d", &ppid); + if (!ret || ret == EOF) { + return 0; + } + return ppid; + } else { + return 0; + } +} + +std::string getcmd(pid_t pid) +{ + char path[1024]; + snprintf(path, sizeof(path), "/proc/%d/cmdline", pid); + if (pid == 0 || pid == 1) { + return std::string(""); + } + + FILE* file = fopen(path, "r"); + if (file) { + char buffer[1024]; // max 1024 chars + size_t bytesRead = fread(buffer, 1, sizeof(buffer), file); + fclose(file); + for (int byte = 0; byte < bytesRead; ++byte) { + if (buffer[byte] == '\0') { + buffer[byte] = '@'; + } + } + return std::string(buffer); + } + return std::string(""); +} + +std::unordered_map good_pid; + +bool is_good_pid(int pid, int maxparent) +{ + auto iter = good_pid.find(pid); + if (iter != good_pid.end()) { + // the result is known + return iter->second; + } + // the result is not known ---> determine it + + // this means determining the whole chain of parent ids + if (pid == maxparent) { + good_pid[pid] = true; + } else if (pid == 0) { + good_pid[pid] = false; + } else { + good_pid[pid] = is_good_pid(getppid(pid), maxparent); + } + return good_pid[pid]; +} + +int main(int argc, char** argv) +{ + int fan; + char buf[4096]; + char fdpath[64]; + char path[PATH_MAX + 1]; + ssize_t buflen, linklen; + struct fanotify_event_metadata* metadata; + + CHK(fan = fanotify_init(FAN_CLASS_NOTIF, O_RDONLY), -1); + CHK(fanotify_mark(fan, FAN_MARK_ADD | FAN_MARK_MOUNT, + FAN_CLOSE_WRITE | FAN_CLOSE_NOWRITE | FAN_EVENT_ON_CHILD, AT_FDCWD, "/"), + -1); + + std::unordered_map pid_to_parents; // mapping of a process id to the whole string of parent pids, separated by ';' + std::unordered_map pid_to_command; // mapping of a process id to a command + + auto MAX_MOTHER_PID_ENV = getenv("MAXMOTHERPID"); + int max_mother_pid = 1; // everything + if (MAX_MOTHER_PID_ENV != nullptr) { + std::cerr << "found env variable MAX_MOTHER_PID_ENV"; + max_mother_pid = std::atoi(MAX_MOTHER_PID_ENV); + std::cerr << "Setting topmost mother process to " << max_mother_pid << "\n"; + } else { + std::cerr << "No environment given. Monitoring globally.\n"; + } + + auto thispid = getpid(); + std::string* parentspid = nullptr; + + for (;;) { + CHK(buflen = read(fan, buf, sizeof(buf)), -1); + metadata = (struct fanotify_event_metadata*)&buf; + while (FAN_EVENT_OK(metadata, buflen)) { + if (metadata->mask & FAN_Q_OVERFLOW) { + printf("Queue overflow!\n"); + continue; + } + sprintf(fdpath, "/proc/self/fd/%d", metadata->fd); + CHK(linklen = readlink(fdpath, path, sizeof(path) - 1), -1); + path[linklen] = '\0'; + auto pid = metadata->pid; + + bool record = true; + + // no need to monitor ourselfs + record = record && pid != thispid; + + // check if we have the right events before continuing + record = record && (((metadata->mask & FAN_CLOSE_WRITE) || (metadata->mask & FAN_CLOSE_NOWRITE))); + + // check if we have the right pid before continuing + record = record && is_good_pid(pid, max_mother_pid); + + if (record) { + auto iter = pid_to_parents.find((int)pid); + if (iter != pid_to_parents.end()) { + parentspid = &iter->second; + } else { + std::stringstream str; + // get chain of parent pids + auto current = (int)pid; + str << current; + while (current != max_mother_pid && current != 0) { + // record command line of current if not already cached + if (pid_to_command.find((int)current) == pid_to_command.end()) { + std::string cmd{getcmd(current)}; + pid_to_command[current] = cmd; + printf("pid-to-command:%i:%s\n", current, cmd.c_str()); + } + + auto next = getppid(current); + current = next; + str << ";" << current; + } + pid_to_parents[(int)pid] = str.str(); + parentspid = &pid_to_parents[(int)pid]; + } + + if (metadata->mask & FAN_CLOSE_WRITE) { + printf("\"%s\",write,%s\n", path, parentspid->c_str()); + } + if (metadata->mask & FAN_CLOSE_NOWRITE) { + printf("\"%s\",read,%s\n", path, parentspid->c_str()); + } + } + + close(metadata->fd); + metadata = FAN_EVENT_NEXT(metadata, buflen); + } + } +} diff --git a/UTILS/FileIOGraph/monitor_fileaccess_v2.cpp b/UTILS/FileIOGraph/monitor_fileaccess_v2.cpp new file mode 100644 index 000000000..b3d89ae8b --- /dev/null +++ b/UTILS/FileIOGraph/monitor_fileaccess_v2.cpp @@ -0,0 +1,224 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CHK(expr, errcode) \ + if ((expr) == errcode) \ + { \ + perror(#expr); \ + exit(EXIT_FAILURE); \ + } + +#define MAXBUF (BUFSIZ * 2) + +int getppid_safe(int pid) +{ + int ppid = 0; + char buf[MAXBUF]; + char procname[64]; // big enough for /proc//status + FILE *fp; + + snprintf(procname, sizeof(procname), "/proc/%d/status", pid); + fp = fopen(procname, "r"); + if (fp != NULL) + { + size_t ret = fread(buf, sizeof(char), MAXBUF - 1, fp); + if (ret > 0) + buf[ret] = '\0'; + fclose(fp); + } + char *ppid_loc = strstr(buf, "\nPPid:"); + if (ppid_loc) + { + if (sscanf(ppid_loc, "\nPPid:%d", &ppid) != 1) + return 0; + return ppid; + } + return 0; +} + +std::string getcmd(pid_t pid) +{ + if (pid == 0 || pid == 1) + return std::string(""); + + char path[64]; + snprintf(path, sizeof(path), "/proc/%d/cmdline", pid); + + FILE *file = fopen(path, "r"); + if (file) + { + char buffer[1024]; // max 1k command line + size_t bytesRead = fread(buffer, 1, sizeof(buffer), file); + fclose(file); + for (size_t i = 0; i < bytesRead; ++i) + { + if (buffer[i] == '\0') + buffer[i] = '@'; + } + return std::string(buffer, bytesRead); + } + return std::string(""); +} + +std::unordered_map good_pid_cache; +std::unordered_map pid_to_parents; +std::unordered_map pid_to_command; + +bool is_good_pid(int pid, int maxparent) +{ + auto iter = good_pid_cache.find(pid); + if (iter != good_pid_cache.end()) + return iter->second; + + if (pid == maxparent) + return good_pid_cache[pid] = true; + if (pid == 0) + return good_pid_cache[pid] = false; + + return good_pid_cache[pid] = is_good_pid(getppid_safe(pid), maxparent); +} + +std::string build_parent_chain(int pid, int maxparent) +{ + auto iter = pid_to_parents.find(pid); + if (iter != pid_to_parents.end()) + return iter->second; + + std::stringstream str; + int current = pid; + str << current; + while (current != maxparent && current != 0) + { + if (pid_to_command.find(current) == pid_to_command.end()) + { + std::string cmd = getcmd(current); + pid_to_command[current] = cmd; + fprintf(stdout, "pid-to-command:%i:%s\n", current, cmd.c_str()); + } + int next = getppid_safe(current); + current = next; + str << ";" << current; + } + pid_to_parents[pid] = str.str(); + return str.str(); +} + +int main(int argc, char **argv) +{ + int fan; + char buf[8192]; + char fdpath[64]; + char path[PATH_MAX + 1]; + ssize_t buflen, linklen; + struct fanotify_event_metadata *metadata; + + // init fanotify + + // with this we can observe specific root directories + auto ROOT_PATH_ENV = getenv("FILEACCESS_MON_ROOTPATH"); + std::string root_path = "/"; + if (ROOT_PATH_ENV) { + root_path = std::string(ROOT_PATH_ENV); + std::cerr << "Observing file access below " << root_path << "\n"; + } + + CHK(fan = fanotify_init(FAN_CLASS_NOTIF, O_RDONLY), -1); + CHK(fanotify_mark(fan, FAN_MARK_ADD | FAN_MARK_MOUNT, + FAN_CLOSE_WRITE | FAN_CLOSE_NOWRITE | FAN_EVENT_ON_CHILD, + AT_FDCWD, root_path.c_str()), + -1); + + // read env for filtering + auto MAX_MOTHER_PID_ENV = getenv("MAXMOTHERPID"); + int max_mother_pid = 1; // default: allow everything + if (MAX_MOTHER_PID_ENV != nullptr) + { + max_mother_pid = std::atoi(MAX_MOTHER_PID_ENV); + std::cerr << "Setting topmost mother process to " << max_mother_pid << "\n"; + } + else + { + std::cerr << "No MAXMOTHERPID environment given\n"; + } + + auto thispid = getpid(); + + struct pollfd fds[1]; + fds[0].fd = fan; + fds[0].events = POLLIN; + + for (;;) + { + int pollres = poll(fds, 1, -1); // wait indefinitely + if (pollres == -1) + { + perror("poll"); + continue; + } + + if (fds[0].revents & POLLIN) + { + buflen = read(fan, buf, sizeof(buf)); + if (buflen == -1) + { + perror("read"); + continue; + } + metadata = (struct fanotify_event_metadata *)&buf; + while (FAN_EVENT_OK(metadata, buflen)) + { + if (metadata->mask & FAN_Q_OVERFLOW) + { + // 'continue' here would re-test the same event forever, because it + // skips FAN_EVENT_NEXT; report and advance instead. + printf("#OVERFLOW\n"); + fflush(stdout); + fprintf(stderr, "Queue overflow! events were dropped\n"); + metadata = FAN_EVENT_NEXT(metadata, buflen); + continue; + } + snprintf(fdpath, sizeof(fdpath), "/proc/self/fd/%d", metadata->fd); + linklen = readlink(fdpath, path, sizeof(path) - 1); + if (linklen >= 0) + { + path[linklen] = '\0'; + int pid = metadata->pid; + + bool record = true; + record = record && pid != thispid; + record = record && (metadata->mask & (FAN_CLOSE_WRITE | FAN_CLOSE_NOWRITE)); + record = record && is_good_pid(pid, max_mother_pid); + + if (record) + { + std::string parent_chain = build_parent_chain(pid, max_mother_pid); + + if (metadata->mask & FAN_CLOSE_WRITE) + { + printf("\"%s\",write,%s\n", path, parent_chain.c_str()); + fflush(stdout); + } + if (metadata->mask & FAN_CLOSE_NOWRITE) + { + printf("\"%s\",read,%s\n", path, parent_chain.c_str()); + fflush(stdout); + } + } + } + close(metadata->fd); + metadata = FAN_EVENT_NEXT(metadata, buflen); + } + } + } +} diff --git a/UTILS/FileIOGraph/tests/equivalence_test.py b/UTILS/FileIOGraph/tests/equivalence_test.py new file mode 100755 index 000000000..04541ee5b --- /dev/null +++ b/UTILS/FileIOGraph/tests/equivalence_test.py @@ -0,0 +1,205 @@ +#!/usr/bin/env python3 +"""Build a workflow whose file-IO graph is known, run it under one or more +backends, and grade every report against that graph and against a reference. + + equivalence_test.py --backends strace --reference fanotify --ntf 8 +""" +from __future__ import annotations + +import argparse +import glob +import json +import os +import subprocess +import sys +import time + +from filegraph_test_support import REPO, filegraph + +from compare_reports import compare # noqa: E402 +from filegraph_report import build_report # noqa: E402 + +RESOURCES = {"cpu": 1, "mem": 100, "relative_cpu": 1.0} + + +def build_workflow(ntf: int, sleep: str): + """A workflow plus the file->tasks maps its commands imply. + + Covers what makes attribution hard: a global task consuming every + timeframe, a file with two readers, a subdirectory created at run time, + four different libc entry points, and tasks that overlap in time. + """ + stages, written, read = [], {}, {} + + def w(f, t): + written.setdefault(f, set()).add(t) + + def r(f, t): + read.setdefault(f, set()).add(t) + + def stage(name, cmd, needs, cwd, tf, label): + stages.append({"name": name, "cmd": f"sleep {sleep}; {cmd}", + "needs": needs, "cwd": cwd, "timeframe": tf, + "labels": [label], "resources": RESOURCES}) + + stage("bkg", "echo bkgdata > bkg.dat", [], "./", -1, "SIM") + w("./bkg.dat", "bkg") + + for i in range(1, ntf + 1): + tf = f"tf{i}" + # shell redirection and Python open() in one task + stage(f"sgnsim_{i}", + f"cat ../bkg.dat > sgn.dat; " + f"python3 -c \"open('kine.dat','w').write('kine{i}')\"", + ["bkg"], f"./{tf}", i, "SIM") + r("./bkg.dat", f"sgnsim_{i}") + w(f"./{tf}/sgn.dat", f"sgnsim_{i}") + w(f"./{tf}/kine.dat", f"sgnsim_{i}") + + # cp, and a subdirectory created while the workflow is running + stage(f"digi_{i}", "cp sgn.dat digi.dat; mkdir -p sub; " + "cp digi.dat sub/extra.dat", + [f"sgnsim_{i}"], f"./{tf}", i, "DIGI") + r(f"./{tf}/sgn.dat", f"digi_{i}") + w(f"./{tf}/digi.dat", f"digi_{i}") + r(f"./{tf}/digi.dat", f"digi_{i}") + w(f"./{tf}/sub/extra.dat", f"digi_{i}") + + # awk opens its output file itself, through fopen + stage(f"reco_{i}", "awk '{print > \"reco.dat\"}' digi.dat; " + "cat kine.dat >> reco.dat", + [f"digi_{i}"], f"./{tf}", i, "RECO") + r(f"./{tf}/digi.dat", f"reco_{i}") + r(f"./{tf}/kine.dat", f"reco_{i}") + w(f"./{tf}/reco.dat", f"reco_{i}") + + stage(f"aod_{i}", "cat reco.dat sub/extra.dat > AO2D.dat", + [f"reco_{i}"], f"./{tf}", i, "AOD") + r(f"./{tf}/reco.dat", f"aod_{i}") + r(f"./{tf}/sub/extra.dat", f"aod_{i}") + w(f"./{tf}/AO2D.dat", f"aod_{i}") + r(f"./tf{i}/AO2D.dat", "aodmerge") + + inputs = " ".join(f"tf{i}/AO2D.dat" for i in range(1, ntf + 1)) + stage("aodmerge", f"cat {inputs} > AO2D_merged.dat", + [f"aod_{i}" for i in range(1, ntf + 1)], "./", -1, "AOD") + w("./AO2D_merged.dat", "aodmerge") + + truth = build_report(written, read, [s["name"] for s in stages]) + return {"stages": stages}, truth + + +def run(argv, **kw): + print("+ " + " ".join(argv), flush=True) + return subprocess.run(argv, **kw) + + +def main(argv=None) -> int: + ap = argparse.ArgumentParser( + description=__doc__, + formatter_class=argparse.RawDescriptionHelpFormatter) + ap.add_argument("--workdir", default=None, + help="scratch directory (default: a fresh one under $TMPDIR)") + ap.add_argument("--backends", default=None, + help="comma-separated backends to test (default: all but " + f"the reference; known: {', '.join(sorted(filegraph.BACKENDS))})") + ap.add_argument("--reference", default="fanotify", + help="backend to compare the others against ('' to skip)") + ap.add_argument("--ntf", type=int, default=4) + ap.add_argument("--cpu-limit", type=int, default=4) + ap.add_argument("--sleep", default="0.4") + ap.add_argument("--python", default=sys.executable) + a = ap.parse_args(argv) + + runner = os.path.join(REPO, "MC", "workflow_runner", + "o2dpg_workflow_runner.py") + if not os.path.exists(runner): + print(f"no runner at {runner}", file=sys.stderr) + return 2 + + ref = a.reference.strip() + candidates = ([b.strip() for b in a.backends.split(",") if b.strip()] + if a.backends is not None + else [b for b in sorted(filegraph.BACKENDS) if b != ref]) + active = list(dict.fromkeys(([ref] if ref else []) + candidates)) + if not active: + print("nothing to run", file=sys.stderr) + return 2 + + workdir = a.workdir or os.path.join( + os.getenv("TMPDIR", "/tmp"), f"filegraph_equiv_{os.getpid()}") + os.makedirs(workdir, exist_ok=True) + print(f"workdir: {workdir}") + + wf, truth = build_workflow(a.ntf, a.sleep) + for name, doc in (("workflow.json", wf), ("truth.json", truth)): + with open(os.path.join(workdir, name), "w") as f: + json.dump(doc, f, indent=2) + print(f"{len(wf['stages'])} stages, {len(truth['file_report'])} files expected") + + t0 = time.time() + r = run([a.python, runner, "-f", "workflow.json", + "--cpu-limit", str(a.cpu_limit), "--mem-limit", "8000", + "--filegraph-backends", ",".join(active)], + cwd=workdir, env=dict(os.environ, O2DPG_ROOT=REPO)) + if r.returncode != 0: + print(f"runner failed with rc={r.returncode}", file=sys.stderr) + return 2 + print(f"workflow finished in {time.time() - t0:.1f}s") + + reports = {} + for b in active: + found = glob.glob(os.path.join(workdir, f"filegraph_{b}_*.json")) + if found: + with open(found[0]) as f: + reports[b] = json.load(f) + + failures = [] + + def grade(title, reference, candidate, backend, why): + print(f"\n{'=' * 72}\n== {title}\n{'=' * 72}") + text, summary = compare(reference, candidate, limit=10) + print(text) + print(f"OVERALL: {summary['verdict']}") + if summary["verdict"] == "UNSAFE": + failures.append((backend, why)) + return summary + + for backend in active: + if backend not in reports: + print(f"\nNO REPORT from {backend}", file=sys.stderr) + if backend != ref: + failures.append((backend, "no report")) + continue + + if backend == ref: + # fanotify resolves the process chain from /proc after the event, + # so on a fast machine it loses accesses by processes that exited + print(f"\n{'=' * 72}\n== {backend} (reference) vs truth\n{'=' * 72}") + text, summary = compare(truth, reports[backend], limit=10) + print(text) + print(f"OVERALL: {summary['verdict']}") + if summary["verdict"] == "UNSAFE": + print(f"NOTE: the reference {backend} itself misses edges") + continue + + grade(f"{backend} vs truth", truth, reports[backend], backend, + "misses edges the truth has") + if ref in reports: + summary = grade(f"{backend} vs {ref} (reference)", reports[ref], + reports[backend], backend, f"misses edges {ref} has") + with open(os.path.join(workdir, f"diff_{backend}_vs_{ref}.json"), "w") as f: + json.dump(summary, f, indent=2) + + print(f"\n{'=' * 72}") + for b, why in failures: + print(f"FAIL {b}: {why}") + if not failures: + print("PASS every backend is at least SAFE against truth" + + (f" and against {ref}" if ref else "")) + print(f"workdir kept at {workdir}") + return 1 if failures else 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/UTILS/FileIOGraph/tests/filegraph_test_support.py b/UTILS/FileIOGraph/tests/filegraph_test_support.py new file mode 100644 index 000000000..69ee796c8 --- /dev/null +++ b/UTILS/FileIOGraph/tests/filegraph_test_support.py @@ -0,0 +1,20 @@ +"""Put the FileIOGraph tools and the runner package on sys.path. + +Imported first by every test module here, so there is one bootstrap +instead of one per file. +""" +import os +import sys + +_HERE = os.path.dirname(os.path.abspath(__file__)) +FILEGRAPH_DIR = os.path.dirname(_HERE) +REPO = os.path.dirname(os.path.dirname(FILEGRAPH_DIR)) +RUNNER_BIN = os.path.join(REPO, "MC", "workflow_runner") + +for _p in (FILEGRAPH_DIR, RUNNER_BIN): + if _p not in sys.path: + sys.path.insert(0, _p) + +from o2dpg_runner import filegraph # noqa: E402 + +__all__ = ["FILEGRAPH_DIR", "REPO", "RUNNER_BIN", "filegraph"] diff --git a/UTILS/FileIOGraph/tests/test_analyse_strace.py b/UTILS/FileIOGraph/tests/test_analyse_strace.py new file mode 100644 index 000000000..66a105e5b --- /dev/null +++ b/UTILS/FileIOGraph/tests/test_analyse_strace.py @@ -0,0 +1,131 @@ +#!/usr/bin/env python3 +"""Offline tests for the strace analyser.""" +from __future__ import annotations + +import json +import os +import re +import subprocess +import sys +import tempfile +import unittest + +from filegraph_test_support import FILEGRAPH_DIR # noqa: F401 + +import analyse_FileIO_strace as A # noqa: E402 + +ALL = [re.compile(r".*")] + +# What `strace -f -qq -y -e status=successful` really writes. The last two +# lines of the open block are a call split around another process's entry. +TRACE = '''\ +101 openat(AT_FDCWD, "sgn.dat", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3 +101 openat(AT_FDCWD, "../bkg.dat", O_RDONLY) = 4 +102 openat(AT_FDCWD, "kine.dat", O_RDONLY|O_CLOEXEC) = 3 +102 openat(AT_FDCWD, "/usr/lib/libc.so.6", O_RDONLY|O_CLOEXEC) = 3 +102 openat(AT_FDCWD, ".", O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_DIRECTORY) = 8 +101 openat(AT_FDCWD, "reco.dat", O_WRONLY|O_CREAT|O_APPEND +102 openat(AT_FDCWD, "digi.dat", O_RDONLY) = 5 +101 <... openat resumed>, 0666) = 6 +101 renameat2(AT_FDCWD, "tmp.dat", AT_FDCWD, "final.dat", 0) = 0 +''' + + +class TestParseTrace(unittest.TestCase): + @classmethod + def setUpClass(cls): + with tempfile.TemporaryDirectory() as d: + p = os.path.join(d, "trace_0_reco_1.log") + with open(p, "w") as f: + f.write(TRACE) + cls.seen, cls.calls = A.parse_trace(p) + + def test_resolved_path_wins_over_the_argument(self): + # the argument said '../bkg.dat'; -y resolved it + self.assertIn(("/w/bkg.dat", "read"), self.seen) + + def test_write_flags_classify_as_write(self): + self.assertIn(("/w/tf1/sgn.dat", "write"), self.seen) + self.assertIn(("/w/tf1/kine.dat", "read"), self.seen) + + def test_split_call_keeps_its_flags(self): + # O_WRONLY appeared on the '' half only + self.assertIn(("/w/tf1/reco.dat", "write"), self.seen) + + def test_interleaved_call_is_not_swallowed(self): + self.assertIn(("/w/tf1/digi.dat", "read"), self.seen) + + def test_rename_destination_counts_as_written(self): + self.assertIn(("final.dat", "write"), self.seen) + + def test_directory_open_is_not_part_of_the_graph(self): + self.assertNotIn("/w/tf1", [p for p, _ in self.seen]) + + def test_repeated_accesses_collapse(self): + with tempfile.TemporaryDirectory() as d: + p = os.path.join(d, "trace_0_t.log") + line = ('9 openat(AT_FDCWD, "a.dat", O_RDONLY) = 3\n') + with open(p, "w") as f: + f.write(line * 500) + seen, calls = A.parse_trace(p) + self.assertEqual(seen, {("/w/a.dat", "read")}) + self.assertEqual(calls, 500) + + +class TestCollect(unittest.TestCase): + def test_attribution_comes_from_the_file_name(self): + with tempfile.TemporaryDirectory() as d: + with open(os.path.join(d, "trace_3_reco_1.log"), "w") as f: + f.write(TRACE) + with open(os.path.join(d, "trace_4_aod_1.log"), "w") as f: + f.write('7 openat(AT_FDCWD, "reco.dat", O_RDONLY) ' + '= 3\n') + written, read, tasks, stats = A.collect(d, "/w", ALL) + + self.assertEqual(tasks, ["aod_1", "reco_1"]) + self.assertEqual(written["./tf1/sgn.dat"], {"reco_1"}) + self.assertEqual(read["./tf1/reco.dat"], {"aod_1"}) + # outside the working directory, so not part of the graph + self.assertNotIn("/usr/lib/libc.so.6", written) + self.assertNotIn("/usr/lib/libc.so.6", read) + + def test_non_trace_files_are_ignored(self): + with tempfile.TemporaryDirectory() as d: + with open(os.path.join(d, "notes.txt"), "w") as f: + f.write("nothing\n") + _, _, tasks, stats = A.collect(d, "/w", ALL) + self.assertEqual(tasks, []) + self.assertEqual(stats["traces"], 0) + + +class TestCli(unittest.TestCase): + def test_end_to_end(self): + with tempfile.TemporaryDirectory() as d: + td = os.path.join(d, "traces") + os.makedirs(td) + with open(os.path.join(td, "trace_1_sgnsim_1.log"), "w") as f: + f.write('9 openat(AT_FDCWD, "sgn.dat", O_WRONLY|O_CREAT, ' + '0666) = 3\n') + with open(os.path.join(td, "trace_2_digi_1.log"), "w") as f: + f.write('9 openat(AT_FDCWD, "sgn.dat", O_RDONLY) ' + '= 3\n') + out = os.path.join(d, "report.json") + subprocess.run( + [sys.executable, + os.path.join(FILEGRAPH_DIR, "analyse_FileIO_strace.py"), + "--straceDir", td, "--basedir", "/w", "-o", out], + check=True, stdout=subprocess.DEVNULL) + with open(out) as f: + doc = json.load(f) + + entry = doc["file_report"][0] + self.assertEqual(entry["file"], "./tf1/sgn.dat") + self.assertEqual(entry["written_by"], ["sgnsim_1"]) + self.assertEqual(entry["read_by"], ["digi_1"]) + tpl = doc["file_template_report"][0] + self.assertEqual(tpl["file"], "./tfX/sgn.dat") + self.assertEqual(tpl["written_by"], ["sgnsim_X"]) + + +if __name__ == "__main__": + unittest.main() diff --git a/UTILS/FileIOGraph/tests/test_filegraph.py b/UTILS/FileIOGraph/tests/test_filegraph.py new file mode 100644 index 000000000..960c452f9 --- /dev/null +++ b/UTILS/FileIOGraph/tests/test_filegraph.py @@ -0,0 +1,237 @@ +#!/usr/bin/env python3 +"""Offline tests for the report machinery, the comparison and the backends. + +Plain unittest: this has to run on the bare interpreter of a GRID worker +or a CVMFS O2 environment, neither of which has pytest. + + python3 -m unittest discover -s UTILS/FileIOGraph/tests -t UTILS/FileIOGraph/tests +""" +from __future__ import annotations + +import json +import logging +import os +import re +import subprocess +import sys +import tempfile +import unittest +from unittest import mock + +from filegraph_test_support import FILEGRAPH_DIR, filegraph + +import equivalence_test # noqa: E402 +from compare_reports import compare # noqa: E402 +from filegraph_report import ( # noqa: E402 + basedir_prefix, build_report, keep_file, relative_to_basedir, +) + +ALL = [re.compile(r".*")] +LOG = logging.getLogger("test") + + +class TestPathHelpers(unittest.TestCase): + def test_relative_to_basedir(self): + for base in ("/w", "/w/"): + prefix = basedir_prefix(base) + self.assertEqual(relative_to_basedir("/w/tf1/a.root", prefix), + "./tf1/a.root") + self.assertIsNone(relative_to_basedir("/usr/lib/libc.so", prefix)) + + def test_logs_and_dpl_config_are_noise(self): + self.assertFalse(keep_file("./tf1/sgnsim_1.log", ALL)) + self.assertFalse(keep_file("./tf1/sgnsim_1.log_done", ALL)) + self.assertFalse(keep_file("./dpl-config.json", ALL)) + self.assertTrue(keep_file("./tf1/AO2D.root", ALL)) + + def test_filters_apply(self): + self.assertTrue(keep_file("./tf1/a.root", [re.compile(r".*\.root")])) + self.assertFalse(keep_file("./tf1/a.dat", [re.compile(r".*\.root")])) + + +class TestBuildReport(unittest.TestCase): + def setUp(self): + self.doc = build_report( + {"./tf1/a.root": {"digi_1"}, "./tf2/a.root": {"digi_2"}, + "./AO2D.root": {"aodmerge"}}, + {"./tf1/a.root": {"reco_1", "aodmerge"}, + "./tf2/a.root": {"reco_2", "aodmerge"}}, + ["digi_1", "digi_2", "reco_1", "reco_2", "aodmerge"]) + self.tpl = {e["file"]: e for e in self.doc["file_template_report"]} + + def test_timeframe_tasks_become_templates(self): + self.assertEqual(self.tpl["./tfX/a.root"]["written_by"], ["digi_X"]) + self.assertEqual(self.tpl["./tfX/a.root"]["source_timeframes"], [1, 2]) + + def test_global_reader_is_not_templated(self): + # a naive "strip the trailing _N" would corrupt aodmerge + self.assertIn("aodmerge", self.tpl["./tfX/a.root"]["read_by"]) + self.assertIn("reco_X", self.tpl["./tfX/a.root"]["read_by"]) + + def test_non_timeframe_file_absent_from_templates(self): + self.assertNotIn("./AO2D.root", self.tpl) + + def test_task_report_covers_every_task(self): + self.assertEqual([t["task"] for t in self.doc["task_report"]], + ["aodmerge", "digi_1", "digi_2", "reco_1", "reco_2"]) + + +class TestCompare(unittest.TestCase): + def _verdict(self, cand_written, cand_read): + ref = build_report({"./tf1/a.root": {"digi_1"}}, + {"./tf1/a.root": {"reco_1"}}, ["digi_1", "reco_1"]) + cand = build_report(cand_written, cand_read, ["digi_1", "reco_1"]) + return compare(ref, cand)[1] + + def test_identical_is_exact(self): + s = self._verdict({"./tf1/a.root": {"digi_1"}}, {"./tf1/a.root": {"reco_1"}}) + self.assertEqual(s["verdict"], "EXACT") + + def test_extra_reader_is_safe(self): + s = self._verdict({"./tf1/a.root": {"digi_1"}}, + {"./tf1/a.root": {"reco_1", "qc_1"}}) + self.assertEqual(s["verdict"], "SAFE") + self.assertEqual(s["sections"]["file_report"]["missing_edges"], 0) + + def test_missing_reader_is_unsafe(self): + s = self._verdict({"./tf1/a.root": {"digi_1"}}, {}) + self.assertEqual(s["verdict"], "UNSAFE") + self.assertEqual(s["sections"]["file_report"]["missing_edges"], 1) + self.assertAlmostEqual(s["sections"]["file_report"]["recall"], 0.5) + + def test_missing_file_is_unsafe(self): + self.assertEqual(self._verdict({}, {})["verdict"], "UNSAFE") + + +class TestAnalyseFanotifyLog(unittest.TestCase): + """End-to-end on the fanotify analyser, so the shared module stays honest.""" + + ACTION = ( + "2026-05-06 16:49:18,267 INFO Task pid=101 tid=0 bkg finished rc=0\n" + "2026-05-06 16:49:19,267 INFO Task pid=102 tid=1 sgnsim_1 finished rc=0\n" + "2026-05-06 16:49:20,267 INFO Task pid=103 tid=2 reco_1 finished rc=0\n" + ) + + def test_report(self): + with tempfile.TemporaryDirectory() as d: + action = os.path.join(d, "action.log") + monitor = os.path.join(d, "monitor.log") + out = os.path.join(d, "report.json") + with open(action, "w") as f: + f.write(self.ACTION) + with open(monitor, "w") as f: + f.write(f'"{d}/bkg.dat",write,101\n' + f'"{d}/tf1/sgn.dat",write,201;102\n' + f'"{d}/bkg.dat",read,201;102\n' + f'"{d}/tf1/sgn.dat",read,103\n' + f'"{d}/tf1/reco_1.log",write,103\n' + f'"/usr/lib/libc.so.6",read,103\n') + subprocess.run( + [sys.executable, os.path.join(FILEGRAPH_DIR, "analyse_FileIO_v2.py"), + "--actionFile", action, "--monitorFile", monitor, + "--basedir", d, "-o", out], + check=True, stdout=subprocess.DEVNULL) + with open(out) as f: + doc = json.load(f) + + got = {e["file"]: (e["written_by"], e["read_by"]) for e in doc["file_report"]} + self.assertEqual(got["./bkg.dat"], (["bkg"], ["sgnsim_1"])) + # a grandchild's access is attributed through the parent chain + self.assertEqual(got["./tf1/sgn.dat"], (["sgnsim_1"], ["reco_1"])) + self.assertNotIn("./tf1/reco_1.log", got) + self.assertNotIn("/usr/lib/libc.so.6", got) + + +class StubBackend(filegraph.FileGraphBackend): + name = "stub" + + def wrap(self, argv, taskname, tid): + return ["stub", taskname, str(tid), "--"] + argv + + +def manager(spec, action_log="action.log"): + return filegraph.FileGraphManager.from_config(spec, "/w", 4242, action_log, LOG) + + +class TestBackendSelection(unittest.TestCase): + def setUp(self): + patch = mock.patch.dict(os.environ, {}, clear=False) + patch.start() + self.addCleanup(patch.stop) + for k in ("O2DPG_PRODUCE_FILEGRAPH", "O2DPG_FILEGRAPH_BACKENDS"): + os.environ.pop(k, None) + filegraph.BACKENDS["stub"] = StubBackend + self.addCleanup(filegraph.BACKENDS.pop, "stub", None) + + def test_nothing_requested_means_no_backend(self): + self.assertEqual(manager("").backends, []) + + def test_named_backends_are_built_in_order(self): + self.assertEqual([b.name for b in manager("stub,fanotify").backends], + ["stub", "fanotify"]) + + def test_legacy_variable_selects_fanotify_with_that_exe(self): + os.environ["O2DPG_PRODUCE_FILEGRAPH"] = "/opt/mon.exe" + m = manager("") + self.assertEqual([b.name for b in m.backends], ["fanotify"]) + self.assertEqual(m.backends[0].exe, "/opt/mon.exe") + + def test_unknown_backend_is_skipped_not_fatal(self): + self.assertEqual([b.name for b in manager("stub,nonsense").backends], ["stub"]) + + def test_the_action_log_reaches_the_backend_that_needs_it(self): + b = manager("fanotify", action_log="pipeline_action_7.log").backends[0] + self.assertIn("pipeline_action_7.log", b.analyser_args()) + + def test_only_fanotify_writes_the_legacy_report_name(self): + self.assertTrue(filegraph.FanotifyBackend.legacy_report) + self.assertFalse(StubBackend.legacy_report) + + +class TestBackendWrapping(unittest.TestCase): + ARGV = ["/bin/bash", "-c", "echo hi"] + + def setUp(self): + filegraph.BACKENDS["stub"] = StubBackend + self.addCleanup(filegraph.BACKENDS.pop, "stub", None) + + def test_a_backend_that_does_not_wrap_leaves_the_command_alone(self): + self.assertEqual(manager("fanotify").wrap(list(self.ARGV), "sgnsim_1", 3), + self.ARGV) + + def test_a_wrapping_backend_keeps_the_command_as_the_tail(self): + argv = manager("stub").wrap(list(self.ARGV), "sgnsim_1", 3) + self.assertEqual(argv[:4], ["stub", "sgnsim_1", "3", "--"]) + self.assertEqual(argv[4:], self.ARGV) + + def test_an_empty_manager_is_a_no_op(self): + m = manager("") + self.assertEqual(m.wrap(list(self.ARGV), "sgnsim_1", 3), self.ARGV) + m.start() + m.stop() + self.assertEqual(m.analyse(), {}) + + +class TestFilegraphDir(unittest.TestCase): + def test_resolves_without_o2dpg_root(self): + with mock.patch.dict(os.environ, {}, clear=False): + os.environ.pop("O2DPG_ROOT", None) + d = filegraph.filegraph_dir() + self.assertTrue(os.path.exists(os.path.join(d, "analyse_FileIO_v2.py")), d) + + +class TestSyntheticWorkflow(unittest.TestCase): + def test_the_truth_matches_the_commands(self): + wf, truth = equivalence_test.build_workflow(2, "0") + files = {e["file"]: e for e in truth["file_report"]} + self.assertEqual(files["./tf1/AO2D.dat"]["read_by"], ["aodmerge"]) + # a run-time subdirectory is part of the expected graph + self.assertEqual(files["./tf1/sub/extra.dat"]["written_by"], ["digi_1"]) + for s in wf["stages"]: + self.assertIn("cwd", s) + self.assertIn("resources", s) + self.assertIn("timeframe", s) + + +if __name__ == "__main__": + unittest.main() diff --git a/UTILS/InitHepMCEventSkip.sh b/UTILS/InitHepMCEventSkip.sh new file mode 100755 index 000000000..a7d45273b --- /dev/null +++ b/UTILS/InitHepMCEventSkip.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +# Path to the JSON file +JSON_FILE=${1:-HepMC_EventSkip_ALT.json} +EVENTS=$2 + +# insert event count offset +echo "[]" > ${JSON_FILE} # init json file +JQ_COMMAND="jq '. + [{"HepMCEventOffset": ${EVENTS}}]' ${JSON_FILE} > tmp_123.json; mv tmp_123.json ${JSON_FILE}" +eval ${JQ_COMMAND} + +echo ${EVENTS} diff --git a/UTILS/Parsers/READMEtreeFastCache.md b/UTILS/Parsers/READMEtreeFastCache.md new file mode 100644 index 000000000..19296ef11 --- /dev/null +++ b/UTILS/Parsers/READMEtreeFastCache.md @@ -0,0 +1,126 @@ +# 📦 ROOT Tree Fast Cache System + +This repository contains ROOT macros for fast lookup and interpolation of values from TTrees, using: + +- `treeFastCache1D.C`: 1D cache with nearest-neighbor lookup +- `treeFastCacheND.C`: ND cache with exact match in N–1 dimensions and nearest-neighbor in 1 dimension + +They are designed for interactive analysis with `TTree::Draw`, e.g., QA plots, calibration overlays, or smoothed time series. + +--- + +## 🔹 `treeFastCache1D.C` + +### ✅ Features + +- Register 1D lookup maps from TTrees +- Nearest-neighbor lookup from `std::map` +- Can register by ID or string name +- Fast evaluation inside `TTree::Draw` +- Alias integration for interactive sessions + +### 🧪 Example + +```cpp +TTree* tree = ...; +int mapID = registerMap1DByName("dcar_vs_time", "time", "dcar_value", tree, "subentry==127"); + +tree->SetAlias("dcar_smooth", ("getNearest1D(time," + std::to_string(mapID) + ")").c_str()); +tree->Draw("dcar_value:dcar_smooth", "indexType==1", "colz", 10000); +``` + +--- + +## 🔸 `treeFastCacheND.C` + +### ✅ Features + +- ND caching with: + - **Exact match** in N–1 dimensions + - **Nearest-neighbor** lookup in 1 dimension (e.g. `time`) +- Uses full `double` precision for all keys +- Alias support for `TTree::Draw` +- Registration by name with hashed map ID +- Variadic interface for direct use + +### 🧪 Example: Time Series + +```cpp +TTree* tree = ...; +int mapID = registerMapND("dcar_vs_time", tree, {"subentry"}, "time", "mTSITSTPC.mDCAr_A_NTracks_median", "1"); +setNearestNDAlias(tree, "dcar_smooth", "dcar_vs_time", "time", {"subentry"}); + +tree->Draw("mTSITSTPC.mDCAr_A_NTracks_median:dcar_smooth", "indexType==1", "colz", 10000); +``` + +### 🖊️ Parameters for `registerMapND` +```cpp +int registerMapND( + const std::string& name, // Unique name of the map + TTree* tree, // Source TTree + const std::vector& exactDims, // Exact-match coordinate names + const std::string& nearestDim, // Nearest-match dimension (e.g. time) + const std::string& valueVar, // Variable to interpolate + const std::string& selection // TTree selection +); +``` + +### 🖊️ Parameters for `setNearestNDAlias` +```cpp +void setNearestNDAlias( + TTree* tree, // Target tree + const std::string& aliasName, // Alias to create + const std::string& mapName, // Name used in registration + const std::string& nearestCoordExpr, // Nearest-match expression + const std::vector& exactCoordExprs // Exact match expressions +); +``` + +### ⚡️ Alternative: Direct expression +```cpp +tree->Draw("val:getNearestND(time,mapID,subentry)", ...); +``` + +--- + +## 📊 Internal Storage + +### 1D: +```cpp +std::map> registeredMaps; +std::map nameToMapID; +``` + +### ND: +```cpp +std::map, std::map>> ndCaches; +std::map ndNameToID; +``` + +--- + +## 📌 Best Practices + +- Use aliases to simplify `TTree::Draw` expressions +- Use double precision for stability in nearest search +- Store maps by string name to simplify re-registration +- Prefer `setNearestNDAlias()` over manual `getNearestND(...)` for readability + +--- + +## 📤 Future Ideas + +- Optional interpolation (linear, spline) +- Graceful handling of unmatched keys +- Caching diagnostics and summary statistics +- C++ class wrapper for lifecycle + reusability + +--- + +## 📜 License + +Intended for use in internal physics analyses. No warranty implied. + +--- + +For more details, see comments and examples inside `treeFastCache1D.C` and `treeFastCacheND.C`. diff --git a/UTILS/Parsers/treeFastCache.C b/UTILS/Parsers/treeFastCache.C new file mode 100644 index 000000000..624a9f02f --- /dev/null +++ b/UTILS/Parsers/treeFastCache.C @@ -0,0 +1,258 @@ +/* +.L $O2DPG/UTILS/Parsers/treeFastCache.C +*/ + +/* + treeFastCache.C + Simple caching system for fast lookup of 1D values from a TTree, using nearest-neighbor interpolation. + This utility allows registration of (X, Y) pairs from a TTree into a std::map, + indexed by a user-defined mapID or map name. The lookup function `getNearest1D(x, mapID)` + retrieves the Y value for the X closest to the query. + Features: + - Register maps via string name or numeric ID + - Query nearest-neighbor value for any X + - Graceful error handling and range checking + - Base for future ND extension +*/ + +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +// Map: mapID -> map +std::map> registeredMaps; +std::map nameToMapID; + +/// Hash a string to create a deterministic mapID +int hashMapName(const std::string& name) { + std::hash hasher; + return static_cast(hasher(name)); +} + +/// Register a 1D lookup map from TTree (X -> Y) +/// @param valX Name of the X-axis variable (lookup key) +/// @param valY Name of the Y-axis variable (value to retrieve) +/// @param tree Pointer to TTree to extract data from +/// @param selection Selection string (TTree::Draw-compatible) +/// @param mapID Integer ID to associate with this map +void registerMap1D(const std::string& valX, const std::string& valY, TTree* tree, const std::string& selection, int mapID) { + if (!tree) { + std::cerr << "[registerMap1D] Null TTree pointer." << std::endl; + return; + } + + int entries = tree->Draw((valY + ":" + valX).c_str(), selection.c_str(), "goff"); + if (entries <= 0) { + std::cerr << "[registerMap1D] No entries matched for mapID=" << mapID << std::endl; + return; + } + + if (!tree->GetV1() || !tree->GetV2()) { + std::cerr << "[registerMap1D] Internal Draw buffer pointers are null." << std::endl; + return; + } + + std::map newMap; + for (int i = 0; i < entries; ++i) { + if (i >= tree->GetSelectedRows()) { + std::cerr << "[registerMap1D] Index out of range at i=" << i << std::endl; + break; + } + double x = tree->GetV2()[i]; // valX + float y = tree->GetV1()[i]; // valY + newMap[x] = y; + } + + registeredMaps[mapID] = std::move(newMap); + std::cout << "[registerMap1D] Registered map " << mapID << " with " << entries << " entries." << std::endl; +} + +/// Register by name; returns mapID computed from name +int registerMap1DByName(const std::string& mapName, const std::string& valX, const std::string& valY, TTree* tree, const std::string& selection) { + int mapID = hashMapName(mapName); + nameToMapID[mapName] = mapID; + registerMap1D(valX, valY, tree, selection, mapID); + return mapID; +} + +/// Get the nearest Y for a given X from the map registered with mapID +/// @param x Query value along X axis +/// @param mapID Map identifier used in registration +/// @return Y value corresponding to nearest X in the map +float getNearest1D(float x, int mapID) { + const auto itMap = registeredMaps.find(mapID); + if (itMap == registeredMaps.end()) { + std::cerr << "[getNearest1D] Map ID " << mapID << " not found." << std::endl; + return NAN; + } + + const auto& map = itMap->second; + if (map.empty()) { + std::cerr << "[getNearest1D] Map ID " << mapID << " is empty." << std::endl; + return NAN; + } + + auto it = map.lower_bound(x); + if (it == map.begin()) return it->second; + if (it == map.end()) return std::prev(it)->second; + + auto prev = std::prev(it); + return (std::abs(prev->first - x) < std::abs(it->first - x)) ? prev->second : it->second; +} + +/// Convenience version: lookup by name +float getNearest1DByName(float x, const std::string& mapName) { + auto it = nameToMapID.find(mapName); + if (it == nameToMapID.end()) { + std::cerr << "[getNearest1DByName] Map name \"" << mapName << "\" not found." << std::endl; + return NAN; + } + return getNearest1D(x, it->second); +} + +/// Example usage +void example1D() { + TFile *f = TFile::Open("timeSeries10000_apass5.root"); + TTree * tree0=(TTree*)f->Get("timeSeries"); + // Fill tree here or load from file + int mapID = registerMap1DByName("dcar_vs_time", "time", "mTSITSTPC.mDCAr_A_NTracks_median", tree0, "subentry==127"); + tree0->SetAlias("mDCAr_A_NTracks_median_All" ,("getNearest1D(time, " + std::to_string(mapID) + ")").data()); + tree0->Draw("mTSITSTPC.mDCAr_A_NTracks_median:mDCAr_A_NTracks_median_All","indexType==1","",10000); +} +/* ------------------------------------------------------------------ + Statistics extension (non‑breaking) ------------------------------- + Added without changing previous API. + + New options: + • Enum‑based interface for better ROOT compatibility + enum StatKind { kMean=0, kMedian=1, kStd=2 }; + float getStat(double x,int mapID,StatKind kind,double dx); + + • Convenience thin wrappers for ROOT aliases + getMean1D , getMedian1D , getStd1D + + • cacheStat unchanged (uses strings internally) + + ------------------------------------------------------------------*/ + +#include +#include +#include + +// --- enum for faster numeric calls -------------------------------- +enum StatKind { kMean=0, kMedian=1, kStd=2 }; + +// Cache: stat → mapID → dx → (x → value) +static std::map>> cacheMean; +static std::map>> cacheMedian; +static std::map>> cacheStd; + +static float _mean(const std::vector& v){ return v.empty()?NAN:std::accumulate(v.begin(),v.end(),0.0f)/v.size(); } +static float _median(std::vector v){ if(v.empty()) return NAN; size_t n=v.size()/2; std::nth_element(v.begin(),v.begin()+n,v.end()); return v[n]; } +static float _std(const std::vector& v){ if(v.size()<2) return NAN; float m=_mean(v); double s2=0; for(float e:v){ double d=e-m; s2+=d*d;} return std::sqrt(s2/(v.size()-1)); } + +//-------------------------------------------------------------------- +static float _computeStat(double x,int mapID,double dx,StatKind k){ + const auto itM=registeredMaps.find(mapID); + if(itM==registeredMaps.end()||itM->second.empty()) return NAN; + const auto &mp=itM->second; + std::vector buf; + for(auto it=mp.lower_bound(x-dx); it!=mp.end()&&it->first<=x+dx; ++it) buf.push_back(it->second); + if(buf.empty()) return NAN; + switch(k){ + case kMean: return _mean(buf); + case kMedian: return _median(buf); + case kStd: return _std(buf); + } + return NAN; +} + +//-------------------------------------------------------------------- +/** + * @brief Return a local statistic (mean / median / std) around a query point. + * + * This version is preferred inside **TTree::Draw** because it uses an enum + * instead of a string literal. + * + * @param x Center of the window (same coordinate used in the cache) + * @param mapID ID returned by registerMap1D / registerMap1DByName + * @param kind kMean (0), kMedian (1) or kStd (2) + * @param dx Half‑window size: the statistic is computed from all points + * with X in [x − dx, x + dx] + * + * Internally the first request builds (and caches) a map x → stat(x) + * for the given (mapID, dx, kind). Subsequent calls are O(log N). + */ +// Fast numeric interface (enum) ------------------------------------ +float getStat(double x,int mapID,StatKind kind,double dx){ + auto *pcache = (kind==kMean? &cacheMean : (kind==kMedian? &cacheMedian : &cacheStd)); + auto &byMap = (*pcache)[mapID]; + auto &byDx = byMap[dx]; + if(byDx.empty()){ + // build lazily for this dx + const auto itM=registeredMaps.find(mapID); + if(itM==registeredMaps.end()) return NAN; + for(const auto &kv: itM->second){ double cx=kv.first; byDx[cx]=_computeStat(cx,mapID,dx,kind);} } + const auto &statMap = byDx; + auto it=statMap.lower_bound(x); + if(it==statMap.begin()) return it->second; + if(it==statMap.end()) return std::prev(it)->second; + auto prev=std::prev(it); + return (fabs(prev->first-x)first-x)?prev->second:it->second); +} + +// String interface kept for backward compat. +float getStat(double x,int mapID,const char* st,double dx){ + std::string s(st); + if(s=="mean") return getStat(x,mapID,kMean ,dx); + if(s=="median") return getStat(x,mapID,kMedian,dx); + if(s=="std"||s=="sigma") return getStat(x,mapID,kStd,dx); + std::cerr<<"[getStat] Unknown statType="<(kind),dx); +} + +//-------------------------------------------------------------------- +// Pre‑cache requested stats (by enum) ------------------------------- (by enum) ------------------------------- +bool cacheStat(int mapID,const std::vector& stats,double dx){ + for(const std::string &s:stats){ + if(s=="mean") getStat(0,mapID,kMean ,dx); // lazy build + else if(s=="median") getStat(0,mapID,kMedian,dx); + else if(s=="std"||s=="sigma") getStat(0,mapID,kStd,dx); + } + return true; +} + +//-------------------------------------------------------------------- +/// Example: statistics with enum wrappers +void exampleStat1D(){ + TFile *f=TFile::Open("timeSeries10000_apass5.root"); + TTree *t=(TTree*)f->Get("timeSeries"); + int id = registerMap1DByName("dcar_time_stat","time","mTSITSTPC.mDCAr_A_NTracks_median",t,"subentry==127"); + + // Pre‑cache mean & std for ±200 window + cacheStat(id,{"mean","std"},200); + + // Use integer selector (0 = mean, 2 = std). This avoids any ROOT + // overload ambiguity and works in TTree::Draw directly. + t->SetAlias("dcar_mean", Form("getStat(time,%d,0,200)", id)); // 0 → kMean + t->SetAlias("dcar_sigma", Form("getStat(time,%d,2,200)", id)); // 2 → kStd + + t->Draw("mTSITSTPC.mDCAr_A_NTracks_median:dcar_mean","indexType==1","colz",10000); + t->Draw("getStat(time,591487517, 0 ,10000+0):getStat(time,591487517, 1 ,10000+0)","indexType==1","colz",100000); +} \ No newline at end of file diff --git a/UTILS/Parsers/treeFastCacheND.C b/UTILS/Parsers/treeFastCacheND.C new file mode 100644 index 000000000..541022746 --- /dev/null +++ b/UTILS/Parsers/treeFastCacheND.C @@ -0,0 +1,221 @@ +/* +.L $O2DPG/UTILS/Parsers/treeFastCacheND.C +*/ +/* + treeFastCacheND.C + + Multi-dimensional cache system for ROOT TTree lookup with mixed matching modes: + - Exact match in N-1 dimensions + - Nearest-neighbor in one dimension (typically time) + + This system complements treeFastCache1D by enabling efficient access to structured ND data. + + Features: + - Caches values based on N-dimensional keys using a combination of exact and nearest lookups + - Provides ROOT-friendly interface usable within TTree::Draw + - Uses full double precision for both exact and nearest match coordinates + - Works interactively with aliases and supports variadic access + + Author: ChatGPT for Marian +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +/// Generic ND key support +typedef std::map NearestMap; ///< 1D interpolation map (e.g., time → value) +typedef std::vector ExactKey; ///< Exact match dimensions (e.g., subentry, CRU, etc.) + +/// Container of ND maps: outer map = mapID → {ExactKey → NearestMap} +std::map> ndCaches; +std::map ndNameToID; ///< Map from user-defined name to hash-based mapID + +/// Deterministically hash a name to a map ID +int hashMapNameND(const std::string& name) { + std::hash hasher; + return static_cast(hasher(name)); +} + +/// Register an ND lookup map from a TTree +/** + * @param name Unique name to identify the map + * @param tree Source TTree + * @param exactDims List of column names for exact-match dimensions + * @param nearestDim Name of nearest-search dimension (e.g., "time") + * @param valueVar Name of value column + * @param selection TTree::Draw-compatible selection expression + * @return mapID (generated from name) + */ +int registerMapND(const std::string& name, + TTree* tree, + const std::vector& exactDims, + const std::string& nearestDim, + const std::string& valueVar, + const std::string& selection) { + if (!tree) throw std::invalid_argument("[registerMapND] Null TTree."); + int mapID = hashMapNameND(name); + ndNameToID[name] = mapID; + + std::string expr = valueVar + ":" + nearestDim; + for (const auto& dim : exactDims) expr += ":" + dim; + int entries = tree->Draw(expr.c_str(), selection.c_str(), "goff"); + if (entries>=tree->GetEstimate()){ + tree->SetEstimate(entries*2); + entries = tree->Draw(expr.c_str(), selection.c_str(), "goff"); + } + if (entries <= 0) { + std::cerr << "[registerMapND] No entries selected." << std::endl; + return mapID; + } + + int dimCount = 2 + exactDims.size(); + std::vector buffers(dimCount); + for (int i = 0; i < dimCount; ++i) { + buffers[i] = tree->GetVal(i); + if (!buffers[i]) throw std::runtime_error("[registerMapND] Missing Draw buffer at " + std::to_string(i)); + } + + std::map newMap; + for (int i = 0; i < entries; ++i) { + double val = buffers[0][i]; + double near = buffers[1][i]; + ExactKey key; + for (size_t j = 0; j < exactDims.size(); ++j) key.push_back(buffers[2 + j][i]); + newMap[key][near] = val; + } + ndCaches[mapID] = std::move(newMap); + std::cout << "[registerMapND] Registered ND map '" << name << "' with ID=" << mapID << " and " << entries << " entries." << std::endl; + return mapID; +} + +/// Query map using exact + nearest key +/** + * @param query Value for nearest-match dimension + * @param mapID ID of registered map + * @param exactKey Vector of exact-match dimensions (must match registration) + * @return Interpolated value or NaN if no match + */ +double getNearestND(double query, int mapID, const ExactKey& exactKey) { + const auto& map = ndCaches[mapID]; + auto itOuter = map.find(exactKey); + if (itOuter == map.end()) return NAN; + + const auto& innerMap = itOuter->second; + if (innerMap.empty()) return NAN; + + auto it = innerMap.lower_bound(query); + if (it == innerMap.begin()) return it->second; + if (it == innerMap.end()) return std::prev(it)->second; + + auto prev = std::prev(it); + return (std::abs(prev->first - query) < std::abs(it->first - query)) ? prev->second : it->second; +} + +/// Variadic interface to getNearestND for use with TTree::Draw +/** + * @tparam Dims Arbitrary number of scalar dimensions (int or float/double) + * @param query Nearest dimension (e.g., time) + * @param mapID Map ID registered via registerMapND + * @param dims... Dimensions to match exactly + */ +template +double getNearestND(double query, int mapID, Dims... dims) { + ExactKey exactKey{static_cast(dims)...}; + return getNearestND(query, mapID, exactKey); +} + +/// Lookup using map name +/** + * @param query Nearest-dimension value (e.g., time) + * @param exactKey Exact-dimension vector + * @param mapName Map name from registration + */ +double getNearestNDByName(double query, const ExactKey& exactKey, const std::string& mapName) { + auto itID = ndNameToID.find(mapName); + if (itID == ndNameToID.end()) return NAN; + return getNearestND(query, itID->second, exactKey); +} + +/// Register alias in tree for use in interactive Draw +/** + * @param tree TTree pointer + * @param aliasName Alias to create + * @param mapName Name of registered ND map + * @param nearestCoordExpr Expression for nearest dimension (e.g., "time") + * @param exactCoordExprs Expressions for exact dimensions (e.g., {"subentry"}) + */ +void setNearestNDAlias(TTree* tree, const std::string& aliasName, const std::string& mapName, const std::string& nearestCoordExpr, const std::vector& exactCoordExprs) { + auto it = ndNameToID.find(mapName); + if (it == ndNameToID.end()) { + std::cerr << "[setNearestNDAlias] Map not found: " << mapName << std::endl; + return; + } + int mapID = it->second; + + std::string expr = "getNearestND(" + nearestCoordExpr + "," + std::to_string(mapID); + for (const auto& ex : exactCoordExprs) expr += "," + ex; + expr += ")"; + tree->SetAlias(aliasName.c_str(), expr.c_str()); +} + +/// Example usage for ND map creation - for very High voltage queries for distertion calibration +void exampleND() { + TTree* tree = new TTree("tree", "demo"); + int mapID = registerMapND("test_map", tree, {"CRU", "iTF"}, "time", "val", "subentry==127"); + setNearestNDAlias(tree, "val_interp", "test_map", "time", {"CRU", "iTF"}); + tree->Draw("val:val_interp", "val!=0", "colz"); +} + +/// Example usage for time series ND lookup +void exampleTimeSeries() { + TFile *f = TFile::Open("timeSeries10000_apass5.root"); + TTree *tree = (TTree*)f->Get("timeSeries"); + int mapID = registerMapND("dcar_vs_time", tree, {"subentry"}, "time", "mTSITSTPC.mDCAr_A_NTracks_median", "1"); + setNearestNDAlias(tree, "mDCAr_A_NTracks_median_interp", "dcar_vs_time", "time", {"subentry"}); + tree->Draw("mTSITSTPC.mDCAr_A_NTracks_median:mDCAr_A_NTracks_median_interp", "indexType==1", "", 10000); +} + +/// Example usage for time series ND lookup +void test_exampleTimeSeries() { + TFile *f5 = TFile::Open("timeSeries10000_LHC23zzx_apass5.root"); + TTree *tree5 = (TTree*)f5->Get("timeSeries"); + TFile *f4 = TFile::Open("timeSeries10000_LHC23zz_combo_apass4.root"); + TTree *tree4 = (TTree*)f4->Get("timeSeries"); + int mapID5A = registerMapND("mDCAr_A_Median_median5", tree5, {"subentry"}, "time", "mTSITSTPC.mDCAr_A_Median_median", "1"); + int mapID5C = registerMapND("mDCAr_C_Median_median5", tree5, {"subentry"}, "time", "mTSITSTPC.mDCAr_C_Median_median", "1"); + int mapID4A = registerMapND("mDCAr_A_Median_median4", tree4, {"subentry"}, "time", "mTSITSTPC.mDCAr_A_Median_median", "1"); + int mapID4C = registerMapND("mDCAr_C_Median_median4", tree4, {"subentry"}, "time", "mTSITSTPC.mDCAr_C_Median_median", "1"); + // + setNearestNDAlias(tree5, "mDCAr_A_Median_median_interp5", "mDCAr_A_Median_median5", "time", {"subentry"}); + setNearestNDAlias(tree5, "mDCAr_C_Median_median_interp5", "mDCAr_C_Median_median5", "time", {"subentry"}); + setNearestNDAlias(tree4, "mDCAr_C_Median_median_interp5", "mDCAr_A_Median_median5", "time", {"subentry"}); + // + setNearestNDAlias(tree5, "mDCAr_A_Median_median_interp4", "mDCAr_A_Median_median4", "time", {"subentry"}); + setNearestNDAlias(tree4, "mDCAr_A_Median_median_interp4", "mDCAr_A_Median_median4", "time", {"subentry"}); + + tree5->Draw("mTSITSTPC.mDCAr_A_Median_median:mDCAr_A_Median_median_interp4", "indexType==1", "", 10000); + // make unit test -RMS should be 0 + int val5=tree5->Draw("mTSITSTPC.mDCAr_A_Median_median==mDCAr_A_Median_median_interp5", "indexType==1", ""); + float rms5=tree5->GetHistogram()->GetRMS(); + float mean5=tree5->GetHistogram()->GetMean(); + //make unit test like output rms5==0, mean5==1 + int va4l=tree4->Draw("mTSITSTPC.mDCAr_A_Median_median==mDCAr_A_Median_median_interp4", "indexType==1", ""); + float rms4=tree4->GetHistogram()->GetRMS(); + float mean4=tree4->GetHistogram()->GetMean(); + //make unit test like output rms5==0, mean5==1 + if ( std::abs(rms4) < 1e-5 && std::abs(mean4 - 1.0) < 1e-5) { + std::cout << "[UnitTest] OK - Interpolation match for apass4 is exact." << std::endl; + } else { + std::cerr << "[UnitTest] ERROR - Interpolation mismatch for apass4. RMS=" << rms4 << ", Mean=" << mean4 << std::endl; + } +} + diff --git a/UTILS/Parsers/workflowToJSON.sh b/UTILS/Parsers/workflowToJSON.sh new file mode 100644 index 000000000..1b6163a58 --- /dev/null +++ b/UTILS/Parsers/workflowToJSON.sh @@ -0,0 +1,206 @@ +# Source path for the script +# source $O2DPG/UTILS/Parsers/workflowToJSON.sh +# source $NOTES/JIRA/ATO-648/workflowToJSON.sh + +# Description: +# This script converts workflow configuration logs into a structured JSON format for enhanced data analysis and readability. + +# Usage: +# Example: source $O2DPG/UTILS/Parsers/workflowToJSON.sh + +alias helpCat=cat +[[ -x "$(command -v pygmentize)" ]] && alias helpCat="pygmentize -O style=borland,linenos=1 -l bash" + +function helpCat0() { + local language="$1" + shift + [[ -x "$(command -v pygmentize)" ]] && pygmentize -O style=monokai,linenos=1 -l "$language" | cat - "$@" + [[ ! -x "$(command -v pygmentize)" ]] && cat - "$@" +} + +init648() { + cat < --configKeyValues + +Example Command: +A typical command in the log might appear like this: +o2-ctf-reader-workflow --session default_1304519_3825 --severity info --shm-segment-id 0 --shm-segment-size 64000000000 --resources-monitoring 50 --resources-monitoring-dump-interval 50 --early-forward-policy noraw --fairmq-rate-logging 0 --timeframes-rate-limit 2 --timeframes-rate-limit-ipcid 0 --ans-version compat --delay 1 --loop 0 --max-tf 2 --ctf-input list.list --onlyDet ITS,TPC,TOF,FV0,FT0,FDD,TRD,CTP --pipeline tpc-entropy-decoder:1 --allow-missing-detectors --its-digits --mft-digits --configKeyValues "keyval.input_dir=/tmp/tmp.rgwfzmuG63;keyval.output_dir=/dev/null;;" + +Transformation: +This script processes each command line from the log, turning them into JSON objects. This structural change not only organizes the data but also enhances accessibility for programmatic queries and analysis. +HELP_USAGE +} + + +makeParse() { + # Use heredoc to send help text through helpCat alias, which will apply syntax highlighting if pygmentize is available. + if [[ -z "$1" ]]; then + # Use heredoc to send help text through helpCat alias, which will apply syntax highlighting if pygmentize is available. + cat <<'HELP_USAGE' | helpCat0 bash +makeParse: Parse the workflow log and create an output.json file. +Usage: + makeParse + +Example usage: + #makeParse workflowconfig.log > ~/output.json # To parse a specific log file. + makeParse /lustre/alice/tpcdata/Run3/SCDprodTests/fullRec/PbPb_Streamers_Tune_ClusterErrors-merge-streamer/avgCharge_fullTPC_sampling_TimeBins16-Average0_rejectEdgeCl-Seed0-Track0-margin0/LHC23zzh.b5p/544116.38kHz/0110/workflowconfig.log > workflow.json + cat workflow.json | jq '.[] | select(.command | test("^o2-dpl"))' # Filter DPL workflows. + jq '.[] | select(.command | test("^o2-gpu"))' workflow.json # Filter GPU related commands. + +HELP_USAGE + return # Exit the function if no parameters provided + fi + # + log_file=$1 +jq -Rn ' + [inputs | split("\n")[] | select(length > 0 and startswith("o2-")) | + { + command: (split(" ")[0]), + switches: (split(" ") | .[1:-1] | + reduce .[] as $item ({}; + if $item | startswith("--") then + if $item | contains("=") then + . + ({($item | ltrimstr("--") | split("=")[0]): ($item | split("=")[1])}) + else + . + ({($item | ltrimstr("--")): true}) + end + else + .[keys_unsorted[-1]] = $item + end + )), + configKeyValues: (if (contains("--configKeyValues")) then + (split("--configKeyValues")[1] | split("|")[0] | gsub("^\\s+\"|\"\\s+;"; "") | split(";") | + map(select(. != "" and contains("="))) | + map(split("=") | select(length == 2)) | + map({(.[0]): .[1]})) | add + else + {} + end + ) + } + ]' "$log_file" +} + + +# makeDiffWorkflow alien:///alice/data/2023/LHC23zzk/544515/apass5/1140/o2_ctf_run00544515_orbit0221337280_tf0000047516_epn242/workflowconfig.log alien:///alice/data/2023/LHC23zzk/544515/apass4/1140/o2_ctf_run00544515_orbit0221337280_tf0000047516_epn242/workflowconfig.log 1 gpu +makeDiffWorkflow() { + # Make diff of workflowConfig.log JSONs. + # Usage: + # makeDiffWorkflow + # file0: path or alien:// to first workflowconfig.log + # file1: path or alien:// to second workflowconfig.log + # diffType: 0 = unified diff, 1 = side-by-side (default: 1) + # filter: string to match command, e.g. gpu (default: gpu) + # Notes: + # Creates workflow0.json and workflow1.json from parsed input. + # Uses makeParse and jq for filtering and diffing. + # Supports Alien paths via alien.py cat. + + if [[ -z "$1" || -z "$2" ]]; then + cat <<'HELP_USAGE' | helpCat0 bash +makeDiffWorkflow: Compare two O2 workflowconfig logs (local or Alien). +Usage: + makeDiffWorkflow +Parameters: + file0 – path to first workflowconfig.log or alien:// path + file1 – path to second workflowconfig.log or alien:// path + diffType – (optional) 0 = unified diff, 1 = side-by-side diff (default: 1) + filter – (optional) command string filter, e.g. "gpu", "hlt" (default: gpu) +Example: + makeDiffWorkflow alien:///path/to/file0.log ./file1.log 1 gpu + makeDiffWorkflow alien:///alice/data/2023/LHC23zzk/544515/apass5/1140/o2_ctf_run00544515_orbit0221337280_tf0000047516_epn242/workflowconfig.log alien:///alice/data/2023/LHC23zzk/544515/apass4/1140/o2_ctf_run00544515_orbit0221337280_tf0000047516_epn242/workflowconfig.log 1 gpu + +HELP_USAGE + return + fi + file0="$1" + file1="$2" + diffType="${3:-1}" + filter="${4:-o2-gpu}" + # Download from alien if needed + if [[ "$file0" == alien://* ]]; then + echo "Fetching $file0 from Alien..." + alien.py cat "$file0" > "${TMPDIR:-/tmp}/workflow0.log" + file0="${TMPDIR:-/tmp}/workflow0.log" + fi + if [[ "$file1" == alien://* ]]; then + echo "Fetching $file1 from Alien..." + alien.py cat "$file1" > "${TMPDIR:-/tmp}/workflow1.log" + file1="${TMPDIR:-/tmp}/workflow1.log" + fi + + makeParse "$file0" > workflow0.json + makeParse "$file1" > workflow1.json + + # Apply filter to both JSON files + jq ".[] | select(.command | test(\"^${filter}\"))" workflow0.json | jq --sort-keys . > workflow0.filtered.json + jq ".[] | select(.command | test(\"^${filter}\"))" workflow1.json | jq --sort-keys . > workflow1.filtered.json + + echo "Comparing workflow commands filtered by '^o2-${filter}'..." + + if [[ "$diffType" -eq 1 ]]; then + diff --side-by-side --left-column --color=always workflow0.filtered.json workflow1.filtered.json | less -R + else + diff --color=always workflow0.filtered.json workflow1.filtered.json | less -R + fi +} + + + + +makeDiffExample(){ + cat < workflow0.json\` + + \`makeParse /lustre/alice/tpcdata/Run3/SCDprodTests/fullRec/PbPb_Streamers_Tune_ClusterErrors-merge-streamer/avgCharge_fullTPC_sampling_TimeBins16-Average0_rejectEdgeCl-Seed0-Track0-margin0-ref/LHC23zzh.b5p/544116.38kHz/0110/workflowconfig.log > workflow1.json\` + +2. Compare two JSON files using \`jq\` and \`diff\` directly without temporary files: + \diff <(jq --sort-keys . workflow1.json) <(jq --sort-keys . workflow0.json)\ + +3. Use \`diff\` with side-by-side view and color using ANSI color codes: + \`diff --side-by-side --left-column --color=always <(jq --sort-keys . workflow1.json) <(jq --sort-keys . workflow0.json) | less -R\` + +4. Compare JSON files focusing only on commands starting with "o2-gpu": + Filtering the entries where the command starts with "o2-gpu" and then comparing: + \ diff --side-by-side --left-column --color=always <(jq '.[] | select(.command | test("^o2-gpu"))' workflow1.json | jq --sort-keys .) <(jq '.[] | select(.command | test("^o2-gpu"))' workflow0.json | jq --sort-keys .) | less -R +HELP_USAGE +} + +init648 \ No newline at end of file diff --git a/UTILS/README.md b/UTILS/README.md new file mode 100644 index 000000000..e97dc862d --- /dev/null +++ b/UTILS/README.md @@ -0,0 +1,22 @@ +# General utilities + +## Fetching PRs (based on labels) + +The tool [`o2dpg_make_github_pr_report.py`](o2dpg_make_github_pr_report.py) can be used to create a markdown report PRs in a given repository. +Note that PRs are fetched and reported based on assigned labels. By default, the report tool will look for labels of the form `async-*`. +PRs are fetched and sorted first by their state, closed and merged, just closed and open. +Within that sorting, they are grouped by assigned labels and the last sorting is based on time. The time-based sorting is based on + +* state `closed` and `merged`: `merged_at`, +* state `closed` and not `merged`: `updated_at`, +* state `open`: `updated_at.` + +To fetch for instance PRs for `O2`, the following command would do the job and it will write the markdown report to `o2dpg_pr_report_O2DPG.md`: +```bash +./UTILS/o2dpg_make_github_pr_report.py --repo O2DPG +``` + +A few more things can be configured. To see the full list of options and flags, type +```bash +./UTILS/o2dpg_make_github_pr_report.py --help +``` diff --git a/UTILS/ReadHepMCEventSkip.sh b/UTILS/ReadHepMCEventSkip.sh new file mode 100755 index 000000000..2fc548b09 --- /dev/null +++ b/UTILS/ReadHepMCEventSkip.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +# Path to the JSON file +JSON_FILE=$1 +tf=$2 + +# get event offset +JQCOMMAND="jq '.[] | select(.HepMCEventOffset) | .HepMCEventOffset' ${JSON_FILE}" +offset=`eval ${JQCOMMAND}` +if [ ! $offset ] +then + offset=0 +fi + +# count generated events +JQCOMMAND="jq '[.[] | select(.tf < ${tf}) | .HepMCEventCount] | add' ${JSON_FILE}" +events=`eval ${JQCOMMAND}` +if [ ! $events ] +then + events=0 +fi + +# total number of events to skip +echo $((offset + events)) diff --git a/UTILS/UpdateHepMCEventSkip.sh b/UTILS/UpdateHepMCEventSkip.sh new file mode 100755 index 000000000..83f93e77f --- /dev/null +++ b/UTILS/UpdateHepMCEventSkip.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +# Path to the JSON file +JSON_FILE=${1:-HepMC_EventSkip_ALT.json} +tf=$2 + +# Value to set or add +EVENTS=$(grep "DISTRIBUTING" ../tf${tf}/sgngen_*.log | tail -n 1 | awk '//{print $5}') + +[ -f $JSON_FILE ] || echo "[]" > ${JSON_FILE} # init json file if it doesn't exist +# insert event count ... if a count for this tf does not already exist +JQ_COMMAND="jq 'if any(.[]; .tf == ${tf}) then . else . + [{\"tf\": ${tf}, "HepMCEventCount": ${EVENTS}}] end' ${JSON_FILE} > tmp_123.json; mv tmp_123.json ${JSON_FILE}" +eval ${JQ_COMMAND} \ No newline at end of file diff --git a/UTILS/checkCorruptedAO2Ds.C b/UTILS/checkCorruptedAO2Ds.C new file mode 100644 index 000000000..d51768b17 --- /dev/null +++ b/UTILS/checkCorruptedAO2Ds.C @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +bool gWarningDetected = false; // Global flag to track the warning + +void MyErrorHandler(int level, Bool_t abort, const char *location, const char *msg) { + if (strstr(msg, "repair") != nullptr) { + gWarningDetected = true; + } + DefaultErrorHandler(level, abort, location, msg); // Call ROOT’s default handler +} + +int checkCorruptedAO2Ds(TString infileName = "/alice/sim/2024/LHC24h2/535545/AOD/005/AO2D.root", bool fromAlien = true) { + + SetErrorHandler(MyErrorHandler); + + if (fromAlien) { + TGrid::Connect("alien://"); + if (!infileName.Contains("alien://")) { + infileName = "alien://" + infileName; + } + } + + auto inFile = TFile::Open(infileName.Data()); + if (!inFile || inFile->IsZombie()) { + return -1; + } + + // all VLA branches in the AO2Ds.root + std::map> branchesToCheck = { + {"O2mcparticle_001", std::vector{"fIndexArray_Mothers", "fVx", "fIndexMcCollisions"}}, + {"O2ft0", std::vector{"fAmplitudeA", "fChannelA", "fAmplitudeC", "fChannelC"}}, + {"O2fv0a", std::vector{"fAmplitude", "fChannel"}}, + {"O2mccalolabel_001", std::vector{"fIndexArrayMcParticles", "fAmplitudeA"}}, + {"O2zdc_001", std::vector{"fEnergy", "fChannelE", "fAmplitude", "fTime", "fChannelT"}} + }; + + for (auto const& dirKey : *inFile->GetListOfKeys()) { + if (TString(dirKey->GetName()).Contains("DF")) { + auto df = static_cast(inFile->Get(dirKey->GetName())); + std::cout << dirKey->GetName() << std::endl; + for (auto const& pair : branchesToCheck) { + auto tree = static_cast(df->Get(pair.first.data())); + for (auto const& branchName : pair.second) { + auto leaf = static_cast(tree->GetLeaf(branchName.data())); + + for (int iEntry{0}; iEntryGetEntries(); ++iEntry) { + if (tree->GetEntry(iEntry) < 0) { + std::cout << "Found corrupted file! DF: " << dirKey->GetName() << " Tree:" << pair.first.data() << " Branch:" << branchName.data() << std::endl; + return -1; + } + if (gWarningDetected) { + std::cout << "Found file in need of repair! DF: " << dirKey->GetName() << " Tree:" << pair.first.data() << " Branch:" << branchName.data() << std::endl; + return -2; + } + } + } + } + } + } + + return 0; +} diff --git a/UTILS/cmpROOTFiles.py b/UTILS/cmpROOTFiles.py new file mode 100755 index 000000000..78a3d5ee1 --- /dev/null +++ b/UTILS/cmpROOTFiles.py @@ -0,0 +1,135 @@ +#!/usr/bin/env python3 +import ROOT +import argparse + +parser = argparse.ArgumentParser(description='Check if 2 ROOT files are binary compatible', + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + +parser.add_argument('-f1','--file1', help='First ROOT TFile', required=True) +parser.add_argument('-f2','--file2', help='Second ROOT TFile', required=True) +args = parser.parse_args() + +def get_total_branch_list(tree): + branches = [] + + # Function to recursively get branches + def get_branches_recursive(branch): + branches.append(branch) + sub_branches = branch.GetListOfBranches() + + if sub_branches: + for sub_branch in sub_branches: + get_branches_recursive(sub_branch) + + # Get top-level branches of the tree + top_level_branches = tree.GetListOfBranches() + + # Traverse recursively through branches + for branch in top_level_branches: + get_branches_recursive(branch) + + return branches + + +def compare_branches(obj1, obj2): + # Check if the object classes match + if obj1.IsA() != obj2.IsA(): + print("Type doesn't match") + return False + + # Check if the byte content is the same + if obj1.GetTitle() != obj2.GetTitle(): + print ("Title doesn't match") + return False + + # Convert objects to TBuffer to compare their byte content + #buffer1 = ROOT.TBuffer(ROOT.TBuffer.EMode.kWrite, 10000) + #buffer2 = ROOT.TBuffer(ROOT.TBuffer.EMode.kWrite, 10000) + + #obj1.Streamer(buffer1) + #obj2.Streamer(buffer2) + # checking branch + print ("Checking branch " + obj1.GetTitle()) + if obj1.GetTotBytes() != obj2.GetTotBytes(): + print ("Bytecount different") + return False + + +# compare 2 TTree objects +def compare_trees(tree1, tree2): + branches1 = get_total_branch_list(tree1) + branches2 = get_total_branch_list(tree2) + + # branch count needs to be same + if len(branches1) != len(branches2): + return False + + # we do not impose same branch order so we build 2 hashsets containing tuples + # of (branchname, totalsize) + + set1 = set() + for br in branches1: + # Print key name and class name + #print("Key: ", br.GetName()) + #print("Class: ", br.ClassName()) + #print("BC: ", str(br.GetTotalSize())) + #print("---------------") + + totals = 0 + for entry in range(br.GetEntries()): + totals = totals + br.GetEntry(entry) + set1.add((br.GetName(), totals, br.GetEntries())) + + set2 = set() + for br in branches2: + totals = 0 + for entry in range(br.GetEntries()): + totals = totals + br.GetEntry(entry) + set2.add((br.GetName(), totals, br.GetEntries())) + + inters = set1.intersection(set2) + #print (inters) + symdiff = (set1.symmetric_difference(set2)) + if (len(symdiff) > 0): + print (symdiff) + return len(symdiff) == 0 + +def compare_root_files(file1, file2): + # Open the ROOT files + tfile1 = ROOT.TFile.Open(file1) + tfile2 = ROOT.TFile.Open(file2) + + # Get the list of keys (TKeys) in the ROOT files + keys1 = tfile1.GetListOfKeys() + keys2 = tfile2.GetListOfKeys() + + # Check if the number of keys is the same + if keys1.GetEntries() != keys2.GetEntries(): + return False + + # Iterate through the keys and compare byte content + # check keys + success = True + for key1, key2 in zip(keys1, keys2): + obj1 = key1.ReadObj() + obj2 = key2.ReadObj() + + isTree1 = isinstance(obj1, ROOT.TTree) + isTree2 = isinstance(obj2, ROOT.TTree) + if isTree1 != isTree2: + success = False + + elif isTree1 and isTree2: + success = success and compare_trees(obj1, obj2) + + # Close the files + tfile1.Close() + tfile2.Close() + + return success + +result = compare_root_files(args.file1, args.file2) +if result: + print("Byte content of the two ROOT files is the same.", args.file1, args.file2) +else: + print("Byte content of the two ROOT files is different.", args.file1, args.file2) diff --git a/UTILS/delete_CCDBObject.sh b/UTILS/delete_CCDBObject.sh new file mode 100755 index 000000000..91ec03f48 --- /dev/null +++ b/UTILS/delete_CCDBObject.sh @@ -0,0 +1,9 @@ +OBJECT=${1} +# object is of the form PATH/VALIDITY/ID +# Users/s/swenzel/MCProdInfo/LHC24d1c_minus50/529663/55ceab78-485e-11f0-8e6c-c0a80209250c + +curl -X DELETE --cert /tmp/tokencert_$(id -u).pem \ + --key /tmp/tokenkey_$(id -u).pem \ + -v -k \ + "http://ccdb-test.cern.ch:8080/${OBJECT}" + diff --git a/UTILS/delete_CCDBPath_recursively.sh b/UTILS/delete_CCDBPath_recursively.sh new file mode 100755 index 000000000..c17add57a --- /dev/null +++ b/UTILS/delete_CCDBPath_recursively.sh @@ -0,0 +1,17 @@ +# Step 1 we browse the complete MCProd (or whatever) subfolder + +P=$1 + +BROWSE_RESULT=$(curl --cert /tmp/tokencert_$(id -u).pem \ + --key /tmp/tokenkey_$(id -u).pem \ + -v -k \ + "http://ccdb-test.cern.ch:8080/browse/${P}/*" | awk -F': ' ' +/^ID:/ {id=$2} +/^Path:/ {path=$2} +/^Validity:/ {split($2, a, " -"); validity=a[1]; print path "/" validity "/" id} +') + +for path in ${BROWSE_RESULT}; do + echo "Will,Would delete ${path}" + ./delete_CCDBObject.sh ${path} +done diff --git a/UTILS/findCorruptedAO2Ds.sh b/UTILS/findCorruptedAO2Ds.sh new file mode 100755 index 000000000..af8e8ddb3 --- /dev/null +++ b/UTILS/findCorruptedAO2Ds.sh @@ -0,0 +1,48 @@ +#!/bin/bash + +# Simple script to find corrupted AO2Ds using the checkCorruptedAO2Ds.C macro + +PRODUCTION=LHC24f3c +RUN=* # use * for all runs +NJOBS=20 +PRODUCTIONCYCLE=0 + +OUTPUTFILE=corrupted_files_$PRODUCTION.txt +if [ -e "$OUTPUTFILE" ]; then + rm $OUTPUTFILE +fi + +# find all files in alien +if [ "$RUN" == "*" ]; then + alien_find alien:///alice/sim/2024/${PRODUCTION}/${PRODUCTIONCYCLE}/5*/AOD/*/AO2D.root > files_to_check.txt +else + alien_find alien:///alice/sim/2024/${PRODUCTION}/${PRODUCTIONCYCLE}/${RUN}/AOD/*/AO2D.root > files_to_check.txt +fi +mapfile -t FILESTOCHECK < files_to_check.txt + +# process AO2Ds +process_file() { + IFS='/' read -a num <<< "$1" + INPUT=$1 + echo '.x checkCorruptedAO2Ds.C("'${INPUT}'", true)' | root -l -b > log_${num[6]}_${num[8]} + echo '.q' +} +export -f process_file + +parallel -j $NJOBS process_file ::: "${FILESTOCHECK[@]}" + +# create list of corrupted files +touch $OUTPUTFILE +ERRORSTR="Found corrupted file!" +REPAIRSTR="Found file in need of repair!" +for FILE in "${FILESTOCHECK[@]}"; do + IFS='/' read -a num <<< "$FILE" + if grep -q "$ERRORSTR" log_${num[6]}_${num[8]}; then + echo $FILE " is corrupted!" >> $OUTPUTFILE + elif grep -q "$REPAIRSTR" log_${num[6]}_${num[8]}; then + echo $FILE " is broken!" >> $OUTPUTFILE + fi +done + +rm files_to_check.txt +rm log_* diff --git a/UTILS/get_cherrypick_commit_list.sh b/UTILS/get_cherrypick_commit_list.sh new file mode 100755 index 000000000..1f452b268 --- /dev/null +++ b/UTILS/get_cherrypick_commit_list.sh @@ -0,0 +1,134 @@ +#!/bin/bash + +# given a commit cp_commit on branch source_branch as well +# as another branch target_branch, this script finds the list +# of all commits that are needed to succesfully cherry-pick cp_commit +# onto the target branch + +cp_commit=$1 # Commit you want to cherry-pick +source_branch=$2 # Branch containing commit cp_commit (e.g., master) +target_branch=$3 # Branch you want to cherry-pick onto (e.g., foo) + +# Check if all required arguments are provided +if [ -z "$cp_commit" ] || [ -z "$source_branch" ] || [ -z "$target_branch" ]; then + echo "Usage: $0 " + exit 1 +fi + +# Function to check if two git commits modify at least one common file +modifies_common_files() { + if [ "$#" -ne 2 ]; then + echo "Usage: check_common_files " + return 1 + fi + + local commit1="$1" + local commit2="$2" + + # Get the list of modified files for each commit + local files_commit1 + files_commit1=$(git diff --name-only "${commit1}^" "${commit1}") + + local files_commit2 + files_commit2=$(git diff --name-only "${commit2}^" "${commit2}") + + # Check for common files + local common_files + common_files=$(echo -e "${files_commit1}\n${files_commit2}" | sort | uniq -d) + + # Output result + if [ -n "$common_files" ]; then + return 1 + fi + return 0 +} + +# function to check if 2 commits can be swapped. This can determine if a commit needs +# to come stricly before another commit. +can_swap_commits() { + local commitA="$1" + shift + local commitB=("$@") # this is the list of commits that should swap (as a whole) with commitA + + reverseCommitList=() + + # Loop through the original array in reverse order + for ((i=${#commitB[@]}-1; i>=0; i--)); do + reverseCommitList+=("${commitB[i]}") + done + + # Create a temporary branch for testing + local temp_branch="temp_swap_test_branch" + + # record current state + GIT_CUR=$(git branch --show-current) + # Create a new temporary branch from the current HEAD + git checkout ${commitA}^ -b "$temp_branch" &> /dev/null + + RC=1 + for commit in "${reverseCommitList[@]}"; do + # Cherry-pick commit B onto a branch without commitA + if git cherry-pick "$commit" &> /dev/null; then + # RC=1 # Commits can be swapped + RC_local=1 + else + RC=0 # Cannot swap due to conflict when cherry-picking B + git cherry-pick --abort + fi + done + + # Cleanup: Reset to the original branch and delete the temp branch + git checkout ${GIT_CUR} &> /dev/null + git branch -D "$temp_branch" &>/dev/null + return ${RC} +} + +# Step 1: Identify branch-break off point +BRANCHPOINT=$(git merge-base "$source_branch" "$target_branch") + + +COMMITLIST=() +# Collect the initial set of commits to consider using a while loop +while IFS= read -r line; do + COMMITLIST+=("$line") +done < <(git log ${cp_commit}^...${BRANCHPOINT} --pretty=format:"%H") + +# filter out commits not touching the same files +FILTERED_COMMITS1=() +for commit_hash in "${COMMITLIST[@]}"; do + modifies_common_files ${commit_hash} ${cp_commit} + RC=$? + if [ ${RC} -eq 1 ]; then + FILTERED_COMMITS1+=(${commit_hash}) + fi +done + +# Next, filter out commits which are irrelevant for ${cp_commit} +CP_COMMIT_LIST=(${cp_commit}) # The list of CP=cherry_pick commits to keep/construct + +for commit_hash in "${FILTERED_COMMITS1[@]}"; do + if [ ! "${commit_hash}" == "${cp_commit}" ]; then + can_swap_commits "${commit_hash}" "${CP_COMMIT_LIST[@]}" + if [ $? -eq 0 ]; then + # echo "COMMIT ${commit_hash} is needed" + # in this case we need to record it to the list of relevant commits + # and also trace it's dependencies in turn + CP_COMMIT_LIST+=(${commit_hash}) + fi + fi +done + +# reverse the final list to have correct cherry-pick order + +CP_COMMITS_REVERSED=() +for ((i=${#CP_COMMIT_LIST[@]}-1; i>=0; i--)); do + CP_COMMITS_REVERSED+=("${CP_COMMIT_LIST[i]}") +done + +# List the commits +echo "To cherry-pick ${cp_commit} onto branch ${target_branch}, we need to apply:" +for ((i=0;i<${#CP_COMMITS_REVERSED[@]}; i++)); do + echo "${i}: ${CP_COMMITS_REVERSED[i]}" +done + +exit 0 diff --git a/UTILS/monitorCPU.cpp b/UTILS/monitorCPU.cpp new file mode 100644 index 000000000..6d28e2e57 --- /dev/null +++ b/UTILS/monitorCPU.cpp @@ -0,0 +1,92 @@ +// A small, standalone code, to quickly record CPU-vs-T +// for a running process, so that one can make plots. +// Simply compile with g++ -O2 monitorCPU.cpp -o monitor.exe and run. + +#include +#include +#include +#include +#include +#include + +double getProcessCpuUtilization(int pid) +{ + std::ifstream statFile("/proc/" + std::to_string(pid) + "/stat"); + if (!statFile.is_open()) { + std::cerr << "Failed to open stat file for PID " << pid << std::endl; + return -1.0; // Error indicator + } + + std::string line; + std::getline(statFile, line); + statFile.close(); + + std::istringstream iss(line); + std::string token; + // We only need the 14th and 15th fields: utime and stime + for (int i = 0; i < 13; ++i) { + iss >> token; + } + unsigned long utime, stime; + iss >> utime >> stime; + + unsigned long process_total = utime + stime; + static unsigned long last_process_total = 0; + + // Read total CPU time from /proc/stat + unsigned long total_cpu_time = 0; + std::ifstream stat("/proc/stat"); + if (stat.is_open()) { + std::string cpuLabel; + while (stat >> cpuLabel && cpuLabel != "cpu") { + // Skip non-cpu lines + stat.ignore(std::numeric_limits::max(), '\n'); + } + unsigned long value; + for (int i = 0; i < 10; ++i) { + stat >> value; + total_cpu_time += value; + } + } else { + std::cerr << "Failed to open /proc/stat" << std::endl; + return -1.0; // Error indicator + } + stat.close(); + + // Calculate CPU utilization over the last 5 seconds + static unsigned long last_total_cpu_time = total_cpu_time; + double utilization = (process_total - last_process_total) * 1.0 / (total_cpu_time - last_total_cpu_time); + + // Update last total CPU time for next calculation + last_total_cpu_time = total_cpu_time; + last_process_total = process_total; + + return utilization; +} + +int getNumberOfCores() +{ + return sysconf(_SC_NPROCESSORS_ONLN); +} + +int main(int argc, char* argv[]) +{ + int pid = -1; + + if (argc > 1) { + pid = atoi(argv[1]); + } + + while (true) { + double cpuUtilization = getProcessCpuUtilization(pid); + if (cpuUtilization >= 0.0) { + std::cerr << "CPU(" << pid << ") " << cpuUtilization * 100 * getNumberOfCores() << "\n"; + } else { + std::cerr << "Error retrieving CPU utilization." << std::endl; + } + + // Wait for 2 seconds + std::this_thread::sleep_for(std::chrono::seconds(2)); + } + return 0; +} diff --git a/UTILS/o2dpg_make_github_pr_report.py b/UTILS/o2dpg_make_github_pr_report.py new file mode 100755 index 000000000..9a3a83b9c --- /dev/null +++ b/UTILS/o2dpg_make_github_pr_report.py @@ -0,0 +1,221 @@ +#!/usr/bin/env python3 + +# Get list of PRs from provided repo that have a certain label assigned +# Can be used to figure out which PRs should be ported + +import sys +import argparse +import requests +import re + + +def organise_prs(prs): + """ + Sort PRs by time merged, starting from old to recent + """ + # collect merged PRs + prs_merged = [] + # collect the time of merged PRs + merged_at = [] + # simply closed + prs_closed = [] + closed_updated_at = [] + # open PRs + prs_open = [] + open_updated_at = [] + + for pr in prs: + if not pr['merged_at']: + if pr['state'] == 'open': + prs_open.append(pr) + open_updated_at.append(pr['updated_at']) + continue + if pr['state'] == 'closed': + prs_closed.append(pr) + closed_updated_at.append(pr['updated_at']) + continue + # get the PR itself and the merged timestamp + prs_merged.append(pr) + merged_at.append(pr['merged_at']) + + # sort the merged PRs by their merged timestamp + prs_merged = [pr for _, pr in sorted(zip(merged_at, prs_merged))] + prs_closed = [pr for _, pr in sorted(zip(closed_updated_at, prs_closed))] + prs_open = [pr for _, pr in sorted(zip(open_updated_at, prs_open))] + + return {'merged': prs_merged, + 'closed': prs_closed, + 'open': prs_open} + + +def get_prs(owner, repo, request_labels, pr_state=None, per_page=50, start_page=1, pages=1): + """ + Get PRs according to some selection + """ + # GitHub API endpoint for listing closed pull requests with a specific label + prs_return = [] + + has_error = False + for page in range(start_page, pages + 1): + pr_state = f'state={pr_state}&' if pr_state else 'state=all&' + url = f'https://api.github.com/repos/{owner}/{repo}/pulls?{pr_state}page={page}&per_page={per_page}' + + # Send GET request to GitHub API + response = requests.get(url) + + # Check if the request was successful (status code 200) + if response.status_code == 200: + # Parse JSON response + prs = response.json() + # PRs to return because we filter on a specific label + for pr in prs: + labels = pr['labels'] + take_pr = False + for label in labels: + if label['name'] in request_labels: + # only with the correct the label will be accepted + take_pr = True + break + if not take_pr: + continue + # we will end up here if accepted, so append + prs_return.append(pr) + + else: + print(f'Failed to retrieve data: {response.status_code} - {response.text}') + has_error = True + break + + if has_error: + return None + + # organise PRs into different lists (merged and others) + return organise_prs(prs_return) + + +def get_labels(owner, repo, regex=None): + """ + Get the labels that match given regex + """ + # the REST API url + url = f'https://api.github.com/repos/{owner}/{repo}/labels' + # Send GET request to GitHub API + response = requests.get(url) + + if response.status_code != 200: + print(f'ERROR: Problem to retrieve labels for owner {owner} and repository {repo}') + return None + + return [label['name'] for label in response.json() if not repo or re.match(regex, label['name'])] + + +def separate_labels_request_accept(labels, accept_suffix=None): + """ + Disentangle labels and ${O2DPG_ROOT}/test/run_tests.sh [--f ``` If you are inside the source directory, you can simply run ```bash -${O2DPG_ROOT}/test/run_tests.sh [--fail-immediately] +${O2DPG_ROOT}/test/run_tests.sh [--fail-immediately] [--keep-artifacts] [SUBTEST...] +``` +If the change you are testing is to a test script itself (`run_tests.sh` or +any `run_*_tests.sh`), invoke the checkout's own entrypoint instead, e.g. +`bash test/run_tests.sh` from inside the checkout: `run_tests.sh` finds its +sub-scripts next to itself, so calling `${O2DPG_ROOT}/test/run_tests.sh` +tests the *released* copy of the script you just edited, not your change, +even with `O2DPG_TEST_REPO_DIR` pointed at the checkout. + +### Running a subset + +`run_tests.sh` runs the generator, workflow and RelVal sub-tests. To run only +some of them, name them: + +```bash +${O2DPG_ROOT}/test/run_tests.sh generator relval +``` + +The offline harnesses under `test/tests/` check the entrypoint's selection, +exit-code aggregation and O2PDPSuite tag resolution without needing an O2 +environment: + +```bash +bash test/tests/run_tests_selection.sh +bash test/tests/exit_code_aggregation.sh +bash test/tests/resolve_tag.sh ``` ### Keeping all test artifacts @@ -68,24 +99,43 @@ ${O2DPG_ROOT}/test/run_tests.sh -h ``` which will give you ``` -usage: run_tests.sh [--fail-immediately] [--keep-artifacts] + +usage: run_tests.sh [--fail-immediately] [--keep-artifacts] [SUBTEST...] + + SUBTEST : one or more of: generator workflow relval (default: all) FLAGS: - --fail-immediately : abort as soon as the first tests fails - --keep-artifacts : keep simulation and tests artifacts, by default everything but the logs is removed after each test + --fail-immediately : stop after the first failing sub-test + --keep-artifacts : keep simulation artifacts, not just the logs ENVIRONMENT VARIABLES: - O2DPG_TEST_REPO_DIR : Point to the source repository you want to test. - O2DPG_TEST_HASH_BASE : The base hash you want to use for comparison (optional) - O2DPG_TEST_HASH_HEAD : The head hash you want to use for comparison (optional) + O2DPG_TEST_REPO_DIR : the source repository to test + O2DPG_TEST_HASH_BASE : base hash for the changed-file diff (optional) + O2DPG_TEST_HASH_HEAD : head hash for the changed-file diff (optional) + +``` - If O2DPG_TEST_HASH_BASE is not set, it will be looked for ALIBUILD_BASE_HASH. - If also not set, this will be set to HEAD~1. However, if there are unstaged - changes, it will be set to HEAD. +## When your change needs an unreleased O2 - If O2DPG_TEST_HASH_HEAD is not set, it will be looked for ALIBUILD_HEAD_HASH. - If also not set, this will be set to HEAD. However, if there are unstaged - changes, it will left blank. +The `Simulation tests against CVMFS` check runs against a published +`O2PDPSuite` release, so a change that depends on an unmerged or unreleased O2 +commit cannot pass it. Two escape hatches, in order of preference: + +1. If the O2 change is already in a published daily, pin it: add a line + `sim-tests-tag: daily-YYYYMMDD-HHMM-1` to the pull request description. +2. If it is not published anywhere yet, run the tests against your own build: + +```bash +aliBuild build O2DPG-sim-tests ``` + + This builds the O2sim stack from your work area and runs `run_tests.sh` + against your O2DPG checkout, so it covers O2 changes that are not committed + anywhere yet. It needs O2DPG as a development package (`aliBuild init + O2DPG`); without one, the sub-tests stop with `is not a git repository`. + + Touching `test/needs-o2-dev` makes the CVMFS check skip, which is useful to + keep a pull request from being gated by a check that cannot pass for it. No + check replaces it, so pair it with the command above. diff --git a/test/ci/resolve_tag.sh b/test/ci/resolve_tag.sh new file mode 100644 index 000000000..940090ba1 --- /dev/null +++ b/test/ci/resolve_tag.sh @@ -0,0 +1,38 @@ +#!/bin/bash + +# Pick the O2PDPSuite release to test against. Kept free of CVMFS paths and of +# CI variables so it can be exercised offline. + +resolve_o2pdpsuite_tag() +{ + local moduledir=${1:-} + local requested=${2:-} + + if [[ ! -d "${moduledir}" ]] ; then + echo "resolve_o2pdpsuite_tag: no such directory: ${moduledir}" >&2 + return 1 + fi + + if [[ -n "${requested}" ]] ; then + case ${requested} in + *[!A-Za-z0-9._-]* ) + echo "resolve_o2pdpsuite_tag: invalid tag: ${requested}" >&2 + return 1 ;; + esac + if [[ ! -e "${moduledir}/${requested}" ]] ; then + echo "resolve_o2pdpsuite_tag: requested tag not available: ${requested}" >&2 + return 1 + fi + echo "${requested}" + return 0 + fi + + local newest + newest=$(find "${moduledir}" -maxdepth 1 -name 'daily-*' -printf '%f\n' 2>/dev/null | + sort -V | tail -n 1) + if [[ -z "${newest}" ]] ; then + echo "resolve_o2pdpsuite_tag: no daily-* tag in ${moduledir}" >&2 + return 1 + fi + echo "${newest}" +} diff --git a/test/common/utils/utils.sh b/test/common/utils/utils.sh index e2cae2171..d2bd59a3c 100644 --- a/test/common/utils/utils.sh +++ b/test/common/utils/utils.sh @@ -4,13 +4,40 @@ # Test utility functionality # +# a global counter for tests +TEST_COUNTER=0 + +# Prepare some colored output +SRED="\033[0;31m" +SGREEN="\033[0;32m" +SYELLOW="\033[0;33m" +SEND="\033[0m" + +echo_green() +{ + echo -e "${SGREEN}${*}${SEND}" +} + + +echo_red() +{ + echo -e "${SRED}${*}${SEND}" +} + + +echo_yellow() +{ + echo -e "${SYELLOW}${*}${SEND}" +} + + remove_artifacts() { [[ "${KEEP_ONLY_LOGS}" == "1" ]] && find . -type f ! -name '*.log' -and ! -name "*serverlog*" -and ! -name "*mergerlog*" -and ! -name "*workerlog*" -delete } -get_changed_files() +git_get_changed_files() { # in the Github CIs, there are env variables that give us the base and head hashes, # so use them if they are there @@ -25,7 +52,12 @@ get_changed_files() [[ ! -z "$(git diff)" && -z ${ALIBUILD_HEAD_HASH+x} && -z ${O2DPG_TEST_HASH_HEAD+x} ]] && hash_head="" # if there are unstaged changes and no base from user, set to HEAD [[ ! -z "$(git diff)" && -z ${ALIBUILD_HEAD_HASH+x} && -z ${O2DPG_TEST_HASH_BASE+x} ]] && hash_base="HEAD" - git diff --diff-filter=AMR --name-only ${hash_base} ${hash_head} + local paths=$(git diff --diff-filter=AMR --name-only ${hash_base} ${hash_head}) + local absolute_paths= + for p in ${paths} ; do + absolute_paths+="$(realpath ${p}) " + done + echo "${absolute_paths}" } @@ -78,7 +110,7 @@ make_wf_creation_script() print_error_logs() { local search_dir=${1} - local search_pattern="TASK-EXIT-CODE: ([1-9][0-9]*)|[Ss]egmentation violation|[Ee]xception caught|\[FATAL\]|uncaught exception|\(int\) ([1-9][0-9]*)|fair::FatalException" + local search_pattern="TASK-EXIT-CODE: ([1-9][0-9]*)|[Ss]egmentation violation|[Ss]egmentation fault|Program crashed|[Ee]xception caught|\[FATAL\]|uncaught exception|\(int\) ([1-9][0-9]*)|fair::FatalException" local error_files=$(find ${search_dir} -maxdepth 4 -type f \( -name "*.log" -or -name "*serverlog*" -or -name "*workerlog*" -or -name "*mergerlog*" \) | xargs grep -l -E "${search_pattern}" | sort) for ef in ${error_files} ; do echo_red "Error found in log $(realpath ${ef})" diff --git a/test/needs-o2-dev b/test/needs-o2-dev new file mode 100644 index 000000000..ee9799078 --- /dev/null +++ b/test/needs-o2-dev @@ -0,0 +1,5 @@ +Touch this file in a pull request that cannot be tested against a published +O2PDPSuite release because it depends on an unreleased change in O2. +Replace this text with a one-line reason and a link to the O2 pull request. +Touching it enables the build/O2DPG/sim/o2dev check, which builds O2 from +source against AliceO2Group/AliceO2 dev. diff --git a/test/run_analysisqc_tests.sh b/test/run_analysisqc_tests.sh deleted file mode 100755 index fab8dd083..000000000 --- a/test/run_analysisqc_tests.sh +++ /dev/null @@ -1,160 +0,0 @@ -#!/bin/bash - -# The test parent dir to be cretaed in current directory -TEST_PARENT_DIR="o2dpg_tests/analysisqc" - -# unified names of log files -LOG_FILE="o2dpg-test-analysisqc.log" - -# Prepare some colored output -SRED="\033[0;31m" -SGREEN="\033[0;32m" -SEND="\033[0m" - - -echo_green() -{ - echo -e "${SGREEN}$@${SEND}" -} - - -echo_red() -{ - echo -e "${SRED}$@${SEND}" -} - - -get_git_repo_directory() -{ - local repo= - if [[ -d .git ]] ; then - pwd - else - repo=$(git rev-parse --git-dir 2> /dev/null) - fi - [[ "${repo}" != "" ]] && repo=${repo%%/.git} - echo ${repo} -} - - -test_analysisqc() -{ - echo "### Testing AnalysisQC creation for MC ###" > ${LOG_FILE} - ${O2DPG_ROOT}/MC/analysis_testing/o2dpg_analysis_test_workflow.py -f AO2D.root --is-mc -o wokflow_test_mc.json >> ${LOG_FILE} 2>&1 - local ret=${?} - [[ "${ret}" != "0" ]] && echo "[FATAL]: O2DPG_TEST failed" >> ${LOG_FILE} - echo "### Testing AnalysisQC creation for data ###" >> ${LOG_FILE} - ${O2DPG_ROOT}/MC/analysis_testing/o2dpg_analysis_test_workflow.py -f AO2D.root -o wokflow_test_data.json >> ${LOG_FILE} 2>&1 - local ret_data=${?} - [[ "${ret_data}" != "0" ]] && { echo "[FATAL]: O2DPG_TEST failed" >> ${LOG_FILE} ; ret=${ret_data} ; } - return ${ret} -} - - -print_usage() -{ - echo - echo "usage: run_workflow_tests.sh" - echo - echo " ENVIRONMENT VARIABLES:" - echo - echo " O2DPG_TEST_REPO_DIR : Point to the source repository you want to test." - echo " O2DPG_TEST_HASH_BASE : The base hash you want to use for comparison (optional)" - echo " O2DPG_TEST_HASH_HEAD : The head hash you want to use for comparison (optional)" - echo - echo " If O2DPG_TEST_HASH_BASE is not set, it will be looked for ALIBUILD_BASE_HASH." - echo " If also not set, this will be set to HEAD~1. However, if there are unstaged" - echo " changes, it will be set to HEAD." - echo - echo " If O2DPG_TEST_HASH_HEAD is not set, it will be looked for ALIBUILD_HEAD_HASH." - echo " If also not set, this will be set to HEAD. However, if there are unstaged" - echo " changes, it will left blank." - echo -} - -while [ "$1" != "" ] ; do - case $1 in - --help|-h ) print_usage - exit 1 - ;; - * ) echo "Unknown argument ${1}" - exit 1 - ;; - esac -done - -echo -echo "################################" -echo "# Run O2DPG AnalysisQC testing #" -echo "################################" -echo - -REPO_DIR=${O2DPG_TEST_REPO_DIR:-$(get_git_repo_directory)} -if [[ ! -d ${REPO_DIR}/.git ]] ; then - echo_red "Directory \"${REPO_DIR}\" is not a git repository." - exit 1 -fi - -if [[ -z ${O2DPG_ROOT+x} ]] ; then - echo_red "O2DPG is not loaded, probably other packages are missing as well in this environment." - exit 1 -fi - -# source the utilities -source ${REPO_DIR}/test/common/utils/utils.sh - -# Do the initial steps in the source dir where we have the full git repo -pushd ${REPO_DIR} > /dev/null - -# flag if anything changed for AnalysisQC -need_testing=$(get_changed_files | grep "MC/.*analysis_testing") - -# go back to where we came from -popd > /dev/null -REPO_DIR=$(realpath ${REPO_DIR}) - -# Now, do the trick: -# We just use the source dir since O2DPG's installation is basically just a copy of the whole repo. -# This makes sense in particular for local testing but also in the CI it works in the same way. We could do -# [[ -z {ALIBUILD_HEAD_HASH+x} ]] && export O2DPG_ROOT=${REPO_DIR} -# but let's do the same for both local and CI consistently -export O2DPG_ROOT=${REPO_DIR} - - -############### -# Let's do it # -############### -ret_global=0 -# prepare our local test directory for PWG tests -rm -rf ${TEST_PARENT_DIR} 2>/dev/null -mkdir -p ${TEST_PARENT_DIR} 2>/dev/null -pushd ${TEST_PARENT_DIR} > /dev/null - -# Test what we found -if [[ "${need_testing}" != "" ]] ; then - test_analysisqc - ret_global=${?} -else - echo "Nothing to test" - exit 0 -fi - -# return to where we came from -popd > /dev/null - -# However, if a central test fails, exit code will be !=0 -if [[ "${ret_global}" != "0" ]] ; then - echo - echo "########################" - echo "# ERROR for AnalysisQC #" - echo "########################" - echo - print_error_logs ${TEST_PARENT_DIR} - exit ${ret_global} -fi - -echo -echo_green "AnalysisQC tests successful" -echo - -exit 0 diff --git a/test/run_generator_tests.sh b/test/run_generator_tests.sh index 5f91db447..e02f1080d 100755 --- a/test/run_generator_tests.sh +++ b/test/run_generator_tests.sh @@ -1,10 +1,27 @@ #!/bin/bash +###################################### +# Add Herwig, GSL, ThePEG and nlohmann_json packages to root include path and library path if not already present +###################################### + +# ROOT_INCLUDE_PATH +[[ -n "$THEPEG_ROOT" && ":$ROOT_INCLUDE_PATH:" != *":$THEPEG_ROOT/include:"* ]] && ROOT_INCLUDE_PATH="$THEPEG_ROOT/include:$ROOT_INCLUDE_PATH" +[[ -n "$HERWIG_ROOT" && ":$ROOT_INCLUDE_PATH:" != *":$HERWIG_ROOT/include:"* ]] && ROOT_INCLUDE_PATH="$HERWIG_ROOT/include:$ROOT_INCLUDE_PATH" +[[ -n "$GSL_ROOT" && ":$ROOT_INCLUDE_PATH:" != *":$GSL_ROOT/include:"* ]] && ROOT_INCLUDE_PATH="$GSL_ROOT/include:$ROOT_INCLUDE_PATH" +[[ -n "$NLOHMANN_JSON_ROOT" && ":$ROOT_INCLUDE_PATH:" != *":$NLOHMANN_JSON_ROOT/include:"* ]] && ROOT_INCLUDE_PATH="$NLOHMANN_JSON_ROOT/include:$ROOT_INCLUDE_PATH" + +# LD_LIBRARY_PATH +[[ -n "$THEPEG_ROOT" && ":$LD_LIBRARY_PATH:" != *":$THEPEG_ROOT/lib/ThePEG:"* ]] && LD_LIBRARY_PATH="$THEPEG_ROOT/lib/ThePEG:$LD_LIBRARY_PATH" +[[ -n "$HERWIG_ROOT" && ":$LD_LIBRARY_PATH:" != *":$HERWIG_ROOT/lib/Herwig:"* ]] && LD_LIBRARY_PATH="$HERWIG_ROOT/lib/Herwig:$LD_LIBRARY_PATH" +[[ -n "$GSL_ROOT" && ":$LD_LIBRARY_PATH:" != *":$GSL_ROOT/lib:"* ]] && LD_LIBRARY_PATH="$GSL_ROOT/lib:$LD_LIBRARY_PATH" + +export ROOT_INCLUDE_PATH LD_LIBRARY_PATH + ###################################### # Entrypoint for O2DPG related tests # ###################################### -CHECK_GENERATORS="Pythia8 External" +CHECK_GENERATORS="Pythia8 External Hybrid" # The test parent dir to be cretaed in current directory TEST_PARENT_DIR="o2dpg_tests/generators" @@ -17,7 +34,7 @@ LOG_FILE_GENERIC_KINE="o2dpg-test-generic-kine.log" # collect any macro files that are not directly used in INI files but that might be included in other macros MACRO_FILES_POTENTIALLY_INCLUDED="" -# collect all INI files to be tested +# global var to collect all INI files to be tested INI_FILES="" # collect test macros that do not have a corresponding INI file @@ -29,24 +46,24 @@ TEST_COUNTER=1 # whether or not to delete everything except logs (default is to delete) KEEP_ONLY_LOGS=1 +# Number of workers for parallel test runs +JOBS=${JOBS:-8} +echo "Running tests with up to ${JOBS} parallel jobs" # Prepare some colored output SRED="\033[0;31m" SGREEN="\033[0;32m" SEND="\033[0m" - echo_green() { - echo -e "${SGREEN}$@${SEND}" + echo -e "${SGREEN}${*}${SEND}" } - echo_red() { - echo -e "${SRED}$@${SEND}" + echo -e "${SRED}${*}${SEND}" } - # Prevent the script from being soured to omit unexpected surprises when exit is used SCRIPT_NAME="$(basename "$(test -L "$0" && readlink "$0" || echo "$0")")" if [ "${SCRIPT_NAME}" != "$(basename ${BASH_SOURCE[0]})" ] ; then @@ -54,19 +71,36 @@ if [ "${SCRIPT_NAME}" != "$(basename ${BASH_SOURCE[0]})" ] ; then return 1 fi - ################################## # Core and utility functionality # ################################## - get_test_script_path_for_ini() { local ini_path=${1} local test_script=$(basename ${ini_path}) - echo $(dirname ${ini_path})/tests/${test_script%.ini}.C + local path_to_test_script=$(dirname ${ini_path})/tests/${test_script%.ini}.C + if [[ ! -f ${path_to_test_script} ]] ; then + # Check if test redirection is applied inside the ini_path file using the #---> syntax + local redirection=$(grep "#--->" ${ini_path}) + if [[ "${redirection}" != "" ]] ; then + test_script=$(echo ${redirection} | awk '{print $2}') + path_to_test_script=$(dirname ${ini_path})/tests/${test_script}.C + fi + fi + echo ${path_to_test_script} } +get_nevents_from_ini() +{ + # function to force the number of events to be simulated from the ini file (default = 100) + # Syntax: #NEV_TEST> 10 (space between #NEV_TEST> and the number is mandatory) + # To be used only if external generator takes too long to run causing timeouts in CI + local ini_path=${1} + local nev=$(grep "#NEV_TEST>" ${ini_path} | tail -n 1 | awk '{print $2}' | tr -d ' ') + [[ "${nev}" == "" ]] && nev=100 + echo ${nev} +} exec_test() { @@ -75,17 +109,30 @@ exec_test() local ini_path=${1} local generator=${2} # for now one of "Pythia8" or "External", at this point we know that settings for the generator are defined in this ini local generator_lower=$(echo "${generator}" | tr '[:upper:]' '[:lower:]') + local test_id=${3} # DPL session ID # TODO Potentially, one could run an external generator that derives from GeneratorPythia8 and so could probably use configuration for TriggerPythia8 - local trigger=${3:+-t ${generator_lower}} + local trigger=${4:+-t ${generator_lower}} + local trigger_dpl=${4:+--trigger ${generator_lower}} local RET=0 # this is how our test script is expected to be called local test_script=$(get_test_script_path_for_ini ${ini_path}) + # get the number of events to be simulated from the ini file + local nev=$(get_nevents_from_ini ${ini_path}) # prepare the header of the log files echo "### Testing ${ini_path} with generator ${generator} ###" > ${LOG_FILE_KINE} echo "### Testing ${ini_path} with generator ${generator} ###" > ${LOG_FILE_GENERIC_KINE} echo "### Testing ${ini_path} with generator ${generator} ###" > ${LOG_FILE_SIM} + echo "### Testing DPL-eventgen ###" >> ${LOG_FILE_SIM} + # run the event generation using the dpl-eventgen executable. + # This is a basic running test, however it's important because the system running on Hyperloop + # is largely used for MCGEN productions and is currently tested only locally. + # Using unique session labels to prevent channel binding conflicts in parallel execution + o2-sim-dpl-eventgen --session test_${test_id} --generator ${generator_lower} ${trigger_dpl} --nEvents ${nev} --configFile ${ini_path} --configKeyValues "GeneratorPythia8.includePartonEvent=true" -b >> ${LOG_FILE_SIM} 2>&1 + RET=${?} + [[ "${RET}" != "0" ]] && { remove_artifacts ; return ${RET} ; } # run the simulation, fail if not successful - o2-sim -g ${generator_lower} ${trigger} --noGeant -n 100 -j 4 --configFile ${ini_path} >> ${LOG_FILE_SIM} 2>&1 + echo "### Testing base o2-sim executable ###" >> ${LOG_FILE_SIM} + o2-sim -g ${generator_lower} ${trigger} --noGeant -n ${nev} -j 1 --configFile ${ini_path} --configKeyValues "GeneratorPythia8.includePartonEvent=true" >> ${LOG_FILE_SIM} 2>&1 RET=${?} [[ "${RET}" != "0" ]] && { remove_artifacts ; return ${RET} ; } @@ -104,6 +151,13 @@ exec_test() return ${RET} } +wait_for_slot() +{ + # Wait until the number of background jobs is within the limit + while (( $(jobs -r | wc -l) >= JOBS )) ; do + sleep 0.1 + done +} check_generators() { @@ -120,38 +174,53 @@ check_generators() # check if this generator is mentioned in the INI file and only then test it if [[ "$(grep ${g} ${ini_path})" != "" ]] ; then local look_for=$(grep " ${g}.*\(\)" ${test_script}) + # For Pythia8 the function in the test script is lowercase for compatibility purposes + if [[ "${g}" == "Pythia8" ]]; then + look_for=$(grep " pythia8.*\(\)" ${test_script}) + fi local has_trigger="$(grep Trigger${g} ${ini_path})" [[ -z "${look_for}" ]] && continue - echo -n "Test ${TEST_COUNTER}: ${ini_path} with generator ${g}" tested_any=1 # prepare the test directory local test_dir=${TEST_COUNTER}_$(basename ${ini})_${g}_dir rm -rf ${test_dir} 2> /dev/null mkdir ${test_dir} - pushd ${test_dir} > /dev/null - # one single test - exec_test ${ini_path} ${g} ${has_trigger} - RET=${?} - popd > /dev/null - if [[ "${RET}" != "0" ]] ; then - echo_red " -> FAILED" - ret_this=${RET} - else - echo_green " -> PASSED" - fi + local test_num=${TEST_COUNTER} ((TEST_COUNTER++)) + + # Wait for an available slot before starting a new test + wait_for_slot + + echo "Test ${test_num}: ${ini_path} with generator ${g} - STARTED" + # Run test in background + ( + cd ${test_dir} + exec_test ${ini_path} ${g} ${test_num} ${has_trigger} + exit $? + ) & + local pid=$! + + # Store test information in global arrays + test_pids+=(${pid}) + test_numbers+=(${test_num}) + test_generators+=(${g}) + test_ini_paths+=("${ini_path}") fi done [[ -z "${tested_any}" ]] && { echo_red "No test scenario was found for any generator. There must be at least one generator to be tested." ; ret_this=1 ; } return ${ret_this} } - add_ini_files_from_macros() { # given a list of macros, collect all INI files which contain at least one of them local macro_files=$@ for mf in ${macro_files} ; do + # Strip anything before MC/config/, if any, to get the macro relative path + if [[ "${mf}" == *"MC/config"* ]] ; then + mf=${mf#*MC/config/} + mf="MC/config/${mf}" + fi local other_ini_files=$(grep -r -l ${mf} | grep ".ini$") # so this macro is not included in any of the INI file, # maybe it is included by another macro which is then included in an INI file @@ -164,7 +233,6 @@ add_ini_files_from_macros() done } - get_root_includes() { # check if some R__ADD_INCLUDE_PATH is used in the including macro and check the included file against that @@ -184,13 +252,12 @@ get_root_includes() echo ${full_includes} } - find_including_macros() { # figure out the macros that INCLUDE macros that have changed, so that in turn we can check # if these including macros are contained in some INI files local repo_dir_head=${REPO_DIR} - local changed_files=$(get_changed_files) + local changed_files=$(git_get_changed_files) local potentially_included=${MACRO_FILES_POTENTIALLY_INCLUDED} # we reset it here but we could fill it again to be able to do it fully recursively MACRO_FILES_POTENTIALLY_INCLUDED="" @@ -231,7 +298,6 @@ find_including_macros() echo ${including_macros} } - add_ini_files_from_tests() { # Collect also those INI files for which the test has been changed @@ -250,35 +316,24 @@ add_ini_files_from_tests() done } -add_ini_files_from_all_tests() -{ - # Collect also those INI files for which the test has been changed - local all_tests=$(find ${REPO_DIR} -name "*.C" | grep "MC/.*/ini/tests") - local repo_dir_head=${REPO_DIR} - for t in ${all_tests} ; do - local this_test=$(realpath ${t}) - this_test=${this_test##${repo_dir_head}/} - local tc=$(basename ${this_test}) - this_test=${this_test%%/tests/*} - tc=${tc%.C}.ini - tc=${this_test}/${tc} - [[ "${INI_FILES}" == *"${tc}"* ]] && continue - INI_FILES+=" ${tc} " - done -} - - collect_ini_files() { # Collect all INI files which have changed - local ini_files=$(get_changed_files | grep ".ini$" | grep "MC/config") - for ini in ${ini_files} ; do + local changed_files=$(git_get_changed_files) + for ini in ${changed_files} ; do + [[ "${ini}" != *"MC/config"*".ini" ]] && continue [[ "${INI_FILES}" == *"${ini}"* ]] && continue || INI_FILES+=" ${ini} " done # this relies on INI_FILES and MACRO_FILES_POTENTIALLY_INCLUDED # collect all INI files that might include some changed macros - add_ini_files_from_macros $(get_changed_files | grep ".C$" | grep "MC/config") + local macros= + for m in ${changed_files} ; do + [[ "${m}" != *"MC/config"*".C" ]] && continue + macros+=" ${m} " + done + + add_ini_files_from_macros ${macros} # this relies on MACRO_FILES_POTENTIALLY_INCLUDED # collect all INI files that might contain macros which in turn include changed macros @@ -286,10 +341,14 @@ collect_ini_files() add_ini_files_from_macros $(find_including_macros) # also tests might have changed in which case we run them - add_ini_files_from_tests $(get_changed_files | grep ".C$" | grep "MC/.*/ini/tests") + local macros= + for m in ${changed_files} ; do + [[ "${m}" != *"MC/"*"ini/tests"*".C" ]] && continue + macros+=" ${m} " + done + add_ini_files_from_tests ${macros} } - get_git_repo_directory() { local repo= @@ -334,11 +393,11 @@ print_usage() # whether or not to exit after first test has failed fail_immediately= -[[ "${1}" == "--fail_immediately" ]] && fail_immediately=1 +[[ "${1}" == "--fail-immediately" ]] && fail_immediately=1 while [ "$1" != "" ] ; do case $1 in - --fail_immediately ) shift + --fail-immediately ) shift fail_immediately=1 ;; --keep-artifacts ) shift @@ -361,12 +420,12 @@ echo REPO_DIR=${O2DPG_TEST_REPO_DIR:-$(get_git_repo_directory)} if [[ ! -d ${REPO_DIR}/.git ]] ; then - echo_red "Directory \"${REPO_DIR}\" is not a git repository." + echo "ERROR: Directory \"${REPO_DIR}\" is not a git repository." exit 1 fi if [[ -z ${O2DPG_ROOT+x} ]] ; then - echo_red "O2DPG is not loaded, probably other packages are missing as well in this environment." + echo "ERROR: O2DPG is not loaded, probably other packages are missing as well in this environment." exit 1 fi @@ -376,11 +435,6 @@ source ${REPO_DIR}/test/common/utils/utils.sh # Do the initial steps in the source dir where we have the full git repo pushd ${REPO_DIR} > /dev/null -# First check, if testing itself has changed. In that case this will add INI files -# for which a test can be found -global_testing_changed=$(get_changed_files | grep -E ".C$|.sh$" | grep "^test/") -[[ "${global_testing_changed}" != "" ]] && add_ini_files_from_all_tests - # Then add the ini files that have changed as well. We need to do that so we get information # about missing tests etc. collect_ini_files @@ -393,7 +447,7 @@ if [[ -z "${INI_FILES}" ]] ; then fi if [[ ! -z "${TEST_WITHOUT_INI}" ]] ; then - echo_red "There are test macros that do not correspond to any INI file:" + echo_red "Warning: There are test macros that do not correspond to any INI file:" for twi in ${TEST_WITHOUT_INI} ; do echo " - ${twi}" done @@ -417,6 +471,7 @@ popd > /dev/null # [[ -z {ALIBUILD_HEAD_HASH+x} ]] && export O2DPG_ROOT=${REPO_DIR} # but let's do the same for both local and CI consistently export O2DPG_ROOT=${REPO_DIR} +export O2DPG_MC_CONFIG_ROOT=${O2DPG_ROOT} # prepare our local test directory rm -rf ${TEST_PARENT_DIR} 2>/dev/null @@ -426,8 +481,31 @@ pushd ${TEST_PARENT_DIR} > /dev/null # global return code to be returned at the end ret_global=0 +# Global arrays to track all test jobs (across all INI files) +declare -a test_pids +declare -a test_numbers +declare -a test_generators +declare -a test_ini_paths + # check each of the INI files for ini in ${ini_files_full_paths} ; do + + # we can apply some matching if it is asked + check_this="1" + echo "Checking ${ini}" + if [[ "${O2DPG_CHECK_FILTER}" ]]; then + echo "Checking with filter ${O2DPG_CHECK_FILTER}" + if [[ "${ini}" == *"${O2DPG_CHECK_FILTER}"* ]]; then + check_this="1" + else + check_this="0" + fi + fi + + if [[ "${check_this}" == "0" ]]; then + continue + fi + check_generators ${ini} RET=${?} if [[ "${RET}" != "0" ]] ; then @@ -435,6 +513,50 @@ for ini in ${ini_files_full_paths} ; do [[ "${fail_immediately}" == "1" ]] && break fi done + +# Wait for all test jobs to complete and collect results +total_tests=${#test_pids[@]} +completed=0 +while (( completed < total_tests )) ; do + # Wait for any background job to complete + wait -n + RET=$? + + # Find which job completed by checking which PID no longer exists + for idx in "${!test_pids[@]}" ; do + pid="${test_pids[$idx]}" + if ! kill -0 ${pid} 2>/dev/null ; then + # This job has completed, get its actual exit status + wait ${pid} 2>/dev/null + RET=$? + + test_num="${test_numbers[$idx]}" + generator="${test_generators[$idx]}" + ini_path="${test_ini_paths[$idx]}" + + if [[ "${RET}" != "0" ]] ; then + echo_red "Test ${test_num}: ${ini_path} with generator ${generator} -> FAILED" + ret_global=${RET} + if [[ "${fail_immediately}" == "1" ]] ; then + # Kill remaining background jobs + for remaining_pid in "${test_pids[@]}" ; do + kill ${remaining_pid} 2>/dev/null + done + completed=${total_tests} + break + fi + else + echo_green "Test ${test_num}: ${ini_path} with generator ${generator} -> PASSED" + fi + + # Remove this job from tracking + unset test_pids[$idx] + ((completed++)) + break + fi + done +done + # return to where we came from popd > /dev/null diff --git a/test/run_relval_tests.sh b/test/run_relval_tests.sh index 6294e1361..95b0506f5 100755 --- a/test/run_relval_tests.sh +++ b/test/run_relval_tests.sh @@ -14,13 +14,13 @@ SEND="\033[0m" echo_green() { - echo -e "${SGREEN}$@${SEND}" + echo -e "${SGREEN}${*}${SEND}" } echo_red() { - echo -e "${SRED}$@${SEND}" + echo -e "${SRED}${*}${SEND}" } @@ -133,7 +133,7 @@ source ${REPO_DIR}/test/common/utils/utils.sh pushd ${REPO_DIR} > /dev/null # flag if anything changed for RelVal -need_testing=$(get_changed_files | grep "RelVal/") +need_testing=$(git_get_changed_files | grep "RelVal/") # go back to where we came from popd > /dev/null diff --git a/test/run_tests.sh b/test/run_tests.sh new file mode 100755 index 000000000..df636d98b --- /dev/null +++ b/test/run_tests.sh @@ -0,0 +1,93 @@ +#!/bin/bash + +# Single entrypoint for the O2DPG tests. Runs the generator, workflow and +# RelVal sub-tests, aggregates their exit codes and prints the collected log. + +set -u + +SUBTEST_DIR=${O2DPG_TEST_SUBTEST_DIR:-$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)} + +ALL_SUBTESTS="generator workflow relval" + +print_usage() +{ + echo + echo "usage: run_tests.sh [--fail-immediately] [--keep-artifacts] [SUBTEST...]" + echo + echo " SUBTEST : one or more of: ${ALL_SUBTESTS} (default: all)" + echo + echo " FLAGS:" + echo + echo " --fail-immediately : stop after the first failing sub-test" + echo " --keep-artifacts : keep simulation artifacts, not just the logs" + echo + echo " ENVIRONMENT VARIABLES:" + echo + echo " O2DPG_TEST_REPO_DIR : the source repository to test" + echo " O2DPG_TEST_HASH_BASE : base hash for the changed-file diff (optional)" + echo " O2DPG_TEST_HASH_HEAD : head hash for the changed-file diff (optional)" + echo +} + +fail_immediately= +subtest_args=() +selected=() + +while [ "$#" -gt 0 ] ; do + case $1 in + --fail-immediately ) fail_immediately=1 ; subtest_args+=("$1") ; shift ;; + --keep-artifacts ) subtest_args+=("$1") ; shift ;; + --help|-h ) print_usage ; exit 1 ;; + generator|workflow|relval ) selected+=("$1") ; shift ;; + * ) echo "Unknown argument ${1}" ; print_usage ; exit 1 ;; + esac +done + +[[ "${#selected[@]}" == "0" ]] && read -r -a selected <<< "${ALL_SUBTESTS}" + +# Geant4 dataset variables, if a Geant4 installation is in the environment. +if [[ -n "${G4INSTALL:-}" ]] ; then + eval "$("${G4INSTALL}/bin/geant4-config" --datasets | + sed -e 's/[^ ]* //' -e 's/G4/export G4/' -e 's/DATA /DATA=/')" +fi + +# LHAPDF data, needed by several generator configurations. +if [[ -z "${LHAPDF_DATA_PATH:-}" && -n "${LHAPDF_ROOT:-}" ]] ; then + export LHAPDF_DATA_PATH="${LHAPDF_ROOT}/share/LHAPDF:${LHAPDF_PDFSETS_ROOT:-}/share/LHAPDF" + echo "Set LHAPDF_DATA_PATH to ${LHAPDF_DATA_PATH}" +fi + +ret_global=0 +for subtest in "${selected[@]}" ; do + script="${SUBTEST_DIR}/run_${subtest}_tests.sh" + if [[ ! -x "${script}" ]] ; then + echo "ERROR: sub-test script ${script} not found or not executable" + ret_global=1 + [[ "${fail_immediately}" == "1" ]] && break + continue + fi + echo + echo "==> START SUBTEST: ${subtest} <==" + # Only run_generator_tests.sh accepts --fail-immediately/--keep-artifacts; + # run_workflow_tests.sh and run_relval_tests.sh accept only --help/-h. + case ${subtest} in + generator ) forward=("${subtest_args[@]:-}") ;; + * ) forward=() ;; + esac + "${script}" "${forward[@]}" + ret_this=$? + echo "==> END SUBTEST: ${subtest} (exit ${ret_this}) <==" + if [[ "${ret_this}" != "0" ]] ; then + ret_global=${ret_this} + [[ "${fail_immediately}" == "1" ]] && break + fi +done + +echo +if [[ "${ret_global}" != "0" ]] ; then + echo "error detected in O2DPG tests, see above" +else + echo "O2DPG tests passed" +fi + +exit ${ret_global} diff --git a/test/run_workflow_tests.sh b/test/run_workflow_tests.sh index cbf05c418..027a5488a 100755 --- a/test/run_workflow_tests.sh +++ b/test/run_workflow_tests.sh @@ -2,51 +2,41 @@ # The test parent dir to be cretaed in current directory TEST_PARENT_DIR_PWG="o2dpg_tests/workflows_pwgs" -TEST_PARENT_DIR_BIN="o2dpg_tests/workflows_bin" - -# a global counter for tests -TEST_COUNTER=0 +TEST_PARENT_DIR_BIN="o2dpg_tests/workflows_analysisqc" +TEST_PARENT_DIR_ANCHORED="o2dpg_tests/anchored" # unified names of log files LOG_FILE_WF="o2dpg-test-wf.log" +LOG_FILE_ANCHORED="o2dpg-test-anchored.log" +LOG_FILE_ANALYSISQC="o2dpg-test_analysisqc.log" -# Prepare some colored output -SRED="\033[0;31m" -SGREEN="\033[0;32m" -SEND="\033[0m" - - -echo_green() -{ - echo -e "${SGREEN}$@${SEND}" -} - - -echo_red() -{ - echo -e "${SRED}$@${SEND}" -} get_git_repo_directory() { + local look_dir=${1:-$(pwd)} + look_dir=$(realpath "${look_dir}") + look_dir=${look_dir%%/.git} local repo= - if [[ -d .git ]] ; then - pwd - else + ( + cd "${look_dir}" repo=$(git rev-parse --git-dir 2> /dev/null) - fi - [[ "${repo}" != "" ]] && repo=${repo%%/.git} + [[ "${repo}" != "" ]] && { repo=$(realpath "${repo}") ; repo=${repo%%/.git} ; } + ) echo ${repo} } -get_all_workflows() +first_nonzero() { - # Collect also those INI files for which the test has been changed - local repo_dir_head=${REPO_DIR} - local grep_dir=${1} - local all_workflows=$(find ${repo_dir_head} -name "*.sh" | grep "${grep_dir}") - echo ${all_workflows} + # Echo the first non-zero argument, or 0. Used instead of summing exit + # codes: a sum can land on a multiple of 256 and exit 0 -- two phases + # failing with 128 each would report success -- and it also loses which + # phase failed. + local code + for code in "$@" ; do + [[ "${code}" != "0" ]] && { echo "${code}" ; return ; } + done + echo 0 } @@ -57,22 +47,32 @@ test_single_wf() make_wf_creation_script ${wf_script} ${wf_script_local} local has_wf_script_local=${?} echo -n "Test ${TEST_COUNTER}: ${wfs}" - [[ "${has_wf_script_local}" != "0" ]] && { echo "No WF creation in script ${wfs} ##########" ; return 1 ; } + [[ "${has_wf_script_local}" != "0" ]] && { echo -n " (No WF creation in script)" ; echo_red " -> FAILED" ; return 1 ; } # Check if there is an "exit" other than the usual # [ ! "${O2DPG_ROOT}" ] && echo "Error: This needs O2DPG loaded" && exit 1 # like ones. # This is not perfect but might prevent us from running into some checks the WF script does before launching the WF creation - [[ "$(grep exit ${wf_script_local} | grep -v "This needs")" != "" ]] && { echo -e -n "\nFound \"exit\" in ${wfs} so will not test automatically" ; return 0 ; } + [[ "$(grep exit ${wf_script_local} | grep -v "This needs")" != "" ]] && { echo -n " (Found \"exit\" in script, not testing automatically)" ; echo_yellow " -> WARNING" ; return 0 ; } # one single test echo "Test ${wf_line} from ${wfs}" > ${LOG_FILE_WF} bash ${wf_script_local} >> ${LOG_FILE_WF} 2>&1 local ret_this=${?} + local ret_this_qc=0 + local ret_this_analysis=0 if [[ "${ret_this}" != "0" ]] ; then + echo_red " -> FAILED" echo "[FATAL]: O2DPG_TEST Workflow creation failed" >> ${LOG_FILE_WF} elif [[ "${execute}" != "" ]] ; then - ${O2DPG_ROOT}/MC/bin/o2_dpg_workflow_runner.py -f workflow.json --cpu-limit 8 -tt aod >> ${LOG_FILE_WF} 2>&1 + local memlimit=${O2DPG_TEST_WORKFLOW_MEMLIMIT:+--mem-limit ${O2DPG_TEST_WORKFLOW_MEMLIMIT}} + ${O2DPG_ROOT}/MC/bin/o2_dpg_workflow_runner.py -f workflow.json --cpu-limit 8 -tt aod ${memlimit} >> ${LOG_FILE_WF} 2>&1 ret_this=${?} - [[ "${ret_this}" != "0" ]] && echo "[FATAL]: O2DPG_TEST Workflow execution failed" >> ${LOG_FILE_WF} + # use -k|--keep-going feature to not stop after the first failure but see, if there are more + [[ "${ret_this}" == "0" ]] && { ${O2DPG_ROOT}/MC/bin/o2_dpg_workflow_runner.py -f workflow.json --cpu-limit 8 --target-labels QC ${memlimit} -k >> ${LOG_FILE_WF} 2>&1 ; ret_this_qc=${?} ; } + [[ "${ret_this}" == "0" ]] && { ${O2DPG_ROOT}/MC/bin/o2_dpg_workflow_runner.py -f workflow.json --cpu-limit 8 --target-labels Analysis ${memlimit} -k >> ${LOG_FILE_WF} 2>&1 ; ret_this_analysis=${?} ; } + ret_this=$(first_nonzero "${ret_this}" "${ret_this_qc}" "${ret_this_analysis}") + [[ "${ret_this}" != "0" ]] && echo "[FATAL]: O2DPG_TEST Workflow execution failed" >> ${LOG_FILE_WF} || echo_green " -> PASSED" + else + echo_green " -> PASSED" fi return ${ret_this} } @@ -83,8 +83,8 @@ run_workflow_creation() local execute= while [ "$1" != "" ] ; do case $1 in - --execute ) shift - execute=1 + --execute ) execute=1 + shift ;; * ) wf_scripts+="${1} " shift @@ -108,31 +108,66 @@ run_workflow_creation() local ret_this=${?} [[ "${ret_this}" != "0" ]] && RET=${ret_this} popd > /dev/null - if [[ "${ret_this}" != "0" ]] ; then - echo_red " -> FAILED" - else - echo_green " -> PASSED" - fi done return ${RET} } -collect_changed_pwg_wf_files() + +test_analysisqc_cli() { - # Collect all INI files which have changed - local wf_scripts=$(get_changed_files | grep ".sh$" | grep "MC/run") - for wfs in ${wf_scripts} ; do - [[ "${WF_FILES}" == *"${wfs}"* ]] && continue || WF_FILES+=" ${wfs} " + ((TEST_COUNTER++)) + local test_dir="${TEST_COUNTER}_analysisqc_cli" + rm -rf ${test_dir} 2> /dev/null + mkdir ${test_dir} + pushd ${test_dir} > /dev/null + echo "### Testing AnalysisQC creation for MC ###" > ${LOG_FILE_ANALYSISQC} + echo -n "Test ${TEST_COUNTER}: Running AnalysisQC CLI" + ${O2DPG_ROOT}/MC/analysis_testing/o2dpg_analysis_test_workflow.py -f AO2D.root --is-mc -o wokflow_test_mc.json >> ${LOG_FILE_ANALYSISQC} 2>&1 + local ret=${?} + [[ "${ret}" != "0" ]] && echo "[FATAL]: O2DPG_TEST failed" >> ${LOG_FILE_ANALYSISQC} + echo "### Testing AnalysisQC creation for data ###" >> ${LOG_FILE_ANALYSISQC} + ${O2DPG_ROOT}/MC/analysis_testing/o2dpg_analysis_test_workflow.py -f AO2D.root -o wokflow_test_data.json >> ${LOG_FILE_ANALYSISQC} 2>&1 + local ret_data=${?} + [[ "${ret_data}" != "0" ]] && { echo "[FATAL]: O2DPG_TEST failed" >> ${LOG_FILE_ANALYSISQC} ; ret=${ret_data} ; } + popd > /dev/null + [[ "${ret}" != "0" ]] && echo_red " -> FAILED" || echo_green " -> PASSED" + return ${ret} +} + + +test_anchored() +{ + local to_run="${1:-${O2DPG_ROOT}/MC/run/ANCHOR/tests/test_anchor_2023_apass2_pp.sh}" + local RET=0 + for anchored_script in ${to_run} ; do + [[ ! -f ${anchored_script} ]] && { echo "Desired test script ${anchored_script} does not exist. Skip." ; continue ; } + ((TEST_COUNTER++)) + local test_dir=${TEST_COUNTER}_$(basename ${anchored_script})_dir + rm -rf ${test_dir} 2> /dev/null + mkdir ${test_dir} + pushd ${test_dir} > /dev/null + echo -n "Test ${TEST_COUNTER}: ${anchored_script}" + ${anchored_script} >> ${LOG_FILE_ANCHORED} 2>&1 + local ret_this=${?} + if [[ "${ret_this}" != "0" ]] ; then + echo_red " -> FAILED" + RET=${ret_this} + else + echo_green " -> PASSED" + fi + popd > /dev/null done + return ${RET} } print_usage() { + echo echo "usage: run_workflow_tests.sh" echo - echo " ENVIRONMENT VARIABLES:" + echo " ENVIRONMENT VARIABLES TO DETERMINE WHAT TO COMPARE:" echo echo " O2DPG_TEST_REPO_DIR : Point to the source repository you want to test." echo " O2DPG_TEST_HASH_BASE : The base hash you want to use for comparison (optional)" @@ -146,8 +181,15 @@ print_usage() echo " If also not set, this will be set to HEAD. However, if there are unstaged" echo " changes, it will left blank." echo + echo " SPECIFIC ENVIRONMENT VARIABLES FOR THIS TEST:" + echo " O2DPG_TEST_WORKFLOW_MEMLIMIT : The memory limit that is passed to the workflow runner in case a workflow is executed (optional)" + echo } + +############# +# Main part # +############# while [ "$1" != "" ] ; do case $1 in --help|-h ) print_usage @@ -159,44 +201,70 @@ while [ "$1" != "" ] ; do esac done -echo -echo "##############################" -echo "# Run O2DPG workflow testing #" -echo "##############################" -echo - +# determine the repository directory REPO_DIR=${O2DPG_TEST_REPO_DIR:-$(get_git_repo_directory)} if [[ ! -d ${REPO_DIR}/.git ]] ; then - echo_red "Directory \"${REPO_DIR}\" is not a git repository." + echo "ERROR: Directory \"${REPO_DIR}\" is not a git repository." exit 1 fi if [[ -z ${O2DPG_ROOT+x} ]] ; then - echo_red "O2DPG is not loaded, probably other packages are missing as well in this environment." + echo "ERROR: O2DPG is not loaded, probably other packages are missing as well in this environment." exit 1 fi # source the utilities source ${REPO_DIR}/test/common/utils/utils.sh + +echo "##############################" +echo "# Run O2DPG workflow testing #" +echo "##############################" + # Do the initial steps in the source dir where we have the full git repo pushd ${REPO_DIR} > /dev/null # flag if anything changed in the sim workflow bin dir -changed_wf_bin=$(get_changed_files | grep MC/bin) +changed_sim_bin=$(git_get_changed_files | grep -E "MC/bin") +# collect if anything has changed related to AnalysisQC +changed_analysis_qc=$(git_get_changed_files | grep -E "MC/analysis_testing|MC/config/analysis_testing/json|MC/config/QC/json") +# check if anything has changed concerning anchoring +changed_anchored=$(git_get_changed_files | grep -E "MC/bin|MC/run/ANCHOR/anchorMC.sh|MC/run/ANCHOR/tests|MC/bin|UTILS/parse-async-WorkflowConfig.py|DATA/production/configurations/asyncReco/setenv_extra.sh|DATA/production/configurations/asyncReco/async_pass.sh|DATA/common/setenv.sh|DATA/production/workflow-multiplicities.sh") +# collect changed workflow scripts +changed_workflows= +# workflows to be executed +execute_workflows= +echo "==> Test outline" +if [[ "${changed_sim_bin}" != "" ]] ; then + # in this case, something central has changed, test creation of all workflows against it + echo " - The creation of simulation workflows from all run scripts (MC/run/**/*.sh) will be tested." + for p in $(find MC/run -name "*.sh") ; do + changed_workflows+="$(realpath ${p}) " + done + # definitely run anchored if central python scripts have changed + echo " - Changes in MC/bin/ detected, mark anchored MC test to be run." + changed_anchored="1" +else + # otherwise, only take the changed shell scripts + changed_workflows= + changed_files=$(git_get_changed_files) + for cf in ${changed_files} ; do + [[ "${cf}" != *"MC/run"*".sh" ]] && continue + changed_workflows+="${cf} " + done + [[ "${changed_workflows}" != "" ]] && echo " - The creation of simulation workflows from changed run scripts (sub-sect of MC/run/**/*.sh) will be tested." +fi -# collect what has changed for PWGs -collect_changed_pwg_wf_files +if [[ "${changed_analysis_qc}" != "" ]] ; then + for p in $(find "MC/bin/tests" -name "*.sh") ; do + execute_workflows+="$(realpath ${p}) " + done + echo " - Test AnalysisQC CLI and execution with a simulation." +fi -# get realpaths for all changes -wf_files_tmp=${WF_FILES} -WF_FILES= -for wf_tmp in ${wf_files_tmp} ; do - # convert to full path so that we can find it from anywhere - WF_FILES+="$(realpath ${wf_tmp}) " -done +[[ "${changed_anchored}" != "" ]] && echo " - Test anchored simulation." -# go back to where we came from +# everything collected, go back to where we came from popd > /dev/null REPO_DIR=$(realpath ${REPO_DIR}) @@ -208,79 +276,83 @@ REPO_DIR=$(realpath ${REPO_DIR}) export O2DPG_ROOT=${REPO_DIR} -######## -# PWGs # -######## -# prepare our local test directory for PWG tests -rm -rf ${TEST_PARENT_DIR_PWG} 2>/dev/null -mkdir -p ${TEST_PARENT_DIR_PWG} 2>/dev/null -pushd ${TEST_PARENT_DIR_PWG} > /dev/null - +############################## +# PWG workflow shell scripts # +############################## # global return code for PWGs ret_global_pwg=0 -if [[ "${changed_wf_bin}" != "" ]] ; then - # Run all the PWG related WF creations, hence overwrite what was collected by collect_changed_pwg_wf_files eal=rlier - WF_FILES=$(get_all_workflows "MC/run/.*/") - echo -fi # Test what we found -if [[ "${WF_FILES}" != "" ]] ; then - echo "### Test PWG-related workflow creation ###" +if [[ "${changed_workflows}" != "" ]] ; then + # prepare our local test directory for PWG tests + rm -rf ${TEST_PARENT_DIR_PWG} 2>/dev/null + mkdir -p ${TEST_PARENT_DIR_PWG} 2>/dev/null + pushd ${TEST_PARENT_DIR_PWG} > /dev/null + echo - run_workflow_creation ${WF_FILES} + echo "==> START BLOCK: Test PWG-related workflow creation <==" + run_workflow_creation ${changed_workflows} ret_global_pwg=${?} - echo + [[ "${ret_global_pwg}" != "0" ]] && { echo "WARNING for workflows creations, some could not be built." ; print_error_logs ./ ; } + echo "==> END BLOCK: Test PWG-related workflow creation <==" + + # return to where we came from + popd > /dev/null fi -# return to where we came from -popd > /dev/null - -#################### -# sim workflow bin # -#################### +#################################### +# sim workflow bin with AnalysisQC # +#################################### # prepare our local test directory for bin tests -rm -rf ${TEST_PARENT_DIR_BIN} 2>/dev/null -mkdir -p ${TEST_PARENT_DIR_BIN} 2>/dev/null -pushd ${TEST_PARENT_DIR_BIN} > /dev/null - # global return code for PWGs -ret_global_bin=0 -if [[ "${changed_wf_bin}" != "" ]] ; then - echo "### Test bin-related workflow creation ###" +ret_analysis_qc=0 +if [[ "${changed_analysis_qc}" != "" ]] ; then + rm -rf ${TEST_PARENT_DIR_BIN} 2>/dev/null + mkdir -p ${TEST_PARENT_DIR_BIN} 2>/dev/null + pushd ${TEST_PARENT_DIR_BIN} > /dev/null + echo + echo "==> START BLOCK: Test running workflow with AnalysisQC <==" + # test command line interface + test_analysisqc_cli + ret_analysis_qc=${?} # Run all the bin test WF creations - run_workflow_creation $(get_all_workflows "MC/bin/tests") --execute - ret_global_bin=${?} - echo + [[ "${ret_analysis_qc}" == "0" ]] && { run_workflow_creation ${execute_workflows} --execute ; ret_analysis_qc=${?} ; } + [[ "${ret_analysis_qc}" != "0" ]] && { echo "ERROR for workflows execution and AnalysisQC." ; print_error_logs ./ ; } + echo "==> END BLOCK: Test running workflow with AnalysisQC <==" + + # return to where we came from + popd > /dev/null fi -# return to where we came from -popd > /dev/null -# final printing of log files of failed tests -# For PWG workflows, this triggers only a warning at the moment -if [[ "${ret_global_pwg}" != "0" ]] ; then - echo - echo "#####################################" - echo "# WARNING for PWG-related workflows #" - echo "#####################################" - echo - print_error_logs ${TEST_PARENT_DIR_PWG} -fi +############### +# ANCHORED MC # +############### +# global return code for PWGs +ret_global_anchored=0 +if [[ "${changed_anchored}" != "" ]] ; then + # prepare our local test directory for PWG tests + rm -rf ${TEST_PARENT_DIR_ANCHORED} 2>/dev/null + mkdir -p ${TEST_PARENT_DIR_ANCHORED} 2>/dev/null + pushd ${TEST_PARENT_DIR_ANCHORED} > /dev/null -# However, if a central test fails, exit code will be !=0 -if [[ "${ret_global_bin}" != "0" ]] ; then echo - echo "###################################" - echo "# ERROR for bin-related workflows #" - echo "###################################" - echo - print_error_logs ${TEST_PARENT_DIR_BIN} - exit ${ret_global_bin} + echo "==> START BLOCK: Test anchored simulation" + # Run an anchored test + test_anchored + ret_global_anchored=${?} + [[ "${ret_global_anchored}" != "0" ]] && { echo "ERROR executing anchored simulation." ; print_error_logs ./ ; } + echo "==> END BLOCK: Test anchored simulation" + + # return to where we came from + popd > /dev/null fi +RET=$(first_nonzero "${ret_global_pwg}" "${ret_analysis_qc}" "${ret_global_anchored}") + echo -echo_green "All required workflow tests successful" -echo +[[ "${RET}" != "0" ]] && echo_red "There were errors, please check!" || echo_green "All required workflow tests successful" + +exit ${RET} diff --git a/test/tests/exit_code_aggregation.sh b/test/tests/exit_code_aggregation.sh new file mode 100755 index 000000000..c7cbef736 --- /dev/null +++ b/test/tests/exit_code_aggregation.sh @@ -0,0 +1,56 @@ +#!/bin/bash +# Static guard against the exit-code-aggregation bugs in run_workflow_tests.sh. +# Two things must hold: +# 1. the final RET= line must reference every aggregate return-code variable +# the script computes, and each must actually be assigned somewhere; +# 2. no exit code may be produced by *summing* codes. Sums can land on a +# multiple of 256 — two phases failing with 128 each sum to 256, which +# exits 0 — so a real failure would report success. + +set -u +HERE=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +SCRIPT=${O2DPG_TEST_WORKFLOW_SCRIPT:-"${HERE}/../run_workflow_tests.sh"} +FAILURES=0 + +check() +{ + local label=$1 expected=$2 actual=$3 + if [[ "${expected}" == "${actual}" ]] ; then + echo "ok - ${label}" + else + echo "FAIL - ${label}: expected '${expected}', got '${actual}'" + FAILURES=$((FAILURES + 1)) + fi +} + +# The three aggregate return-code variables the final exit code must include. +# Explicit and hard-coded on purpose: a fourth aggregate added later must make +# whoever adds it come here and update this list. +REQUIRED_VARS="ret_global_pwg ret_analysis_qc ret_global_anchored" + +RET_LINE=$(grep -m1 -E '^RET=' "${SCRIPT}") + +for var in ${REQUIRED_VARS} ; do + present=$(grep -c -- "${var}" <<< "${RET_LINE}") + check "RET= line includes ${var}" 1 "${present}" +done + +for var in ${REQUIRED_VARS} ; do + assigned=$(grep -c -E "^[[:space:]]*${var}=" "${SCRIPT}") + check "${var} is assigned in the script" 1 "$( [[ "${assigned}" -ge 1 ]] && echo 1 || echo 0 )" +done + +# No exit code may be a sum of codes. These are the two sites that had it. +summed_ret=$(grep -c -E '^RET=\$\(\(' "${SCRIPT}") +check "RET is not a sum of codes" 0 "${summed_ret}" + +summed_this=$(grep -c -E 'ret_this=\$\(\(ret_this[[:space:]]*\+' "${SCRIPT}") +check "test_single_wf does not sum codes" 0 "${summed_this}" + +echo +if [[ "${FAILURES}" == "0" ]] ; then + echo "All exit-code aggregation checks passed" +else + echo "${FAILURES} test(s) failed" +fi +exit $(( FAILURES > 0 ? 1 : 0 )) diff --git a/test/tests/resolve_tag.sh b/test/tests/resolve_tag.sh new file mode 100755 index 000000000..d896ece11 --- /dev/null +++ b/test/tests/resolve_tag.sh @@ -0,0 +1,65 @@ +#!/bin/bash +# Offline harness for resolve_o2pdpsuite_tag: builds a fake modulefile +# directory so the function can be checked without CVMFS. + +set -u +HERE=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +# shellcheck source=../ci/resolve_tag.sh +source "${HERE}/../ci/resolve_tag.sh" +FAILURES=0 + +check() +{ + local label=$1 expected=$2 actual=$3 + if [[ "${expected}" == "${actual}" ]] ; then + echo "ok - ${label}" + else + echo "FAIL - ${label}: expected '${expected}', got '${actual}'" + FAILURES=$((FAILURES + 1)) + fi +} + +TMP=$(mktemp -d) +D="${TMP}/O2PDPSuite" +mkdir -p "${D}" +# Deliberately out of lexical-creation order, and with non-daily neighbours. +touch "${D}/daily-20260919-0000-1" \ + "${D}/daily-20260921-0000-1" \ + "${D}/daily-20260920-0000-1" \ + "${D}/async-2024-pp-apass1-1" \ + "${D}/slc9-nightly-20260921-1" + +out=$(resolve_o2pdpsuite_tag "${D}") ; rc=$? +check "newest daily is chosen" "daily-20260921-0000-1" "${out}" +check "success exit code" 0 "${rc}" + +out=$(resolve_o2pdpsuite_tag "${D}" "daily-20260919-0000-1") ; rc=$? +check "requested tag honoured" "daily-20260919-0000-1" "${out}" +check "requested tag exit code" 0 "${rc}" + +out=$(resolve_o2pdpsuite_tag "${D}" "daily-20991231-0000-1" 2>/dev/null) ; rc=$? +check "absent requested tag fails" 1 "${rc}" +check "absent requested tag prints nothing on stdout" "" "${out}" + +out=$(resolve_o2pdpsuite_tag "${TMP}/does-not-exist" 2>/dev/null) ; rc=$? +check "missing directory fails" 1 "${rc}" + +mkdir -p "${TMP}/empty" +out=$(resolve_o2pdpsuite_tag "${TMP}/empty" 2>/dev/null) ; rc=$? +check "directory with no dailies fails" 1 "${rc}" + +# A requested tag from a fork PR's body must not be able to escape the +# O2PDPSuite module directory via "..". +mkdir -p "${TMP}/O2" +touch "${TMP}/O2/something" +out=$(resolve_o2pdpsuite_tag "${D}" "../O2/something" 2>/dev/null) ; rc=$? +check "path traversal tag fails" 1 "${rc}" +check "path traversal tag prints nothing on stdout" "" "${out}" + +echo +if [[ "${FAILURES}" == "0" ]] ; then + echo "All resolve_tag tests passed" +else + echo "${FAILURES} test(s) failed" +fi +exit $(( FAILURES > 0 ? 1 : 0 )) diff --git a/test/tests/run_tests_selection.sh b/test/tests/run_tests_selection.sh new file mode 100755 index 000000000..a876b0a29 --- /dev/null +++ b/test/tests/run_tests_selection.sh @@ -0,0 +1,94 @@ +#!/bin/bash +# Offline harness for run_tests.sh: stubs the sub-test scripts and checks that +# selection and exit-code aggregation behave. + +set -u +HERE=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +ENTRY="${HERE}/../run_tests.sh" +FAILURES=0 + +check() +{ + local label=$1 expected=$2 actual=$3 + if [[ "${expected}" == "${actual}" ]] ; then + echo "ok - ${label}" + else + echo "FAIL - ${label}: expected '${expected}', got '${actual}'" + FAILURES=$((FAILURES + 1)) + fi +} + +# A stub directory with three scripts whose exit codes we control via env +# vars, and which each record the arguments they received to a sibling +# "*.args" file (one per line) so forwarding can be checked. +make_stubs() +{ + local dir=$1 + mkdir -p "${dir}" + for name in generator workflow relval ; do + cat > "${dir}/run_${name}_tests.sh" < '${dir}/run_${name}_tests.args' +exit \${STUB_${name^^}_RC:-0} +EOF + chmod +x "${dir}/run_${name}_tests.sh" + done +} + +run_entry() +{ + # usage: run_entry [args...]; prints nothing, sets OUT and RC + local dir=$1 ; shift + OUT=$(O2DPG_TEST_SUBTEST_DIR="${dir}" O2DPG_ROOT="${HERE}/../.." \ + O2DPG_TEST_REPO_DIR="${HERE}/../.." \ + bash "${ENTRY}" "$@" 2>&1) + RC=$? +} + +TMP=$(mktemp -d) +make_stubs "${TMP}/subtests" + +# 1. all three sub-tests run by default and a clean run exits 0 +run_entry "${TMP}/subtests" +check "default selection exits 0" 0 "${RC}" +check "default runs generator" 1 "$(grep -c 'STUB generator ran' <<< "${OUT}")" +check "default runs workflow" 1 "$(grep -c 'STUB workflow ran' <<< "${OUT}")" +check "default runs relval" 1 "$(grep -c 'STUB relval ran' <<< "${OUT}")" + +# 2. an explicit selection runs only what was asked for +run_entry "${TMP}/subtests" relval +check "explicit selection exits 0" 0 "${RC}" +check "explicit selection skips generator" 0 "$(grep -c 'STUB generator ran' <<< "${OUT}")" +check "explicit selection runs relval" 1 "$(grep -c 'STUB relval ran' <<< "${OUT}")" + +# 3. a failing sub-test makes the entrypoint fail, and the others still run +STUB_GENERATOR_RC=3 run_entry "${TMP}/subtests" +check "failing generator propagates" 3 "${RC}" +check "failure does not stop workflow" 1 "$(grep -c 'STUB workflow ran' <<< "${OUT}")" + +# 4. --fail-immediately stops after the first failure +STUB_GENERATOR_RC=3 run_entry "${TMP}/subtests" --fail-immediately +check "fail-immediately propagates" 3 "${RC}" +check "fail-immediately stops early" 0 "$(grep -c 'STUB relval ran' <<< "${OUT}")" + +# 5. a later failure is not masked by an earlier success +STUB_RELVAL_RC=4 run_entry "${TMP}/subtests" +check "late failure propagates" 4 "${RC}" + +# 6. flag forwarding is per sub-test: only "generator" accepts these flags +run_entry "${TMP}/subtests" --keep-artifacts +check "keep-artifacts reaches generator" \ + 1 "$(grep -cx -- '--keep-artifacts' "${TMP}/subtests/run_generator_tests.args")" +check "keep-artifacts does not reach workflow" \ + 0 "$(grep -cx -- '--keep-artifacts' "${TMP}/subtests/run_workflow_tests.args")" +check "keep-artifacts does not reach relval" \ + 0 "$(grep -cx -- '--keep-artifacts' "${TMP}/subtests/run_relval_tests.args")" + +echo +if [[ "${FAILURES}" == "0" ]] ; then + echo "All run_tests.sh selection tests passed" +else + echo "${FAILURES} test(s) failed" +fi +exit $(( FAILURES > 0 ? 1 : 0 ))