ldmx-sw icon indicating copy to clipboard operation
ldmx-sw copied to clipboard

Use Geant4-object pointer comparisons instead of string-comparisons where possible

Open EinarElen opened this issue 1 year ago • 5 comments

We have a lot of code in SimCore and Biasing which does things like


auto MyThing {track->GetThing()->GetThingName()};
if (MyThing.compare(SomeString)) {
   // Do the work here
}

Where thing can be something like

  • A particular process, e.g. photoNuclear
  • A particular geometry region, e.g. "CalorimeterRegion"
  • A particular physical/logical volume, e.g. "hadronic_calorimeter"

This has two big downsides

  1. It is way too easy to mess this up and introduce bugs. For example: https://github.com/LDMX-Software/ldmx-sw/blob/28fd69677fca6250588d02b182661c24156f1e60/Biasing/src/Biasing/EcalProcessFilter.cxx#L101-L111

This is a bug. There is no physical volume called hcal_PV so that condition will never trigger. The volume in question happens to be called "hadronic_calorimeter" which differs from all the others that have names like tagger_PV etc. If we are using pointers as our comparison tool, we can enforce at configuration time that these are initialized and throw otherwise (so issues like the hcal name here would be caught).

  1. String comparisons can be really slow, especially for anything that is called ~at every Geant4 step (biasing operators and stepping actions). I made a quick and dirty comparison and there was a ~30% performance improvement by moving from string comparisons to pointer comparisons for EcalPN samples (!!!)

I think the best way to do this would be to have a helper type in SimCore that can be used to get the various pointers people might need and make sure that things like biasing wrappers are handled correctly.

class Geant4ObjectUtility // Someone please suggest a better name 
{ 
   const G4VProcess* GetPhotonuclearProcess() const {
   return GetProcess(G4Gamma::Definition(), "photonNuclear");
   }
   const G4VProcess* GetProcess(G4ParticleDefinition* particle, std::string processName) const {
         const auto manager {particle->GetProcessManager()};
      const auto processes {manager->GetProcessList()};
      for (int i{0}; i < processes->size(); ++i) {
        const auto process {(*processes)[i]};
        const auto name {process->GetProcessName()};
        if (name.contains(processName)) {
          return process;
        }
      }
      return nullptr;

   }
  G4Region* GetRegion(std::string name) {
  return G4RegionStore::GetInstance()->GetRegion(name);
  }
};

etc

EinarElen avatar Jul 17 '24 11:07 EinarElen

I don't think we want a class since it would never hold its own data, instead maybe just a namespace? ptr_retrieval?

namespace ptr_retrieval { 
const G4VProcess* GetPhotonuclearProcess() const {
  return GetProcess(G4Gamma::Definition(), "photonNuclear");
}
const G4VProcess* GetProcess(G4ParticleDefinition* particle, std::string processName) const {
  const auto manager {particle->GetProcessManager()};
  const auto processes {manager->GetProcessList()};
  for (int i{0}; i < processes->size(); ++i) {
    const auto process {(*processes)[i]};
    const auto name {process->GetProcessName()};
    if (name.contains(processName)) {
      return process;
    }
  }
  // for processes from the particle table, I can't think of a situation where this would be useful
  // maybe code-in the exception here?
  return nullptr;
}
G4Region* GetRegion(std::string name) {
  return G4RegionStore::GetInstance()->GetRegion(name);
}
} // namespace ptr_retrieval

and then the call-site would look readable like

auto pn = ptr_retrieval::GetPhotonuclearProcess();

tomeichlersmith avatar Jul 18 '24 17:07 tomeichlersmith

Yeah, that seems sensible :)

EinarElen avatar Jul 18 '24 17:07 EinarElen

So where should this go? And then have a GetProcess for all other kinda processes?

It's not clear to me how the proposed snippet gets rid of string comparisons, there is still a name.contains() in it.

But I guess if we do this, it will resolve https://github.com/LDMX-Software/ldmx-sw/issues/1286 by having the same things for the detector elements, right?

tvami avatar Sep 04 '24 13:09 tvami

This would be utility functions available from within SimCore and then the places that know they need to do region or volume checks (e.g. in filters) would store the values of these pointers and then only use the values of the pointers during running.

tomeichlersmith avatar Sep 04 '24 14:09 tomeichlersmith

You only do the string comparison once :)

EinarElen avatar Sep 04 '24 15:09 EinarElen

Resolved in https://github.com/LDMX-Software/ldmx-sw/pull/1633

tvami avatar Apr 08 '25 20:04 tvami