Skip to content

FairModule IsSensitive checks medium property not volume name#1628

Draft
rlalik wants to merge 3 commits intoFairRootGroup:devfrom
rlalik:issensitive_medium
Draft

FairModule IsSensitive checks medium property not volume name#1628
rlalik wants to merge 3 commits intoFairRootGroup:devfrom
rlalik:issensitive_medium

Conversation

@rlalik
Copy link
Copy Markdown

@rlalik rlalik commented Mar 27, 2026

This is faster way of checking the sensitivity property however medium needs to have proper sensitivity flag set.

See the discussion in this MR: https://git.cbm.gsi.de/CbmSoft/cbmroot_geometry/-/merge_requests/342#note_60660

@fuhlig1 is this is something you wanted to have?

I tried to check all the modified examples and tests whether they are still correct (all tests are passing), I need someone who is more familiar to check as well.

But it is not easy to test against cbmroot

Checklist:

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 27, 2026

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 410a0bab-d224-401b-b938-ee359863962c

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Member

@ChristianTackeGSI ChristianTackeGSI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi,

others might be more into the current thigns. So these are just my random comments.

Generally (I hope, we have that in our contribution guidelines?)

  • Please update the copyright year on any changed files
  • Add yourself to the CONTRIBUTORS list (possibly with OCRID)
  • By no longer calling the old virtual function, this code is a breaking change. I don't know, whether the main authors are okay with that?
  • Stop using those ROOT specific bool type. C++ has a good one since 20+ years.

Non virtual interface pattern:

  • I'd really suggest to separate the calling and the customization points. That is:
  • Introduce a new non-virtual interface. Maybe something like IsVolumeSensitive(TGeoVolume const&). (implementation suggestions below)
  • Make your new virtual IsVolumeSensitiveImpl(TGeoVolume const&) private (one can override private functions!)
  • Before calling any of these TGeoVolume& functions, make sure that the volume is != nullptr.
bool IsVolumeSensitive(TGeoVolume const& vol) const
{
    if (IsVolumeSensitiveImpl(vol))
        return true;
    // Call old code, for legacy reasons:
    return IsSensitive(vol.GetName());
}

@rlalik
Copy link
Copy Markdown
Author

rlalik commented Mar 29, 2026

@ChristianTackeGSI thanks for comments. I read Contributing guidelines, but somehow forgot to return to it after adjusting PR to guideline no. 1.

How many (reasonable) users of FairRoot are at the moment beside CbmRoot and PandaRoot that I am aware of?

The consequence of this PR would be:

  • all the currently defined virtual Bool_t IsSensitive(const std::string& name) functions in derived code will lost effect. Perhaps the deprecation message should rather inform to abandon this function and use medium sensitivity property. But beside that, the code will compile and run.
  • At runtime, the behavior may change if the medium for sensitive volumes are not marked as sensitive. This change should be clearly documented and the derived projects will need to update the medium definitions.
  • With use as intended, the virtual Bool_t IsSensitive(TGeoVolume* vol) { return (vol->GetMedium()->GetParam(0) == 1.0); } function does not really need to be overridden because that's all one needs. However, if someone wants to preserve the old behavior, the reference override would be:
virtual Bool_t IsSensitive(TGeoVolume* vol) override { return IsSensitive(vol->GetName()); }
  • I'm fine with bool, I also do not like ROOT types. But I wanted to keep the changes in respect to previous version as little as possible. My rule of thumb would be, the data which are intended to be stored in the ROOT files should use ROOT data type, variables which control execution logic should/could use standard types. Perhaps things like this could go into Contributing gudelines?
  • Your example of non-virtual interface will not work because both results of the IsVolumeSensitiveImpl(vol) are valid results and there is no justification to fall-back to old code. Unless IsVolumeSensitiveImpl(vol) returns something like std::optional ...
  • But ideally, there should be no reason for user to really override the function.

@rlalik rlalik marked this pull request as draft March 29, 2026 00:24
@ChristianTackeGSI
Copy link
Copy Markdown
Member

Thanks for updating contributors!
Is https://github.com/FairRootGroup/FairRoot/actions/runs/23697951124/job/69058720544?pr=1628 visible to you? If yes, please update .zenodo.json and codemeta.json accordingly. Otherwise we'll do that for you. :-)

  • Your example of non-virtual interface will not work because both results of the IsVolumeSensitiveImpl(vol) are valid results and there is no justification to fall-back to old code. Unless IsVolumeSensitiveImpl(vol) returns something like std::optional ...

I had the hope that the old legacy IsSensitive function would return false if not overriden. So if it's not implemented, the new one would be used fully.

Generally speaking, I am hoping that @fuhlig1 or @karabowi have a much better understanding of the "business logic" / machinery and will comment on it.

Nevertheless I would prefer private customization points (virtual functions) and non virtual interfaces (if needed). My one was just an idea.

The consequence of this PR would be:

  • all the currently defined virtual Bool_t IsSensitive(const std::string& name) functions in derived code will lost effect. Perhaps the deprecation message should rather inform to abandon this function and use medium sensitivity property. But beside that, the code will compile and run.

The problem here: deprecations in C++ only work for the caller. So anyone calling a deprecated function will get a warning. overriding a deprecated function does not create a warning. I don't know of any way to output a warning for just overriding a virtual function. Sadly.

So either we set the function to final (which will disallow overriding), or remove it (and hope others have annonated their functions with override). Both result in a compile error.
Breaking change. New major version.

  • I'm fine with bool, I also do not like ROOT types. But I wanted to keep the changes in respect to previous version as little as possible. My rule of thumb would be, the data which are intended to be stored in the ROOT files should use ROOT data type, variables which control execution logic should/could use standard types. Perhaps things like this could go into Contributing gudelines?

Very interesting idea!

Maybe open another PR to change the guidelines? I'd certainly give it an upvote!

rlalik added 3 commits March 30, 2026 02:24
…ume name

This is faster way of checking the sensitivity property however medium needs to have proper sensitivity flag set.
By ,arking old IsSensitive function as final, all deriving classes will generate compilation error.
@rlalik rlalik force-pushed the issensitive_medium branch from d336cce to b3c2c2a Compare March 30, 2026 00:28
@karabowi
Copy link
Copy Markdown
Collaborator

@rlalik I had a look at your PR and if I were the only one to decide, I would straight reject it.
Unfortunately @fuhlig1 is on Eastern vacation for about a week, so he cannot comment,
especially to this comment by David.

The rationale behind (I had also a short discussion with @MohammadAlTurany ) is that accepting this PR would totally screw up the sensitive detectors in all experiments, including CBM.
I agree that there is a sensitivity flag in the media description, and that it was originally the basis for the calling of the Stepping function during transport. However, years ago, a IsSensitive/CheckIfSensitive function was introduced to the MC detector steering classes that makes detectors sensitive by detector specific sting matching.

To summarize: this PR should wait for @fuhlig1

@rlalik
Copy link
Copy Markdown
Author

rlalik commented Mar 30, 2026

@rlalik I had a look at your PR and if I were the only one to decide, I would straight reject it. Unfortunately @fuhlig1 is on Eastern vacation for about a week, so he cannot comment, especially to this comment by David.

The rationale behind (I had also a short discussion with @MohammadAlTurany ) is that accepting this PR would totally screw up the sensitive detectors in all experiments, including CBM. I agree that there is a sensitivity flag in the media description, and that it was originally the basis for the calling of the Stepping function during transport. However, years ago, a IsSensitive/CheckIfSensitive function was introduced to the MC detector steering classes that makes detectors sensitive by detector specific sting matching.

To summarize: this PR should wait for @fuhlig1

Thanks @karabowi for this opinion and your concern. I am aware of the impact this PR has. However I don't think the solution will be very difficult. In case of CBM, one need to update material definitions in the cbmroot_geometry repository to mark certain materials as sensitive, if not yet. If someone reused the same medium for different sensitive and non-sensitive parts, this will require a bit more changes to duplicate the material and update the geometry macro.

For cbmroot_geometry this is only twelve active sensors (perhaps medium as well, can be less if different sensors use same medium, magnet is always non-sensitive):

  • RICH - three sensors/mediums?
sim/detectors/rich/CbmRich.cxx:86:Bool_t CbmRich::IsSensitive(const std::string& name)
sim/detectors/rich/CbmRich.cxx-87-{
sim/detectors/rich/CbmRich.cxx-88-  //return true;
sim/detectors/rich/CbmRich.cxx-89-  TString volName = name;
sim/detectors/rich/CbmRich.cxx-90-  if (volName.Contains("pmt_pixel") || volName.Contains("sens_plane")) return kTRUE;
sim/detectors/rich/CbmRich.cxx-91-  // mirrors
sim/detectors/rich/CbmRich.cxx-92-  if (volName.Contains("mirror_tile_type")) return kTRUE;
sim/detectors/rich/CbmRich.cxx-93-  return kFALSE;
sim/detectors/rich/CbmRich.cxx-94-}
  • TRD - one sensors/medium?
sim/detectors/trd/CbmTrd.cxx:324:Bool_t CbmTrd::IsSensitive(const std::string& name)
sim/detectors/trd/CbmTrd.cxx-325-{
sim/detectors/trd/CbmTrd.cxx-326-  TString tsname = name;
sim/detectors/trd/CbmTrd.cxx-327-  if (tsname.EqualTo("gas")) {
sim/detectors/trd/CbmTrd.cxx-328-    LOG(debug) << "CbmTrd::CheckIfSensitive: Register active volume: " << tsname;
sim/detectors/trd/CbmTrd.cxx-329-    return kTRUE;
sim/detectors/trd/CbmTrd.cxx-330-  }
sim/detectors/trd/CbmTrd.cxx-331-  return kFALSE;
sim/detectors/trd/CbmTrd.cxx-332-}
  • MDV - 3+ sensors (all names with -P0, perhaps all use the same medium)
sim/detectors/mvd/CbmMvd.cxx:277:Bool_t CbmMvd::IsSensitive(const std::string& name)
sim/detectors/mvd/CbmMvd.cxx-278-{
sim/detectors/mvd/CbmMvd.cxx-279-  TString tsname = name;
sim/detectors/mvd/CbmMvd.cxx-280-  if (tsname.Contains("sensorActive") || tsname.Contains("MimosaActive")
sim/detectors/mvd/CbmMvd.cxx-281-      || (tsname.Contains("mvdstation") && !(tsname.Contains("PartAss")))) {
sim/detectors/mvd/CbmMvd.cxx-282-    LOG(debug) << "*** Register " << tsname << " as active volume.";
sim/detectors/mvd/CbmMvd.cxx-283-    return kTRUE;
sim/detectors/mvd/CbmMvd.cxx-284-  }
sim/detectors/mvd/CbmMvd.cxx-285-  else if (tsname.EndsWith("-P0")) {
sim/detectors/mvd/CbmMvd.cxx-286-    // if(fVerboseLevel>1)
sim/detectors/mvd/CbmMvd.cxx-287-    LOG(debug) << "*** Register " << tsname << " as active volume.";
sim/detectors/mvd/CbmMvd.cxx-288-
sim/detectors/mvd/CbmMvd.cxx-289-    return kTRUE;
sim/detectors/mvd/CbmMvd.cxx-290-  }
sim/detectors/mvd/CbmMvd.cxx-291-  return kFALSE;
sim/detectors/mvd/CbmMvd.cxx-292-}
  • STS - one sensors/medium?
sim/detectors/sts/CbmStsMC.h:62:  virtual Bool_t IsSensitive(const std::string& name) { return (TString(name).Contains("Sensor") ? kTRUE : kFALSE); }
  • PSD - one sensors/medium?
sim/detectors/psd/CbmPsdMC.h:55:  virtual Bool_t IsSensitive(const std::string& name)
sim/detectors/psd/CbmPsdMC.h-56-  {
sim/detectors/psd/CbmPsdMC.h-57-    return (TString(name).Contains("scint", TString::kIgnoreCase) ? kTRUE : kFALSE);
sim/detectors/psd/CbmPsdMC.h-58-  }
  • TOF - this one is crazy as it has some extra checks, either should be designed better or candidate to override, but one sensor/medium?
sim/detectors/tof/CbmTof.cxx:547:Bool_t CbmTof::IsSensitive(const std::string& name)
sim/detectors/tof/CbmTof.cxx-548-{
sim/detectors/tof/CbmTof.cxx-549-  // If the current Cell volume belongs to a counter declared inactive w.r.t.
sim/detectors/tof/CbmTof.cxx-550-  // Monte Carlo point creation, it is not declared sensitive
sim/detectors/tof/CbmTof.cxx-551-  TString tsname = name;
sim/detectors/tof/CbmTof.cxx-552-  if (tsname.Contains("Cell")) {
sim/detectors/tof/CbmTof.cxx-553-    // FIXME: This does not work at the moment (see comment in the 'Initialize' method).
sim/detectors/tof/CbmTof.cxx-554-    for (auto const& InactiveCounter : fInactiveCounters) {
sim/detectors/tof/CbmTof.cxx-555-      if (std::get<0>(InactiveCounter) == fCurrentModuleType && std::get<1>(InactiveCounter) == fCurrentModuleIndex
sim/detectors/tof/CbmTof.cxx-556-          && std::get<2>(InactiveCounter) == fCurrentCounterIndex) {
sim/detectors/tof/CbmTof.cxx-557-        return kFALSE;
sim/detectors/tof/CbmTof.cxx-558-      }
sim/detectors/tof/CbmTof.cxx-559-    }
sim/detectors/tof/CbmTof.cxx-560-
sim/detectors/tof/CbmTof.cxx-561-    return kTRUE;
sim/detectors/tof/CbmTof.cxx-562-  }
  • FSD - one sensor/medium?
sim/detectors/fsd/CbmFsdMC.h:58:  virtual Bool_t IsSensitive(const std::string& name)
sim/detectors/fsd/CbmFsdMC.h-59-  {
sim/detectors/fsd/CbmFsdMC.h-60-    return (TString(name).Contains("scint", TString::kIgnoreCase) ? kTRUE : kFALSE);
sim/detectors/fsd/CbmFsdMC.h-61-  }
  • MUCH - one sensor/medium
sim/detectors/much/CbmMuch.cxx:390:Bool_t CbmMuch::IsSensitive(const std::string& name)
sim/detectors/much/CbmMuch.cxx-391-{
sim/detectors/much/CbmMuch.cxx-392-  TString tsname = name;
sim/detectors/much/CbmMuch.cxx-393-
sim/detectors/much/CbmMuch.cxx-394-
sim/detectors/much/CbmMuch.cxx-395-  if (tsname.Contains("active")) {
sim/detectors/much/CbmMuch.cxx-396-    LOG(debug1) << "CbmMuch::CheckIfSensitive: Register active volume: " << tsname;
sim/detectors/much/CbmMuch.cxx-397-    return kTRUE;
sim/detectors/much/CbmMuch.cxx-398-  }
sim/detectors/much/CbmMuch.cxx-399-  return kFALSE;
sim/detectors/much/CbmMuch.cxx-400-}

I wouldn't say that amount of work is here massive.

Don't know about PANDA. And what are the other user of FairRoot.

@karabowi
Copy link
Copy Markdown
Collaborator

karabowi commented Mar 30, 2026

Alice? R3B? Ship?
It is a breaking change, though.

Assume only one detector in CBM that uses a certain medium as active or passive using FairRoot's naming scheme.
This change forces the geometry to be redone using two different media: active and passive.
One would have to redo and test all the geometries. Ask David, if that's feasible.

We will discuss it.

@jose-luis-rs
Copy link
Copy Markdown

Hi, in R3BRoot we don’t use that function.

@ChristianTackeGSI
Copy link
Copy Markdown
Member

However, years ago, a IsSensitive/CheckIfSensitive function was introduced to the MC detector steering classes that makes detectors sensitive by detector specific sting matching.

string based matching is sooo inefficient. All you guys make a fuzz about C++ and performance, and then you're going for interpreter style tooling?

Okay, enough stupid ranting by me.

@ChristianTackeGSI
Copy link
Copy Markdown
Member

ChristianTackeGSI commented Mar 30, 2026

Okay, alternative new "interface" function:

bool IsVolumeSensitive(TGeoVolume const& vol) const
{
    // Ask old / legacy stuff.
    if (IsSensitive(vol.GetName()))
        return true;
    // Call new
    return IsVolumeSensitiveImpl(vol);
}

It first tries the old path. So that all the legacy stuff still works.
If the old path returns false (which it hopefully does as a fallback, if nobody has overriden it), it will call the new code.

This gives us a transition without a breaking change?

Then let's add a note to the CHANGELOG, that people should migrate.

Then we could add a Warning to the above like this

    if (IsSensitive(vol.GetName())) {
        LOG(warning) << "FairModule::IsSensitive is deprecated, please override IsVolumeSensitiveImpl()!";
        return true;
    }

v->RegisterYourself();
}
if ((this->InheritsFrom("FairDetector")) && IsSensitive(v->GetName())) {
if ((this->InheritsFrom("FairDetector")) && IsSensitive(v)) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this->InheritsFrom("FairDetector")

BTW: Can we please (in another PR!)

  • Move this out of the loop? It's run each time inside a loop!
  • Replace this by a (dynamic_cast<FairDetector>(this) != nullptr)?

*
* See Bool_t IsSensitive(const std::string& name) for deprecation notice.
*/
virtual bool IsSensitive(TGeoVolume* vol) { return (vol->GetMedium()->GetParam(0) == 1.0); }
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IF I got some of the notes correctly, then this is a standard attribute of the medium?

The 0 looks like a magic constant? Is there some const / define available for this?

Thinking about my backward compatible request, maybe the current default implementation should be return false;?

And in a later update we could use this one?

Yes, this means that everyone who wants to use this customization point would need to implement this code. But it would maybe help with PR acceptance?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Geant 3 defienes ISVOL flag:
image

And we use TGeoMedium which defines it in similar way as integer:
https://root.cern.ch/doc/master/TGeoMedium_8cxx_source.html#l00064

There is no constant/enum defined, just 0 - not sensitive, > 0 - sensitive. TGeoMedium stores parameters as floating points. The proper check could be actually vol->GetMedium()->GetParam(0) > 0.0.

So, as I also wrote it before, this is bi-state, either volume is senstivie or non-sensitive. Thats way false as a return is also a valid result of the check. we would need tri-state return value to check whether the function was implemented good candidate would be std::optional with {} as return. But this also requires to change function signature.

Other idea - using exceptions. Throw in the base function and catch it, or the derived function returns valid value. Executing the string-based check will give some deprecation message, and perhaps full removal in the future (?).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I meant, whether there's a constant for the 0 in ->GetParam(0)? This really feels like a magic constant. I don't like those.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, as you can find in line 84 of this code https://root.cern.ch/doc/master/TGeoMedium_8cxx_source.html#l00064, it is just magic number.

@rlalik
Copy link
Copy Markdown
Author

rlalik commented Mar 30, 2026

Alice? R3B? Ship? It is a breaking change, though.

OFFTOPIC: Alice uses FairRoot? Don't they have their own AliRoot? I'm just surprised.

@rlalik
Copy link
Copy Markdown
Author

rlalik commented Mar 30, 2026

Hi, in R3BRoot we don’t use that function.

@jose-luis-rs Out of curiosity, how do you test for sensitive detector? This function is called anyway. Do you have separate checks in ProcessHits?

@ChristianTackeGSI
Copy link
Copy Markdown
Member

About the fallback / etc:

the current IsSensitive(string) return false if not implemented, right?
If the new IsVolumeSensitiveImp(vol) would also return false by default (not the volume.Param(0)).

So if you want to return true at all, you need to override one of the two?

@rlalik
Copy link
Copy Markdown
Author

rlalik commented Apr 1, 2026

Yes, false is default, but if you want to check if function returned false to decided whether check for the fallback, like in your example:

bool IsVolumeSensitive(TGeoVolume const& vol) const
{
    // Ask old / legacy stuff.
    if (IsSensitive(vol.GetName()))
        return true;
    // Call new
    return IsVolumeSensitiveImpl(vol);
}

you cannot use false to make decision because this is valid return value. The volume may be set to inactive and the return value must be false then.

Or other case, we moved to the new interface and the volume is sensitive. At some point one decided to make it insensitive but the old override of the IsSensitive(vol.GetName()) still returns true and the new IsVolumeSensitiveImpl(vol) is never called.

I think this is bug prone and it is best to get rid of the old one entirely.

@ChristianTackeGSI
Copy link
Copy Markdown
Member

Let's leave this for @fuhlig1.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants