Skip to content

StripEnergyThresholdFinder for per-strip slow and fast threshold extraction with diagnostics - #166

Open
JarredMRoberts wants to merge 6 commits into
cositools:develop/emfrom
JarredMRoberts:feature/strip-threshold-finder
Open

StripEnergyThresholdFinder for per-strip slow and fast threshold extraction with diagnostics#166
JarredMRoberts wants to merge 6 commits into
cositools:develop/emfrom
JarredMRoberts:feature/strip-threshold-finder

Conversation

@JarredMRoberts

Copy link
Copy Markdown

Adds a standalone application, StripEnergyThresholdFinder, for computing per-strip slow and fast energy thresholds. Slow thresholds are determined from ADC spectra using a noise peak and trough method, while fast thresholds are determined from dt0/dt1 timing crossover. The tool reads calibrated HDF5 data via MModuleLoaderMeasurementsHDF, applies strip mapping and energy calibration from a YAML configuration, and produces ROOT diagnostic outputs (energy spectra with thresholds, dt0 vs dt1 per strip, and threshold distributions) along with CSV export files. The implementation is self-contained under apps/ and does not modify existing modules. Tested on COSI datasets with consistent threshold behavior and expected diagnostic results. Target branch is develop/em.

@JarredMRoberts

Copy link
Copy Markdown
Author

I still need to fix all of the code style issues and work on some optimizations to speed the code up a bit.

@fhagemann

Copy link
Copy Markdown

Is this different from #143 or making #143 obsolete?

Comment thread apps/StripEnergyThresholdFinder.cxx Outdated
Comment on lines +77 to +89
struct StripKey
{
int det;
char side;
int strip;

bool operator<(const StripKey& o) const
{
if(det!=o.det) return det<o.det;
if(side!=o.side) return side<o.side;
return strip<o.strip;
}
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Can you not reuse the code from MStripMap.cxx here?

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.

Good point. I checked MStripMap, but it looks to me like it's centered on ReadOutID lookups. I’m currently using a local StripKey for (det, side, strip) grouping. It made more sense to me to think of the strip mapping from a detector -> side -> strip direction, especially when reviewing the code. I'd be happy to adjust it if we want to prioritize tighter integration.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Isn't that functionality exactly what MReadOutElementDoubleStrip does?

@JarredMRoberts JarredMRoberts Jul 28, 2026

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.

I did take a look at MReadOutElementDoubleStrip, and it looks like it's doing something similar (detector, strip, side), but it also looks like it's part of the MReadOutElement hierarchy, and it includes additional things (parsing, cloning, comparing objects) that we don't need here. We only need a quick identifier to group histogram data as a key in a container. It seems like using MReadOutElementDoubleStrip could bring in additional dependencies that complicate things. If there's a preferred way to represent strip identifiers in this context, then I'd be more than happy to try to figure it out. Let me know!

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This can indeed be replaced by MReadOutElementDoubleStrip. I opened a PR onto your branch, where one of the commits replaces StripKey by MReadOutElementDoubleStrip.

Comment thread apps/StripEnergyThresholdFinder.cxx Outdated
Comment on lines +97 to +189
class EnergyCalHelper
{
public:

struct Coeff
{
double c0=0;
double c1=0;
double c2=0;
double c3=0;
};

bool Load(const string& fileName,int nDet=64,int nStrip=65)
{
m_NDet=nDet;
m_NStrip=nStrip;

m_Coeffs.resize(m_NDet);

for(int d=0;d<m_NDet;d++)
{
m_Coeffs[d].resize(2);

for(int s=0;s<2;s++)
m_Coeffs[d][s].resize(m_NStrip);
}

ifstream in(fileName);

if(!in)
{
cout<<"Unable to open calibration file "<<fileName<<endl;
return false;
}

string line;

while(getline(in,line))
{
if(line.empty()) continue;

stringstream ss(line);

string tag;
ss>>tag;

if(tag!="CM") continue;

string unused;
int det=-1;
int strip=-1;
string side;
string order;

ss>>unused>>det>>strip>>side>>order;

if(det<0||det>=m_NDet) continue;
if(strip<0||strip>=m_NStrip) continue;

int sideInt=(side=="l")?0:1;

Coeff c;

if(order=="poly1zero")
ss>>c.c1;
else if(order=="poly1")
ss>>c.c0>>c.c1;
else if(order=="poly2")
ss>>c.c0>>c.c1>>c.c2;
else
ss>>c.c0>>c.c1>>c.c2>>c.c3;

m_Coeffs[det][sideInt][strip]=c;
}

return true;
}

double ADCToEnergy(int det,char side,int strip,double adc) const
{
int sideInt=(side=='l')?0:1;
const Coeff& c=m_Coeffs[det][sideInt][strip];

return c.c3*pow(adc,3)+c.c2*pow(adc,2)+c.c1*adc+c.c0;
}

private:

int m_NDet;
int m_NStrip;

vector<vector<vector<Coeff>>> m_Coeffs;
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This essentially does what MModuleEnergyCalibration does.
Can you create a MModuleEnergyCalibration variable in this app and use its functions here to avoid code duplication and keep the code for this app short?

@JarredMRoberts JarredMRoberts Jul 14, 2026

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.

Yes. I wrote the threshold app to be standalone, not requiring the full MSupervisor pipeline for calibration, so I built a lightweight calibration helper for direct ADC-to-energy conversion to occur within the app. But, it definitely duplicates functionality found in MModuleEnergyCalibration. I'll refactor the app to use the standard module chain.

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.

Alright, so I worked on integrating MModuleEnergyCalibration into the event processing, and right now it's being used to calibrate the strip hits (AnalyzeEvent and SH->GetEnergy()). It worked just fine, there. I also took a look at replacing EnergyCalHelper entirely, but I ran into some issues that broke things. In this app we're mostly operating on histogrammed data (ADC counts), not individual hits within an event. At a basic level, we just need to convert arbitrary ADC values into keV (like when we need to determine thresholds or set histogram axes), and not just values associated with a specific hit. The MModule applies calibration within the event, but it doesn't seem to provide a way to convert the ADC values outside of the event context. So, I think it might be best to keep EnergyCalHelper for now to perform those conversions, while using the MModule for all event-level calibration. If there is a more "MEGAlib" way to handle the quick ADC to keV conversions outside of the event-level pipeline, I'd be happy to look into that!

@fhagemann fhagemann Aug 10, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Same thing here: check out the PR JarredMRoberts#1 onto your branch, that replaces EnergyCalHelper with the MModuleEnergyCalibration functionality, outside of the event-level pipeline.

Comment thread apps/StripEnergyThresholdFinder.cxx Outdated
Comment on lines +195 to +251
class TACCalHelper
{
public:

struct Coeff
{
double slope = 0;
double offset = 0;
};

bool Load(const string& fileName)
{
ifstream in(fileName);
if(!in)
{
cout<<"Failed to open TAC calibration file: "<<fileName<<endl;
return false;
}

string line;
getline(in,line); // skip header

while(getline(in,line))
{
if(line.empty()) continue;

stringstream ss(line);

int strip_id, det, side, strip;
double slope, slope_err, offset, offset_err;

ss >> strip_id >> det >> side >> strip
>> slope >> slope_err >> offset >> offset_err;

char sideChar = (side==0) ? 'l' : 'h';

StripKey key{det, sideChar, strip};

m_Coeffs[key] = {slope, offset};
}

return true;
}

double TACToEnergy(int det, char side, int strip, double tac) const
{
StripKey key{det, side, strip};

auto it = m_Coeffs.find(key);
if(it == m_Coeffs.end()) return 0;

return it->second.slope * tac + it->second.offset;
}

private:
map<StripKey, Coeff> m_Coeffs;
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This already exists in MModuleTACcut --> use existing class to avoid code duplication here.

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.

I took another close look at MModuleTACCut. This issue I'm running into is that this requires both a TAC Calibration file and a TAC cut file to run smoothly. It's also performing event filtering as part of its operation. For the threshold finding app, I'd prefer that we only use the TAC calibration file as an input, and I'd like to avoid applying any TAC cuts to the data in order to find the fast shaper thresholds. I did attempt to see if I could figure out a workaround to trick MModuleTACCut into thinking it was receiving a TAC cut file with arbitrarily low values, but this ended up being a bit odd. I'd prefer to use the simple TACCalHelper I've written instead.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

It looks like TACCalHelper is never used. I believe for this app you don't need to calibrate TAC values, you just care if a strip had FastTiming or not. So, I believe this whole thing can go.

Comment thread apps/StripEnergyThresholdFinder.cxx Outdated
private:

// --- Configuration ---
EnergyCalHelper m_EnergyCal;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggested change
EnergyCalHelper m_EnergyCal;
MModuleEnergyCalibration m_EnergyCal;

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.

MModuleEnergyCalibration is now being used for the event-level calibration but since this code operates on histogram ADC data, we still need a way to convert arbitrary AC values to keV outside of the event-level data. I'd prefer to keep EnergyCalHelper for this reason.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

You can convert arbitrary ADC values to keV outside of the event-level data, by just using MModuleEnergyCalibration::GetEnergy instead of EnergyCalHelper::ADCToEnergy, which essentially does the same thing.

Comment thread apps/StripEnergyThresholdFinder.cxx Outdated
Comment thread apps/StripEnergyThresholdFinder.cxx Outdated


//EnergyCalHelper m_EnergyCal;
TACCalHelper m_EnergyCal_TAC;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

That's a weird name, is Energy intended in this name? I would just call it m_TACCal.. And if it's called m_..., consider making this a (private) member variable of the class -- just like m_EnergyCal

Also: where is this TACCalHelper ever used?

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.

Ugh, good point. It's a bad name, and it isn't actually used anywhere :|

It's removed now.

Comment thread apps/StripEnergyThresholdFinder.cxx Outdated
//EnergyCalHelper m_EnergyCal;
TACCalHelper m_EnergyCal_TAC;

if(!m_EnergyCal.Load(m_CalibrationFile.Data()))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

MModuleEnergyCalibration has a ReadEnergyCalibrationFile function that you could call here instead:

Suggested change
if(!m_EnergyCal.Load(m_CalibrationFile.Data()))
if (m_EnergyCal.ReadEnergyCalibrationFile(m_CalibrationFile) == false)

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.

Based on the comments above about how I'd like to keep the EnergyCalHelper for ADC to keV conversions in histogrammed data outside of the event context, let's keep this one as it is if that's ok.

@fhagemann fhagemann Aug 10, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

See my PR JarredMRoberts#1 to see how this can still be achieved without the need of EnergyCalHelper.

Comment thread apps/StripEnergyThresholdFinder.cxx Outdated
EnergyCalHelper m_EnergyCal;
vector<string> m_InputFiles;
MString m_CalibrationFile;
MString m_TACCalibrationFile;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This seems to never be used.

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.

Hm, I'm seeing that only m_TACCalibrationFile is no longer being used. I can't remember what it used to be a part of, but it can be removed now. It looks like the other ones are being used for histogram building and ADC conversion.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

See comment above: I don't think that you are applying any TAC calibration or TAC cuts, so this can be safely removed (or the code to calibrate TAC values from ADC units to ns still needs to be added).

Comment thread apps/StripEnergyThresholdFinder.cxx Outdated
Comment thread apps/StripEnergyThresholdFinder.cxx Outdated
@fhagemann
fhagemann marked this pull request as draft June 23, 2026 21:50
JarredMRoberts and others added 3 commits June 30, 2026 07:33
Co-authored-by: Felix Hagemann <hagemann@berkeley.edu>
Co-authored-by: Felix Hagemann <hagemann@berkeley.edu>
…nventions and integrate energy calibration improvements
@JarredMRoberts
JarredMRoberts marked this pull request as ready for review July 28, 2026 08:40
#include <chrono>

/* YAML */
#include <yaml-cpp/yaml.h>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I had to add -lyaml-cpp to ALLLIBS in the Makefile to compile this app:

ALLLIBS = -L$(LB) -lResponseCreator -lFretalonBase -lSivan -lRevanGui -lRevan -lMimrec -lGeomega -lSpectralyzeGui -lSpectralyze -lCommonMisc -lCommonGui -L$(MEGALIB)/lib -L$(LB)

Is there an alternative to running this app without depending on YAML? 😇

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

But even if it compiled, running StripEnergyThresholdFinder $NUCLEARIZER/apps/StripEnergyThresholdFinder_config.yaml resulted in the following error:

terminate called after throwing an instance of 'YAML::TypedBadConversion<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >'
  what():  bad conversion
Aborted (core dumped)

@fhagemann fhagemann Aug 10, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The error I posted went away once the mismatch in capitalization between strip_map in the config.yaml and parsing Strip_map in the app was resolved :)

@fhagemann fhagemann left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hi Jarred,
I had an extensive look at this app and finally got this to run.
I replied to all comments in this PR, and opened a separate PR onto your fork/branch, addressing some code changes to

  1. get this app running
  2. remove the helper classes by replacing them with existing nuclearizer/megalib code

Here is the PR onto your branch with detailed code changes: JarredMRoberts#1



m_CalibrationFile = config["input"]["calibration_file"].as<string>().c_str();
m_StripMapFile = config["input"]["Strip_map"].as<string>().c_str();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This is what broke my code:

Suggested change
m_StripMapFile = config["input"]["Strip_map"].as<string>().c_str();
m_StripMapFile = config["input"]["strip_map"].as<string>().c_str();

You might need to add specific guards here to make sure that the fields in the YAML file exist, before trying to parse it.
Or avoid YAML completely and write your own custom parser here.

#include <chrono>

/* YAML */
#include <yaml-cpp/yaml.h>

@fhagemann fhagemann Aug 10, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The error I posted went away once the mismatch in capitalization between strip_map in the config.yaml and parsing Strip_map in the app was resolved :)

Comment thread apps/StripEnergyThresholdFinder.cxx Outdated
Comment on lines +77 to +89
struct StripKey
{
int det;
char side;
int strip;

bool operator<(const StripKey& o) const
{
if(det!=o.det) return det<o.det;
if(side!=o.side) return side<o.side;
return strip<o.strip;
}
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This can indeed be replaced by MReadOutElementDoubleStrip. I opened a PR onto your branch, where one of the commits replaces StripKey by MReadOutElementDoubleStrip.

Comment thread apps/StripEnergyThresholdFinder.cxx Outdated
Comment on lines +97 to +189
class EnergyCalHelper
{
public:

struct Coeff
{
double c0=0;
double c1=0;
double c2=0;
double c3=0;
};

bool Load(const string& fileName,int nDet=64,int nStrip=65)
{
m_NDet=nDet;
m_NStrip=nStrip;

m_Coeffs.resize(m_NDet);

for(int d=0;d<m_NDet;d++)
{
m_Coeffs[d].resize(2);

for(int s=0;s<2;s++)
m_Coeffs[d][s].resize(m_NStrip);
}

ifstream in(fileName);

if(!in)
{
cout<<"Unable to open calibration file "<<fileName<<endl;
return false;
}

string line;

while(getline(in,line))
{
if(line.empty()) continue;

stringstream ss(line);

string tag;
ss>>tag;

if(tag!="CM") continue;

string unused;
int det=-1;
int strip=-1;
string side;
string order;

ss>>unused>>det>>strip>>side>>order;

if(det<0||det>=m_NDet) continue;
if(strip<0||strip>=m_NStrip) continue;

int sideInt=(side=="l")?0:1;

Coeff c;

if(order=="poly1zero")
ss>>c.c1;
else if(order=="poly1")
ss>>c.c0>>c.c1;
else if(order=="poly2")
ss>>c.c0>>c.c1>>c.c2;
else
ss>>c.c0>>c.c1>>c.c2>>c.c3;

m_Coeffs[det][sideInt][strip]=c;
}

return true;
}

double ADCToEnergy(int det,char side,int strip,double adc) const
{
int sideInt=(side=='l')?0:1;
const Coeff& c=m_Coeffs[det][sideInt][strip];

return c.c3*pow(adc,3)+c.c2*pow(adc,2)+c.c1*adc+c.c0;
}

private:

int m_NDet;
int m_NStrip;

vector<vector<vector<Coeff>>> m_Coeffs;
};

@fhagemann fhagemann Aug 10, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Same thing here: check out the PR JarredMRoberts#1 onto your branch, that replaces EnergyCalHelper with the MModuleEnergyCalibration functionality, outside of the event-level pipeline.

Comment thread apps/StripEnergyThresholdFinder.cxx Outdated
Comment on lines +195 to +251
class TACCalHelper
{
public:

struct Coeff
{
double slope = 0;
double offset = 0;
};

bool Load(const string& fileName)
{
ifstream in(fileName);
if(!in)
{
cout<<"Failed to open TAC calibration file: "<<fileName<<endl;
return false;
}

string line;
getline(in,line); // skip header

while(getline(in,line))
{
if(line.empty()) continue;

stringstream ss(line);

int strip_id, det, side, strip;
double slope, slope_err, offset, offset_err;

ss >> strip_id >> det >> side >> strip
>> slope >> slope_err >> offset >> offset_err;

char sideChar = (side==0) ? 'l' : 'h';

StripKey key{det, sideChar, strip};

m_Coeffs[key] = {slope, offset};
}

return true;
}

double TACToEnergy(int det, char side, int strip, double tac) const
{
StripKey key{det, side, strip};

auto it = m_Coeffs.find(key);
if(it == m_Coeffs.end()) return 0;

return it->second.slope * tac + it->second.offset;
}

private:
map<StripKey, Coeff> m_Coeffs;
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

It looks like TACCalHelper is never used. I believe for this app you don't need to calibrate TAC values, you just care if a strip had FastTiming or not. So, I believe this whole thing can go.

Comment thread apps/StripEnergyThresholdFinder.cxx Outdated
private:

// --- Configuration ---
EnergyCalHelper m_EnergyCal;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

You can convert arbitrary ADC values to keV outside of the event-level data, by just using MModuleEnergyCalibration::GetEnergy instead of EnergyCalHelper::ADCToEnergy, which essentially does the same thing.

Comment thread apps/StripEnergyThresholdFinder.cxx Outdated
EnergyCalHelper m_EnergyCal;
vector<string> m_InputFiles;
MString m_CalibrationFile;
MString m_TACCalibrationFile;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

See comment above: I don't think that you are applying any TAC calibration or TAC cuts, so this can be safely removed (or the code to calibrate TAC values from ADC units to ns still needs to be added).

Comment thread apps/StripEnergyThresholdFinder.cxx Outdated
//EnergyCalHelper m_EnergyCal;
TACCalHelper m_EnergyCal_TAC;

if(!m_EnergyCal.Load(m_CalibrationFile.Data()))

@fhagemann fhagemann Aug 10, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

See my PR JarredMRoberts#1 to see how this can still be achieved without the need of EnergyCalHelper.

line->Draw("SAME");

// Legend entry (fix from earlier)
leg->AddEntry(line, "Fast Thresholdeshold", "l");

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggested change
leg->AddEntry(line, "Fast Thresholdeshold", "l");
leg->AddEntry(line, "Fast Threshold", "l");

#include <chrono>

/* YAML */
#include <yaml-cpp/yaml.h>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

After reviewing #184: Please remove the YAML dependency.

I would strongly suggest that we pass the required parameters either via terminal command options (see #184 for example), or via a custom file format with a custom parser. But in my opinion, it is an overkill to add the YAML dependency to nuclearizer and requiring it in the make process just for this app (also for users who might not even need to run this app).

@zoglauer

zoglauer commented Aug 17, 2026

Copy link
Copy Markdown
Collaborator

I second not to have an additional parser linked.
You can use the nuclearizer/megalib XML file format instead; your file then would be:

<strip-threshold-finder>
    <input>
        <data_files>
            <file>/path/to/data/file.hdf5</file>
        </data_files>
        <calibration_file>/path/to/calibration/file.ecal</calibration_file>
        <tac_calibration_file>/path/to/tac/calibration/file.csv</tac_calibration_file>
        <strip_map>/path/to/strip/map/file.map</strip_map>
    </input>

    <analysis>
        <!-- number of skipped strips with minimum statistics -->
        <min_entries>10</min_entries> 
        
        <!-- If a threshold cannot be determined set threshold to default value -->
        <fallback_threshold_keV>20</fallback_threshold_keV> 
        
        <!-- limit for locating the low-energy noise peak -->
        <!-- most noise peaks should be between 100 and 250 -->
        <!-- Worst case the ADC max should be set to ~1000 -->
        <noise_search_max_adc>1800</noise_search_max_adc>
    </analysis>
    
    <output>
        <prefix>output_file_prefix</prefix>
    </output>
</strip-threshold-finder>

@fhagemann fhagemann left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

One more remark: I got this app to run and got reasonable results for the fast and slow thresholds only when using a dataset taken with NN off.
We might want to always filter out NN events, for this app to also run using datasets taken with NN on.

Comment on lines +690 to +697
MModuleLoaderMeasurementsHDF* Loader = new MModuleLoaderMeasurementsHDF();

Loader->SetFileName(m_InputFiles[0].c_str());

cout << "Loading file: " << m_InputFiles[0] << endl;
cout << "Number of input files: " << m_InputFiles.size() << endl;

Loader->SetFileNameStripMap(m_StripMapFile.Data());

@fhagemann fhagemann Aug 17, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This app only works when using a dataset where NN was off.
If NN was on, we might require filtering those NN events out for later analysis by adding
Loader->SetIncludeNearestNeighbor(false);:

Suggested change
MModuleLoaderMeasurementsHDF* Loader = new MModuleLoaderMeasurementsHDF();
Loader->SetFileName(m_InputFiles[0].c_str());
cout << "Loading file: " << m_InputFiles[0] << endl;
cout << "Number of input files: " << m_InputFiles.size() << endl;
Loader->SetFileNameStripMap(m_StripMapFile.Data());
MModuleLoaderMeasurementsHDF* Loader = new MModuleLoaderMeasurementsHDF();
Loader->SetFileName(m_InputFiles[0].c_str());
cout << "Loading file: " << m_InputFiles[0] << endl;
cout << "Number of input files: " << m_InputFiles.size() << endl;
Loader->SetFileNameStripMap(m_StripMapFile.Data());
Loader->SetIncludeNearestNeighbor(false);

Using gse_20260217T115220.hdf5:

Current state, not filtering NN events:

Slow thresholds Example energy spectrum (HV10)
image image

Filtering NN events:

Slow thresholds Example energy spectrum (HV10)
image image

@fhagemann fhagemann Aug 17, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

For some strips, the app still seems to pick up the wrong "noise peak" in NN-on datasets, after filtering NN events:
image

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.

3 participants