Software for Liquid Argon time projection chambers
LArSoft architecture review on 2015 affected most of the “core” services.
Main changes are summarized in the table of contents:
The old pattern: use the service directly, e.g.:
art::ServiceHandle<util::DetectorProperties> detprop;
double sampling_rate = detprop->SamplingRate();
The new pattern: obtain the service provider first, and use that one, e.g.:
detinfo::DetectorProperties const* detprop
= lar::providerFrom<detinfo::DetectorPropertiesService>();
double sampling_rate = detprop->SamplingRate();
lar::providerFrom<>
is defined in larcore/CoreUtils/ServiceUtil.h
.
The first line can be also written as:
detinfo::DetectorPropertiesService::provider_type const* detprop
= lar::providerFrom<detinfo::DetectorPropertiesService>();
if you don’t want to bother to learn the real provider name, or:
auto const* detprop = lar::providerFrom<detinfo::DetectorPropertiesService>();
The service provider pointer should be obtained anew on each new event rather than saved in the module. In the module/algorithm factorization model, the algorithm should store the pointer and the module should update the algorithm pointer on each event.
Important: you will not be able any more to access methods from the service:
art::ServiceHandle<detinfo::DetectorPropertiesService>()->Efield()
will not work.
As described above, you can’t use the art services, nor their ServiceHandle
, directly any more, as all the functionality has been moved one layer deeper, in the service providers.
… a service provider needs to be used instead.
art::ServiceHandle<util::DetectorProperties> detprop;
double sampling_rate = detprop->SamplingRate();
becomes:
auto const* detprop = lar::providerFrom<detinfo::DetectorPropertiesService>();
double sampling_rate = detprop->SamplingRate();
Note that the access looks the same (the second line in the example does not change), and only one header file is included (was Utilities/DetectorProperties.h
, now is DetectorInfoServices/DetectorPropertiesService.h
, see below).
… a service provider needs to be used instead.
util::DetectorProperties const& detprop = *art::ServiceHandle<util::DetectorProperties>();
double sampling_rate = detprop.SamplingRate();
becomes:
auto const& detprop = *lar::providerFrom<detinfo::DetectorPropertiesService>();
double sampling_rate = detprop.SamplingRate();
This case is very similar to the previous one.
… a service provider needs to be used instead.
You may have in your class declaration:
art::ServiceHandle<util::DetectorProperties> fDetProp;
and in your code:
double sampling_rate = fDetProp->SamplingRate();
This fix is more articulated.
The class declaration should host a provider pointer instead:
detinfo::DetectorProperties const* fDetProp = nullptr;
Note that here you need to include the provider header (#include "DetectorInfo/DetectorProperties.h"
). And no auto
option here, sorry.
That pointer needs to be initialized with a valid pointer to provider, for example in the constructor of your class (MyAlg
):
MyAlg::MyAlg(fhicl::ParameterSet const&)
: fDetProp(lar::providerFrom<detinfo::DetectorPropertiesService>())
// ... the normal constructor follows
and in this case the source file also needs to explicitly include the service header (#include "DetectorInfoService/DetectorPropertiesService.h"
).
Or you can opt for other initialization methods, like a deferred set up function:
void MyAlg::Setup(detinfo::DetectorProperties const& detprop) {
fDetProp = &detprop;
} // MyAlg::Setup()
This approach puts an additional burden to the owner ofthe algorithm, that needs to fetch the provider and pass it to the algorithm, but it makes the algorithm independent of the framework (art) as the service is concerned.
There are a couple of structures to make this a bit easier, especially if our algorithm uses many services and it needs to set up sub-algorithms.
See the ProviderPack
section below.
Finally, the user code:
double sampling_rate = fDetProp->SamplingRate();
remains the same.
Old service | New service | I/F |
---|---|---|
geo::Geometry |
||
util::DatabaseUtil |
||
util::TimeService |
detinfo::DetectorClocksService |
I |
util::LArProperties |
detinfo::LArPropertiesService |
I |
util::DetectorProperties |
detinfo::DetectorPropertiesService |
I |
lariov::IChannelStatusService |
lariov::ChannelStatusService |
I |
lariov::IDetPedestalService |
lariov::DetPedestalService |
I |
The column “I/F” says “I” if the service is actually an interface. See the next section about service interfaces.
Expand the following reference tables for the details necessary for the update:
| | |. repo. |. #include
path |
|. Old service name: | geo::Geometry
| larcore
| Geometry/Geometry.h
|
|. Old provider name: | geo::GeometryCore
| larcore
| Geometry/GeometryCore.h
|
|. Was interface: | no | | |
|*. New service name: |*(unchanged) | | |
|. New provider name: |(unchanged)_ | | |
| | |. repo. |. #include
path |
|. Old service name: | util::DatabaseUtil
| lardata
| Utilities/DatabaseUtil.h
|
|. Old provider name: | n/a | | |
|. Was interface: | no | | |
|*. New service name: |*(unchanged) | | |
|. New provider name: |(unchanged)_ | | |
|_. Is interface: | no | | |
bq. DatabaseUtil
service is being deprecated for all experiments except ArgoNeuT. It provides direct access to databases, which has proven to be a non-scalable approach.
MicroBooNE-specific components in there will be moved into a MicroBooNE-specific implementation.
ArgoNeuT will possibly keep using it, so it is not going to disappear, but it might be moved into their repository.
| | |. repo. |. #include
path |
|. Old service name: | util::LArProperties
| lardata
| Utilities/LArProperties.h
|
|. Old provider name: | n/a | | |
|. Was interface: | no | | |
|. New service name: | detinfo::LArPropertiesService
| lardata
| Utilities/LArPropertiesService.h
|
|. New provider name: | detinfo::LArProperties
| lardata
| DataProviders/LArProperties.h
|
|. Is interface: | yes | | |
|_. Default implementation: | detinfo::LArPropertiesServiceStandard
| lardata
| Utilities/LArPropertiesServiceStandard.h
|
|. | |. repo. |. #include
path |
|. Old service name: | util::DetectorProperties
| lardata
| Utilities/DetectorProperties.h
|
|. Old provider name: | *n/a* | | |
|. Was interface: | no | | |
|. New service name: | detinfo::DetectorPropertiesService
| lardata
| Utilities/DetectorPropertiesService.h
|
|. New provider name: | detinfo::DetectorProperties
| lardata
| DataProviders/DetectorProperties.h
|
|. Is interface: | yes | | |
|. Default implementation: | detinfo::DetectorPropertiesServiceStandard
| lardata
| Utilities/DetectorPropertiesServiceStandard.h
|
|. | |. repo. |. #include
path |
|. Old service name: | util::TimeService
| lardata
| Utilities/TimeService.h
|
|. Old provider name: | util::SimpleTimeService
| lardata
| Utilities/SimpleTimeService.h
|
|. Was interface: | no | | |
|. New service name: | detinfo::DetectorClocksService
| lardata
| Utilities/DetectorClocksService.h
|
|. New provider name: | detinfo::DetectorClocks
| lardata
| DataProviders/DetectorClocks.h
|
|. Is interface: | yes | | |
|. Default implementation: | detinfo::DetectorClocksServiceStandard
| lardata
| Utilities/DetectorClocksServiceStandard.h
|
|. | |. repo. |. #include
path |
|. Old service name: | lariov::IChannelStatusService
| larevt
| CalibrationDBI/Interface/IChannelStatusService.h
|
|. Old provider name: | lariov::IChannelStatusProvider
| larevt
| CalibrationDBI/Interface/IChannelStatusProvider.h
|
|. Was interface: | yes | | |
|. Old service name: | lariov::ChannelStatusService
| larevt
| CalibrationDBI/Interface/ChannelStatusService.h
|
|. Old provider name: | lariov::ChannelStatusProvider
| larevt
| CalibrationDBI/Interface/ChannelStatusProvider.h
|
|. Is interface: | yes | | |
|. Default implementation: | lariov::SIOVChannelStatusService
| larevt
| CalibrationDBI/Services/SIOVChannelStatusService.cc
|
Note that there is no header for the “standard” implementation of the art service (there is one for the provider).
|. | |. repo. |. #include
path |
|. Old service name: | lariov::IDetPedestalService
| larevt
| CalibrationDBI/Interface/IDetPedestalService.h
|
|. Old provider name: | lariov::IDetPedestalProvider
| larevt
| CalibrationDBI/Interface/IDetPedestalProvider.h
|
|. Was interface: | yes | | |
|. Old service name: | lariov::DetPedestalService
| larevt
| CalibrationDBI/Interface/DetPedestalService.h
|
|. Old provider name: | lariov::DetPedestalProvider
| larevt
| CalibrationDBI/Interface/DetPedestalProvider.h
|
|. Is interface: | yes | | |
|. Default implementation: | lariov::SIOVDetPedestalService
| larevt
| CalibrationDBI/Services/SIOVDetPedestalService.cc
|
Note that there is no header for the “standard” implementation of the art service (there is one for the provider).
DetectorProperties
serviceThe first visible issue is that compilation fails for a missing header Utilities/DetectorProperties.h
.
Changes:
CMakeLists.txt
:
DetectorProperties_service
1#include "Utilities/DetectorProperties.h"
-> #include "Utilities/DetectorPropertiesService.h"
#include "DataProviders/DetectorProperties.h"
#include "CoreUtils/ServiceUtil.h" // lar::providerFrom<>()
art::ServiceHandle<util::DetectorProperties> detprop;
-> detinfo::DetectorProperties const* detprop = lar::providerFrom<detinfo::DetectorPropertiesService>();
auto const* detprop = lar::providerFrom<detinfo::DetectorPropertiesService>();
)DetectorProperties:
-> DetectorPropertiesService:
either within a services
parameter set or services.
stand-alone line (note: no user
section should appear)service_provider: DetectorPropertiesServiceStandard
(or the implementation of your choice) in the DetectorPropertiesService
parameter setTimeService
serviceThe first visible issue is that compilation fails for a missing header Utilities/TimeService.h
.
Changes:
CMakeLists.txt
:
TimeService_service
2#include "Utilities/TimeService.h"
-> #include "Utilities/DetectorClocksService.h"
#include "DataProviders/DetectorClocks.h"
#include "CoreUtils/ServiceUtil.h" // lar::providerFrom<>()
art::ServiceHandle<util::TimeService> detprop;
-> detinfo::DetectorClocks const* detprop = lar::providerFrom<detinfo::DetectorClocksService>();
auto const* detprop = lar::providerFrom<detinfo::DetectorClocksService>();
)TimeService:
-> DetectorClocksService:
either within a services
parameter set or services.
stand-alone line (note: no user
section should appear)service_provider: DetectorClocksServiceStandard
(or the implementation of your choice) in the DetectorClocksService
parameter setGeometry
serviceThe changes are backward-compatible, but future changes might break the “legacy” compatibility.
Changes:
#include "Geometry/Geometry.h"
(should be there already…)#include "Geometry/GeometryCore.h"
#include "#include "CoreUtils/ServiceUtil.h" // lar::providerFrom<>()
art::ServiceHandle<geo::Geometry> geom;
-> geo::GeometryCore const* geom = lar::providerFrom<geo::Geometry>();
auto const* geom = lar::providerFrom<geo::Geometry>();
)DetectorPropertiesService
and LArPropertiesService
Some parameters have been moved from LArProperties
to DetectorPropertiesService
(rather than LArPropertiesService
) (see the table below).
If you are defining or overriding those parameters in your FHiCL file, you have to fix the definition to happen in the right parameter set, by replacing LArProperties.
with DetectorPropertiesService.
the path (in case of overriding) or by moving the line to the right service configuration (in case of complete definition of the service parameters).
Some dependencies have changed. In particular, note the dropping of DatabaseUtil
from the dependencies; DatabaseUtil
is being now considered ArgoNeuT-specific.
Service | depends on |
---|---|
geo::Geometry |
geo::ExptGeometryHelperInterface |
util::DatabaseUtil |
none (and: deprecated) |
detinfo::DetectorClocksServiceStandard |
none |
detinfo::LArPropertiesServiceStandard |
none |
detinfo::DetectorPropertiesServiceStandard |
geo::Geometry , detinfo::LArPropertiesService , detinfo::DetectorClocksService |
To use a services marked as interface, the job FHiCL configuration must contain for it the specific implementation that is requested, by a service_provider: ImplementationName
parameter.
The following table shows implementations provided within LArSoft:
Service | Default/other implementations | |
---|---|---|
DetectorClocksService |
DetectorClocksServiceStandard (lardata ) |
standard |
LArPropertiesService |
LArPropertiesServiceStandard (lardata ) |
standard |
LArPropertiesServiceArgoNeuT (lardata ) |
legacy, for ArgoNeuT; depends on DatabaseUtil |
|
DetectorPropertiesService |
DetectorPropertiesServiceStandard (lardata ) |
standard |
DetectorPropertiesServiceArgoNeuT (lardata ) |
legacy, for ArgoNeuT; depends on LArPropertiesServiceArgoNeuT |
The current organization of namespaces is:
detinfo |
service interfaces for the core services in lardata |
detinfo |
“standard” service implementations for the core services in lardata |
detinfo |
service provider interfaces for the core services in lardata |
detinfo |
“standard” service provider implementations for the core services in lardata |
(also called “shove everything into detinfo
”)
Function | Old service | New service | New name | FHiCL parameter name |
---|---|---|---|---|
DriftVelocity |
LArProperties |
DetectorPropertiesService |
none | |
Efield |
LArProperties |
DetectorPropertiesService |
Efield |
|
ElectronLifetime() |
LArProperties |
DetectorPropertiesService |
Electronlifetime |
|
BirksCorrection() |
LArProperties |
DetectorPropertiesService |
Electronlifetime |
|
ModBoxCorrection() |
LArProperties |
DetectorPropertiesService |
Electronlifetime |
|
Temperature() |
LArProperties |
DetectorPropertiesService |
Temperature |
|
LArProperties |
DetectorPropertiesService |
SternheimerA |
||
LArProperties |
DetectorPropertiesService |
SternheimerK |
||
LArProperties |
DetectorPropertiesService |
SternheimerX0 |
||
LArProperties |
DetectorPropertiesService |
SternheimerX1 |
||
LArProperties |
DetectorPropertiesService |
SternheimerCbar |
detutil::LArPropertiesStandard
and detutil::DetectorPropertiesStandard
The new standard implementation of detutil::LArProperties
and detutil::DetectorProperties
use a feature of FHiCL C library, that allows documentation and validation of the configuration parameters.
Documentation means that with lar --service-description LArPropertiesServiceStandard
you get an example FHiCL file to configure, in fact, detinfo::LArPropertiesServiceStandard
. Cool.
Validation means that now if you have in your configuration a parameter that is not supported, you’ll get a run-time error.
That is typically because of mistyping; but in this case it may also be that, e.g., you still have Efield
in LArPropertiesServiceStandard
configuration, while it should have been moved into DetectorPropertiesServiceStandard
configuration.
This exciting new feature is documented in FHiCL wiki documentation, and we encourage everybody to use it for algorithms as well. Also note that the validation does not depend on art (in fact, our service providers do use it).
service_provider
to service FHiCL configuration (if not inherited)As the previous paragraph also states, you are provided no tool for automatic conversion; but almost.
It is now provided in larsoft
repository: ${LARSOFT_DIR}/bin/UpdateCoreServices.py
(you can copy it, but note that it needs its SerialSubstitution.py
to be available, e.g., in the same directory).
To get a list of supported options run UpdateCoreServices.py --help
.
In the end, these are not extremely sophisticated. You run the script with:
./UpdateCoreServices.py --verbose --color "${MRB_SOURCE}/my_package"
and you get a long list of messages describing what is changing.
Most of the changes are ok. But pay special attention to CMakeLists.txt
files and to class code.
If you have a class member of type art::ServiceHandle<>, the script will probably change the code in a way you don’t want.
Also, do pay attention to warnings: they usually show a pattern that needs to be updated, and the script misses some information that would allow an automatic fix. So: you fix it by hand.
You can process file by file or directory by directory if you want to be selective.
When you think it’s time to give it a try:
git
will let you revert if something goes bad--doit
optiongit diff
to see what happened, try to understand each change--doit
) again, and consider what’s left (including the warnings, that won’t go away)If you are lost or need help, please contact the LArSoft team (e.g. send an e-mail to
the larsoft mailing list
or open a redmine support ticket).
ProviderPack
and ServicePack
classesIf your algorithm needs multiple providers, it may be more convenient to define all of them in a structure.
Actually, first think: if it’s just a single value that is needed, it’s better to pass just that value (e.g., the electric field).
But if services are extensively used:
/// Type containing all the service providers we need
using providers_t = lar::ProviderPack<geo::GeometryCore, detinfo::LArProperties>;
providers_t providers;
void Setup(providers_t new_providers) { providers = new_providers; }
With this approach, to use the service provider in your code you would do something like:
auto nCryo = providers.get<geo::GeomeryCore>()->Ncryostats();
One advantage is that if your algorithm calls sub-algorithms that also use provider packs, you can write:
MyAlgo algo;
algo.Setup(providers);
and let the compiler figure out which ones are needed (MyAlgo
does not need to use all the providers in providers
, either).
Another advantage is that the art module that owns and initializes the algorithm can also take some shortcut:
MyAlgo algo;
algo.Setup(lar::extractProviders<geo::Geometry, detinfo::LArPropertiesService>());
All this is mostly typing convenience, since all is coded to be statically resolved, that is not to have run-time cost.
ProviderPack
is currently declared in lardata/DetectorInfo/ProviderPack.h
, while ServicePack
is currently declared in lardata/DetectorInfoServices/ServicePack.h
. They are pure templates, so there is no additional link-time dependency.
Last update: March 9, 2016 Gianluca Petrillo
Because the service has become an interface, and the interface itself is pure abstract, service library contains no code. The actual code will be loaded and linked by the framework at run time. ↩
Because the service has become an interface, and the interface itself is pure abstract, service library contains no code. The actual code will be loaded and linked by the framework at run time. ↩