Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding the functionality to include analysis specific c++ code to CROWN #288

Merged
merged 10 commits into from
Jan 16, 2025
9 changes: 8 additions & 1 deletion cmake/ConfigureCrownlib.cmake
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
# build a shared lib from all CROWN functions
include_directories(${CMAKE_SOURCE_DIR}/src)
include_directories(${CMAKE_SOURCE_DIR}/include)

include_directories(${CMAKE_SOURCE_DIR}/analysis_configurations/${ANALYSIS}/cpp_addons/src)
include_directories(${CMAKE_SOURCE_DIR}/analysis_configurations/${ANALYSIS}/cpp_addons/include)

file(GLOB SOURCES_1 ${CMAKE_SOURCE_DIR}/src/*.cxx)
file(GLOB SOURCES_2 ${CMAKE_SOURCE_DIR}/src/utility/*.cxx
${CMAKE_SOURCE_DIR}/src/RecoilCorrections/*.cxx
${CMAKE_SOURCE_DIR}/src/SVFit/*.cxx)
set(SOURCES ${SOURCES_1} ${SOURCES_2})

file(GLOB SOURCES_3 ${CMAKE_SOURCE_DIR}/analysis_configurations/${ANALYSIS}/cpp_addons/src/*.cxx)

set(SOURCES ${SOURCES_1} ${SOURCES_2} ${SOURCES_3})

if(BUILD_CROWNLIB_ONLY)
message(STATUS "Building only the CROWNLIB library")
Expand Down
2 changes: 2 additions & 0 deletions code_generation/analysis_template.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
#include "include/triggers.hxx"
#include "include/fakefactors.hxx"

// {INCLUDE_ANALYSISADDONS}

// {INCLUDES}

int main(int argc, char *argv[]) {
Expand Down
3 changes: 3 additions & 0 deletions code_generation/analysis_template_friends.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@
#include "include/topreco.hxx"
#include "include/triggers.hxx"
#include "include/tripleselection.hxx"

// {INCLUDE_ANALYSISADDONS}

// {INCLUDES}

int validate_rootfile(std::string file, std::string &basetree) {
Expand Down
41 changes: 39 additions & 2 deletions code_generation/code_generation.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,32 @@
log = logging.getLogger(__name__)


def addon_includes(analysis_name: str, file_name: str) -> str:
"""
Add the includes all .hxx files from analysis configuration folder:
analysis_configurations/{analysis_name}/cpp_addons/include
Args:
analysis_name: the name of the analysis
file_name: Name of file that is templated
Returns:
str - the include statements for the cpp addons
"""
path = f"analysis_configurations/{analysis_name}/cpp_addons/include"
if os.path.exists(path) and os.path.isdir(path) and os.listdir(path):
log.debug(
f"Adding addons from {path} to {file_name}: {' '.join(os.listdir(path))}"
)
paths = "\n".join(
f'#include "{os.path.abspath(os.path.join(path, item))}"'
for item in os.listdir(path)
if item.endswith(".hxx")
)
return paths
else:
log.debug(f"No addons found in {path}")
return ""


class CodeSubset(object):
"""
Class used to generate code for a smaller subset. For each subset, a new object must be created.
Expand All @@ -24,6 +50,7 @@ class CodeSubset(object):
folder: The folder in which the code will be generated.
parameters: The parameters to be used for the generation.
name: The name of the code subset.
analysis_name: Name of the analysis configuration.

Returns:
None
Expand All @@ -38,6 +65,7 @@ def __init__(
folder: str,
configuration_parameters: Dict[str, Any],
name: str,
analysis_name: str,
):
self.file_name = file_name
self.template = template
Expand All @@ -48,6 +76,7 @@ def __init__(
self.count = 0
self.folder = folder
self.commands: List[str] = []
self.analysis_name = analysis_name
self.headerfile = os.path.join(
self.folder, "include", self.scope, "{}.hxx".format(self.file_name)
)
Expand Down Expand Up @@ -120,8 +149,11 @@ def write(self):
with open(self.sourcefile + ".new", "w") as f:
commandstring = "".join(self.commands)
f.write(
self.template.replace("// { commands }", commandstring).replace(
"{subsetname}", self.name
self.template.replace("// { commands }", commandstring)
.replace("{subsetname}", self.name)
.replace(
"// {INCLUDE_ANALYSISADDONS}",
addon_includes(self.analysis_name, self.file_name),
)
)
if os.path.isfile(self.sourcefile):
Expand Down Expand Up @@ -350,6 +382,10 @@ def write_code(self, calls: str, includes: str, run_commands: str) -> None:
" // {ZERO_EVENTS_FALLBACK}", self.zero_events_fallback()
)
.replace(" // {CODE_GENERATION}", calls)
.replace(
"// {INCLUDE_ANALYSISADDONS}",
addon_includes(self.analysis_name, self.executable_name + ".cxx"),
)
.replace("// {INCLUDES}", includes)
.replace(" // {RUN_COMMANDS}", run_commands)
.replace("// {MULTITHREADING}", threadcall)
Expand Down Expand Up @@ -458,6 +494,7 @@ def generate_subsets(self, scope: str) -> None:
),
configuration_parameters=self.configuration.config_parameters[scope],
name=producer_name + "_" + scope,
analysis_name=self.analysis_name,
)
subset.create()
subset.write()
Expand Down
3 changes: 3 additions & 0 deletions code_generation/subset_template.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@
#include "include/topreco.hxx"
#include "include/triggers.hxx"
#include "include/fakefactors.hxx"

// {INCLUDE_ANALYSISADDONS}

ROOT::RDF::RNode {subsetname} (ROOT::RDF::RNode df0, OnnxSessionManager &onnxSessionManager, correctionManager::CorrectionManager &correctionManager) {

// { commands }
Expand Down
61 changes: 61 additions & 0 deletions docs/sphinx_source/cpp_addons.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
C++ Add-ons
==========

In some cases, the core codebase of CROWN (CROWNLIB) may not include all the features required for an analysis. To address this, users can add custom C++ code within their analysis configurations. These add-ons are automatically integrated to the C++ code during the code generation process.

Location and directory structure
--------------------------------

The expected structure within the analysis configuration is as follows:

.. code-block:: console

analysis_configurations
└── <analysis>
└── cpp_addons
├── include
│ ├── <file1>.hxx
│ ├── <file2>.hxx
│ └── ...
└── src
├── <file1>.cxx
├── <file2>.cxx
└── ...


If an analysis does not require any additional C++ code and can rely solely on CROWNLIB, the ``cpp_addons`` folder can be omitted entirely from the analysis configuration.

``.cxx`` and ``.hxx`` File structure
------------------------------------

This functionality considers files in ``analysis_configuration/<analysis>/cpp_addons/src`` and ``analysis_configuration/<analysis>/cpp_addons/include`` during compilation. The following points should be followed when adding and using custom C++ code:

* Use unique guards for each ``.cxx`` file you introduce, especially concerning CROWNLIB. For the corresponding ``.hxx`` file(s), the same unique guard(s) should be applied.
* Use a unique function name or function signature if the custom function needs to reside in a namespace that already exists in CROWNLIB
* Use ``../../../../include/<filename>.hxx`` if you explicitly want to import functionality from CROWNLIB. Importing CROWNLIB files using different relative paths can lead to unexpected behavior.

A example ``.cxx`` file could have the following structure:


.. code-block:: cpp

#ifndef UNIQUE_GUARD_NAME_H // unique w.r.t. CROWNLIB and other files in cpp_addons
#define UNIQUE_GUARD_NAME_H

// Include CROWNLIB functionalities
#include "../../../../include/utility/CorrectionManager.hxx"
#include "../../../../include/utility/Logger.hxx"

// Feature.hxx file defined in cpp_addons
#include "../Feature.hxx"

// Globally present, i.e. from the ROOT framework
#include "ROOT/RDataFrame.hxx"
#include "correction.h"

/* Your code here */

// End of the file
#endif // UNIQUE_GUARD_NAME_H


1 change: 1 addition & 0 deletions docs/sphinx_source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ Documentation
contrib.rst
py_configuration.rst
correction_manager.rst
cpp_addons.rst

.. toctree::
:maxdepth: 2
Expand Down