From 94e73e0ccf62e506d742e831c31f30e2df85c309 Mon Sep 17 00:00:00 2001 From: Jannis Maier Date: Tue, 23 Jun 2026 13:13:03 +0200 Subject: [PATCH] Handle energy spread units in source generation --- .../rayx-core/src/Beamline/StringConversion.h | 4 +- Intern/rayx-core/src/Design/DesignSource.cpp | 19 +++++- Intern/rayx-core/src/Design/DesignSource.h | 1 + Intern/rayx-core/src/Rml/DesignSourceWriter.h | 14 ++++- Intern/rayx-core/src/Rml/xml.h | 1 - .../src/Shader/LightSources/DipoleSource.cpp | 2 +- .../src/Shader/LightSources/LightSource.h | 2 +- .../input/PointSourceHardEdgePercent.rml | 59 +++++++++++++++++++ .../input/dipole_energySpread_percent.rml | 50 ++++++++++++++++ Intern/rayx-core/tests/testSources.cpp | 13 ++++ 10 files changed, 157 insertions(+), 8 deletions(-) create mode 100644 Intern/rayx-core/tests/input/PointSourceHardEdgePercent.rml create mode 100644 Intern/rayx-core/tests/input/dipole_energySpread_percent.rml diff --git a/Intern/rayx-core/src/Beamline/StringConversion.h b/Intern/rayx-core/src/Beamline/StringConversion.h index 435bee03d..eba076954 100644 --- a/Intern/rayx-core/src/Beamline/StringConversion.h +++ b/Intern/rayx-core/src/Beamline/StringConversion.h @@ -39,8 +39,8 @@ const std::map StringToElectronEnergyOri {"Clockwise", ElectronEnergyOrientation::Clockwise}, {"Counterclockwise", ElectronEnergyOrientation::Counterclockwise}}; // EnergySpreadUnit conversion -const std::map EnergySpreadUnitToString = {{EnergySpreadUnit::EU_PERCENT, "Percent"}, {EnergySpreadUnit::EU_eV, "eV"}}; -const std::map StringToEnergySpreadUnit = {{"Percent", EnergySpreadUnit::EU_PERCENT}, {"eV", EnergySpreadUnit::EU_eV}}; +const std::map EnergySpreadUnitToString = {{EnergySpreadUnit::EU_eV, "eV"}, {EnergySpreadUnit::EU_PERCENT, "Percent"}}; +const std::map StringToEnergySpreadUnit = {{"eV", EnergySpreadUnit::EU_eV}, {"Percent", EnergySpreadUnit::EU_PERCENT}}; // RZPType conversion const std::map RZPTypeToString = {{RZPType::Elliptical, "Elliptical"}, {RZPType::Meriodional, "Meriodional"}}; diff --git a/Intern/rayx-core/src/Design/DesignSource.cpp b/Intern/rayx-core/src/Design/DesignSource.cpp index 40a91ccb0..a34c9daca 100644 --- a/Intern/rayx-core/src/Design/DesignSource.cpp +++ b/Intern/rayx-core/src/Design/DesignSource.cpp @@ -184,7 +184,22 @@ void DesignSource::setEnergySpread(double value) { m_elementParameters["energySp double DesignSource::getEnergySpread() const { return m_elementParameters["energySpread"].as_double(); } void DesignSource::setEnergySpreadUnit(EnergySpreadUnit value) { m_elementParameters["energySpreadUnit"] = value; } -EnergySpreadUnit DesignSource::getEnergySpreadUnit() const { return m_elementParameters["energySpreadUnit"].as_energySpreadUnit(); } +EnergySpreadUnit DesignSource::getEnergySpreadUnit() const { + if (!m_elementParameters.hasKey("energySpreadUnit")) { + return EnergySpreadUnit::EU_eV; + } + + return m_elementParameters["energySpreadUnit"].as_energySpreadUnit(); +} + +double DesignSource::getEnergySpreadInEv() const { + const double energySpread = getEnergySpread(); + if (getEnergySpreadUnit() == EnergySpreadUnit::EU_PERCENT) { + return getEnergy() * energySpread / 100.0; + } + + return energySpread; +} void DesignSource::setEnergyDistributionType(EnergyDistributionType value) { m_elementParameters["energyDistributionType"] = value; } EnergyDistributionType DesignSource::getEnergyDistributionType() const { @@ -217,7 +232,7 @@ EnergyDistributionVariant DesignSource::getEnergyDistribution() const { en = EnergyDistributionVariant(df); } else if (energyDistributionType == EnergyDistributionType::Values) { double photonEnergy = m_elementParameters["energy"].as_double(); - double energySpread = m_elementParameters["energySpread"].as_double(); + double energySpread = getEnergySpreadInEv(); if (spreadType == SpreadType::SoftEdge) { if (energySpread == 0) { energySpread = 1; } diff --git a/Intern/rayx-core/src/Design/DesignSource.h b/Intern/rayx-core/src/Design/DesignSource.h index eb387c7c2..1ff6f864c 100644 --- a/Intern/rayx-core/src/Design/DesignSource.h +++ b/Intern/rayx-core/src/Design/DesignSource.h @@ -83,6 +83,7 @@ class RAYX_API DesignSource : public BeamlineNode { void setEnergySpreadUnit(EnergySpreadUnit value); EnergySpreadUnit getEnergySpreadUnit() const; + double getEnergySpreadInEv() const; void setElectronEnergy(double value); double getElectronEnergy() const; diff --git a/Intern/rayx-core/src/Rml/DesignSourceWriter.h b/Intern/rayx-core/src/Rml/DesignSourceWriter.h index 0525de56d..ea075a8f2 100644 --- a/Intern/rayx-core/src/Rml/DesignSourceWriter.h +++ b/Intern/rayx-core/src/Rml/DesignSourceWriter.h @@ -11,6 +11,17 @@ namespace rayx { +namespace { +EnergySpreadUnit parseEnergySpreadUnitOrDefault(xml::Parser parser) { + int energySpreadUnit = 0; + if (!xml::paramInt(parser.node, "energySpreadUnit", &energySpreadUnit)) { + return EnergySpreadUnit::EU_eV; + } + + return static_cast(energySpreadUnit); +} +} // unnamed namespace + void setAllMandatory(xml::Parser parser, DesignSource* ds) { ds->setName(parser.name()); ds->setType(parser.type()); @@ -28,6 +39,7 @@ void setDefaultEnergy(xml::Parser parser, DesignSource* ds) { } else { ds->setEnergy(parser.parsePhotonEnergy()); ds->setEnergySpread(parser.parseEnergySpread()); + ds->setEnergySpreadUnit(parseEnergySpreadUnitOrDefault(parser)); if (ds->getEnergySpreadType() == SpreadType::SeparateEnergies) ds->setNumberOfSeparateEnergies(parser.parseNumberOfSeparateEnergies()); } } @@ -84,7 +96,7 @@ void setDipoleSource(xml::Parser parser, DesignSource* ds) { ds->setSourceWidth(parser.parseSourceWidth()); ds->setVerEBeamDivergence(parser.parseVerEbeamDivergence()); ds->setEnergy(parser.parsePhotonEnergy()); - ds->setEnergySpreadUnit(parser.parseEnergySpreadUnit()); + ds->setEnergySpreadUnit(parseEnergySpreadUnitOrDefault(parser)); ds->setEnergyDistributionType(parser.parseEnergyDistributionType()); ds->setHorDivergence(parser.parseHorDiv()); } diff --git a/Intern/rayx-core/src/Rml/xml.h b/Intern/rayx-core/src/Rml/xml.h index e1271f9b6..eb84026e8 100644 --- a/Intern/rayx-core/src/Rml/xml.h +++ b/Intern/rayx-core/src/Rml/xml.h @@ -175,7 +175,6 @@ struct RAYX_API Parser { inline EnergyDistributionType parseEnergyDistributionType() const { return static_cast(parseInt("energyDistributionType")); } - inline EnergySpreadUnit parseEnergySpreadUnit() const { return static_cast(parseInt("energySpreadUnit")); } inline int parseNumberOfSeparateEnergies() const { return parseInt("SeparateEnergies"); } inline int parseNumOfEquidistantCircles() const { return static_cast(parseDouble("numberCircles")); } inline Rad parseMaxOpeningAngle() const { return parseDouble("maximumOpeningAngle") / 1000.0; } diff --git a/Intern/rayx-core/src/Shader/LightSources/DipoleSource.cpp b/Intern/rayx-core/src/Shader/LightSources/DipoleSource.cpp index 8f6ea7cc8..c62bfdae4 100644 --- a/Intern/rayx-core/src/Shader/LightSources/DipoleSource.cpp +++ b/Intern/rayx-core/src/Shader/LightSources/DipoleSource.cpp @@ -374,7 +374,7 @@ DipoleSource::DipoleSource(const DesignSource& dSource) m_verEbeamDivergence(dSource.getVerEBeamDivergence()), // m_bandwidth(1.0e-3), // m_photonWaveLength(hvlam(m_photonEnergy)), - m_energySpread(dSource.getEnergySpread()), + m_energySpread(dSource.getEnergySpreadInEv()), m_horDivergence(dSource.getHorDivergence()) { auto rand = Rand(randomUint()); m_gamma = calcGamma(m_electronEnergy); diff --git a/Intern/rayx-core/src/Shader/LightSources/LightSource.h b/Intern/rayx-core/src/Shader/LightSources/LightSource.h index aa7ceef94..66249fa2a 100644 --- a/Intern/rayx-core/src/Shader/LightSources/LightSource.h +++ b/Intern/rayx-core/src/Shader/LightSources/LightSource.h @@ -15,7 +15,7 @@ namespace rayx { enum class SourceDist { Uniform, Gaussian, Thirds, Circle }; // SourceDist::Thirds represents PixelSource Footprint enum class ElectronEnergyOrientation { Clockwise, Counterclockwise }; -enum class EnergySpreadUnit { EU_PERCENT, EU_eV }; +enum class EnergySpreadUnit { EU_eV, EU_PERCENT }; enum class SigmaType { ST_STANDARD, ST_ACCURATE }; enum class SourcePulseType { None }; diff --git a/Intern/rayx-core/tests/input/PointSourceHardEdgePercent.rml b/Intern/rayx-core/tests/input/PointSourceHardEdgePercent.rml new file mode 100644 index 000000000..8c5f57cb1 --- /dev/null +++ b/Intern/rayx-core/tests/input/PointSourceHardEdgePercent.rml @@ -0,0 +1,59 @@ + + +1.12 + + + + 200 + 0 + 0.065 + 1 + 0.04 + 1 + 1 + 1 + 1 + 1 + 1 + 0 + 0 + 0 + 0 + 1 + + 100 + 0 + 1 + 10 + 1 + 0 + 0 + 0 + 0 + + 0 + 0 + 0 + + + 1 + 0 + 0 + + + 0 + 1 + 0 + + + 0 + 0 + 1 + + + + + + + + diff --git a/Intern/rayx-core/tests/input/dipole_energySpread_percent.rml b/Intern/rayx-core/tests/input/dipole_energySpread_percent.rml new file mode 100644 index 000000000..1f78d367e --- /dev/null +++ b/Intern/rayx-core/tests/input/dipole_energySpread_percent.rml @@ -0,0 +1,50 @@ + + + 1.13 + + + 200 + 0.065 + 0.04 + 1 + 100 + 1.7 + 0 + 4.35 + 1 + 0 + 0 + 0 + 0 + + 0.0000000000000000 + 0.0000000000000000 + 0.0000000000000000 + + + 1.0000000000000000 + 0.0000000000000000 + 0.0000000000000000 + + + 0.0000000000000000 + 1.0000000000000000 + 0.0000000000000000 + + + 0.0000000000000000 + 0.0000000000000000 + 1.0000000000000000 + + 1 + + 1000 + 0 + 1 + 10 + 0 + 0 + 2.76089e+12 + + + diff --git a/Intern/rayx-core/tests/testSources.cpp b/Intern/rayx-core/tests/testSources.cpp index 2d534360b..cd6b49a73 100644 --- a/Intern/rayx-core/tests/testSources.cpp +++ b/Intern/rayx-core/tests/testSources.cpp @@ -8,6 +8,11 @@ void checkEnergyDistribution(const Rays& rays, double photonEnergy, double energ for (const auto energy : rays.energy) { CHECK_IN(energy, photonEnergy - energySpread, photonEnergy + energySpread); } } +void checkEnergyDistributionWindow(const Rays& rays, double minEnergy, double maxEnergy) { + CHECK(rays.energy.size() > 0); + for (const auto energy : rays.energy) { CHECK_IN(energy, minEnergy, maxEnergy); } +} + void checkZDistribution(const Rays& rays, double center, double spread) { CHECK(rays.position_z.size() > 0); for (const auto position_z : rays.position_z) { CHECK_IN(position_z, center - spread, center + spread); } @@ -51,6 +56,10 @@ TEST_F(TestSuite, MatrixSourceTracedRayUI) { TEST_F(TestSuite, PointSourceHardEdge) { checkEnergyDistribution(traceRml("PointSourceHardEdge", RayAttrMask::Energy), 120.97, 12.1); } +TEST_F(TestSuite, PointSourceHardEdgePercentUnit) { + checkEnergyDistributionWindow(traceRml("PointSourceHardEdgePercent", RayAttrMask::Energy), 95.0, 105.0); +} + TEST_F(TestSuite, PointSourceSoftEdge) { checkEnergyDistribution(traceRml("PointSourceSoftEdge", RayAttrMask::Energy), 151, 6); } TEST_F(TestSuite, MatrixSourceEnergyDistribution) { checkEnergyDistribution(traceRml("PointSourceSoftEdge", RayAttrMask::Energy), 151, 6); } @@ -61,6 +70,10 @@ TEST_F(TestSuite, DipoleSourcePosition) { TEST_F(TestSuite, DipoleEnergyDistribution) { checkEnergyDistribution(traceRml("dipole_energySpread", RayAttrMask::Energy), 1000, 23000); } +TEST_F(TestSuite, DipoleEnergyDistributionPercentUnit) { + checkEnergyDistributionWindow(traceRml("dipole_energySpread_percent", RayAttrMask::Energy), 950.0, 1050.0); +} + TEST_F(TestSuite, PixelPositionTest) { const auto [beamline, rays] = loadBeamlineAndTrace("PixelSource", RayAttrMask::PositionX | RayAttrMask::PositionY | RayAttrMask::DirectionX | RayAttrMask::DirectionZ);