7. Assumed Usage¶
It might be obvious or not, but the NA64sw is designed having certain workflow organisations in mind. Taking into account the variety of possible usage scenarios this is a flexible framework that can facilitate analysis needs by its own, or be used as embeddable library or extension in both cases providing software piecies in a granular manner.
In this chapter we describe some (pretty common and generic) tips of C/C++ project organization followed by an example of performing detector study. For instance, we consider ECAL calibration based on LMS method based on pseudo-inversion matrix with SVD decomposition done by standalone tool in the environment providing external build of NA64sw (e.g. on CVMFS share at CERN LXPLUS). We use NA64sw to read the data.
7.1. Setting up the project¶
The project we consider here is maintained as standalone repository. In most
cases this is a preferable usage scenario as user should not deal with NA64sw
codebase and can focus on their particular problem. So, assume that NA64sw
installation exists on the target host (on CERN LXPLUS it is one of the
deployments at /cvmfs/na64.cern.ch/sft/
share).
7.2. Structuring the project¶
NA64sw does not assume any particular structure of user’s projects and gets
linked just as any other library sets. The only restriction made by
co-operating/using NA64sw might come from the fact that all the extensions are
loaded from shared libraries (.so
), so one should consider linking their
code in the library at some point.
For those who are not familiar with how typical C/C++ project is organized, the general tips are:
Keep implementation files in
src/
and headers ininclude/
dirsFor C projects provide a distinguishable naming suffix for headers, as it may be required at some point of the project’s lifecycle to install the headers in system-wide dir (we choose
ecp-*.h
in the example below). Alternatively, it can be a dir instead of naming suffix.Provide a
Makefile
(or maintain aCMakeLists.txt
to automatize the builds), keep/maintain dependency managementKeep reasonable logical splitting between software components in your repo. It is a nice practice to explicitly mark your executables within the structure by
Main*.cc
suffix/creating a dedicated dir for it.
You might want to split NA64sw-related entities from the main
project structure: to make a dedicated source files for handlers (by using name
suffix or dedicated folder) and data source class(es). But this is just an
optional recommendation as soon as them are compiled and linked into .so
file.
7.3. Config files¶
Besides of run-configs (ones provided to pipeline app with -r,--run
option
argument) and data source configs (provided with -i,--input-cfg
)
NA64sw facilitates logging system and variety of calibration utils that can be
(re)configured in one way or another. The logging system is configured with
config file that is provided by option -J,--log-cfg
, the calibration
config can be overriden by -c,--calibrations
. User may want to customize
these files slightly and keep it within their projects repo.
7.4. C/C++ Integration¶
In general, no special remarks needed to integrate your project with NA64sw as it is organized as most of the C/C++ distributions. Below are some tips for the integration.
7.4.1. Project location¶
Let us start with creating a repo folder for new project. This is not required, but users often may want to maintain their projects by certain VCS.
$ mkdir ~/projects/ecal-calibration
$ cd ~/projects/ecal-calibration
We created a folder for the project. You may want to init a git repo in this folder or use your existing project. The structure of project is arbitrary and shall suit your needs.
Next, you may want to:
use pipeline application to produce some data with your data treatment requirements,
create custom handlers and sources to write your own low-level additions to data analysis,
write a custom extension based on the pipeline that is capable to steer the pipeline execution.
For 1st scenario of simply running the pipeline, you would like to just store
the .yaml
run-configs somewhere and that’s it. Structure the config files
at your convenience.
For the rest of scenarios one should consider making some sort of C/C++
project. Typical project organization structure (often proposed by modern IDEs)
is totally fine with NA64sw with latter being a just one another dependency.
All the linkage information is accessible via pkg-config
, an easy to-use
(but rather simplistic) dependency-management tool provided by most UNIXes.
Below are two cases, a simple Makefile
, and snippet for CMake.
7.4.2. Makefile or plain shell script¶
Basic utilization within the plain shell invocation was considered in chapter
devoted to custom handlers writing. It is enough to append your build flags
with ones provided by pkg-config na64sw --cflags --libs
command once
proper environment is activated (as was described in first chapter). For
multifile projects one may consider modifying rules with something like:
obj/my-src.o: src/my-src.cc
g++ $(shell pkg-config na64sw --cflags --libs) $(CXXFLAGS) -fPIC src/my-src.cc -o obj/my-src.o
One may also consider project-wide linking by appendind CXXFLAGS
with
the result of $(shell pkg-config na64sw --cflags --libs)
command.
Note, that to link a library then a rule must be declared. Something like:
libMyHandlers.so: obj/myHandlerOne.o obj/myHandlerTwo.o
g++ obj/myHandlerOne.o obj/myHandlerTwo.o -shared -o libMyHandlers.so
The resulting lib libMyHandlers.so
then becomes an extension module that
can be utilized from within a pipeline app by -m
.
7.4.3. CMake¶
For CMake consider the following snippet:
# Include this to use `pkg_search_module()':
find_package(PkgConfig)
# Resolve na64sw package variables (it will be prefixed with PC_na64sw_...):
pkg_search_module(PC_na64sw REQUIRED na64sw)
# Resolve `na64app` library and `na64sw-config.h` header as hints for
# library and include dirs
find_path( NA64SW_INCLUDE_DIR
NAMES na64sw-config.h
PATHS ${PC_na64sw_INCLUDE_DIRS} )
find_library( NA64SW_LIB
NAMES na64app
PATHS ${PC_na64sw_LIBRARY_DIRS} )
# Use native CMake `find_package_handle_standard_args()' function to handle
# includes, libraries and stuff:
find_package_handle_standard_args(na64sw
REQUIRED_VARS ${NA64SW_LIB} ${NA64SW_INCLUDE_DIR})
Notes:
If you would like to have NA64sw as an optional dependency, consider using
pkg_check_modules()
instead ofpkg_search_module()
.If linking versus
na64app
lib is redundant (might be true in some fine-grained and esoteric cases) consider elaborating the snippet above to look for only the needed libraries of the framework.
It is planned (but is not a topmost priority so far) to have a generated CMake module with namespaces in a future.
Starting from scratch, one may use a simple Makefile
and then migrate to
CMake, as the project gets elaborated.
7.5. Example¶
Let us consider a project consisting of:
A handler for the pipeline that does a pretty simple job of generating binary files with excerpt of event data (maximum amplitude and sum of SADC hit raw data);
A solver application capable to read the binary data and perform the computation. It does not rely on any NA64sw routines, however it expects the data to be provided in certain format;
A generator application that provides some sort of random data to test the solver (in certain format);
A set of BASH scripts and HTCondor submission files to perform multi-staged calibration procedure on set of files
A
Makefile
that builds the project.
First, let’s make a usual C/C++ project, to put emphasis then on the differences that might be introduced by NA64sw framework integration.
7.5.1. An ordinary C/C++ boilerplate project example¶
In the newly created project dir we start by creating somewhat standard structure for C/C++ projects:
$ mkdir src include obj
$ touch Makefile
One can initialize a Git repo, add a README
file and so on.
Then we write a MC-generator and solver applications which have certain shared functionality (both apps are using the same data exchange codec). We also place entry points for both applications in the project’s root, so overall project source distribution looks like:
$ tree
.
├── include/
│ ├── ecp-cell.h
│ ├── ecp-cumulist.h
│ ├── ecp-file.h
│ ├── ecp-generator.h
│ ├── ecp-solver.h
│ └── ecp-types.h
├── main-generator.c
├── main-resolve.c
├── Makefile
├── obj/
└── src/
├── cell.c
├── cumulist.c
├── solver.c
├── solver-lms.c
├── file.c
├── generator.c
└── profile-types.c
The corresponding Makefile
that builds both applications sharing some
common object files (one can build a shared library from it, but for our little
project this seems to be redundant):
CFLAGS+=-Wall -g -ggdb -Iinclude/ -Wfatal-errors $(shell gsl-config --cflags)
all: ecp-generate ecp-resolve
obj/main-%.o: main-%.c
$(CC) -x c $(CFLAGS) -c -o $@ $^
obj/%.o: src/%.c
$(CC) -x c $(CFLAGS) -c -o $@ $^
exec/ecp-generate: obj/generator.o \
obj/cell.o \
obj/file.o \
obj/main-generator.o
g++ -o $@ $^ -lgsl
exec/ecp-resolve: obj/generator.o \
obj/cell.o \
obj/file.o \
obj/cumulist.o \
obj/solver.o \
obj/solver-lms.o \
obj/main-resolve.o
g++ $(shell gsl-config --libs) -o $@ $^
clean:
rm -f obj/*
rm -f exec/ecp-generate exec/ecp-resolve
.PHONY: all clean
This is quite ordinary Makefile
building two applications linked with GSL
library, with clean target. The executables are placed into exec/
dir and
there is no install
target meaning that executables are supposed to run
from within project’s root dir – a typical cheak’n’dirty project for
fast scientific calculus.
7.5.2. Adding NA64sw handler¶
We create a directory specific for NA64sw-related configs:
$ mkdir na64sw-run
and put there some .yaml
files corresponding to different stages of
calibration procedure.
We also add a loadable handler module into src/
(but one may prefer another
location) by adding a following target to a Makefile
:
libSADCDumpMaxAndSum.so: src/handler-SADCDump.cc
g++ $^ $(shell pkg-config na64sw --cflags --libs) -shared -fPIC -o $@
… (to be continued)