Repository navigation
Add energy bounds functions for ICRU and PSTAR data #306
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
ed81e84
46131eb
b1df4d1
023576f
69cbac4
75fe32f
938591d
011eead
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -50,6 +50,25 @@ int AT_ICRU_wrapper( const long n, | |
| const char info[], | ||
| double mass_stopping_power_MeV_cm2_g[]); | ||
|
|
||
| /** | ||
| * @brief Returns the minimum kinetic energy covered by the ICRU data. | ||
| * | ||
| * @param[in] material_no Material number identifying the material. | ||
| * @param[in] particle_no Particle number identifying the particle. | ||
| * @return Minimum tabulated kinetic energy in MeV/u, or -1.0 if | ||
| * material_no or particle_no has no ICRU data. | ||
| */ | ||
| double AT_ICRU_min_energy_MeV_u(long material_no, long particle_no); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same as for PSTAR: /** ICRU 49 data for helium end at 250 MeV/u (1000 MeV for He-4) */
#define ICRU_HELIUM_MAX_E_MEV_U 250.0
int AT_ICRU_has_data( const long particle_no,
const long material_no);
double AT_ICRU_E_MeV_u_min( const long particle_no,
const long material_no);
double AT_ICRU_E_MeV_u_max( const long particle_no,
const long material_no);Why: the constant replaces the two hard-coded 250s (see the comments in the |
||
|
|
||
| /** | ||
| * @brief Returns the maximum kinetic energy covered by the ICRU data. | ||
| * | ||
| * @param[in] material_no Material number identifying the material. | ||
| * @param[in] particle_no Particle number identifying the particle. | ||
| * @return Maximum tabulated kinetic energy in MeV/u, or -1.0 if | ||
| * material_no or particle_no has no ICRU data. | ||
| */ | ||
| double AT_ICRU_max_energy_MeV_u(long material_no, long particle_no); | ||
|
|
||
| /** | ||
| * @struct AT_stopping_power_ICRU_table | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -66,6 +66,25 @@ int AT_PSTAR_wrapper( const long n, | |
| const char info[], | ||
| double mass_stopping_power_MeV_cm2_g[]); | ||
|
|
||
|
|
||
| /** | ||
| * @brief Returns the minimum kinetic energy covered by the PSTAR data. | ||
| * | ||
| * @param[in] material_no Material number identifying the material. | ||
| * @return Minimum tabulated kinetic energy in MeV/u, or -1.0 if | ||
| * material_no is outside the PSTAR material range. | ||
| */ | ||
| double AT_PSTAR_min_energy_MeV_u(long material_no); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please give the PSTAR and ICRU functions the same signature, with int AT_PSTAR_has_data( const long particle_no,
const long material_no);
double AT_PSTAR_E_MeV_u_min( const long particle_no,
const long material_no);
double AT_PSTAR_E_MeV_u_max( const long particle_no,
const long material_no);Why:
In the docs, please say that the tables are in MeV for protons and that |
||
|
|
||
| /** | ||
| * @brief Returns the maximum kinetic energy covered by the PSTAR data. | ||
| * | ||
| * @param[in] material_no Material number identifying the material. | ||
| * @return Maximum tabulated kinetic energy in MeV/u, or -1.0 if | ||
| * material_no is outside the PSTAR material range. | ||
| */ | ||
| double AT_PSTAR_max_energy_MeV_u(long material_no); | ||
|
|
||
| /** PSTAR data downloaded from NIST database: http://www.nist.gov/pml/data/star/index.cfm | ||
| * Stopping-Power and Range Tables | ||
| * for Electrons, Protons, and Helium Ions | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -318,3 +318,35 @@ double AT_Mass_Stopping_Power_E_MeV_u_single( const long stopping_power_source_n | |
| &stopping_power_MeV_cm2_g); | ||
| return stopping_power_MeV_cm2_g; | ||
| } | ||
|
|
||
| int AT_Stopping_Power_has_data(const long stopping_power_source_no, | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd make int AT_Stopping_Power_has_data( const long stopping_power_source_no,
const long particle_no,
const long material_no){
switch(stopping_power_source_no){
case Bethe:
/* analytical formula: needs a valid particle and a known material */
return AT_Z_from_particle_no_single(particle_no) >= 1 &&
AT_material_index_from_material_number(material_no) >= 0;
case PSTAR:
return AT_PSTAR_has_data(particle_no, material_no);
case ICRU:
return AT_ICRU_has_data(particle_no, material_no);
default:
/* FromFile: no file name is given here, so availability cannot be checked */
return 0;
}
}
double AT_Stopping_Power_E_MeV_u_min( const long stopping_power_source_no,
const long particle_no,
const long material_no){
switch(stopping_power_source_no){
case Bethe:
if(!AT_Stopping_Power_has_data(Bethe, particle_no, material_no)){
return -1.0;
}
/* below this limit AT_Bethe_wrapper returns zero */
return BETHE_LOWER_LIMIT_E_MEV_U;
case PSTAR:
return AT_PSTAR_E_MeV_u_min(particle_no, material_no);
case ICRU:
return AT_ICRU_E_MeV_u_min(particle_no, material_no);
default:
return -1.0;
}
}
double AT_Stopping_Power_E_MeV_u_max( const long stopping_power_source_no,
const long particle_no,
const long material_no){
switch(stopping_power_source_no){
case Bethe:
if(!AT_Stopping_Power_has_data(Bethe, particle_no, material_no)){
return -1.0;
}
/* analytical formula, no upper limit */
return HUGE_VAL;
case PSTAR:
return AT_PSTAR_E_MeV_u_max(particle_no, material_no);
case ICRU:
return AT_ICRU_E_MeV_u_max(particle_no, material_no);
default:
return -1.0;
}
}Why: each table's rules (materials, Z range, energy range) then live only in that table's file, and this file just picks the table by source number. That is the same pattern The A >= 1 check is dropped because none of the three sources use A. The PSTAR and ICRU wrappers take |
||
| const long particle_no, | ||
| const long material_no) | ||
| { | ||
| switch (stopping_power_source_no) { | ||
| case FromFile: | ||
| /* | ||
| * The filename and file contents are not provided here, | ||
| * so availability cannot be checked. | ||
| */ | ||
| return 0; | ||
| case Bethe: | ||
| /* Bethe is an analytical model, not a finite data table. */ | ||
| return 1; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Why it matters: callers such as pyamtrack use |
||
| case PSTAR: | ||
| if (AT_Z_from_particle_no_single(particle_no) < 1 || | ||
| AT_A_from_particle_no_single(particle_no) < 1) { | ||
| return 0; | ||
| } | ||
| return material_no >= Water_Liquid && | ||
| material_no <= Copper; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This repeats the list of PSTAR materials from Why it's a problem: if a PSTAR table is added, or a slot is left |
||
| case ICRU: | ||
| if (AT_Z_from_particle_no_single(particle_no) < 1 || | ||
| AT_A_from_particle_no_single(particle_no) < 1) { | ||
| return 0; | ||
| } | ||
| return AT_ICRU_min_energy_MeV_u(material_no, particle_no) >= 0.0; | ||
| default: | ||
| return 0; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -29,6 +29,7 @@ | |
| */ | ||
|
|
||
| #include "AT_StoppingPowerDataICRU.h" | ||
| #include "AT_Error.h" | ||
|
|
||
| AT_stopping_power_ICRU_table_struct AT_stopping_power_ICRU_table[2] = { | ||
| { Water_Liquid, | ||
|
|
@@ -128,6 +129,24 @@ AT_stopping_power_ICRU_table_struct AT_stopping_power_ICRU_table[2] = { | |
|
|
||
|
|
||
|
|
||
| static long AT_ICRU_max_atomic_number(const long material_no) { | ||
| if (material_no == Water_Liquid) { | ||
| return 18; | ||
| } | ||
| if (material_no == Aluminum_Oxide) { | ||
| return 10; | ||
| } | ||
| return 0; | ||
| } | ||
|
|
||
| static int AT_ICRU_has_data_for_atomic_number( | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The per-material Z limit is the right fix: Ar-40 in Al2O3 now gets -1 instead of a stopping power of 0. I'd turn this into the public int AT_ICRU_has_data( const long particle_no,
const long material_no){
const long Z = AT_Z_from_particle_no_single(particle_no);
return Z >= 1 && Z <= AT_ICRU_max_atomic_number(material_no);
}Why: then "does ICRU have data for this particle in this material?" is answered by exactly one function, used by |
||
| const long material_no, | ||
| const long atomic_number) { | ||
| const long max_atomic_number = | ||
| AT_ICRU_max_atomic_number(material_no); | ||
| return atomic_number >= 1 && atomic_number <= max_atomic_number; | ||
| } | ||
|
|
||
| int AT_ICRU_wrapper( const long n, | ||
| const double E_MeV_u[], | ||
| const long particle_no[], | ||
|
|
@@ -148,7 +167,7 @@ int AT_ICRU_wrapper( const long n, | |
|
|
||
| for(i = 0; i < n; i++){ | ||
|
|
||
| if( Z[i] > 18){ /* Data for H ... Ar */ | ||
| if(!AT_ICRU_has_data_for_atomic_number(material_no, Z[i])){ | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. With the functions above, the three checks in the wrapper (this one, plus the hard-coded if(!AT_ICRU_has_data(particle_no[i], material_no)){
mass_stopping_power_MeV_cm2_g[i] = -1.0;
continue;
}
if (E_MeV_u[i] < AT_ICRU_E_MeV_u_min(particle_no[i], material_no) ||
E_MeV_u[i] > AT_ICRU_E_MeV_u_max(particle_no[i], material_no)){
mass_stopping_power_MeV_cm2_g[i] = -1.0;
continue;
}Why: the wrapper and the min/max functions must agree on the range. Otherwise a caller who checks |
||
| mass_stopping_power_MeV_cm2_g[i] = -1.0; | ||
| continue; | ||
| } | ||
|
|
@@ -178,3 +197,50 @@ int AT_ICRU_wrapper( const long n, | |
| return AT_Success; | ||
| } | ||
|
|
||
| static int AT_ICRU_energy_bounds(long material_no, | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. With double AT_ICRU_E_MeV_u_min( const long particle_no,
const long material_no){
if(!AT_ICRU_has_data(particle_no, material_no)){
return -1.0;
}
return AT_stopping_power_ICRU_table[material_no-1].energy_and_stopping_power[0][0];
}
double AT_ICRU_E_MeV_u_max( const long particle_no,
const long material_no){
if(!AT_ICRU_has_data(particle_no, material_no)){
return -1.0;
}
if(AT_Z_from_particle_no_single(particle_no) == 2){
return ICRU_HELIUM_MAX_E_MEV_U;
}
const AT_stopping_power_ICRU_table_struct * table = &AT_stopping_power_ICRU_table[material_no-1];
return table->energy_and_stopping_power[0][table->number_of_data_points - 1];
}Why: half the code, and each check runs once. Also, this block is indented with 4 spaces while the rest of the file uses tabs; please use tabs. |
||
| long particle_no, | ||
| double *minimum, | ||
| double *maximum) { | ||
| if (material_no != Water_Liquid && | ||
| material_no != Aluminum_Oxide) { | ||
| return AT_No_ICRU_Data; | ||
| } | ||
|
|
||
| long atomic_number = AT_Z_from_particle_no_single(particle_no); | ||
| if (!AT_ICRU_has_data_for_atomic_number(material_no, atomic_number)) { | ||
| return AT_No_ICRU_Data; | ||
| } | ||
|
|
||
| const AT_stopping_power_ICRU_table_struct *table = | ||
| &AT_stopping_power_ICRU_table[material_no - 1]; | ||
|
|
||
| *minimum = table->energy_and_stopping_power[0][0]; | ||
| *maximum = table->energy_and_stopping_power[0] | ||
| [table->number_of_data_points - 1]; | ||
|
|
||
| if (atomic_number == 2) { | ||
| *maximum = 250.0; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please use the named constant Why: 250 is currently written both here and in |
||
| } | ||
|
|
||
| return AT_Success; | ||
| } | ||
|
|
||
| double AT_ICRU_min_energy_MeV_u(long material_no, long particle_no) { | ||
| double minimum; | ||
| double maximum; | ||
| if (AT_ICRU_energy_bounds(material_no, particle_no, | ||
| &minimum, &maximum) != AT_Success) { | ||
| return -1.0; | ||
| } | ||
| return minimum; | ||
| } | ||
|
|
||
| double AT_ICRU_max_energy_MeV_u(long material_no, long particle_no) { | ||
| double minimum; | ||
| double maximum; | ||
| if (AT_ICRU_energy_bounds(material_no, particle_no, | ||
| &minimum, &maximum) != AT_Success) { | ||
| return -1.0; | ||
| } | ||
| return maximum; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,5 @@ | ||
| #include "AT_StoppingPowerDataPSTAR.h" | ||
| #include "AT_Error.h" | ||
|
|
||
| /* | ||
| * PSTAR contains experimental stopping power data for protons | ||
|
|
@@ -1341,3 +1342,37 @@ int AT_PSTAR_wrapper( const long n, | |
| return AT_Success; | ||
| } | ||
|
|
||
| static int AT_PSTAR_energy_bounds(long material_no, | ||
| double *minimum, | ||
| double *maximum) { | ||
| if (material_no < 0 || material_no >= N_PSTAR_MATERIALS) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Crash for material 0 ( I'd put the table lookup in one small helper before /* NULL if material_no is out of range or has no PSTAR table (e.g. User_Defined_Material) */
static const PSTAR_data_for_material_struct * AT_PSTAR_table( const long material_no){
if( material_no < 0 || material_no >= N_PSTAR_MATERIALS ){
return NULL;
}
return PSTAR_data.stopping_power_source_data[material_no];
}and use it in the wrapper too: - const PSTAR_data_for_material_struct * source_for_given_material = PSTAR_data.stopping_power_source_data[material_no];
+ const PSTAR_data_for_material_struct * source_for_given_material = AT_PSTAR_table(material_no);Why the wrapper as well: it indexes |
||
| return AT_No_PSTAR_Data; | ||
| } | ||
|
|
||
| *minimum = | ||
| PSTAR_data.stopping_power_source_data[material_no] | ||
| ->energy_and_stopping_power[0][0]; | ||
| *maximum = | ||
| PSTAR_data.stopping_power_source_data[material_no] | ||
| ->energy_and_stopping_power[N_PSTAR_DATAPOINTS - 1][0]; | ||
|
|
||
| return AT_Success; | ||
| } | ||
|
|
||
| double AT_PSTAR_min_energy_MeV_u(long material_no) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. With int AT_PSTAR_has_data( const long particle_no,
const long material_no){
return AT_Z_from_particle_no_single(particle_no) >= 1 &&
AT_PSTAR_table(material_no) != NULL;
}
double AT_PSTAR_E_MeV_u_min( const long particle_no,
const long material_no){
if(!AT_PSTAR_has_data(particle_no, material_no)){
return -1.0;
}
return AT_E_MeV_u_from_E_MeV(AT_PSTAR_table(material_no)->energy_and_stopping_power[0][0],
PARTICLE_PROTON_NUMBER);
}
double AT_PSTAR_E_MeV_u_max( const long particle_no,
const long material_no){
if(!AT_PSTAR_has_data(particle_no, material_no)){
return -1.0;
}
return AT_E_MeV_u_from_E_MeV(AT_PSTAR_table(material_no)->energy_and_stopping_power[N_PSTAR_DATAPOINTS - 1][0],
PARTICLE_PROTON_NUMBER);
}Why:
The MeV to MeV/u conversion itself is right: the max is 9921.62 MeV/u, which the wrapper turns back into exactly the 10000 MeV table end. |
||
| double minimum; | ||
| double maximum; | ||
| if (AT_PSTAR_energy_bounds(material_no, &minimum, &maximum) != AT_Success) { | ||
| return -1.0; | ||
| } | ||
| return AT_E_MeV_u_from_E_MeV(minimum, PARTICLE_PROTON_NUMBER); | ||
| } | ||
|
|
||
| double AT_PSTAR_max_energy_MeV_u(long material_no) { | ||
| double minimum; | ||
| double maximum; | ||
| if (AT_PSTAR_energy_bounds(material_no, &minimum, &maximum) != AT_Success) { | ||
| return -1.0; | ||
| } | ||
| return AT_E_MeV_u_from_E_MeV(maximum, PARTICLE_PROTON_NUMBER); | ||
| } | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please add a newline at the end of the file. Without it, the next change to this file shows this line as modified too. |
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please declare the source-based min/max here, next to
has_data:Why: this header is the public stopping-power API. With these functions, pyamtrack (and the JS package) needs only this header and the source number it already passes to
AT_Mass_Stopping_Power_with_no. At the moment pyamtrack has to include the ICRU and PSTAR data headers and pick the table-specific function itself.Please also say in the
has_datadoc thatFromFilealways returns 0. Otherwise that is a surprise for callers.