Skip to content

Commit e1566e1

Browse files
committed
Specialize reading in Symetrica N42 files.
1 parent 2641dbb commit e1566e1

File tree

2 files changed

+134
-14
lines changed

2 files changed

+134
-14
lines changed

src/SpecFile.cpp

+5-6
Original file line numberDiff line numberDiff line change
@@ -3029,11 +3029,11 @@ void Measurement::equal_enough( const Measurement &lhs, const Measurement &rhs )
30293029
}
30303030
*/
30313031

3032-
if( lhs.detector_description_ != rhs.detector_description_
3032+
if( trim_copy(lhs.detector_description_) != trim_copy(rhs.detector_description_)
30333033
&& rhs.detector_description_!="Gamma and Neutron"
3034-
&& ("NaI, " + lhs.detector_description_) != rhs.detector_description_
3035-
&& ("LaBr3, " + lhs.detector_description_) != rhs.detector_description_
3036-
&& ("unknown, " + lhs.detector_description_) != rhs.detector_description_
3034+
&& ("NaI, " + trim_copy(lhs.detector_description_)) != trim_copy(rhs.detector_description_)
3035+
&& ("LaBr3, " + trim_copy(lhs.detector_description_)) != trim_copy(rhs.detector_description_)
3036+
&& ("unknown, " + trim_copy(lhs.detector_description_)) != trim_copy(rhs.detector_description_)
30373037
)
30383038
{
30393039
//static std::atomic<int> ntimesprint(0);
@@ -3318,8 +3318,7 @@ void Measurement::equal_enough( const Measurement &lhs, const Measurement &rhs )
33183318
}//for( size_t i = 0; i < lhs.neutron_counts_.size(); ++i )
33193319
}//if( lhs.neutron_counts_.size() != rhs.neutron_counts_.size() ) / else
33203320

3321-
3322-
if( lhs.title_ != rhs.title_ )
3321+
if( trim_copy(lhs.title_) != trim_copy(rhs.title_) )
33233322
issues.push_back( "Measurement: Title for LHS ('" + lhs.title_
33243323
+ "') doesnt match RHS ('" + rhs.title_ + "')" );
33253324

src/SpecFile_n42.cpp

+129-8
Original file line numberDiff line numberDiff line change
@@ -3206,7 +3206,7 @@ struct N42DecodeHelper2012
32063206
}//std::string concat_2012_N42_characteristic_node( const rapidxml::xml_node<char> *node )
32073207

32083208

3209-
static void set_deriv_data( shared_ptr<Measurement> &meas, const string &dd_id, const string &spec_id )
3209+
static void set_deriv_data( const shared_ptr<Measurement> &meas, const string &dd_id, const string &spec_id )
32103210
{
32113211
typedef Measurement::DerivedDataProperties DerivedProps;
32123212

@@ -7927,7 +7927,7 @@ namespace SpecUtils
79277927

79287928
get_2012_N42_energy_calibrations( calibrations, rad_data_node, remarks_, parse_warnings_ );
79297929

7930-
//XXX - implement using RadItemInformation
7930+
//TODO: implement using RadItemInformation
79317931
// for( const rapidxml::xml_node<char> *rad_item_node = XML_FIRST_NODE(rad_data_node, "RadItemInformation");
79327932
// rad_item_node;
79337933
// rad_item_node = XML_NEXT_TWIN(rad_item_node) )
@@ -8182,25 +8182,52 @@ namespace SpecUtils
81828182
size_t numRadMeasNodes = 0;
81838183
std::mutex meas_mutex, calib_mutex;
81848184

8185+
// Symetrica detectors _may_ have a <RadMeasurement> node with id="ForegroundMeasureSum",
8186+
// "BackgroundMeasure...5" (for gamma), "BackgroundMeasure...6" (for neutron),
8187+
// "CalMeasurementGamma-...", "StabMeasurement...", and then they have a bunch
8188+
// of ("TickMeasurement..."|"ForegroundMeasure...") that are 0.2 seconds long.
8189+
// We probably want the "ForegroundMeasureSum" + "BackgroundMeasure..." (neut+gamma
8190+
// combined) - so we will separate all these from the "Tick" measurements, by
8191+
// marking them as derived (which I guess they kinda are).
8192+
// Its not great, but maybe better than nothing.
8193+
// A particular example where this was giving trouble can be seen in ref3Z3LPD6CY6
8194+
const bool is_symetrica = SpecUtils::istarts_with( manufacturer_, "symetrica" );
8195+
vector<shared_ptr<vector<shared_ptr<Measurement>>>> symetrica_special_meas;
8196+
vector<pair<string,string>> symetrica_special_attribs;
8197+
8198+
8199+
81858200
// See note above about system that has multiple N42 documents in a single file.
81868201
for( const rapidxml::xml_node<char> *rad_data_node = data_node; rad_data_node;
81878202
rad_data_node = rad_data_node->next_sibling("RadInstrumentData") )
81888203
{
81898204
XML_FOREACH_CHILD( meas_node, rad_data_node, "RadMeasurement" )
81908205
{
8191-
//see ref3Z3LPD6CY6
8192-
if( numRadMeasNodes > 32 && xml_value_compare(meas_node->first_attribute("id"), "ForegroundMeasureSum") )
8193-
{
8194-
continue;
8195-
}
8196-
81978206
std::shared_ptr<std::mutex> mutexptr = std::make_shared<std::mutex>();
81988207
auto these_meas = std::make_shared< vector<std::shared_ptr<Measurement> > >();
81998208

82008209
++numRadMeasNodes;
82018210
meas_mutexs.push_back( mutexptr );
82028211
measurements_each_meas.push_back( these_meas );
82038212

8213+
if( is_symetrica )
8214+
{
8215+
const rapidxml::xml_attribute<char> *id_attrib = meas_node->first_attribute("id");
8216+
const string id_val_str = xml_value_str( id_attrib );
8217+
8218+
if( SpecUtils::istarts_with(id_val_str, "ForegroundMeasureSum")
8219+
|| SpecUtils::istarts_with(id_val_str, "BackgroundMeasure")
8220+
|| SpecUtils::istarts_with(id_val_str, "StabMeasurement")
8221+
|| SpecUtils::istarts_with(id_val_str, "CalMeasurementGamma")
8222+
)
8223+
{
8224+
symetrica_special_meas.push_back( these_meas );
8225+
8226+
const string spec_id = xml_value_str( XML_FIRST_NODE(meas_node, "Spectrum") );
8227+
symetrica_special_attribs.push_back( std::make_pair(id_val_str, spec_id) );
8228+
}//
8229+
}//if( is_symetrica )
8230+
82048231
workerpool.post( [these_meas,meas_node,&id_to_dettype,&calibrations,mutexptr,&calib_mutex](){
82058232
N42DecodeHelper2012::decode_2012_N42_rad_measurement_node( *these_meas, meas_node, &id_to_dettype, &calibrations, *mutexptr, calib_mutex );
82068233
} );
@@ -8223,6 +8250,100 @@ namespace SpecUtils
82238250

82248251
workerpool.join();
82258252

8253+
8254+
// Now go back up and mark Symetrica special measurements as derived, so this way the
8255+
// "raw" and "derived" datas will kinda be sensical.
8256+
assert( symetrica_special_meas.size() == symetrica_special_attribs.size() );
8257+
if( !symetrica_special_meas.empty() )
8258+
{
8259+
// We will look for a background neutron-only record, and a background gamma-only
8260+
// record, and combine them, if found (and then remove from `measurements_each_meas`)
8261+
// TODO: For multi-detector systems (portals) we should actually try to match the neutrons
8262+
// to gammas for each detector, but we arent doing that yet
8263+
bool single_gamma_neutron_back = true;
8264+
shared_ptr<Measurement> neut_only_back, gamma_only_back;
8265+
8266+
for( size_t sym_index = 0;
8267+
single_gamma_neutron_back && (sym_index < symetrica_special_meas.size()); ++sym_index )
8268+
{
8269+
if( !symetrica_special_meas[sym_index] || (sym_index >= symetrica_special_attribs.size()) )
8270+
continue;
8271+
8272+
const shared_ptr<vector<shared_ptr<Measurement>>> &measv = symetrica_special_meas[sym_index];
8273+
const pair<string,string> &attribs = symetrica_special_attribs[sym_index];
8274+
8275+
for( const shared_ptr<Measurement> &m : *measv )
8276+
{
8277+
N42DecodeHelper2012::set_deriv_data( m, attribs.first, attribs.second );
8278+
if( m->source_type() == SourceType::Background )
8279+
{
8280+
if( (m->num_gamma_channels() > 6) && !m->contained_neutron() )
8281+
{
8282+
single_gamma_neutron_back = (single_gamma_neutron_back && !gamma_only_back);
8283+
gamma_only_back = m;
8284+
}else if( !m->num_gamma_channels() && m->contained_neutron() )
8285+
{
8286+
single_gamma_neutron_back = (single_gamma_neutron_back && !neut_only_back);
8287+
neut_only_back = m;
8288+
}
8289+
if( !single_gamma_neutron_back )
8290+
break;
8291+
}//if( m->source_type() == SourceType::Background )
8292+
}//for( const shared_ptr<Measurement> &m : *measv )
8293+
}//for( const auto &meass : symetrica_special_meas )
8294+
8295+
if( single_gamma_neutron_back && gamma_only_back && neut_only_back )
8296+
{
8297+
// Below is commented out 20231004 - only leaving in for a short while to look at different variants
8298+
//cout << "Symetrica\n\tNeutron:"
8299+
//"\n\t\tstart_time=" << to_iso_string( neut_only_back->start_time() )
8300+
//<< "\n\t\tRealTime=" << neut_only_back->real_time()
8301+
//<< "\n\t\tLiveTime=" << neut_only_back->live_time()
8302+
//<< "\n\tGamma:"
8303+
//<< "\n\t\tstart_time=" << to_iso_string( gamma_only_back->start_time() )
8304+
//<< "\n\t\tRealTime=" << gamma_only_back->real_time()
8305+
//<< "\n\t\tLiveTime=" << gamma_only_back->live_time()
8306+
//<< "\nDiffs:"
8307+
//<< "\n\t\tStartTime: " << chrono::duration_cast<chrono::microseconds>(neut_only_back->start_time() - gamma_only_back->start_time()).count() << " us"
8308+
//<< "\n\t\tRealTime: " << (neut_only_back->real_time() - gamma_only_back->real_time())
8309+
//<< "\n\t\tLiveTime: " << (neut_only_back->live_time() - gamma_only_back->live_time())
8310+
//<< endl;
8311+
8312+
// The neutron background isnt always at the same time, or same duration, so dont combine
8313+
// in these cases
8314+
const float gamma_rt = gamma_only_back->real_time();
8315+
if( ((neut_only_back->start_time() - gamma_only_back->start_time()) < chrono::seconds(15)) //Arbitrary 15 seconds
8316+
&& fabs((neut_only_back->real_time() - gamma_rt) < 0.01*gamma_rt) ) //Arbitrary 1% match
8317+
{
8318+
gamma_only_back->contained_neutron_ = true;
8319+
gamma_only_back->remarks_.push_back( "Neutron and gamma backgrounds have been combined into a single record." );
8320+
gamma_only_back->remarks_.push_back( "Neutron Real Time: PT" + to_string(neut_only_back->real_time()) + "S" );
8321+
gamma_only_back->remarks_.push_back( "Neutron Live Time: PT" + to_string(neut_only_back->live_time()) + "S" );
8322+
8323+
gamma_only_back->neutron_counts_ = neut_only_back->neutron_counts_;
8324+
gamma_only_back->neutron_counts_sum_ = neut_only_back->neutron_counts_sum_;
8325+
8326+
if( (gamma_only_back->dose_rate_ >= 0.0) && (neut_only_back->dose_rate_ >= 0.0) )
8327+
gamma_only_back->dose_rate_ += neut_only_back->dose_rate_;
8328+
if( (gamma_only_back->exposure_rate_ >= 0.0) && (neut_only_back->exposure_rate_ >= 0.0) )
8329+
gamma_only_back->exposure_rate_ += neut_only_back->exposure_rate_;
8330+
8331+
for( auto miter = begin(measurements_each_meas); miter != end(measurements_each_meas); ++miter )
8332+
{
8333+
auto &measv = **miter;
8334+
auto pos = std::find( begin(measv), end(measv), neut_only_back );
8335+
if( pos != end(measv) )
8336+
{
8337+
measv.erase( pos );
8338+
break;
8339+
}//if( pos != end(measv) )
8340+
}
8341+
}//if( gamma/neutron start and real times are reasonably close )
8342+
}//if( gamma_only_back && neut_only_back )
8343+
}//if( !symetrica_special_meas.empty() )
8344+
8345+
8346+
82268347
for( size_t i = 0; i < measurements_each_meas.size(); ++i )
82278348
for( size_t j = 0; j < measurements_each_meas[i]->size(); ++j )
82288349
measurements_.push_back( (*measurements_each_meas[i])[j] );

0 commit comments

Comments
 (0)