From 1a2a08fdf7316d0266be4d9714773b2a3dca9f6a Mon Sep 17 00:00:00 2001 From: "Documenter.jl" Date: Thu, 21 Nov 2024 10:29:28 +0000 Subject: [PATCH] build based on 197a149 --- dev/.documenter-siteinfo.json | 2 +- dev/api/index.html | 2 +- dev/assets/documenter.js | 302 ++++++++++++++++++---------------- dev/index.html | 5 +- dev/objects.inv | Bin 1020 -> 1038 bytes dev/release_notes/index.html | 2 +- dev/search_index.js | 2 +- dev/tutorial/a07d98b0.png | Bin 23281 -> 0 bytes dev/tutorial/ed8198ca.png | Bin 0 -> 23124 bytes dev/tutorial/index.html | 11 +- 10 files changed, 171 insertions(+), 155 deletions(-) delete mode 100644 dev/tutorial/a07d98b0.png create mode 100644 dev/tutorial/ed8198ca.png diff --git a/dev/.documenter-siteinfo.json b/dev/.documenter-siteinfo.json index da25255..4253ed0 100644 --- a/dev/.documenter-siteinfo.json +++ b/dev/.documenter-siteinfo.json @@ -1 +1 @@ -{"documenter":{"julia_version":"1.11.1","generation_timestamp":"2024-11-07T11:10:11","documenter_version":"1.7.0"}} \ No newline at end of file +{"documenter":{"julia_version":"1.11.1","generation_timestamp":"2024-11-21T10:29:24","documenter_version":"1.8.0"}} \ No newline at end of file diff --git a/dev/api/index.html b/dev/api/index.html index b9d4044..00e60de 100644 --- a/dev/api/index.html +++ b/dev/api/index.html @@ -1,2 +1,2 @@ -Public APIs · PYTHIA8.jl
+Public APIs · PYTHIA8.jl
diff --git a/dev/assets/documenter.js b/dev/assets/documenter.js index 82252a1..7d68cd8 100644 --- a/dev/assets/documenter.js +++ b/dev/assets/documenter.js @@ -612,176 +612,194 @@ function worker_function(documenterSearchIndex, documenterBaseURL, filters) { }; } -// `worker = Threads.@spawn worker_function(documenterSearchIndex)`, but in JavaScript! -const filters = [ - ...new Set(documenterSearchIndex["docs"].map((x) => x.category)), -]; -const worker_str = - "(" + - worker_function.toString() + - ")(" + - JSON.stringify(documenterSearchIndex["docs"]) + - "," + - JSON.stringify(documenterBaseURL) + - "," + - JSON.stringify(filters) + - ")"; -const worker_blob = new Blob([worker_str], { type: "text/javascript" }); -const worker = new Worker(URL.createObjectURL(worker_blob)); - /////// SEARCH MAIN /////// -// Whether the worker is currently handling a search. This is a boolean -// as the worker only ever handles 1 or 0 searches at a time. -var worker_is_running = false; - -// The last search text that was sent to the worker. This is used to determine -// if the worker should be launched again when it reports back results. -var last_search_text = ""; - -// The results of the last search. This, in combination with the state of the filters -// in the DOM, is used compute the results to display on calls to update_search. -var unfiltered_results = []; - -// Which filter is currently selected -var selected_filter = ""; - -$(document).on("input", ".documenter-search-input", function (event) { - if (!worker_is_running) { - launch_search(); - } -}); - -function launch_search() { - worker_is_running = true; - last_search_text = $(".documenter-search-input").val(); - worker.postMessage(last_search_text); -} - -worker.onmessage = function (e) { - if (last_search_text !== $(".documenter-search-input").val()) { - launch_search(); - } else { - worker_is_running = false; - } - - unfiltered_results = e.data; - update_search(); -}; +function runSearchMainCode() { + // `worker = Threads.@spawn worker_function(documenterSearchIndex)`, but in JavaScript! + const filters = [ + ...new Set(documenterSearchIndex["docs"].map((x) => x.category)), + ]; + const worker_str = + "(" + + worker_function.toString() + + ")(" + + JSON.stringify(documenterSearchIndex["docs"]) + + "," + + JSON.stringify(documenterBaseURL) + + "," + + JSON.stringify(filters) + + ")"; + const worker_blob = new Blob([worker_str], { type: "text/javascript" }); + const worker = new Worker(URL.createObjectURL(worker_blob)); + + // Whether the worker is currently handling a search. This is a boolean + // as the worker only ever handles 1 or 0 searches at a time. + var worker_is_running = false; + + // The last search text that was sent to the worker. This is used to determine + // if the worker should be launched again when it reports back results. + var last_search_text = ""; + + // The results of the last search. This, in combination with the state of the filters + // in the DOM, is used compute the results to display on calls to update_search. + var unfiltered_results = []; + + // Which filter is currently selected + var selected_filter = ""; + + $(document).on("input", ".documenter-search-input", function (event) { + if (!worker_is_running) { + launch_search(); + } + }); -$(document).on("click", ".search-filter", function () { - if ($(this).hasClass("search-filter-selected")) { - selected_filter = ""; - } else { - selected_filter = $(this).text().toLowerCase(); + function launch_search() { + worker_is_running = true; + last_search_text = $(".documenter-search-input").val(); + worker.postMessage(last_search_text); } - // This updates search results and toggles classes for UI: - update_search(); -}); + worker.onmessage = function (e) { + if (last_search_text !== $(".documenter-search-input").val()) { + launch_search(); + } else { + worker_is_running = false; + } -/** - * Make/Update the search component - */ -function update_search() { - let querystring = $(".documenter-search-input").val(); + unfiltered_results = e.data; + update_search(); + }; - if (querystring.trim()) { - if (selected_filter == "") { - results = unfiltered_results; + $(document).on("click", ".search-filter", function () { + if ($(this).hasClass("search-filter-selected")) { + selected_filter = ""; } else { - results = unfiltered_results.filter((result) => { - return selected_filter == result.category.toLowerCase(); - }); + selected_filter = $(this).text().toLowerCase(); } - let search_result_container = ``; - let modal_filters = make_modal_body_filters(); - let search_divider = `
`; + // This updates search results and toggles classes for UI: + update_search(); + }); - if (results.length) { - let links = []; - let count = 0; - let search_results = ""; - - for (var i = 0, n = results.length; i < n && count < 200; ++i) { - let result = results[i]; - if (result.location && !links.includes(result.location)) { - search_results += result.div; - count++; - links.push(result.location); - } - } + /** + * Make/Update the search component + */ + function update_search() { + let querystring = $(".documenter-search-input").val(); - if (count == 1) { - count_str = "1 result"; - } else if (count == 200) { - count_str = "200+ results"; + if (querystring.trim()) { + if (selected_filter == "") { + results = unfiltered_results; } else { - count_str = count + " results"; + results = unfiltered_results.filter((result) => { + return selected_filter == result.category.toLowerCase(); + }); } - let result_count = `
${count_str}
`; - search_result_container = ` + let search_result_container = ``; + let modal_filters = make_modal_body_filters(); + let search_divider = `
`; + + if (results.length) { + let links = []; + let count = 0; + let search_results = ""; + + for (var i = 0, n = results.length; i < n && count < 200; ++i) { + let result = results[i]; + if (result.location && !links.includes(result.location)) { + search_results += result.div; + count++; + links.push(result.location); + } + } + + if (count == 1) { + count_str = "1 result"; + } else if (count == 200) { + count_str = "200+ results"; + } else { + count_str = count + " results"; + } + let result_count = `
${count_str}
`; + + search_result_container = ` +
+ ${modal_filters} + ${search_divider} + ${result_count} +
+ ${search_results} +
+
+ `; + } else { + search_result_container = `
${modal_filters} ${search_divider} - ${result_count} -
- ${search_results} -
-
+
0 result(s)
+ +
No result found!
`; - } else { - search_result_container = ` -
- ${modal_filters} - ${search_divider} -
0 result(s)
-
-
No result found!
- `; - } + } - if ($(".search-modal-card-body").hasClass("is-justify-content-center")) { - $(".search-modal-card-body").removeClass("is-justify-content-center"); - } + if ($(".search-modal-card-body").hasClass("is-justify-content-center")) { + $(".search-modal-card-body").removeClass("is-justify-content-center"); + } - $(".search-modal-card-body").html(search_result_container); - } else { - if (!$(".search-modal-card-body").hasClass("is-justify-content-center")) { - $(".search-modal-card-body").addClass("is-justify-content-center"); + $(".search-modal-card-body").html(search_result_container); + } else { + if (!$(".search-modal-card-body").hasClass("is-justify-content-center")) { + $(".search-modal-card-body").addClass("is-justify-content-center"); + } + + $(".search-modal-card-body").html(` +
Type something to get started!
+ `); } + } - $(".search-modal-card-body").html(` -
Type something to get started!
- `); + /** + * Make the modal filter html + * + * @returns string + */ + function make_modal_body_filters() { + let str = filters + .map((val) => { + if (selected_filter == val.toLowerCase()) { + return `${val}`; + } else { + return `${val}`; + } + }) + .join(""); + + return ` +
+ Filters: + ${str} +
`; } } -/** - * Make the modal filter html - * - * @returns string - */ -function make_modal_body_filters() { - let str = filters - .map((val) => { - if (selected_filter == val.toLowerCase()) { - return `${val}`; - } else { - return `${val}`; - } - }) - .join(""); - - return ` -
- Filters: - ${str} -
`; +function waitUntilSearchIndexAvailable() { + // It is possible that the documenter.js script runs before the page + // has finished loading and documenterSearchIndex gets defined. + // So we need to wait until the search index actually loads before setting + // up all the search-related stuff. + if (typeof documenterSearchIndex !== "undefined") { + runSearchMainCode(); + } else { + console.warn("Search Index not available, waiting"); + setTimeout(waitUntilSearchIndexAvailable, 1000); + } } +// The actual entry point to the search code +waitUntilSearchIndexAvailable(); + }) //////////////////////////////////////////////////////////////////////////////// require(['jquery'], function($) { diff --git a/dev/index.html b/dev/index.html index c748de4..90b6f16 100644 --- a/dev/index.html +++ b/dev/index.html @@ -1,5 +1,5 @@ -Introduction · PYTHIA8.jl

Julia bindings for PYTHIA

Description

Julia bindings for the PYTHIA package used for generating high-energy physics collision events. It facilitates the interface with the PYTHIA client library, by writing Julia code instead of having to write C++ code. This package is developed using the CxxWrap.jl package to wrap C++ types and functions to Julia. Wrapper C++ code is generated with the help of WrapIt tool that uses of the clang library.

The Julia interface has been inspired by the functionality provided by Pythia Python interface.

Installation

The PYTHIA8.jl package does no require any special installation. Stable releases are registered into the Julia general registry, and therefore can be deployed with the standard Pkg Julia package manager. This would bring automatically an installation of PYTHIA binary libraries as artifacts corresponding to the current platform (the so called _jll packages). Linux and MacOS operating systems with x86_64, powerpc64le and aarch64 architectures are supported.

julia> using Pkg
+Introduction · PYTHIA8.jl

Julia bindings for PYTHIA

Description

Julia bindings for the PYTHIA package used for generating high-energy physics collision events. It facilitates the interface with the PYTHIA client library, by writing Julia code instead of having to write C++ code. This package is developed using the CxxWrap.jl package to wrap C++ types and functions to Julia. Wrapper C++ code is generated with the help of WrapIt tool that uses of the clang library.

The Julia interface has been inspired by the functionality provided by Pythia Python interface.

Installation

The PYTHIA8.jl package does no require any special installation. Stable releases are registered into the Julia general registry, and therefore can be deployed with the standard Pkg Julia package manager. This would bring automatically an installation of PYTHIA binary libraries as artifacts corresponding to the current platform (the so called _jll packages). Linux and MacOS operating systems with x86_64, powerpc64le and aarch64 architectures are supported.

julia> using Pkg
 julia> Pkg.add("PYTHIA8")

Interface

Only the classes that provide the everyday functionality of PYTHIA have been wrapped. Additional classes and functionality can be included upon request. This is a summary of the currently provided classes:

  • top level: Pythia, PythiaParallel
  • event access: Event, Particle, Vec4
  • settings and information: HIInfo, Settings, Flag, Mode, Parm, Word, FVec, MVec, PVec, WVec
  • user interface pointers: Rndm, PDF, DecayHandler, RndmEngine, UserHooks, MergingHooks, BeamShape, SigmaProcess, TimeShower, SpaceShower, HeavyIons
  • analysis tools: Hist, HistPlot, SlowJet
  • kinematic functions: m, m2, dot3, cross3, cross4, theta, costheta, phi, cosphi, RRapPhi, REtaPhi

Limitations

  • Methods returning or accepting as argument a std::map have not been wrapped since this is a current limitation of the CxxWrap package.

Getting Started

Have a look at the following trivial interactive session:

julia> using PYTHIA8
 
 julia> pythia = PYTHIA8.Pythia("", false)
@@ -24,4 +24,5 @@
  |                                                                                                                 | 
  *-------  End PYTHIA Flag + Mode + Parm + Word + FVec + MVec + PVec + WVec Settings  -----------------------------* 
  julia> parm(csets, "Beams:eA")
-7000.0

Note that class methods are called with the object instance as first argument. In C++ the parm(...) method of the Settings class would be called as csets.parm("Beams:eA") being csets an instance of Settings, while in Julia it is called as parm(csets, "Beams:eA"). Thanks to the Julia multi-dispatch we do not need to prefix the methods with the module name PYTHIA8.parm, even for very common function names such as list.

Also notice that the default printout of any wrapped object is of the form C++/Julia type(at some address @0xXXXXXX) like this one: CxxRef{Pythia8!Settings}(Ptr{Pythia8!Settings} @0x00000001200d02d0). In this particular example, the C++ type is a reference to Pythia8::Settings and the object is at the memory address 0x00000001200d02d0.

Examples

Currently the following examples are implemented replicating the equivalent Python ones.

main291.jl

Simple test program, equivalent to main101.cc, but written in Julia. It fits on one slide in a talk. It studies the charged multiplicity distribution at the LHC.

main292.jl

Simple test program to illustrate the usage of PythiaParallel in Julia. The physics case is equivalent to main291, but in parallel.

main293.jl

Example how you can use UserHooks to trace pT spectrum through the program, and veto undesirable jet multiplicities. It is based on main242.cc.

main294.jl

Simple Julia script which reads and parses the Pythia 8 particle database (XML format), without requiring the Pythia8 Julia bindings.

main296.jl

Example of how PYTHIA can be used as a shared library from Julia. The physics case is a study of total cross sections, which are not exposed currently cannot through the normal Julia interface.

Tests

Unit tests can be run with julia --project=. test/runtests.jl. It runs in addition all the implemented examples to ensure their correct execution and detection of any regression.

+7000.0

Note that class methods are called with the object instance as first argument. In C++ the parm(...) method of the Settings class would be called as csets.parm("Beams:eA") being csets an instance of Settings, while in Julia it is called as parm(csets, "Beams:eA"). Thanks to the Julia multi-dispatch we do not need to prefix the methods with the module name PYTHIA8.parm, even for very common function names such as list.

Also notice that the default printout of any wrapped object is of the form C++/Julia type(at some address @0xXXXXXX) like this one: CxxRef{Pythia8!Settings}(Ptr{Pythia8!Settings} @0x00000001200d02d0). In this particular example, the C++ type is a reference to Pythia8::Settings and the object is at the memory address 0x00000001200d02d0.

Examples

Currently the following examples are implemented replicating the equivalent Python ones.

main291.jl

Simple test program, equivalent to main101.cc, but written in Julia. It fits on one slide in a talk. It studies the charged multiplicity distribution at the LHC.

main292.jl

Simple test program to illustrate the usage of PythiaParallel in Julia. The physics case is equivalent to main291, but in parallel.

main293.jl

Example how you can use UserHooks to trace pT spectrum through the program, and veto undesirable jet multiplicities. It is based on main242.cc.

main294.jl

Simple Julia script which reads and parses the Pythia 8 particle database (XML format), without requiring the Pythia8 Julia bindings.

main296.jl

Example of how PYTHIA can be used as a shared library from Julia. The physics case is a study of total cross sections, which are not exposed currently cannot through the normal Julia interface.

Tests

Unit tests can be run with julia --project=. test/runtests.jl. It runs in addition all the implemented examples to ensure their correct execution and detection of any regression.

Re-generating wrapper code

It is possible to re-generate the wrapper library locally instead of using the registered Pythia8_cxxwrap_jll package. This can be useful for adding new classes or functions to the wrapper library. These are the instructions:

  • The configuration file gen/Pythia8.wit.in is the input to the automated process. New header files can be added to the input list.
  • The script gen/build.jl does the work of generating the code and building the library. The command to execute are:
    julia --project=gen -e 'import Pkg; Pkg.instantiate()'
    +julia --project=gen gen/build.jl
diff --git a/dev/objects.inv b/dev/objects.inv index 00057007ab2481d3b53a87c3e6b86b1d4efd0c03..b9acfc4fa4c1f79be907fdc4ea7a8d21c5f790ea 100644 GIT binary patch delta 930 zcmV;T16}<52aX7kLIE+6Lp*=mPTVjMea}}+1(E23$%W^CT#on#^f5&>M5T8gYrH5a9)Yb_+F zdEdgl3t(cOJX-O-1ug>^*GbM(I2~UpPqLg*#ipguO!X}X9|HK+F5!QxS$g4ayc?07 z=!irSiNkpGzJca6Vfiw6t2OuIqYmK5$I!fnK8yE}l}u922zy|qR)m%rsdAx-o-vZn zsJvwvDe7GF3Pa%fiDX=9$&=cMGTg*mjje5PywLIj@`B+7+rbUl5C z0f&JWgLJM#mlOAy7ra$_Rr)>)nwbQcC`*xWvpU&LMG<_eb51WW#zDLNBeV;WTqJ?P1y&;!bSw*5-tza) zx{v<;IcB(Vqr5Nlo)Oxf_U-^M;JSYzH#9}Y4GBjm(Dp!gecwW% zg<`8}Ch?iAXhu8Q$k+J@T&LXj(in`)7!3DF6h2eoWL(Qc-!;#!a7)y!*ixm$*bit*nG8ej$TF1(Iz=ZZz$(zAg5Wmzz$v| zu!Bv39lU>GUcwh?F6pX3YZGuet*X5P<)){xV z=WC?Ii{JA{&jMgRdm3d(!b>y16}b^|W&I;VV+qa@(6YO3D2?}z6?eV%N@({#opS7) z=@dgomH&>P^o-M^ci$D0mqOgDe{h$d6im-V2KE-hvKs0io>{OTM{~G;c#h8g14L34 ETkI0iW&i*H delta 912 zcmV;B18@9}2>b_-LIE(5Lp*t_dI7mcW0l-VtfLn8dcY`_rZhjwF!UM#&o`7h6UHi z@BuTe7|&vsCsH##Ll(_=af>la%S4NuA_@J>Vxe>)!qTk03X|{V2&=JCRYDQV{-f_m+RcrljXcDr+sWTXv&)p=slb{PsK8@6KQ=*c!0S!5;H{77T*n zmp-JQ(v8~P4fb1r8yrCO8oDgbl~J5>TOf}qu@6`%TAvZxErJ&kV?zH*xyXWp?XA1y_vTFN zTpfDet zedGpb^lO8W(S(1a$2K4tCM5kCCi-i_+BcS)YO1wX)Y4n)rqL&O7kz?t`UD@SPteoH zSk%P6Y2sjq4Gvz_#D3kxEud~<-!yTD#kt9m^rq$Jyby}oH$?r2m{rxSA7zt@HHBSK zKuZCZqN;SFXtJZ6D}IlR*LOm5Oc-#Pvnh{=aZSPr3cPns2 zk(nD_v8gPU);=**p3ry#dA0+I(lntgnFi~j(JC;R22ghV5S|BjyZOz{0rKNJ?{ mQr@e7aF-mWsAn<;JJKwNx_ieK><0cE?(UsXu>S -Release Notes · PYTHIA8.jl
+Release Notes · PYTHIA8.jl

Release Notes

0.2.1 (18-11-2024)

New functionality

  • Adapted gen/build.jl to use the WrapIt.jl package instead of a locally installed wrapit

Fixes

  • Removed Hist::operator+(double) to avoid #457
  • Do not export cd to avoid warnings

0.2.0 (07-11-2024)

New functionality

  • Added 2 more new examples: main294.jl and main296.jl
  • Added a tutorial in the documentation using the module Literate.jl to generate markdown and notebook from single source.

0.1.1 (03-10-2024)

Fixes

  • Lower the version requirements InteractiveUtils.

0.1.0 (25-09-2024)

  • Initial release with basic functionality needed to run the first 3 examples.
diff --git a/dev/search_index.js b/dev/search_index.js index 99fa3d8..d3c7244 100644 --- a/dev/search_index.js +++ b/dev/search_index.js @@ -1,3 +1,3 @@ var documenterSearchIndex = {"docs": -[{"location":"release_notes/#Release-Notes","page":"Release Notes","title":"Release Notes","text":"","category":"section"},{"location":"release_notes/#0.2.0-(07-11-2024)","page":"Release Notes","title":"0.2.0 (07-11-2024)","text":"","category":"section"},{"location":"release_notes/#New-functionality","page":"Release Notes","title":"New functionality","text":"","category":"section"},{"location":"release_notes/","page":"Release Notes","title":"Release Notes","text":"Added 2 more new examples: main294.jl and main296.jl\nAdded a tutorial in the documentation using the module Literate.jl to generate markdown and notebook from single source.","category":"page"},{"location":"release_notes/#0.1.1-(03-10-2024)","page":"Release Notes","title":"0.1.1 (03-10-2024)","text":"","category":"section"},{"location":"release_notes/#Fixes","page":"Release Notes","title":"Fixes","text":"","category":"section"},{"location":"release_notes/","page":"Release Notes","title":"Release Notes","text":"Lower the version requirements InteractiveUtils.","category":"page"},{"location":"release_notes/#0.1.0-(25-09-2024)","page":"Release Notes","title":"0.1.0 (25-09-2024)","text":"","category":"section"},{"location":"release_notes/","page":"Release Notes","title":"Release Notes","text":"Initial release with basic functionality needed to run the first 3 examples.","category":"page"},{"location":"api/#Public-Documentation","page":"Public APIs","title":"Public Documentation","text":"","category":"section"},{"location":"api/","page":"Public APIs","title":"Public APIs","text":"Documentation for PYTHIA8.jl public interface.","category":"page"},{"location":"api/","page":"Public APIs","title":"Public APIs","text":"","category":"page"},{"location":"api/#Index","page":"Public APIs","title":"Index","text":"","category":"section"},{"location":"api/","page":"Public APIs","title":"Public APIs","text":"Pages = [\"api.md\"]\nModules = [PYTHIA8]\nOrder = [:type]","category":"page"},{"location":"api/","page":"Public APIs","title":"Public APIs","text":"Pages = [\"api.md\"]\nModules = [PYTHIA8, Base]\nOrder = [:function]","category":"page"},{"location":"api/","page":"Public APIs","title":"Public APIs","text":"","category":"page"},{"location":"api/#Types","page":"Public APIs","title":"Types","text":"","category":"section"},{"location":"api/","page":"Public APIs","title":"Public APIs","text":"This is the list of all types and functions defined for PYTHIA8","category":"page"},{"location":"api/","page":"Public APIs","title":"Public APIs","text":"Modules = [PYTHIA8]\nOrder = [:type]","category":"page"},{"location":"api/#Functions","page":"Public APIs","title":"Functions","text":"","category":"section"},{"location":"api/","page":"Public APIs","title":"Public APIs","text":"Modules = [PYTHIA8]\nOrder = [:function]","category":"page"},{"location":"api/#PYTHIA8.__init__-Tuple{Pythia8!UserHooks}","page":"Public APIs","title":"PYTHIA8.__init__","text":"__init__(self::UserHooks)\n\nThis function initializes the UserHooks object by creating an instance of Pythia8UserHooks and setting the hooks for each method defined by the user.\n\n\n\n\n\n","category":"method"},{"location":"#Julia-bindings-for-PYTHIA","page":"Introduction","title":"Julia bindings for PYTHIA","text":"","category":"section"},{"location":"#Description","page":"Introduction","title":"Description","text":"","category":"section"},{"location":"","page":"Introduction","title":"Introduction","text":"Julia bindings for the PYTHIA package used for generating high-energy physics collision events. It facilitates the interface with the PYTHIA client library, by writing Julia code instead of having to write C++ code. This package is developed using the CxxWrap.jl package to wrap C++ types and functions to Julia. Wrapper C++ code is generated with the help of WrapIt tool that uses of the clang library.","category":"page"},{"location":"","page":"Introduction","title":"Introduction","text":"The Julia interface has been inspired by the functionality provided by Pythia Python interface.","category":"page"},{"location":"#Installation","page":"Introduction","title":"Installation","text":"","category":"section"},{"location":"","page":"Introduction","title":"Introduction","text":"The PYTHIA8.jl package does no require any special installation. Stable releases are registered into the Julia general registry, and therefore can be deployed with the standard Pkg Julia package manager. This would bring automatically an installation of PYTHIA binary libraries as artifacts corresponding to the current platform (the so called _jll packages). Linux and MacOS operating systems with x86_64, powerpc64le and aarch64 architectures are supported. ","category":"page"},{"location":"","page":"Introduction","title":"Introduction","text":"julia> using Pkg\njulia> Pkg.add(\"PYTHIA8\")","category":"page"},{"location":"#Interface","page":"Introduction","title":"Interface","text":"","category":"section"},{"location":"","page":"Introduction","title":"Introduction","text":"Only the classes that provide the everyday functionality of PYTHIA have been wrapped. Additional classes and functionality can be included upon request. This is a summary of the currently provided classes:","category":"page"},{"location":"","page":"Introduction","title":"Introduction","text":"top level: Pythia, PythiaParallel\nevent access: Event, Particle, Vec4\nsettings and information: HIInfo, Settings, Flag, Mode, Parm, Word, FVec, MVec, PVec, WVec\nuser interface pointers: Rndm, PDF, DecayHandler, RndmEngine, UserHooks, MergingHooks, BeamShape, SigmaProcess, TimeShower, SpaceShower, HeavyIons\nanalysis tools: Hist, HistPlot, SlowJet\nkinematic functions: m, m2, dot3, cross3, cross4, theta, costheta, phi, cosphi, RRapPhi, REtaPhi","category":"page"},{"location":"#Limitations","page":"Introduction","title":"Limitations","text":"","category":"section"},{"location":"","page":"Introduction","title":"Introduction","text":"Methods returning or accepting as argument a std::map have not been wrapped since this is a current limitation of the CxxWrap package.","category":"page"},{"location":"#Getting-Started","page":"Introduction","title":"Getting Started","text":"","category":"section"},{"location":"","page":"Introduction","title":"Introduction","text":"Have a look at the following trivial interactive session:","category":"page"},{"location":"","page":"Introduction","title":"Introduction","text":"julia> using PYTHIA8\n\njulia> pythia = PYTHIA8.Pythia(\"\", false)\nPYTHIA8.Pythia8!PythiaAllocated(Ptr{Nothing} @0x00000001200d0000)\n\njulia> csets = pythia |> settings # which equivalent to csets = settings(pythia)\nCxxRef{Pythia8!Settings}(Ptr{Pythia8!Settings} @0x00000001200d02d0)\n\njulia> list(csets, \"Init:\")\n *------- PYTHIA Flag + Mode + Parm + Word + FVec + MVec + PVec + WVec Settings (with requested string) ----------* \n | | \n | Name | Now | Default Min Max | \n | | | | \n | Init:showAllParticleData | off | off | \n | Init:showAllSettings | off | off | \n | Init:showChangedParticleData | on | on | \n | Init:showChangedResonanceData | off | off | \n | Init:showChangedSettings | on | on | \n | Init:showMultipartonInteractions | on | on | \n | Init:showOneParticleData | 0 | 0 0 | \n | Init:showProcesses | on | on | \n | | \n *------- End PYTHIA Flag + Mode + Parm + Word + FVec + MVec + PVec + WVec Settings -----------------------------* \n julia> parm(csets, \"Beams:eA\")\n7000.0","category":"page"},{"location":"","page":"Introduction","title":"Introduction","text":"Note that class methods are called with the object instance as first argument. In C++ the parm(...) method of the Settings class would be called as csets.parm(\"Beams:eA\") being csets an instance of Settings, while in Julia it is called as parm(csets, \"Beams:eA\"). Thanks to the Julia multi-dispatch we do not need to prefix the methods with the module name PYTHIA8.parm, even for very common function names such as list.","category":"page"},{"location":"","page":"Introduction","title":"Introduction","text":"Also notice that the default printout of any wrapped object is of the form C++/Julia type(at some address @0xXXXXXX) like this one: CxxRef{Pythia8!Settings}(Ptr{Pythia8!Settings} @0x00000001200d02d0). In this particular example, the C++ type is a reference to Pythia8::Settings and the object is at the memory address 0x00000001200d02d0.","category":"page"},{"location":"#Examples","page":"Introduction","title":"Examples","text":"","category":"section"},{"location":"","page":"Introduction","title":"Introduction","text":"Currently the following examples are implemented replicating the equivalent Python ones.","category":"page"},{"location":"#[main291.jl](https://github.com/JuliaHEP/PYTHIA8.jl/blob/main/examples/main291.jl)","page":"Introduction","title":"main291.jl","text":"","category":"section"},{"location":"","page":"Introduction","title":"Introduction","text":"Simple test program, equivalent to main101.cc, but written in Julia. It fits on one slide in a talk. It studies the charged multiplicity distribution at the LHC.","category":"page"},{"location":"#[main292.jl](https://github.com/JuliaHEP/PYTHIA8.jl/blob/main/examples/main292.jl)","page":"Introduction","title":"main292.jl","text":"","category":"section"},{"location":"","page":"Introduction","title":"Introduction","text":"Simple test program to illustrate the usage of PythiaParallel in Julia. The physics case is equivalent to main291, but in parallel.","category":"page"},{"location":"#[main293.jl](https://github.com/JuliaHEP/PYTHIA8.jl/blob/main/examples/main293.jl)","page":"Introduction","title":"main293.jl","text":"","category":"section"},{"location":"","page":"Introduction","title":"Introduction","text":"Example how you can use UserHooks to trace pT spectrum through the program, and veto undesirable jet multiplicities. It is based on main242.cc.","category":"page"},{"location":"#[main294.jl](https://github.com/JuliaHEP/PYTHIA8.jl/blob/main/examples/main294.jl)","page":"Introduction","title":"main294.jl","text":"","category":"section"},{"location":"","page":"Introduction","title":"Introduction","text":"Simple Julia script which reads and parses the Pythia 8 particle database (XML format), without requiring the Pythia8 Julia bindings.","category":"page"},{"location":"#[main296.jl](https://github.com/JuliaHEP/PYTHIA8.jl/blob/main/examples/main296.jl)","page":"Introduction","title":"main296.jl","text":"","category":"section"},{"location":"","page":"Introduction","title":"Introduction","text":"Example of how PYTHIA can be used as a shared library from Julia. The physics case is a study of total cross sections, which are not exposed currently cannot through the normal Julia interface.","category":"page"},{"location":"#Tests","page":"Introduction","title":"Tests","text":"","category":"section"},{"location":"","page":"Introduction","title":"Introduction","text":"Unit tests can be run with julia --project=. test/runtests.jl. It runs in addition all the implemented examples to ensure their correct execution and detection of any regression.","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"EditURL = \"tutorial_lit.jl\"","category":"page"},{"location":"tutorial/#PYTHIA8.jl-Tutorial","page":"Tutorial","title":"PYTHIA8.jl Tutorial","text":"","category":"section"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"This turorial demostrates a simple application of PYTHIA8.jl to generate some events and present the results in form of histograms. Please refer to the PYTHIA documentation for more details on the parameters and the physics processes.","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"note: Note that\nYou can also download this tutorial as a Jupyter notebook and a plain Julia source file.","category":"page"},{"location":"tutorial/#Table-of-contents","page":"Tutorial","title":"Table of contents","text":"","category":"section"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"Pages = [\"tutorial.md\"]\nDepth = 2:3","category":"page"},{"location":"tutorial/#Generate-events-and-plot-the-charged-multiplicity-distribution","page":"Tutorial","title":"Generate events and plot the charged multiplicity distribution","text":"","category":"section"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"In this example, we will generate 200 events at a center of mass energy of 8000 GeV and plot the charged multiplicity distribution.","category":"page"},{"location":"tutorial/#Loading-the-necessary-modules","page":"Tutorial","title":"Loading the necessary modules","text":"","category":"section"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"We will use the PYTHIA8 module to run the event generation.\nWe will use the FHist module to create the histograms.\nWe will use the Plots module to plot them.","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"If these modules are not installed, you can install them by running the following commands:","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"using Pkg\nPkg.add(\"PYTHIA8\")\nPkg.add(\"FHist\")\nPkg.add(\"Plots\")","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"using PYTHIA8\nusing FHist\nusing Plots: plot, plot!, theme\nimport DisplayAs: PNG #hide","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"Define the theme for the plots. See available themes and attributes in Plots.jl.","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"theme(:wong2, frame=:box, grid=false, minorticks=true,\n guidefontvalign=:top, guidefonthalign=:right,\n xlims=(:auto, :auto), lw=1.2, lab=\"\", colorbar=false)","category":"page"},{"location":"tutorial/#Initialize-PYTHIA","page":"Tutorial","title":"Initialize PYTHIA","text":"","category":"section"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"We will initialize PYTHIA with the following settings:","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"Center of mass energy of the collision: 8000 GeV\nAll hard QCD processes are enabled\nMinimum transverse momentum of the hard process: 20 GeV","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"We use the operator << to set the parameters and the operator |> to pipe to the init function. This added interface is quite ergonomic, however alternatively, we can use the functions directly provided by C++ interface, e.g.,","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"readString(pythia, \"Beams:eCM = 8000.\") or\nparm( settings(pythia), \"Beams:eCM\", 8000.)","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"pythia = PYTHIA8.Pythia(\"\", false);\npythia << \"Beams:eCM = 8000.\" <<\n \"HardQCD:all = on\" <<\n \"PhaseSpace:pTHatMin = 20.\";\nnothing #hide","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"The purpose of the next two lines is to reduce the amount of output during the event generation","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"pythia << \"Next:numberShowEvent = 0\" <<\n \"Next:numberShowProcess = 0\";\nnothing #hide","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"Initialize the event generation. The init function returns a boolean value. Alternatively, we can use the direct call to init(pythia) function.","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"pythia |> init;\nnothing #hide","category":"page"},{"location":"tutorial/#Create-a-1D-histogram","page":"Tutorial","title":"Create a 1D histogram","text":"","category":"section"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"We will create a histogram to store the charged multiplicity distribution. The histogram is defined with 25 bins ranging from -0.5 to 499.5. Note that with ranges we need to specify the number of edges (26) and not the number of bins (25)","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"mult = Hist1D(binedges=range(-0.5, 499.5, 26));\nnothing #hide","category":"page"},{"location":"tutorial/#Generate-events","page":"Tutorial","title":"Generate events","text":"","category":"section"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"We will generate 200 events and fill the histogram with the number of charged particles making use of the count function. We use the push! function to fill the histogram.","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"for iEvent in 1:200\n pythia |> next || continue\n # Find number of all final charged particles and fill histogram.\n nCharged = count(p -> isFinal(p) && isCharged(p), pythia |> event)\n push!(mult, nCharged)\nend","category":"page"},{"location":"tutorial/#Print-the-statistics-and-plot-charged-multiplicity-distribution","page":"Tutorial","title":"Print the statistics and plot charged multiplicity distribution","text":"","category":"section"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"Print the generation statistics using the stat function from PYTHIA.","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"pythia |> PYTHIA8.stat","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"Plot the histogram with the plot function from Plots.jl. We use the stepbins seriestype to plot the histogram as a step plot.","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"img = plot(mult, xlabel=\"counts\", ylabel=\"#particles\",\n title=\"Charged Multiplicity Distribution\", seriestype=:stepbins,\n c=2, lc=1, fill=0);\ndisplay(img)\nPNG(img) #hide","category":"page"},{"location":"tutorial/#Multi-threaded-version-of-the-same-example","page":"Tutorial","title":"Multi-threaded version of the same example","text":"","category":"section"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"In this example we repeat the same example as before but using multi-threading to generate the events faster.","category":"page"},{"location":"tutorial/#Initialize-Parallel-PYTHIA","page":"Tutorial","title":"Initialize Parallel PYTHIA","text":"","category":"section"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"Create an instance of PythiaParallel with the same settings as before. The PythiaParallel class is used to generate events concurrently using multiple threads. The settings are copied for each Pythia instance.","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"pythia_mt = PYTHIA8.PythiaParallel(\"\", false);\nnothing #hide","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"PythiaParallel reads settings the same way as the normal Pythia does.","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"pythia_mt << \"Beams:eCM = 8000.\" <<\n \"HardQCD:all = on\" <<\n \"PhaseSpace:pTHatMin = 20.\";\nnothing #hide","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"The maximum degree of parallelism. If set to 0 (default), the program will use the maximum number of threads supported by the hardware.","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"pythia_mt << \"Parallelism:numThreads = 4\";\nnothing #hide","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"This defines the number of events generated by PythiaParallel::run.","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"pythia_mt << \"Main:numberOfEvents = 200\";\nnothing #hide","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"The next is a user provided function to initialize each underlying Pythia instance (one per thread). Please note that the function is called concurrently, therefore the use needs to avoid calling thread-unsafe functions. This is the reason we use the Core.println function instead of println.","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"function w_init(pythia)::CxxBool\n Core.println(\"Initializing Pythia with index $(mode(pythia |> settings, \"Parallelism:index\")).\")\n return pythia |> init\nend;\nnothing #hide","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"Initialize the event generation. The init function returns a boolean value to indicate the success. The user provided function w_init is called for each thread.","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"init(pythia_mt, w_init);\nnothing #hide","category":"page"},{"location":"tutorial/#Create-a-1D-histogram-2","page":"Tutorial","title":"Create a 1D histogram","text":"","category":"section"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"mult_mt = Hist1D(binedges=range(-0.5, 499.5, 26));\nnothing #hide","category":"page"},{"location":"tutorial/#Generate-events-2","page":"Tutorial","title":"Generate events","text":"","category":"section"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"Define a user function that will be called for each generated event. This function will be called concurrently by the threads. Therefore, we need to use thread-safe functions. Filling the histogram with atomic_push is thread-safe. The function takes a Pythia object as the first argument and returns Nothing.","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"function analyze(pythiaNow)::Nothing\n nCharged = count(p -> isFinal(p) && isCharged(p), pythiaNow |> event)\n atomic_push!(mult_mt, nCharged)\n return\nend","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"Run the event generation. The number of events has been set in the Main:numberOfEvents parameter.","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"PYTHIA8.run(pythia_mt, analyze);\nnothing #hide","category":"page"},{"location":"tutorial/#Print-the-statistics-and-plot-charged-multiplicity-distribution-2","page":"Tutorial","title":"Print the statistics and plot charged multiplicity distribution","text":"","category":"section"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"Print the statistics using the stat function from PYTHIA8.jl","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"pythia_mt |> PYTHIA8.stat","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"Plot the result histogram","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"img = plot(mult_mt, xlabel=\"counts\", ylabel=\"#particles\",\n title=\"Charged Multiplicity Distribution (multi-threaded)\", seriestype=:stepbins,\n c=2, lc=1, fill=0);\ndisplay(img)\nPNG(img) #hide","category":"page"},{"location":"tutorial/#Speed-comparison-between-the-two-versions","page":"Tutorial","title":"Speed comparison between the two versions","text":"","category":"section"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"We will generate 1000 events and compare the time taken by the two versions.","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"empty!(mult);\nempty!(mult_mt);\nnothing #hide","category":"page"},{"location":"tutorial/#Single-threaded-version","page":"Tutorial","title":"Single-threaded version","text":"","category":"section"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"Generate 1000 events and measure the time taken. This is done using the @elapsed macro.","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"st_elap = @elapsed begin\n for iEvent in 1:1000\n pythia |> next || continue\n nCharged = count(p -> isFinal(p) && isCharged(p), pythia |> event)\n atomic_push!(mult, nCharged)\n end\nend","category":"page"},{"location":"tutorial/#Multi-threaded-version","page":"Tutorial","title":"Multi-threaded version","text":"","category":"section"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"Generate 1000 events and measure the time taken. This is done using the @elapsed macro.","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"pythia_mt << \"Main:numberOfEvents = 1000\"\nmt_elap = @elapsed begin\n PYTHIA8.run(pythia_mt, analyze)\nend","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"Print the speedup factor (4 threads)","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"println(\"Speedup is $(st_elap/mt_elap).\")","category":"page"},{"location":"tutorial/#Make-a-plot-of-the-speedup-factor","page":"Tutorial","title":"Make a plot of the speedup factor","text":"","category":"section"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"We will generate events using 1, 2, 4, 8, 16, and 32 threads and plot the speedup factor. The speedup factor is defined as the ratio of the time taken by the single-threaded version to the time taken by the multi-threaded version.","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"n_threads = [2^n for n in 0:5]\nspeedup = Float64[]\nw_init(pythiaNow)::CxxBool = pythiaNow |> init\nfor n_thread in n_threads\n empty!(mult_mt)\n _pythia = PYTHIA8.PythiaParallel(\"\", false);\n _pythia << \"Beams:eCM = 8000.\" <<\n \"HardQCD:all = on\" <<\n \"PhaseSpace:pTHatMin = 20.\" <<\n \"Parallelism:numThreads = $n_thread\" <<\n \"Main:numberOfEvents = 1000\"\n init(_pythia, w_init)\n\n elap = @elapsed begin\n PYTHIA8.run(_pythia, analyze)\n end\n push!(speedup, st_elap/elap)\nend;\nnothing #hide","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"Plot the speedup factor vs the number of threads and overlay the line for the ideal scaling.","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"img = plot(n_threads, speedup, xlabel=\"#threads\", ylabel=\"speedup\",\n title=\"Speedup vs #threads (#cores = $(Sys.CPU_THREADS))\",\n seriestype=:scatter, legend=false,\n xscale=:log10, yscale=:log10);\nnothing #hide","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"Add a line to the same plot","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"plot!(n_threads, n_threads, seriestype=:line)\ndisplay(img)\nPNG(img) #hide","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"note: Note that\nThe plot is generated in a CI node using the cores available in the node. The speedup factor may not scale as expected.","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"This page was generated using Literate.jl.","category":"page"}] +[{"location":"release_notes/#Release-Notes","page":"Release Notes","title":"Release Notes","text":"","category":"section"},{"location":"release_notes/#0.2.1-(18-11-2024)","page":"Release Notes","title":"0.2.1 (18-11-2024)","text":"","category":"section"},{"location":"release_notes/#New-functionality","page":"Release Notes","title":"New functionality","text":"","category":"section"},{"location":"release_notes/","page":"Release Notes","title":"Release Notes","text":"Adapted gen/build.jl to use the WrapIt.jl package instead of a locally installed wrapit ","category":"page"},{"location":"release_notes/#Fixes","page":"Release Notes","title":"Fixes","text":"","category":"section"},{"location":"release_notes/","page":"Release Notes","title":"Release Notes","text":"Removed Hist::operator+(double) to avoid #457\nDo not export cd to avoid warnings ","category":"page"},{"location":"release_notes/#0.2.0-(07-11-2024)","page":"Release Notes","title":"0.2.0 (07-11-2024)","text":"","category":"section"},{"location":"release_notes/#New-functionality-2","page":"Release Notes","title":"New functionality","text":"","category":"section"},{"location":"release_notes/","page":"Release Notes","title":"Release Notes","text":"Added 2 more new examples: main294.jl and main296.jl\nAdded a tutorial in the documentation using the module Literate.jl to generate markdown and notebook from single source.","category":"page"},{"location":"release_notes/#0.1.1-(03-10-2024)","page":"Release Notes","title":"0.1.1 (03-10-2024)","text":"","category":"section"},{"location":"release_notes/#Fixes-2","page":"Release Notes","title":"Fixes","text":"","category":"section"},{"location":"release_notes/","page":"Release Notes","title":"Release Notes","text":"Lower the version requirements InteractiveUtils.","category":"page"},{"location":"release_notes/#0.1.0-(25-09-2024)","page":"Release Notes","title":"0.1.0 (25-09-2024)","text":"","category":"section"},{"location":"release_notes/","page":"Release Notes","title":"Release Notes","text":"Initial release with basic functionality needed to run the first 3 examples.","category":"page"},{"location":"api/#Public-Documentation","page":"Public APIs","title":"Public Documentation","text":"","category":"section"},{"location":"api/","page":"Public APIs","title":"Public APIs","text":"Documentation for PYTHIA8.jl public interface.","category":"page"},{"location":"api/","page":"Public APIs","title":"Public APIs","text":"","category":"page"},{"location":"api/#Index","page":"Public APIs","title":"Index","text":"","category":"section"},{"location":"api/","page":"Public APIs","title":"Public APIs","text":"Pages = [\"api.md\"]\nModules = [PYTHIA8]\nOrder = [:type]","category":"page"},{"location":"api/","page":"Public APIs","title":"Public APIs","text":"Pages = [\"api.md\"]\nModules = [PYTHIA8, Base]\nOrder = [:function]","category":"page"},{"location":"api/","page":"Public APIs","title":"Public APIs","text":"","category":"page"},{"location":"api/#Types","page":"Public APIs","title":"Types","text":"","category":"section"},{"location":"api/","page":"Public APIs","title":"Public APIs","text":"This is the list of all types and functions defined for PYTHIA8","category":"page"},{"location":"api/","page":"Public APIs","title":"Public APIs","text":"Modules = [PYTHIA8]\nOrder = [:type]","category":"page"},{"location":"api/#Functions","page":"Public APIs","title":"Functions","text":"","category":"section"},{"location":"api/","page":"Public APIs","title":"Public APIs","text":"Modules = [PYTHIA8]\nOrder = [:function]","category":"page"},{"location":"api/#PYTHIA8.__init__-Tuple{Pythia8!UserHooks}","page":"Public APIs","title":"PYTHIA8.__init__","text":"__init__(self::UserHooks)\n\nThis function initializes the UserHooks object by creating an instance of Pythia8UserHooks and setting the hooks for each method defined by the user.\n\n\n\n\n\n","category":"method"},{"location":"#Julia-bindings-for-PYTHIA","page":"Introduction","title":"Julia bindings for PYTHIA","text":"","category":"section"},{"location":"#Description","page":"Introduction","title":"Description","text":"","category":"section"},{"location":"","page":"Introduction","title":"Introduction","text":"Julia bindings for the PYTHIA package used for generating high-energy physics collision events. It facilitates the interface with the PYTHIA client library, by writing Julia code instead of having to write C++ code. This package is developed using the CxxWrap.jl package to wrap C++ types and functions to Julia. Wrapper C++ code is generated with the help of WrapIt tool that uses of the clang library.","category":"page"},{"location":"","page":"Introduction","title":"Introduction","text":"The Julia interface has been inspired by the functionality provided by Pythia Python interface.","category":"page"},{"location":"#Installation","page":"Introduction","title":"Installation","text":"","category":"section"},{"location":"","page":"Introduction","title":"Introduction","text":"The PYTHIA8.jl package does no require any special installation. Stable releases are registered into the Julia general registry, and therefore can be deployed with the standard Pkg Julia package manager. This would bring automatically an installation of PYTHIA binary libraries as artifacts corresponding to the current platform (the so called _jll packages). Linux and MacOS operating systems with x86_64, powerpc64le and aarch64 architectures are supported. ","category":"page"},{"location":"","page":"Introduction","title":"Introduction","text":"julia> using Pkg\njulia> Pkg.add(\"PYTHIA8\")","category":"page"},{"location":"#Interface","page":"Introduction","title":"Interface","text":"","category":"section"},{"location":"","page":"Introduction","title":"Introduction","text":"Only the classes that provide the everyday functionality of PYTHIA have been wrapped. Additional classes and functionality can be included upon request. This is a summary of the currently provided classes:","category":"page"},{"location":"","page":"Introduction","title":"Introduction","text":"top level: Pythia, PythiaParallel\nevent access: Event, Particle, Vec4\nsettings and information: HIInfo, Settings, Flag, Mode, Parm, Word, FVec, MVec, PVec, WVec\nuser interface pointers: Rndm, PDF, DecayHandler, RndmEngine, UserHooks, MergingHooks, BeamShape, SigmaProcess, TimeShower, SpaceShower, HeavyIons\nanalysis tools: Hist, HistPlot, SlowJet\nkinematic functions: m, m2, dot3, cross3, cross4, theta, costheta, phi, cosphi, RRapPhi, REtaPhi","category":"page"},{"location":"#Limitations","page":"Introduction","title":"Limitations","text":"","category":"section"},{"location":"","page":"Introduction","title":"Introduction","text":"Methods returning or accepting as argument a std::map have not been wrapped since this is a current limitation of the CxxWrap package.","category":"page"},{"location":"#Getting-Started","page":"Introduction","title":"Getting Started","text":"","category":"section"},{"location":"","page":"Introduction","title":"Introduction","text":"Have a look at the following trivial interactive session:","category":"page"},{"location":"","page":"Introduction","title":"Introduction","text":"julia> using PYTHIA8\n\njulia> pythia = PYTHIA8.Pythia(\"\", false)\nPYTHIA8.Pythia8!PythiaAllocated(Ptr{Nothing} @0x00000001200d0000)\n\njulia> csets = pythia |> settings # which equivalent to csets = settings(pythia)\nCxxRef{Pythia8!Settings}(Ptr{Pythia8!Settings} @0x00000001200d02d0)\n\njulia> list(csets, \"Init:\")\n *------- PYTHIA Flag + Mode + Parm + Word + FVec + MVec + PVec + WVec Settings (with requested string) ----------* \n | | \n | Name | Now | Default Min Max | \n | | | | \n | Init:showAllParticleData | off | off | \n | Init:showAllSettings | off | off | \n | Init:showChangedParticleData | on | on | \n | Init:showChangedResonanceData | off | off | \n | Init:showChangedSettings | on | on | \n | Init:showMultipartonInteractions | on | on | \n | Init:showOneParticleData | 0 | 0 0 | \n | Init:showProcesses | on | on | \n | | \n *------- End PYTHIA Flag + Mode + Parm + Word + FVec + MVec + PVec + WVec Settings -----------------------------* \n julia> parm(csets, \"Beams:eA\")\n7000.0","category":"page"},{"location":"","page":"Introduction","title":"Introduction","text":"Note that class methods are called with the object instance as first argument. In C++ the parm(...) method of the Settings class would be called as csets.parm(\"Beams:eA\") being csets an instance of Settings, while in Julia it is called as parm(csets, \"Beams:eA\"). Thanks to the Julia multi-dispatch we do not need to prefix the methods with the module name PYTHIA8.parm, even for very common function names such as list.","category":"page"},{"location":"","page":"Introduction","title":"Introduction","text":"Also notice that the default printout of any wrapped object is of the form C++/Julia type(at some address @0xXXXXXX) like this one: CxxRef{Pythia8!Settings}(Ptr{Pythia8!Settings} @0x00000001200d02d0). In this particular example, the C++ type is a reference to Pythia8::Settings and the object is at the memory address 0x00000001200d02d0.","category":"page"},{"location":"#Examples","page":"Introduction","title":"Examples","text":"","category":"section"},{"location":"","page":"Introduction","title":"Introduction","text":"Currently the following examples are implemented replicating the equivalent Python ones.","category":"page"},{"location":"#[main291.jl](https://github.com/JuliaHEP/PYTHIA8.jl/blob/main/examples/main291.jl)","page":"Introduction","title":"main291.jl","text":"","category":"section"},{"location":"","page":"Introduction","title":"Introduction","text":"Simple test program, equivalent to main101.cc, but written in Julia. It fits on one slide in a talk. It studies the charged multiplicity distribution at the LHC.","category":"page"},{"location":"#[main292.jl](https://github.com/JuliaHEP/PYTHIA8.jl/blob/main/examples/main292.jl)","page":"Introduction","title":"main292.jl","text":"","category":"section"},{"location":"","page":"Introduction","title":"Introduction","text":"Simple test program to illustrate the usage of PythiaParallel in Julia. The physics case is equivalent to main291, but in parallel.","category":"page"},{"location":"#[main293.jl](https://github.com/JuliaHEP/PYTHIA8.jl/blob/main/examples/main293.jl)","page":"Introduction","title":"main293.jl","text":"","category":"section"},{"location":"","page":"Introduction","title":"Introduction","text":"Example how you can use UserHooks to trace pT spectrum through the program, and veto undesirable jet multiplicities. It is based on main242.cc.","category":"page"},{"location":"#[main294.jl](https://github.com/JuliaHEP/PYTHIA8.jl/blob/main/examples/main294.jl)","page":"Introduction","title":"main294.jl","text":"","category":"section"},{"location":"","page":"Introduction","title":"Introduction","text":"Simple Julia script which reads and parses the Pythia 8 particle database (XML format), without requiring the Pythia8 Julia bindings.","category":"page"},{"location":"#[main296.jl](https://github.com/JuliaHEP/PYTHIA8.jl/blob/main/examples/main296.jl)","page":"Introduction","title":"main296.jl","text":"","category":"section"},{"location":"","page":"Introduction","title":"Introduction","text":"Example of how PYTHIA can be used as a shared library from Julia. The physics case is a study of total cross sections, which are not exposed currently cannot through the normal Julia interface.","category":"page"},{"location":"#Tests","page":"Introduction","title":"Tests","text":"","category":"section"},{"location":"","page":"Introduction","title":"Introduction","text":"Unit tests can be run with julia --project=. test/runtests.jl. It runs in addition all the implemented examples to ensure their correct execution and detection of any regression.","category":"page"},{"location":"#Re-generating-wrapper-code","page":"Introduction","title":"Re-generating wrapper code","text":"","category":"section"},{"location":"","page":"Introduction","title":"Introduction","text":"It is possible to re-generate the wrapper library locally instead of using the registered Pythia8_cxxwrap_jll package. This can be useful for adding new classes or functions to the wrapper library. These are the instructions:","category":"page"},{"location":"","page":"Introduction","title":"Introduction","text":"The configuration file gen/Pythia8.wit.in is the input to the automated process. New header files can be added to the input list.\nThe script gen/build.jl does the work of generating the code and building the library. The command to execute are:\njulia --project=gen -e 'import Pkg; Pkg.instantiate()'\njulia --project=gen gen/build.jl","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"EditURL = \"tutorial_lit.jl\"","category":"page"},{"location":"tutorial/#PYTHIA8.jl-Tutorial","page":"Tutorial","title":"PYTHIA8.jl Tutorial","text":"","category":"section"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"This turorial demostrates a simple application of PYTHIA8.jl to generate some events and present the results in form of histograms. Please refer to the PYTHIA documentation for more details on the parameters and the physics processes.","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"note: Note that\nYou can also download this tutorial as a Jupyter notebook and a plain Julia source file.","category":"page"},{"location":"tutorial/#Table-of-contents","page":"Tutorial","title":"Table of contents","text":"","category":"section"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"Pages = [\"tutorial.md\"]\nDepth = 2:3","category":"page"},{"location":"tutorial/#Generate-events-and-plot-the-charged-multiplicity-distribution","page":"Tutorial","title":"Generate events and plot the charged multiplicity distribution","text":"","category":"section"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"In this example, we will generate 200 events at a center of mass energy of 8000 GeV and plot the charged multiplicity distribution.","category":"page"},{"location":"tutorial/#Loading-the-necessary-modules","page":"Tutorial","title":"Loading the necessary modules","text":"","category":"section"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"We will use the PYTHIA8 module to run the event generation.\nWe will use the FHist module to create the histograms.\nWe will use the Plots module to plot them.","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"If these modules are not installed, you can install them by running the following commands:","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"using Pkg\nPkg.add(\"PYTHIA8\")\nPkg.add(\"FHist\")\nPkg.add(\"Plots\")","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"using PYTHIA8\nusing FHist\nusing Plots: plot, plot!, theme\nimport DisplayAs: PNG #hide","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"Define the theme for the plots. See available themes and attributes in Plots.jl.","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"theme(:wong2, frame=:box, grid=false, minorticks=true,\n guidefontvalign=:top, guidefonthalign=:right,\n xlims=(:auto, :auto), lw=1.2, lab=\"\", colorbar=false)","category":"page"},{"location":"tutorial/#Initialize-PYTHIA","page":"Tutorial","title":"Initialize PYTHIA","text":"","category":"section"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"We will initialize PYTHIA with the following settings:","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"Center of mass energy of the collision: 8000 GeV\nAll hard QCD processes are enabled\nMinimum transverse momentum of the hard process: 20 GeV","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"We use the operator << to set the parameters and the operator |> to pipe to the init function. This added interface is quite ergonomic, however alternatively, we can use the functions directly provided by C++ interface, e.g.,","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"readString(pythia, \"Beams:eCM = 8000.\") or\nparm( settings(pythia), \"Beams:eCM\", 8000.)","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"pythia = PYTHIA8.Pythia(\"\", false);\npythia << \"Beams:eCM = 8000.\" <<\n \"HardQCD:all = on\" <<\n \"PhaseSpace:pTHatMin = 20.\";\nnothing #hide","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"The purpose of the next two lines is to reduce the amount of output during the event generation","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"pythia << \"Next:numberShowEvent = 0\" <<\n \"Next:numberShowProcess = 0\";\nnothing #hide","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"Initialize the event generation. The init function returns a boolean value. Alternatively, we can use the direct call to init(pythia) function.","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"pythia |> init;\nnothing #hide","category":"page"},{"location":"tutorial/#Create-a-1D-histogram","page":"Tutorial","title":"Create a 1D histogram","text":"","category":"section"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"We will create a histogram to store the charged multiplicity distribution. The histogram is defined with 25 bins ranging from -0.5 to 499.5. Note that with ranges we need to specify the number of edges (26) and not the number of bins (25)","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"mult = Hist1D(binedges=range(-0.5, 499.5, 26));\nnothing #hide","category":"page"},{"location":"tutorial/#Generate-events","page":"Tutorial","title":"Generate events","text":"","category":"section"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"We will generate 200 events and fill the histogram with the number of charged particles making use of the count function. We use the push! function to fill the histogram.","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"for iEvent in 1:200\n pythia |> next || continue\n # Find number of all final charged particles and fill histogram.\n nCharged = count(p -> isFinal(p) && isCharged(p), pythia |> event)\n push!(mult, nCharged)\nend","category":"page"},{"location":"tutorial/#Print-the-statistics-and-plot-charged-multiplicity-distribution","page":"Tutorial","title":"Print the statistics and plot charged multiplicity distribution","text":"","category":"section"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"Print the generation statistics using the stat function from PYTHIA.","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"pythia |> PYTHIA8.stat","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"Plot the histogram with the plot function from Plots.jl. We use the stepbins seriestype to plot the histogram as a step plot.","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"img = plot(mult, xlabel=\"counts\", ylabel=\"#particles\",\n title=\"Charged Multiplicity Distribution\", seriestype=:stepbins,\n c=2, lc=1, fill=0);\ndisplay(img)\nPNG(img) #hide","category":"page"},{"location":"tutorial/#Multi-threaded-version-of-the-same-example","page":"Tutorial","title":"Multi-threaded version of the same example","text":"","category":"section"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"In this example we repeat the same example as before but using multi-threading to generate the events faster.","category":"page"},{"location":"tutorial/#Initialize-Parallel-PYTHIA","page":"Tutorial","title":"Initialize Parallel PYTHIA","text":"","category":"section"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"Create an instance of PythiaParallel with the same settings as before. The PythiaParallel class is used to generate events concurrently using multiple threads. The settings are copied for each Pythia instance.","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"pythia_mt = PYTHIA8.PythiaParallel(\"\", false);\nnothing #hide","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"PythiaParallel reads settings the same way as the normal Pythia does.","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"pythia_mt << \"Beams:eCM = 8000.\" <<\n \"HardQCD:all = on\" <<\n \"PhaseSpace:pTHatMin = 20.\";\nnothing #hide","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"The maximum degree of parallelism. If set to 0 (default), the program will use the maximum number of threads supported by the hardware.","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"pythia_mt << \"Parallelism:numThreads = 4\";\nnothing #hide","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"This defines the number of events generated by PythiaParallel::run.","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"pythia_mt << \"Main:numberOfEvents = 200\";\nnothing #hide","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"The next is a user provided function to initialize each underlying Pythia instance (one per thread). Please note that the function is called concurrently, therefore the use needs to avoid calling thread-unsafe functions. This is the reason we use the Core.println function instead of println.","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"function w_init(pythia)::CxxBool\n Core.println(\"Initializing Pythia with index $(mode(pythia |> settings, \"Parallelism:index\")).\")\n return pythia |> init\nend;\nnothing #hide","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"Initialize the event generation. The init function returns a boolean value to indicate the success. The user provided function w_init is called for each thread.","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"init(pythia_mt, w_init);\nnothing #hide","category":"page"},{"location":"tutorial/#Create-a-1D-histogram-2","page":"Tutorial","title":"Create a 1D histogram","text":"","category":"section"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"mult_mt = Hist1D(binedges=range(-0.5, 499.5, 26));\nnothing #hide","category":"page"},{"location":"tutorial/#Generate-events-2","page":"Tutorial","title":"Generate events","text":"","category":"section"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"Define a user function that will be called for each generated event. This function will be called concurrently by the threads. Therefore, we need to use thread-safe functions. Filling the histogram with atomic_push is thread-safe. The function takes a Pythia object as the first argument and returns Nothing.","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"function analyze(pythiaNow)::Nothing\n nCharged = count(p -> isFinal(p) && isCharged(p), pythiaNow |> event)\n atomic_push!(mult_mt, nCharged)\n return\nend","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"Run the event generation. The number of events has been set in the Main:numberOfEvents parameter.","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"PYTHIA8.run(pythia_mt, analyze);\nnothing #hide","category":"page"},{"location":"tutorial/#Print-the-statistics-and-plot-charged-multiplicity-distribution-2","page":"Tutorial","title":"Print the statistics and plot charged multiplicity distribution","text":"","category":"section"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"Print the statistics using the stat function from PYTHIA8.jl","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"pythia_mt |> PYTHIA8.stat","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"Plot the result histogram","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"img = plot(mult_mt, xlabel=\"counts\", ylabel=\"#particles\",\n title=\"Charged Multiplicity Distribution (multi-threaded)\", seriestype=:stepbins,\n c=2, lc=1, fill=0);\ndisplay(img)\nPNG(img) #hide","category":"page"},{"location":"tutorial/#Speed-comparison-between-the-two-versions","page":"Tutorial","title":"Speed comparison between the two versions","text":"","category":"section"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"We will generate 1000 events and compare the time taken by the two versions.","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"empty!(mult);\nempty!(mult_mt);\nnothing #hide","category":"page"},{"location":"tutorial/#Single-threaded-version","page":"Tutorial","title":"Single-threaded version","text":"","category":"section"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"Generate 1000 events and measure the time taken. This is done using the @elapsed macro.","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"st_elap = @elapsed begin\n for iEvent in 1:1000\n pythia |> next || continue\n nCharged = count(p -> isFinal(p) && isCharged(p), pythia |> event)\n atomic_push!(mult, nCharged)\n end\nend","category":"page"},{"location":"tutorial/#Multi-threaded-version","page":"Tutorial","title":"Multi-threaded version","text":"","category":"section"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"Generate 1000 events and measure the time taken. This is done using the @elapsed macro.","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"pythia_mt << \"Main:numberOfEvents = 1000\"\nmt_elap = @elapsed begin\n PYTHIA8.run(pythia_mt, analyze)\nend","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"Print the speedup factor (4 threads)","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"println(\"Speedup is $(st_elap/mt_elap).\")","category":"page"},{"location":"tutorial/#Make-a-plot-of-the-speedup-factor","page":"Tutorial","title":"Make a plot of the speedup factor","text":"","category":"section"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"We will generate events using 1, 2, 4, 8, 16, and 32 threads and plot the speedup factor. The speedup factor is defined as the ratio of the time taken by the single-threaded version to the time taken by the multi-threaded version.","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"n_threads = [2^n for n in 0:5]\nspeedup = Float64[]\nw_init(pythiaNow)::CxxBool = pythiaNow |> init\nfor n_thread in n_threads\n empty!(mult_mt)\n _pythia = PYTHIA8.PythiaParallel(\"\", false);\n _pythia << \"Beams:eCM = 8000.\" <<\n \"HardQCD:all = on\" <<\n \"PhaseSpace:pTHatMin = 20.\" <<\n \"Parallelism:numThreads = $n_thread\" <<\n \"Main:numberOfEvents = 1000\"\n init(_pythia, w_init)\n\n elap = @elapsed begin\n PYTHIA8.run(_pythia, analyze)\n end\n push!(speedup, st_elap/elap)\nend;\nnothing #hide","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"Plot the speedup factor vs the number of threads and overlay the line for the ideal scaling.","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"img = plot(n_threads, speedup, xlabel=\"#threads\", ylabel=\"speedup\",\n title=\"Speedup vs #threads (#cores = $(Sys.CPU_THREADS))\",\n seriestype=:scatter, legend=false,\n xscale=:log10, yscale=:log10);\nnothing #hide","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"Add a line to the same plot","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"plot!(n_threads, n_threads, seriestype=:line)\ndisplay(img)\nPNG(img) #hide","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"note: Note that\nThe plot is generated in a CI node using the cores available in the node. The speedup factor may not scale as expected.","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"This page was generated using Literate.jl.","category":"page"}] } diff --git a/dev/tutorial/a07d98b0.png b/dev/tutorial/a07d98b0.png deleted file mode 100644 index b2384ca4c8d61ad653518b664992d8d72e884628..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 23281 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 D&omSM literal 0 HcmV?d00001 diff --git a/dev/tutorial/index.html b/dev/tutorial/index.html index 6950e17..3305749 100644 --- a/dev/tutorial/index.html +++ b/dev/tutorial/index.html @@ -123,10 +123,7 @@ "PhaseSpace:pTHatMin = 20.";

The maximum degree of parallelism. If set to 0 (default), the program will use the maximum number of threads supported by the hardware.

pythia_mt << "Parallelism:numThreads = 4";

This defines the number of events generated by PythiaParallel::run.

pythia_mt << "Main:numberOfEvents = 200";

The next is a user provided function to initialize each underlying Pythia instance (one per thread). Please note that the function is called concurrently, therefore the use needs to avoid calling thread-unsafe functions. This is the reason we use the Core.println function instead of println.

function w_init(pythia)::CxxBool
     Core.println("Initializing Pythia with index $(mode(pythia |> settings, "Parallelism:index")).")
     return pythia |> init
-end;

Initialize the event generation. The init function returns a boolean value to indicate the success. The user provided function w_init is called for each thread.

init(pythia_mt, w_init);
Initializing Pythia with index 2.Initializing Pythia with index 1.Initializing Pythia with index 3.
-
-
-Initializing Pythia with index 0.

Create a 1D histogram

mult_mt = Hist1D(binedges=range(-0.5, 499.5, 26));

Generate events

Define a user function that will be called for each generated event. This function will be called concurrently by the threads. Therefore, we need to use thread-safe functions. Filling the histogram with atomic_push is thread-safe. The function takes a Pythia object as the first argument and returns Nothing.

function analyze(pythiaNow)::Nothing
+end;

Initialize the event generation. The init function returns a boolean value to indicate the success. The user provided function w_init is called for each thread.

init(pythia_mt, w_init);
Initializing Pythia with index 3.Initializing Pythia with index 1.Initializing Pythia with index 0.Initializing Pythia with index 2.

Create a 1D histogram

mult_mt = Hist1D(binedges=range(-0.5, 499.5, 26));

Generate events

Define a user function that will be called for each generated event. This function will be called concurrently by the threads. Therefore, we need to use thread-safe functions. Filling the histogram with atomic_push is thread-safe. The function takes a Pythia object as the first argument and returns Nothing.

function analyze(pythiaNow)::Nothing
     nCharged = count(p -> isFinal(p) && isCharged(p), pythiaNow |> event)
     atomic_push!(mult_mt, nCharged)
     return
@@ -149,10 +146,10 @@
         nCharged = count(p -> isFinal(p) && isCharged(p), pythia |> event)
         atomic_push!(mult, nCharged)
     end
-end
4.764615183

Multi-threaded version

Generate 1000 events and measure the time taken. This is done using the @elapsed macro.

pythia_mt << "Main:numberOfEvents = 1000"
+end
4.752427345

Multi-threaded version

Generate 1000 events and measure the time taken. This is done using the @elapsed macro.

pythia_mt << "Main:numberOfEvents = 1000"
 mt_elap = @elapsed begin
     PYTHIA8.run(pythia_mt, analyze)
-end
1.979248502

Print the speedup factor (4 threads)

println("Speedup is $(st_elap/mt_elap).")
Speedup is 2.40728497618436.

Make a plot of the speedup factor

We will generate events using 1, 2, 4, 8, 16, and 32 threads and plot the speedup factor. The speedup factor is defined as the ratio of the time taken by the single-threaded version to the time taken by the multi-threaded version.

n_threads = [2^n for n in 0:5]
+end
1.974015419

Print the speedup factor (4 threads)

println("Speedup is $(st_elap/mt_elap).")
Speedup is 2.4074925146266044.

Make a plot of the speedup factor

We will generate events using 1, 2, 4, 8, 16, and 32 threads and plot the speedup factor. The speedup factor is defined as the ratio of the time taken by the single-threaded version to the time taken by the multi-threaded version.

n_threads = [2^n for n in 0:5]
 speedup = Float64[]
 w_init(pythiaNow)::CxxBool = pythiaNow |> init
 for n_thread in n_threads
@@ -173,4 +170,4 @@
            title="Speedup vs #threads (#cores = $(Sys.CPU_THREADS))",
            seriestype=:scatter, legend=false,
            xscale=:log10, yscale=:log10);

Add a line to the same plot

plot!(n_threads, n_threads, seriestype=:line)
-display(img)
Example block output
Note that

The plot is generated in a CI node using the cores available in the node. The speedup factor may not scale as expected.


This page was generated using Literate.jl.

+display(img)Example block output
Note that

The plot is generated in a CI node using the cores available in the node. The speedup factor may not scale as expected.


This page was generated using Literate.jl.