Skip to content

Commit fdee6ef

Browse files
committed
Merge branch 'master' of github.com:sandialabs/SpecUtils
2 parents 4e12082 + 0b9939e commit fdee6ef

File tree

3 files changed

+146
-16
lines changed

3 files changed

+146
-16
lines changed

.github/workflows/wheels.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ jobs:
7171
url: https://pypi.org/p/SandiaSpecUtils
7272
permissions:
7373
id-token: write # IMPORTANT: this permission is mandatory for trusted publishing
74-
if: github.event_name == 'release' && github.event.action == 'published'
74+
#if: github.event_name == 'release' && github.event.action == 'published'
7575

7676
steps:
7777
- uses: actions/setup-python@v5

bindings/python/SpecFile_py.cpp

Lines changed: 144 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -584,6 +584,17 @@ bool loadFromUri_wrapper(SpecUtils::SpecFile* info, py::object pystream) {
584584
l.append( p );
585585
return l;
586586
}
587+
588+
py::list meas_cal_coefficients_wrapper( const SpecUtils::Measurement *meas )
589+
{
590+
py::list l;
591+
if( !meas )
592+
return l;
593+
594+
for( const float p : meas->calibration_coeffs() )
595+
l.append( static_cast<double>(p) );
596+
return l;
597+
}
587598

588599
py::list gamma_counts_wrapper( const SpecUtils::Measurement *meas )
589600
{
@@ -979,6 +990,29 @@ bool loadFromUri_wrapper(SpecUtils::SpecFile* info, py::object pystream) {
979990
info->set_parse_warnings( remarks );
980991
}
981992

993+
py::list cal_channel_energies_wrapper( const SpecUtils::EnergyCalibration *cal )
994+
{
995+
py::list l;
996+
if( !cal || !cal->channel_energies() )
997+
return l;
998+
999+
for( auto p : *cal->channel_energies() )
1000+
l.append( p );
1001+
return l;
1002+
}
1003+
1004+
py::list cal_coefficients_wrapper( const SpecUtils::EnergyCalibration *cal )
1005+
{
1006+
py::list l;
1007+
if( !cal )
1008+
return l;
1009+
1010+
for( const float p : cal->coefficients() )
1011+
l.append( static_cast<double>(p) );
1012+
return l;
1013+
}
1014+
1015+
9821016
std::shared_ptr<SpecUtils::EnergyCalibration> energyCalFromPolynomial_wrapper( const size_t num_channels,
9831017
py::list py_coefs )
9841018
{
@@ -1058,7 +1092,70 @@ std::shared_ptr<SpecUtils::EnergyCalibration> energyCalFromLowerChannelEnergies_
10581092
cal->set_lower_channel_energy( num_channels, energies );
10591093
return cal;
10601094
}
1095+
1096+
py::list polynomial_coef_to_fullrangefraction_wrapper( py::list py_coefs, const size_t nchannel )
1097+
{
1098+
vector<float> coefs;
1099+
size_t n = py_coefs.size();
1100+
for( size_t i = 0; i < n; ++i )
1101+
coefs.push_back( py::cast<float>( py_coefs[i] ) );
1102+
1103+
const vector<float> frf_coefs = SpecUtils::polynomial_coef_to_fullrangefraction( coefs, nchannel );
1104+
1105+
py::list answer;
1106+
for( const float p : frf_coefs )
1107+
answer.append( static_cast<double>(p) );
1108+
return answer;
1109+
}
1110+
1111+
py::list fullrangefraction_coef_to_polynomial_wrapper( py::list py_coefs, const size_t nchannel )
1112+
{
1113+
vector<float> coefs;
1114+
size_t n = py_coefs.size();
1115+
for( size_t i = 0; i < n; ++i )
1116+
coefs.push_back( py::cast<float>( py_coefs[i] ) );
10611117

1118+
const vector<float> poly_coefs = SpecUtils::fullrangefraction_coef_to_polynomial( coefs, nchannel );
1119+
1120+
py::list answer;
1121+
for( const float p : poly_coefs )
1122+
answer.append( static_cast<double>(p) );
1123+
return answer;
1124+
}
1125+
1126+
1127+
py::list mid_channel_polynomial_to_fullrangeFraction_wrapper( py::list py_coefs, const size_t nchannel )
1128+
{
1129+
vector<float> coefs;
1130+
size_t n = py_coefs.size();
1131+
for( size_t i = 0; i < n; ++i )
1132+
coefs.push_back( py::cast<float>( py_coefs[i] ) );
1133+
1134+
const vector<float> frf_coefs = SpecUtils::mid_channel_polynomial_to_fullrangeFraction( coefs, nchannel );
1135+
1136+
py::list answer;
1137+
for( const float p : frf_coefs )
1138+
answer.append( static_cast<double>(p) );
1139+
return answer;
1140+
}
1141+
1142+
py::list mid_channel_polynomial_to_lower_channel_energy_poly_wrapper( py::list py_coefs, const size_t nchannel )
1143+
{
1144+
vector<float> coefs;
1145+
size_t n = py_coefs.size();
1146+
for( size_t i = 0; i < n; ++i )
1147+
coefs.push_back( py::cast<float>( py_coefs[i] ) );
1148+
1149+
const vector<float> frf_coefs = SpecUtils::mid_channel_polynomial_to_fullrangeFraction( coefs, nchannel );
1150+
const vector<float> poly_coefs = SpecUtils::fullrangefraction_coef_to_polynomial( frf_coefs, nchannel );
1151+
1152+
py::list answer;
1153+
for( const float p : poly_coefs )
1154+
answer.append( static_cast<double>(p) );
1155+
return answer;
1156+
}
1157+
1158+
10621159

10631160
#if( SpecUtils_ENABLE_D3_CHART )
10641161
bool write_spectrum_data_js_wrapper( py::object pystream,
@@ -1370,12 +1467,11 @@ py::class_<SpecUtils::EnergyCalibration>(m, "EnergyCalibration")
13701467
"Returns the energy calibration type" )
13711468
.def( "valid", &SpecUtils::EnergyCalibration::valid,
13721469
"Returns if the energy calibration is valid." )
1373-
.def( "coefficients", &SpecUtils::EnergyCalibration::coefficients, py::rv_policy::reference_internal,
1374-
"Returns the list of energy calibration coeficients.\n"
1470+
.def( "coefficients", &cal_coefficients_wrapper,
1471+
"Returns the list of energy calibration coefficients.\n"
13751472
"Will only be empty for SpecUtils.EnergyCalType.InvalidEquationType." )
1376-
// TODO: I think we should put a wrapper around channel_energies, and return a proper python list
1377-
.def( "channelEnergies", &SpecUtils::EnergyCalibration::channel_energies, py::rv_policy::reference_internal,
1378-
"Returns lower channel energies; will have one more entry than the number of channels." )
1473+
.def( "channelEnergies", &cal_channel_energies_wrapper,
1474+
"Returns lower channel energies; will have one more entry than the number of channels." )
13791475
.def( "deviationPairs", &SpecUtils::EnergyCalibration::deviation_pairs, py::rv_policy::reference_internal )
13801476
.def( "numChannels", &SpecUtils::EnergyCalibration::num_channels,
13811477
"Returns the number of channels this energy calibration is for." )
@@ -1388,32 +1484,66 @@ py::class_<SpecUtils::EnergyCalibration>(m, "EnergyCalibration")
13881484
.def( "upperEnergy", &SpecUtils::EnergyCalibration::upper_energy,
13891485
"Returns highest energy of this energy calibration." )
13901486
.def( "setPolynomial", &SpecUtils::EnergyCalibration::set_polynomial,
1391-
"NumChannels"_a, "Coeffiecients"_a, "DeviationPairs"_a,
1392-
"Sets the energy calibration information from Polynomial defined coefficents." )
1487+
"NumChannels"_a, "Coefficients"_a, "DeviationPairs"_a,
1488+
"Sets the energy calibration information from Polynomial defined coefficients." )
13931489
.def( "setFullRangeFraction", &SpecUtils::EnergyCalibration::set_full_range_fraction,
1394-
"NumChannels"_a, "Coeffiecients"_a, "DeviationPairs"_a,
1395-
"Sets the energy calibration information from Full Range Fraction (e.g., what PCF files use) defined coefficents." )
1490+
"NumChannels"_a, "Coefficients"_a, "DeviationPairs"_a,
1491+
"Sets the energy calibration information from Full Range Fraction (e.g., what PCF files use) defined coefficients." )
13961492
.def( "setLowerChannelEnergy", set_lower_channel_energy_fcn_ptr,
13971493
"NumChannels"_a, "Energies"_a,
13981494
"Sets the energy calibration information from lower channel energies." )
13991495
.def_static( "fromPolynomial", &energyCalFromPolynomial_wrapper,
1400-
"NumChannels"_a, "Coeffiecients"_a,
1496+
"NumChannels"_a, "Coefficients"_a,
14011497
"Creates a new energy calibration object from a polynomial definition." )
14021498
.def_static( "fromPolynomial", &energyCalFromPolynomial_2_wrapper,
1403-
"NumChannels"_a, "Coeffiecients"_a, "DeviationPairs"_a,
1499+
"NumChannels"_a, "Coefficients"_a, "DeviationPairs"_a,
14041500
"Creates a new energy calibration object from a polynomial definition, with some nonlinear-deviation pairs." )
14051501
.def_static( "fromFullRangeFraction", &energyCalFromFullRangeFraction_wrapper,
1406-
"NumChannels"_a, "Coeffiecients"_a,
1502+
"NumChannels"_a, "Coefficients"_a,
14071503
"Creates a new energy calibration object from a Full Range Fraction definition." )
14081504
.def_static( "fromFullRangeFraction", &energyCalFromFullRangeFraction_2_wrapper,
1409-
"NumChannels"_a, "Coeffiecients"_a, "DeviationPairs"_a,
1505+
"NumChannels"_a, "Coefficients"_a, "DeviationPairs"_a,
14101506
"Creates a new energy calibration object from a Full Range Fraction definition, with some nonlinear-deviation pairs." )
14111507
.def_static( "fromLowerChannelEnergies", &energyCalFromLowerChannelEnergies_wrapper,
14121508
"NumChannels"_a, "Energies"_a,
14131509
"Creates a new energy calibration object from a lower channel energies." )
14141510
;
14151511

14161512

1513+
m.def("polynomialCoefToFullRangeFraction",
1514+
&polynomial_coef_to_fullrangefraction_wrapper,
1515+
"coeffs"_a, "nchannel"_a,
1516+
"Converts from polynomial energy calibration coefficients, to Full Range Fraction coefficients."
1517+
" Only works for up to the first four coefficients, as the fifth"
1518+
" one for FRF does not translate easily to polynomial, so it will be ignored if"
1519+
" present."
1520+
).def("fullRangeFractionCoefToPolynomial",
1521+
&fullrangefraction_coef_to_polynomial_wrapper,
1522+
"coeffs"_a, "nchannel"_a,
1523+
"Converts from Full Range Fraction energy calibration coefficients, to polynomial coefficients."
1524+
" Only works for up to the first four coefficients, as the fifth"
1525+
" one for FRF does not translate easily to polynomial, so it will be ignored if"
1526+
" present."
1527+
)
1528+
.def("MidChannelPolynomialToFullRangeFraction",
1529+
&mid_channel_polynomial_to_fullrangeFraction_wrapper,
1530+
"coeffs"_a, "nchannel"_a,
1531+
"Converts coefficients from polynomial equation that uses the convention"
1532+
" the energy given by the equation is the middle of the channel (which is"
1533+
"non-standard) to standard full range fraction equation coefficients.\n"
1534+
"Only considers up to the first four coefficients."
1535+
).def("MidChannelPolynomialToLowerEdgePolynomial",
1536+
&mid_channel_polynomial_to_lower_channel_energy_poly_wrapper,
1537+
"coeffs"_a, "nchannel"_a,
1538+
"Converts coefficients from polynomial equation that uses the convention"
1539+
" the energy given by the equation is the middle of the channel (which is"
1540+
"non-standard) to standard polynomial equation coefficients that give"
1541+
" lower energy of each channel.\n"
1542+
"Only considers up to the first four coefficients."
1543+
);
1544+
1545+
1546+
14171547
{//begin Measurement class scope
14181548
py::class_<SpecUtils::Measurement>(m, "Measurement")
14191549
//.def(py::new_([](){ return make_shared<SpecUtils::Measurement>();}))
@@ -1439,7 +1569,7 @@ py::class_<SpecUtils::EnergyCalibration>(m, "EnergyCalibration")
14391569
.def( "energyCalibrationModel", &SpecUtils::Measurement::energy_calibration_model )
14401570
.def( "remarks", &measurement_remarks_wrapper )
14411571
.def( "startTime", &start_time_wrapper )
1442-
.def( "calibrationCoeffs", &SpecUtils::Measurement::calibration_coeffs, py::rv_policy::reference )
1572+
.def( "calibrationCoeffs", &meas_cal_coefficients_wrapper )
14431573
.def( "deviationPairs", &SpecUtils::Measurement::deviation_pairs, py::rv_policy::reference )
14441574
.def( "channelEnergies", &channel_energies_wrapper )
14451575
.def( "gammaCounts", &gamma_counts_wrapper )

bindings/python/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "scikit_build_core.build"
44

55
[project]
66
name = "SandiaSpecUtils"
7-
version = "0.0.2"
7+
version = "0.0.4"
88
description = "Python bindings for SpecUtils using nanobind and scikit-build"
99
readme = "README.md"
1010
requires-python = ">=3.9"

0 commit comments

Comments
 (0)