diff --git a/Documentation/Examples/vertical_EAS.cc b/Documentation/Examples/vertical_EAS.cc index 78972df91f591a56a23c0bf6aeb6e8022a293549..ca27eac3d43e6f37cac296af3e7523de6ac7fa6b 100644 --- a/Documentation/Examples/vertical_EAS.cc +++ b/Documentation/Examples/vertical_EAS.cc @@ -20,6 +20,7 @@ #include <corsika/setup/SetupStack.h> #include <corsika/setup/SetupTrajectory.h> +#include <corsika/environment/LayeredSphericalAtmosphereBuilder.h> #include <corsika/environment/Environment.h> #include <corsika/environment/FlatExponential.h> #include <corsika/environment/NuclearComposition.h> @@ -79,26 +80,19 @@ int main() { using EnvType = Environment<setup::IEnvironmentModel>; EnvType env; const CoordinateSystem& rootCS = env.GetCoordinateSystem(); - auto& universe = *(env.GetUniverse()); - - auto theMedium = EnvType::CreateNode<Sphere>( - Point{rootCS, 0_m, 0_m, 0_m}, 1_km * std::numeric_limits<double>::infinity()); - - auto constexpr temperature = 295_K; // AIRES default temperature for isothermal model - auto constexpr lambda = - -constants::R * temperature / (constants::g_sub_n * 28.966_g / mole); - // 1036 g/cm² taken from AIRES code - auto constexpr rho0 = -1036_g / square(1_cm) / lambda; - using FlatExp = environment::FlatExponential<environment::IMediumModel>; - theMedium->SetModelProperties<FlatExp>( - Point{rootCS, 0_m, 0_m, 0_m}, Vector<dimensionless_d>{rootCS, {0., 0., 1.}}, rho0, - lambda, - environment::NuclearComposition( - std::vector<particles::Code>{particles::Code::Nitrogen, - particles::Code::Oxygen}, - std::vector<float>{ - 0.7847f, - 1.f - 0.7847f})); // values taken from AIRES manual, Ar removed for now + + environment::LayeredSphericalAtmosphereBuilder builder(Point{rootCS, 0_m, 0_m, 0_m}); + builder.setNuclearComposition( + {{particles::Code::Nitrogen, particles::Code::Oxygen}, + {0.7847f, 1.f - 0.7847f}}); // values taken from AIRES manual, Ar removed for now + + builder.addExponentialLayer(1222.6562_g / (1_cm * 1_cm), 994186.38_cm, 4_km); + builder.addExponentialLayer(1144.9069_g / (1_cm * 1_cm), 878153.55_cm, 10_km); + builder.addExponentialLayer(1305.5948_g / (1_cm * 1_cm), 636143.04_cm, 40_km); + builder.addExponentialLayer(540.1778_g / (1_cm * 1_cm), 772170.16_cm, 100_km); + builder.addLinearLayer(1e9_cm, 112.8_km); + + builder.assemble(env); // setup particle stack, and add primary particle setup::Stack stack; @@ -110,7 +104,9 @@ int main() { double phi = 0.; Point const injectionPos( - rootCS, 0_m, 0_m, 112.8_km); // this is the CORSIKA 7 start of atmosphere/universe + rootCS, 0_m, 0_m, + 112.8_km * 0.999 + + builder.earthRadius); // this is the CORSIKA 7 start of atmosphere/universe // { auto elab2plab = [](HEPEnergyType Elab, HEPMassType m) { @@ -137,17 +133,10 @@ int main() { Line const line(injectionPos, plab.normalized() * 1_m * 1_Hz); auto const velocity = line.GetV0().norm(); - auto const observationHeight = 1.425_km; + auto const observationHeight = 1.425_km + builder.earthRadius; setup::Trajectory const showerAxis(line, (112.8_km - observationHeight) / velocity); - auto const grammage = theMedium->GetModelProperties().IntegratedGrammage( - showerAxis, (112.8_km - observationHeight)); - std::cout << "Grammage to ground: " << grammage / (1_g / square(1_cm)) << " g/cm²" - << std::endl; - - universe.AddChild(std::move(theMedium)); - // setup processes, decays and interactions const std::vector<particles::Code> trackedHadrons = { diff --git a/Environment/CMakeLists.txt b/Environment/CMakeLists.txt index 28e5523163014cef5709d39d8d113e738d282889..f4b93c759a2f32128d98406b8e4130dd851b8c45 100644 --- a/Environment/CMakeLists.txt +++ b/Environment/CMakeLists.txt @@ -1,3 +1,8 @@ +set ( + ENVIRONMENT_SOURCES + LayeredSphericalAtmosphereBuilder.cc +) + set ( ENVIRONMENT_HEADERS VolumeTreeNode.h @@ -13,6 +18,7 @@ set ( BaseExponential.h FlatExponential.h SlidingPlanarExponential.h + LayeredSphericalAtmosphereBuilder.h ) set ( @@ -20,13 +26,20 @@ set ( corsika/environment ) -add_library (CORSIKAenvironment INTERFACE) +add_library (CORSIKAenvironment STATIC ${ENVIRONMENT_SOURCES}) CORSIKA_COPY_HEADERS_TO_NAMESPACE (CORSIKAenvironment ${ENVIRONMENT_NAMESPACE} ${ENVIRONMENT_HEADERS}) +set_target_properties ( + CORSIKAgeometry + PROPERTIES + VERSION ${PROJECT_VERSION} + SOVERSION 1 + PUBLIC_HEADER "${ENVIRONMENT_HEADERS}" + ) + # target dependencies on other libraries (also the header onlys) target_link_libraries ( CORSIKAenvironment - INTERFACE CORSIKAgeometry CORSIKAparticles CORSIKAunits @@ -35,7 +48,7 @@ target_link_libraries ( target_include_directories ( CORSIKAenvironment - INTERFACE + PUBLIC $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include> $<INSTALL_INTERFACE:include> ) diff --git a/Environment/LayeredSphericalAtmosphereBuilder.cc b/Environment/LayeredSphericalAtmosphereBuilder.cc new file mode 100644 index 0000000000000000000000000000000000000000..4d1631d54e42431cd1b36299af9083c643bce0e7 --- /dev/null +++ b/Environment/LayeredSphericalAtmosphereBuilder.cc @@ -0,0 +1,85 @@ +/* + * (c) Copyright 2020 CORSIKA Project, corsika-project@lists.kit.edu + * + * See file AUTHORS for a list of contributors. + * + * This software is distributed under the terms of the GNU General Public + * Licence version 3 (GPL Version 3). See file LICENSE for a full version of + * the license. + */ + +#include <corsika/environment/LayeredSphericalAtmosphereBuilder.h> +#include <corsika/environment/FlatExponential.h> +#include <corsika/environment/HomogeneousMedium.h> +#include <corsika/environment/SlidingPlanarExponential.h> + +using namespace corsika::environment; + +void LayeredSphericalAtmosphereBuilder::checkRadius(units::si::LengthType r) const { + if (r <= previousRadius_) { + throw std::runtime_error("radius must be greater than previous"); + } +} + +void LayeredSphericalAtmosphereBuilder::setNuclearComposition( + NuclearComposition composition) { + composition_ = std::make_unique<NuclearComposition>(composition); +} + +void LayeredSphericalAtmosphereBuilder::addExponentialLayer( + units::si::GrammageType b, units::si::LengthType c, + units::si::LengthType upperBoundary) { + auto const radius = seaLevel_ + upperBoundary; + checkRadius(radius); + previousRadius_ = radius; + + auto node = std::make_unique<VolumeTreeNode<IMediumModel>>( + std::make_unique<geometry::Sphere>(center_, radius)); + + auto const rho0 = b / c; + std::cout << "rho0 = " << rho0 << ", c = " << c << std::endl; + + node->SetModelProperties<SlidingPlanarExponential<IMediumModel>>( + center_, rho0, -c, *composition_, seaLevel_); + + layers_.push(std::move(node)); +} + +void LayeredSphericalAtmosphereBuilder::addLinearLayer( + units::si::LengthType c, units::si::LengthType upperBoundary) { + using namespace units::si; + + auto const radius = seaLevel_ + upperBoundary; + checkRadius(radius); + previousRadius_ = radius; + + units::si::GrammageType constexpr b = 1 * 1_g / (1_cm * 1_cm); + auto const rho0 = b / c; + + std::cout << "rho0 = " << rho0; + + auto node = std::make_unique<VolumeTreeNode<environment::IMediumModel>>( + std::make_unique<geometry::Sphere>(center_, radius)); + node->SetModelProperties<HomogeneousMedium<IMediumModel>>(rho0, *composition_); + + layers_.push(std::move(node)); +} + +Environment<IMediumModel> LayeredSphericalAtmosphereBuilder::assemble() { + Environment<IMediumModel> env; + assemble(env); + return env; +} + +void LayeredSphericalAtmosphereBuilder::assemble(Environment<IMediumModel>& env) { + auto& universe = env.GetUniverse(); + auto* outmost = universe.get(); + + while (!layers_.empty()) { + auto l = std::move(layers_.top()); + auto* tmp = l.get(); + outmost->AddChild(std::move(l)); + layers_.pop(); + outmost = tmp; + } +} diff --git a/Environment/LayeredSphericalAtmosphereBuilder.h b/Environment/LayeredSphericalAtmosphereBuilder.h new file mode 100644 index 0000000000000000000000000000000000000000..ab2b8a25d95887d198dc568536fec81c3d40b461 --- /dev/null +++ b/Environment/LayeredSphericalAtmosphereBuilder.h @@ -0,0 +1,55 @@ +/* + * (c) Copyright 2020 CORSIKA Project, corsika-project@lists.kit.edu + * + * See file AUTHORS for a list of contributors. + * + * This software is distributed under the terms of the GNU General Public + * Licence version 3 (GPL Version 3). See file LICENSE for a full version of + * the license. + */ + +#include <corsika/environment/Environment.h> +#include <corsika/environment/IMediumModel.h> +#include <corsika/environment/NuclearComposition.h> +#include <corsika/environment/VolumeTreeNode.h> +#include <corsika/geometry/Point.h> +#include <corsika/units/PhysicalUnits.h> + +#include <memory> +#include <stack> + +namespace corsika::environment { + + class LayeredSphericalAtmosphereBuilder { + std::unique_ptr<NuclearComposition> composition_; + geometry::Point center_; + units::si::LengthType previousRadius_{units::si::LengthType::zero()}; + units::si::LengthType seaLevel_; + + std::stack<VolumeTreeNode<environment::IMediumModel>::VTNUPtr> + layers_; // innermost layer first + + void checkRadius(units::si::LengthType) const; + + public: + static auto constexpr earthRadius = 6'371'000 * units::si::meter; + + LayeredSphericalAtmosphereBuilder(corsika::geometry::Point center, + units::si::LengthType seaLevel = earthRadius) + : center_(center) + , seaLevel_(seaLevel) {} + + void setNuclearComposition(NuclearComposition); + + void addExponentialLayer(units::si::GrammageType, units::si::LengthType, + units::si::LengthType); + + auto size() const { return layers_.size(); } + + void addLinearLayer(units::si::LengthType, units::si::LengthType); + + void assemble(Environment<IMediumModel>&); + Environment<IMediumModel> assemble(); + }; + +} // namespace corsika::environment diff --git a/Environment/SlidingPlanarExponential.h b/Environment/SlidingPlanarExponential.h index 71f8e27d917e3e34fc5d8c2fc663d5142d07aa59..fdbde0f4e1d3257ba73768ea1e4d123e6ee6fbbc 100644 --- a/Environment/SlidingPlanarExponential.h +++ b/Environment/SlidingPlanarExponential.h @@ -38,36 +38,38 @@ namespace corsika::environment { template <class T> class SlidingPlanarExponential : public BaseExponential<SlidingPlanarExponential<T>>, public T { - NuclearComposition const fNuclComp; + NuclearComposition const nuclComp_; + units::si::LengthType const referenceHeight_; using Base = BaseExponential<SlidingPlanarExponential<T>>; public: - SlidingPlanarExponential(geometry::Point const& vP0, units::si::MassDensityType vRho, - units::si::LengthType vLambda, NuclearComposition vNuclComp) - : Base(vP0, vRho, vLambda) - , fNuclComp(vNuclComp) {} + SlidingPlanarExponential(geometry::Point const& p0, units::si::MassDensityType rho0, + units::si::LengthType lambda, NuclearComposition nuclComp, units::si::LengthType referenceHeight = units::si::LengthType::zero()) + : Base(p0, rho0, lambda) + , nuclComp_(nuclComp), + referenceHeight_(referenceHeight) {} units::si::MassDensityType GetMassDensity( - geometry::Point const& vP) const override { - auto const height = (vP - Base::fP0).norm(); + geometry::Point const& p) const override { + auto const height = (p - Base::fP0).norm() - referenceHeight_; return Base::fRho0 * exp(Base::fInvLambda * height); } - NuclearComposition const& GetNuclearComposition() const override { return fNuclComp; } + NuclearComposition const& GetNuclearComposition() const override { return nuclComp_; } units::si::GrammageType IntegratedGrammage( - geometry::Trajectory<geometry::Line> const& vLine, - units::si::LengthType vL) const override { - auto const axis = (vLine.GetR0() - Base::fP0).normalized(); - return Base::IntegratedGrammage(vLine, vL, axis); + geometry::Trajectory<geometry::Line> const& line, + units::si::LengthType l) const override { + auto const axis = (line.GetR0() - Base::fP0).normalized(); + return Base::IntegratedGrammage(line, l, axis); } units::si::LengthType ArclengthFromGrammage( - geometry::Trajectory<geometry::Line> const& vLine, - units::si::GrammageType vGrammage) const override { - auto const axis = (vLine.GetR0() - Base::fP0).normalized(); - return Base::ArclengthFromGrammage(vLine, vGrammage, axis); + geometry::Trajectory<geometry::Line> const& line, + units::si::GrammageType grammage) const override { + auto const axis = (line.GetR0() - Base::fP0).normalized(); + return Base::ArclengthFromGrammage(line, grammage, axis); } }; diff --git a/Environment/testEnvironment.cc b/Environment/testEnvironment.cc index cae91a86000b027c3e2687d9515df977c23d0eb9..5c314d1d07b5b9a5faad15ea2345114a5fb8f3f1 100644 --- a/Environment/testEnvironment.cc +++ b/Environment/testEnvironment.cc @@ -8,6 +8,7 @@ * the license. */ +#include <corsika/environment/LayeredSphericalAtmosphereBuilder.h> #include <corsika/environment/DensityFunction.h> #include <corsika/environment/FlatExponential.h> #include <corsika/environment/HomogeneousMedium.h> @@ -196,3 +197,35 @@ TEST_CASE("InhomogeneousMedium") { inhMedium.ArclengthFromGrammage(trajectory, 20_g / (1_cm * 1_cm))); } } + +TEST_CASE("LayeredSphericalAtmosphereBuilder") { + LayeredSphericalAtmosphereBuilder builder(gOrigin); + builder.setNuclearComposition( + {{{particles::Code::Nitrogen, particles::Code::Oxygen}}, {{.6, .4}}}); + + builder.addLinearLayer(1_km, 10_km); + builder.addLinearLayer(2_km, 20_km); + builder.addLinearLayer(3_km, 30_km); + + REQUIRE(builder.size() == 3); + + auto const builtEnv = builder.assemble(); + auto const& univ = builtEnv.GetUniverse(); + + REQUIRE(builder.size() == 0); + + auto constexpr R = LayeredSphericalAtmosphereBuilder::earthRadius; + + REQUIRE(univ->GetChildNodes().size() == 1); + + REQUIRE(univ->GetContainingNode(Point(gCS, 0_m, 0_m, R + 35_km)) == univ.get()); + REQUIRE(dynamic_cast<Sphere const&>( + univ->GetContainingNode(Point(gCS, 0_m, 0_m, R + 8_km))->GetVolume()) + .GetRadius() == R + 10_km); + REQUIRE(dynamic_cast<Sphere const&>( + univ->GetContainingNode(Point(gCS, 0_m, 0_m, R + 12_km))->GetVolume()) + .GetRadius() == R + 20_km); + REQUIRE(dynamic_cast<Sphere const&>( + univ->GetContainingNode(Point(gCS, 0_m, 0_m, R + 24_km))->GetVolume()) + .GetRadius() == R + 30_km); +}