diff --git a/examples/dedx_list.c b/examples/dedx_list.c index ef87c17..dcbb86d 100644 --- a/examples/dedx_list.c +++ b/examples/dedx_list.c @@ -25,6 +25,22 @@ int main(int argc, char *argv[]) { for (i = 0; i < 300; ++i) printf(" %3i %s\n", i, dedx_get_material_name(i)); + /* Material / ion property accessors. Each accessor writes to *err, so the + * values are read into temporaries first — passing one &err to several + * calls inside a single printf would leave the writes unsequenced. */ + { + int err = 0; + int carbon_a = dedx_get_nucleon_number(DEDX_CARBON, &err); + float carbon_mass = dedx_get_atom_mass(DEDX_CARBON, &err); + float water_rho = dedx_get_density(DEDX_WATER, &err); + int water_gas = dedx_is_gas(DEDX_WATER, &err); + float air_rho = dedx_get_density(DEDX_AIR, &err); + int air_gas = dedx_is_gas(DEDX_AIR, &err); + printf("\nCarbon ion: A=%d, atomic mass=%.4f u\n", carbon_a, carbon_mass); + printf("Water: density=%.4f g/cm^3, gas=%d\n", water_rho, water_gas); + printf("Air: density=%.4g g/cm^3, gas=%d\n", air_rho, air_gas); + } + // printf("NB: %i\n", DEDX_WATER); return 0; } diff --git a/include/dedx.h b/include/dedx.h index c424bd9..ade11bf 100644 --- a/include/dedx.h +++ b/include/dedx.h @@ -147,6 +147,38 @@ void dedx_get_composition(int target, float composition[][2], unsigned int *comp */ float dedx_get_i_value(int target, int *err); +/** @brief Return the nucleon number (mass number A) of an elemental ion. + * @param[in] ion Elemental ion identifier (atomic number Z, 1–112). + * @param[out] err Error code; DEDX_OK on success, DEDX_ERR_NOT_AN_ELEMENT + * if @p ion is outside the tabulated element range. + * @return Nucleon number, or -1 on error. + */ +int dedx_get_nucleon_number(int ion, int *err); + +/** @brief Return the standard atomic mass of an elemental ion. + * @param[in] ion Elemental ion identifier (atomic number Z, 1–112). + * @param[out] err Error code; DEDX_OK on success, DEDX_ERR_NOT_AN_ELEMENT + * if @p ion is outside the tabulated element range. + * @return Atomic mass in u (unified atomic mass units), or -1 on error. + */ +float dedx_get_atom_mass(int ion, int *err); + +/** @brief Return the default density of a target material. + * @param[in] material Material identifier. + * @param[out] err Error code; DEDX_OK on success, DEDX_ERR_TARGET_NOT_FOUND + * if @p material has no embedded density metadata. + * @return Density in g/cm³, or 0 on error. + */ +float dedx_get_density(int material, int *err); + +/** @brief Report whether a target material is gaseous. + * @param[in] target Material identifier. + * @param[out] err Error code; always set to DEDX_OK — an unknown target is + * reported as non-gas rather than as an error. + * @return 1 if the target is flagged gaseous, 0 otherwise (including unknown targets). + */ +int dedx_is_gas(int target, int *err); + /** @brief Return a null-terminated list of supported program identifiers. * @return Pointer to a static array terminated by 0; do not free. */ diff --git a/src/dedx.c b/src/dedx.c index ca27ce1..4142bd6 100644 --- a/src/dedx.c +++ b/src/dedx.c @@ -221,6 +221,35 @@ float dedx_get_i_value(int target, int *err) { return dedx_internal_get_i_value(target, DEDX_GAS, err); } +/* Highest atomic number with tabulated mass/nucleon data (see dedx_amu / dedx_nucl). */ +#define DEDX_MAX_ELEMENT_Z 112 + +int dedx_get_nucleon_number(int ion, int *err) { + /* The internal lookup only guards the upper bound; reject ion <= 0 here + * so we never index the table with a negative offset. */ + if (ion < 1 || ion > DEDX_MAX_ELEMENT_Z) { + *err = DEDX_ERR_NOT_AN_ELEMENT; + return -1; + } + return dedx_internal_get_nucleon(ion, err); +} + +float dedx_get_atom_mass(int ion, int *err) { + if (ion < 1 || ion > DEDX_MAX_ELEMENT_Z) { + *err = DEDX_ERR_NOT_AN_ELEMENT; + return -1.0f; + } + return dedx_internal_get_atom_mass(ion, err); +} + +float dedx_get_density(int material, int *err) { + return dedx_internal_read_density(material, err); +} + +int dedx_is_gas(int target, int *err) { + return dedx_internal_target_is_gas(target, err) != 0 ? 1 : 0; +} + const int *dedx_get_program_list(void) { /* returns a list of available programs, terminated with -1 */ return dedx_available_programs; diff --git a/tests/test_accessors.c b/tests/test_accessors.c new file mode 100644 index 0000000..1011f02 --- /dev/null +++ b/tests/test_accessors.c @@ -0,0 +1,127 @@ +#include +#include +#include +#include + +/* Tests for the public material/ion property accessors: + * dedx_get_nucleon_number, dedx_get_atom_mass, dedx_get_density, dedx_is_gas. + * These promote previously-internal helpers to the public API (issue #119). + * + * All assertions funnel through the three expect_* helpers below so the only + * lines that stay uncovered on a passing run are their (shared) failure paths. + */ + +static int failures = 0; + +static void expect_int(const char *label, long got, long expected) { + if (got != expected) { + fprintf(stderr, "FAIL %s: got %ld expected %ld\n", label, got, expected); + failures++; + } +} + +static void expect_near(const char *label, double got, double expected, double rel) { + double scale = fabs(expected) > 1e-12 ? fabs(expected) : 1.0; + if (fabs(got - expected) > rel * scale) { + fprintf(stderr, "FAIL %s: got %.8g expected %.8g\n", label, got, expected); + failures++; + } +} + +static void expect_true(const char *label, int condition) { + if (!condition) { + fprintf(stderr, "FAIL %s\n", label); + failures++; + } +} + +int main(void) { + int err; + float air; + + /* --- nucleon number: valid elements --- */ + err = -1; + expect_int("nucleon H value", dedx_get_nucleon_number(DEDX_HYDROGEN, &err), 1); + expect_int("nucleon H err", err, DEDX_OK); + + err = -1; + expect_int("nucleon He value", dedx_get_nucleon_number(DEDX_HELIUM, &err), 4); + expect_int("nucleon He err", err, DEDX_OK); + + err = -1; + expect_int("nucleon C value", dedx_get_nucleon_number(DEDX_CARBON, &err), 12); + expect_int("nucleon C err", err, DEDX_OK); + + /* Highest tabulated element (Z=112) must be accepted (upper boundary). */ + err = -1; + expect_true("nucleon Z=112 value", dedx_get_nucleon_number(112, &err) > 0); + expect_int("nucleon Z=112 err", err, DEDX_OK); + + /* --- nucleon number: out-of-range guards (lower bound the internal lacks) --- */ + err = DEDX_OK; + expect_int("nucleon 0 value", dedx_get_nucleon_number(0, &err), -1); + expect_int("nucleon 0 err", err, DEDX_ERR_NOT_AN_ELEMENT); + + err = DEDX_OK; + expect_int("nucleon negative value", dedx_get_nucleon_number(-5, &err), -1); + expect_int("nucleon negative err", err, DEDX_ERR_NOT_AN_ELEMENT); + + err = DEDX_OK; + expect_int("nucleon 113 value", dedx_get_nucleon_number(113, &err), -1); + expect_int("nucleon 113 err", err, DEDX_ERR_NOT_AN_ELEMENT); + + /* --- atom mass: valid elements --- */ + err = -1; + expect_near("mass H value", dedx_get_atom_mass(DEDX_HYDROGEN, &err), 1.00794, 1e-4); + expect_int("mass H err", err, DEDX_OK); + + err = -1; + expect_near("mass C value", dedx_get_atom_mass(DEDX_CARBON, &err), 12.0107, 1e-4); + expect_int("mass C err", err, DEDX_OK); + + err = -1; + expect_near("mass O value", dedx_get_atom_mass(DEDX_OXYGEN, &err), 15.9994, 1e-4); + expect_int("mass O err", err, DEDX_OK); + + /* --- atom mass: out-of-range guards --- */ + err = DEDX_OK; + expect_true("mass 0 sentinel", dedx_get_atom_mass(0, &err) == -1.0f); + expect_int("mass 0 err", err, DEDX_ERR_NOT_AN_ELEMENT); + + err = DEDX_OK; + expect_true("mass 200 sentinel", dedx_get_atom_mass(200, &err) == -1.0f); + expect_int("mass 200 err", err, DEDX_ERR_NOT_AN_ELEMENT); + + /* --- density: known liquid and gaseous materials --- */ + err = -1; + expect_near("density water value", dedx_get_density(DEDX_WATER, &err), 1.0, 0.02); + expect_int("density water err", err, DEDX_OK); + + err = -1; + air = dedx_get_density(DEDX_AIR, &err); + expect_true("density air range", air > 0.0f && air < 0.01f); + expect_int("density air err", err, DEDX_OK); + + /* --- density: unknown material returns a safe sentinel + error --- */ + err = DEDX_OK; + expect_true("density unknown sentinel", dedx_get_density(9999, &err) == 0.0f); + expect_int("density unknown err", err, DEDX_ERR_TARGET_NOT_FOUND); + + /* --- is_gas: liquid vs gas --- */ + err = -1; + expect_int("is_gas water value", dedx_is_gas(DEDX_WATER, &err), 0); + expect_int("is_gas water err", err, DEDX_OK); + + err = -1; + expect_int("is_gas air value", dedx_is_gas(DEDX_AIR, &err), 1); + expect_int("is_gas air err", err, DEDX_OK); + + /* Unknown target is reported as non-gas, never an error (documented). */ + err = -1; + expect_int("is_gas unknown value", dedx_is_gas(9999, &err), 0); + expect_int("is_gas unknown err", err, DEDX_OK); + + if (failures == 0) + printf("test_accessors: all checks passed\n"); + return failures; +}