geometry_unit_test_base.h
Go to the documentation of this file.
1 /**
2  * @file geometry_unit_test_base.h
3  * @brief Base class for objects initializing a geometry
4  * @date May 7th, 2015
5  * @author petrillo@fnal.gov
6  * @see unit_test_base.h
7  *
8  * Provides an environment for easy set up of a Geometry-aware test.
9  * Keep in mind that, as much as I could push on flexibility, the channel
10  * mapping algorithm must be hard-coded and, if using Boost unit test,
11  * the configuration file location must be hard coded too
12  * (or you can use the provided configuration).
13  *
14  * For an example of usage, see larcore/test/Geometry/geometry_iterator_test.cxx
15  *
16  * The standard TesterEnvironment<> class can't handle gar::geo::GeometryCore.
17  * The reason is twofold: for once, ExptGeoHelperInterface service is not
18  * factorized, so we need to choose explicitly the ChannelMapAlg implementation
19  * (here this is obtained by a template argument). Another is that GeometryCore
20  * both is required and requires ChannelMapAlg to have a complete
21  * initialisation. There are ways to overcome the issue at the cost of added
22  * complication.
23  *
24  * Currently provides:
25  * - BasicGeometryEnvironmentConfiguration: a test environment configuration
26  * - GeometryTesterEnvironment: a prepacked geometry-aware test environment
27  *
28  */
29 
30 
31 #ifndef TEST_GEOMETRY_UNIT_TEST_BASE_H
32 #define TEST_GEOMETRY_UNIT_TEST_BASE_H
33 
34 // GArSoft libraries
35 #include "TestUtils/unit_test_base.h"
36 #include "Geometry/GeometryCore.h"
38 
39 // utility libraries
40 #include "fhiclcpp/ParameterSet.h"
42 
43 // CET libraries
44 #include "cetlib/filesystem.h" // cet::is_absolute_filepath()
45 #include "cetlib/filepath_maker.h"
46 #include "cetlib/search_path.h"
47 
48 // C/C++ standard libraries
49 #include <iostream> // for output before message facility is set up
50 #include <string>
51 #include <memory> // std::unique_ptr<>
52 #include <map>
53 
54 
55 namespace testing {
56 
57 
58  /** **************************************************************************
59  * @brief Class holding a configuration for a test environment
60  * @tparam CHANNELMAP the class used for channel mapping
61  * @see GeometryTesterEnvironment
62  *
63  * This class needs to be fully constructed by the default constructor
64  * in order to be useful as Boost unit test fixture.
65  * It is supposed to be passed as a template parameter to another class
66  * that can store an instance of it and extract configuration information
67  * from it.
68  */
69  template <typename CHANNELMAP>
72  {
73  using ChannelMapClass = CHANNELMAP;
74 
75  /// Default constructor; this is what is used in Boost unit test
78  { LocalInit(); }
79 
80  /// Constructor: acquires parameters from the command line
83  { LocalInit(); }
84 
85  /// Constructor; accepts the name as parameter
88  { LocalInit(); }
89 
91  (int argc, char** argv, std::string name):
92  BasicEnvironmentConfiguration(argc, argv, name)
93  { LocalInit(); }
94 
95 
96  /// @{
97  /// @name Access to configuration
98  /// FHiCL path for the geometry configuration
101 
102  /// A string describing the default parameter set to configure geometry
105 
106  ///@}
107 
108 
109  /// @{
110  /// @name Set configuration
111 
112  /// Sets the FHiCL path for the geometry configuration
115 
116  /// Sets a string describing the default parameter set to configure geometry
119 
120  ///@}
121 
122 
123  /// Returns the name of the service
124  static std::string GeometryServiceName() { return "Geometry"; }
125 
126  protected:
127 
128  /// Initialize with some default values
129  void LocalInit()
130  {
132  SurfaceY: 200. # in cm, vertical distance to the surface
133  Name: "lartpcdetector"
134  GDML: "LArTPCdetector.gdml"
135  ROOT: "LArTPCdetector.gdml"
136  SortingParameters: {} # empty parameter set for default
137  )");
138  } // LocalInit()
139 
140  }; // class BasicGeometryEnvironmentConfiguration<>
141 
142 
143 
144  /** **************************************************************************
145  * @brief Environment for a geometry test
146  * @tparam ConfigurationClass a class providing compile-time configuration
147  *
148  * The test environment is set up on construction.
149  *
150  * The environment provides:
151  * - Geometry() method to access geometry (as a constant pointer)
152  * - Parameters() method returning the complete FHiCL configuration
153  * - TesterParameters() method returning the configuration for the test
154  *
155  * This class or a derived one can be used as global fixture for unit tests
156  * that require the presence of geometry (in the form of gar::geo::GeometryCore
157  * instance).
158  *
159  * Unfortunately Boost does not give any control on the initialization of the
160  * object, so everything must be ready to go as hard coded.
161  * The ConfigurationClass class tries to alleviate that.
162  * That is another, small static class that GeometryTesterEnvironment uses to
163  * get its parameters.
164  *
165  * The requirements for the ConfigurationClass are:
166  * - `ChannelMapClass`: concrete type of channel mapping algorithm class
167  * - `std::string ApplicationName()`: the application name
168  * - `std::string ConfigurationPath()`: path to the configuration file
169  * - `std::string GeometryParameterSetPath()`: FHiCL path to the configuration
170  * of the geometry; in art is `"services.Geometry"`
171  * - `std::string TesterParameterSetPath()`: FHiCL path to the configuration
172  * of the geometry
173  * - `std::string DefaultGeometryConfiguration()` returning a FHiCL string
174  * to be parsed to extract the default geometry configuration
175  * - `std::string DefaultTesterConfiguration()` returning a FHiCL string
176  * to be parsed to extract the default test configuration
177  *
178  * Whether the configuration comes from a file or from the two provided
179  * defaults, it is always expected within the parameter set paths:
180  * the default configuration must also contain that path.
181  *
182  * Note that there is no room for polymorphism here since the setup happens
183  * on construction.
184  * Some methods are declared virtual in order to allow to tweak some steps
185  * of the set up, but it's not trivial to create a derived class that works
186  * correctly: the derived class must declare a new default constructor,
187  * and that default constructor must call the protected constructor
188  * (GeometryTesterEnvironment<ConfigurationClass>(no_setup))
189  */
190  template <typename ConfigurationClass>
192  virtual public TesterEnvironment<ConfigurationClass>
193  {
194 
195  /// Base class
197 
198  /// this implements the singleton interface
200 
201  public:
203 
204  /**
205  * @brief Constructor: sets everything up and declares the test started
206  *
207  * The configuration is from a default-constructed ConfigurationClass.
208  * This is suitable for use as Boost unit test fixture.
209  */
210  GeometryTesterEnvironment(bool bSetup = true)
211  : TesterEnvironment_t(false)
212  { if (bSetup) Setup(); }
213 
214  //@{
215  /**
216  * @brief Setup from a configuration
217  * @param configurer an instance of ConfigurationClass
218  *
219  * The configuration is from the specified configurer class.
220  *
221  * This constructor allows to use a non-default-constructed configuration.
222  * This can't be used (at best of my knowledge) when using this class as
223  * Boost unit test fixture.
224  *
225  * In the r-value-reference constructor, the configurer is moved.
226  */
228  (ConfigurationClass const& cfg_obj, bool bSetup = true)
229  : TesterEnvironment_t(cfg_obj, false)
230  { if (bSetup) Setup(); }
231  GeometryTesterEnvironment(ConfigurationClass&& cfg_obj, bool bSetup = true)
232  : TesterEnvironment_t(std::move(cfg_obj), false)
233  { if (bSetup) Setup(); }
234  //@}
235 
236  /// Destructor: closing remarks
237  virtual ~GeometryTesterEnvironment();
238 
239 
240  //@{
241  /// Returns a pointer to the geometry
242  gar::geo::GeometryCore const* Geometry() const { return geom.get(); }
243  SharedGeoPtr_t SharedGeometry() const { return geom; }
244  //@}
245 
246 
247  /// Returns the current global geometry instance
248  /// @throws std::out_of_range if not present
250  { return &GeoResources_t::Resource(); }
251 
252  /// Returns the current global geometry instance (may be nullptr if none)
254  { return GeoResources_t::ShareResource(); }
255 
256  protected:
257 
258  using ChannelMapClass = typename ConfigurationClass::ChannelMapClass;
259 
260 
261  /// The complete initialization, ran at construction by default
262  virtual void Setup();
263 
264  /// Creates a new geometry
265  virtual std::unique_ptr<gar::geo::GeometryCore> CreateNewGeometry() const;
266 
267  //@{
268  /// Get ownership of the specified geometry and registers it as global
269  virtual void RegisterGeometry(SharedGeoPtr_t new_geom);
270  virtual void RegisterGeometry(gar::geo::GeometryCore const* new_geom)
271  { RegisterGeometry(SharedGeoPtr_t(new_geom)); }
272  //@}
273 
274  /// Sets up the geometry (creates and registers it)
275  virtual void SetupGeometry();
276 
277  private:
278 
279  ConfigurationClass config; ///< instance of the configurer
280 
281  SharedGeoPtr_t geom; ///< pointer to the geometry
282 
283  }; // class GeometryTesterEnvironment<>
284 
285 
286 
287  //****************************************************************************
288  template <typename ConfigurationClass>
290 
291  mf::LogInfo("Test") << config.ApplicationName() << " completed.";
292 
293  } // GeometryTesterEnvironment<>::~GeometryTesterEnvironment()
294 
295 
296  /** **************************************************************************
297  * @brief Sets the geometry of the standard detector up
298  *
299  * This function sets up the geometry according to the provided information:
300  * - the configuration must contain enough information to locate the geometry
301  * description file
302  * - we trust that that geometry works well with the ChannelMapClass specified
303  * in ConfigurationClass
304  *
305  */
306  template <typename ConfigurationClass>
307  std::unique_ptr<gar::geo::GeometryCore>
309  {
310 
311  std::string ProviderParameterSetPath
312  = this->Config().GeometryParameterSetPath();
313 
314  //
315  // create the new geometry service provider
316  //
317  fhicl::ParameterSet ProviderConfig
318  = this->Parameters().template get<fhicl::ParameterSet>
319  (ProviderParameterSetPath);
320  auto new_geom = std::make_unique<gar::geo::GeometryCore>(ProviderConfig);
321 
322  std::string RelativePath
323  = ProviderConfig.get< std::string>("RelativePath", "");
324 
326  GDMLFileName = RelativePath + ProviderConfig.get<std::string>("GDML"),
327  ROOTFileName = RelativePath + ProviderConfig.get<std::string>("ROOT");
328 
329  // Search all reasonable locations for the geometry file;
330  // we see if by any chance art's FW_SEARCH_PATH directory is set and try
331  // there;
332  // if not, we do expect the path to be complete enough for ROOT to cope.
333  cet::search_path sp("FW_SEARCH_PATH");
334 
335  std::string ROOTfile;
336  if (!sp.find_file(ROOTFileName, ROOTfile)) ROOTfile = ROOTFileName;
337 
338  // we really don't care of GDML file, since we are not going to run Geant4
339  std::string GDMLfile;
340  if (!sp.find_file(GDMLFileName, GDMLfile))
341  mf::LogWarning("CreateNewGeometry") << "GDML file not found.";
342 
343  // initialize the geometry with the files we have found
344  new_geom->LoadGeometryFile(GDMLfile, ROOTfile);
345 
346 
347  //
348  // create the new channel map
349  //
350  fhicl::ParameterSet SortingParameters
351  = ProviderConfig.get<fhicl::ParameterSet>("SortingParameters", {});
352  std::shared_ptr<gar::geo::seg::ChannelMapAlg> pChannelMap
353  (new ChannelMapClass(SortingParameters));
354 
355  // connect the channel map with the geometry, that shares ownsership
356  // (we give up ours at the end of this method)
357  new_geom->ApplyChannelMap(pChannelMap);
358 
359  return new_geom;
360  } // GeometryTesterEnvironment<>::CreateNewGeometry()
361 
362 
363  template <typename ConfigurationClass>
365  (SharedGeoPtr_t new_geom)
366  {
367  // update the current geometry, that becomes owner;
368  // also update the global one if it happens to be already our previous
369  // (in this case, it becomes co-owner)
370  SharedGeoPtr_t my_old_geom = geom;
371  geom = new_geom;
372  // if the global geometry is already the one we register, don't bother
373  if (SharedGlobalGeometry() != new_geom)
374  GeoResources_t::ReplaceDefaultSharedResource(my_old_geom, new_geom);
375  } // GeometryTesterEnvironment<>::RegisterGeometry()
376 
377 
378 
379  template <typename ConfigurationClass>
381  //
382  // horrible, shameful hack to support the "new" testing environment
383  // while the old one, informally deprecated, is still around;
384  // we will have TWO versions of GeometryCore around.
385  // Ugh.
386  //
387  RegisterGeometry(CreateNewGeometry()); // old
388  // new
389  this->template AcquireProvider<gar::geo::GeometryCore>(CreateNewGeometry());
390  } // GeometryTesterEnvironment<>::SetupGeometry()
391 
392 
393 
394  template <typename ConfigurationClass>
396 
397  //
398  // parse configuration, set up message facility
399  //
400  TesterEnvironment_t::Setup();
401 
402  //
403  // set up the geometry
404  //
405  SetupGeometry();
406 
407  mf::LogInfo("Test")
408  << config.ApplicationName() << " Geometry setup complete.";
409 
410  } // GeometryTesterEnvironment<>::Setup()
411 
412 
413 } // namespace testing
414 
415 #endif // TEST_GEOMETRY_UNIT_TEST_BASE_H
static QCString name
Definition: declinfo.cpp:673
virtual ~GeometryTesterEnvironment()
Destructor: closing remarks.
LArSoft test utilities.
static std::string GeometryServiceName()
Returns the name of the service.
BasicGeometryEnvironmentConfiguration()
Default constructor; this is what is used in Boost unit test.
void LocalInit()
Initialize with some default values.
std::string string
Definition: nybbler.cc:12
MaybeLogger_< ELseverityLevel::ELsev_info, false > LogInfo
std::shared_ptr< Resource_t > ResourcePtr_t
std::string DefaultGeometryConfiguration() const
A string describing the default parameter set to configure geometry.
virtual void RegisterGeometry(gar::geo::GeometryCore const *new_geom)
virtual void RegisterGeometry(SharedGeoPtr_t new_geom)
Get ownership of the specified geometry and registers it as global.
gar::geo::GeometryCore const * Geometry() const
Returns a pointer to the geometry.
Class holding a configuration for a test environment.
STL namespace.
Description of geometry of one entire detector.
Definition: GeometryCore.h:436
typename StandardGeometryConfiguration::ChannelMapClass ChannelMapClass
SharedGeoPtr_t geom
pointer to the geometry
static SharedGeoPtr_t SharedGlobalGeometry()
Returns the current global geometry instance (may be nullptr if none)
void AddDefaultServiceConfiguration(std::string service_name, std::string service_cfg)
Adds a default configuration for the specified service.
std::string ServiceParameterSetPath(std::string name) const
FHiCL path for the configuration of the service.
typename config_impl< T >::type Config
Definition: ModuleMacros.h:52
ConfigurationClass config
instance of the configurer
BasicGeometryEnvironmentConfiguration(int argc, char **argv)
Constructor: acquires parameters from the command line.
void SetGeometryParameterSetPath(std::string path)
Sets the FHiCL path for the geometry configuration.
std::string DefaultServiceConfiguration(std::string service_name) const
A string describing the default parameter set to configure the test.
static Config * config
Definition: config.cpp:1054
Environment for a geometry test.
def move(depos, offset)
Definition: depos.py:107
T get(std::string const &key) const
Definition: ParameterSet.h:271
GeometryTesterEnvironment(bool bSetup=true)
Constructor: sets everything up and declares the test started.
Utility class providing singleton objects to the derived classes.
void SetDefaultGeometryConfiguration(std::string cfg)
Sets a string describing the default parameter set to configure geometry.
static gar::geo::GeometryCore const * GlobalGeometry()
virtual void SetupGeometry()
Sets up the geometry (creates and registers it)
A test environment with some support for service providers.
BasicEnvironmentConfiguration()
Default constructor; this is what is used in Boost unit test.
Class holding a configuration for a test environment.
std::unique_ptr< geo::GeometryCore > SetupGeometry(fhicl::ParameterSet const &pset, Args &&...args)
Initializes a LArSoft geometry object.
std::string find_file(std::string const &filename) const
Definition: search_path.cc:96
virtual std::unique_ptr< gar::geo::GeometryCore > CreateNewGeometry() const
Creates a new geometry.
BasicGeometryEnvironmentConfiguration(std::string name)
Constructor; accepts the name as parameter.
virtual void Setup()
The complete initialization, ran at construction by default.
GeometryTesterEnvironment(ConfigurationClass &&cfg_obj, bool bSetup=true)
void SetServiceParameterSetPath(std::string service_name, std::string path)
Sets the FHiCL path for the configuration of a test algorithm.