IAP GITLAB

Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • AirShowerPhysics/corsika
  • rulrich/corsika
  • AAAlvesJr/corsika
  • Andre/corsika
  • arrabito/corsika
  • Nikos/corsika
  • olheiser73/corsika
  • AirShowerPhysics/papers/corsika
  • pranav/corsika
9 results
Show changes
Showing
with 0 additions and 1599 deletions
/*
* (c) Copyright 2018 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.
*/
#ifndef _include_HELIX_H_
#define _include_HELIX_H_
#include <corsika/geometry/Point.h>
#include <corsika/geometry/Vector.h>
#include <corsika/units/PhysicalUnits.h>
#include <cmath>
namespace corsika::geometry {
/*!
* A Helix is defined by the cyclotron frequency \f$ \omega_c \f$, the initial
* Point r0 and
* the velocity vectors \f$ \vec{v}_{\parallel} \f$ and \f$ \vec{v}_{\perp} \f$
* denoting the projections of the initial velocity \f$ \vec{v}_0 \f$ parallel
* and perpendicular to the axis \f$ \vec{B} \f$, respectively, i.e.
* \f{align*}{
\vec{v}_{\parallel} &= \frac{\vec{v}_0 \cdot \vec{B}}{\vec{B}^2} \vec{B} \\
\vec{v}_{\perp} &= \vec{v}_0 - \vec{v}_{\parallel}
\f}
*/
class Helix {
using VelocityVec = Vector<corsika::units::si::SpeedType::dimension_type>;
Point const r0;
corsika::units::si::FrequencyType const omegaC;
VelocityVec const vPar;
VelocityVec const vPerp, uPerp;
corsika::units::si::LengthType const radius;
public:
Helix(Point const& pR0, corsika::units::si::FrequencyType pOmegaC,
VelocityVec const& pvPar, VelocityVec const& pvPerp)
: r0(pR0)
, omegaC(pOmegaC)
, vPar(pvPar)
, vPerp(pvPerp)
, uPerp(vPerp.cross(vPar.normalized()))
, radius(pvPar.norm() / abs(pOmegaC)) {}
Point GetPosition(corsika::units::si::TimeType t) const {
return r0 + vPar * t +
(vPerp * (cos(omegaC * t) - 1) + uPerp * sin(omegaC * t)) / omegaC;
}
Point PositionFromArclength(corsika::units::si::LengthType l) const {
return GetPosition(TimeFromArclength(l));
}
auto GetRadius() const { return radius; }
corsika::units::si::LengthType ArcLength(corsika::units::si::TimeType t1,
corsika::units::si::TimeType t2) const {
return (vPar + vPerp).norm() * (t2 - t1);
}
corsika::units::si::TimeType TimeFromArclength(
corsika::units::si::LengthType l) const {
return l / (vPar + vPerp).norm();
}
};
} // namespace corsika::geometry
#endif
/*
* (c) Copyright 2018 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.
*/
#ifndef _include_LINETRAJECTORY_H
#define _include_LINETRAJECTORY_H
#include <corsika/geometry/Point.h>
#include <corsika/geometry/Vector.h>
#include <corsika/units/PhysicalUnits.h>
namespace corsika::geometry {
class Line {
using VelocityVec = Vector<corsika::units::si::SpeedType::dimension_type>;
Point const r0;
VelocityVec const v0;
public:
Line(Point const& pR0, VelocityVec const& pV0)
: r0(pR0)
, v0(pV0) {}
Point GetPosition(corsika::units::si::TimeType t) const { return r0 + v0 * t; }
Point PositionFromArclength(corsika::units::si::LengthType l) const {
return r0 + v0.normalized() * l;
}
LengthType ArcLength(corsika::units::si::TimeType t1,
corsika::units::si::TimeType t2) const {
return v0.norm() * (t2 - t1);
}
corsika::units::si::TimeType TimeFromArclength(
corsika::units::si::LengthType t) const {
return t / v0.norm();
}
auto GetR0() const { return r0; }
auto GetV0() const { return v0; }
};
} // namespace corsika::geometry
#endif
/*
* (c) Copyright 2018 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.
*/
#ifndef _include_Framework_Geometry_Plane_h_
#define _include_Framework_Geometry_Plane_h_
#include <corsika/geometry/Point.h>
#include <corsika/geometry/Vector.h>
#include <corsika/units/PhysicalUnits.h>
namespace corsika::geometry {
class Plane {
using DimLessVec = Vector<corsika::units::si::dimensionless_d>;
Point const fCenter;
DimLessVec const fNormal;
public:
Plane(Point const& vCenter, DimLessVec const& vNormal)
: fCenter(vCenter)
, fNormal(vNormal) {}
bool IsAbove(Point const& vP) const {
return fNormal.dot(vP - fCenter) > corsika::units::si::LengthType::zero();
}
Point const& GetCenter() const { return fCenter; }
DimLessVec const& GetNormal() const { return fNormal; }
};
} // namespace corsika::geometry
#endif
/*
* (c) Copyright 2018 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.
*/
#ifndef _include_POINT_H_
#define _include_POINT_H_
#include <corsika/geometry/BaseVector.h>
#include <corsika/geometry/QuantityVector.h>
#include <corsika/geometry/Vector.h>
#include <corsika/units/PhysicalUnits.h>
namespace corsika::geometry {
using corsika::units::si::length_d;
using corsika::units::si::LengthType;
/*!
* A Point represents a point in position space. It is defined by its
* coordinates with respect to some CoordinateSystem.
*/
class Point : public BaseVector<length_d> {
public:
Point(CoordinateSystem const& pCS, QuantityVector<length_d> pQVector)
: BaseVector<length_d>(pCS, pQVector) {}
Point(CoordinateSystem const& cs, LengthType x, LengthType y, LengthType z)
: BaseVector<length_d>(cs, {x, y, z}) {}
// TODO: this should be private or protected, we don NOT want to expose numbers
// without reference to outside:
auto GetCoordinates() const { return BaseVector<length_d>::qVector; }
auto GetX() const { return BaseVector<length_d>::qVector.GetX(); }
auto GetY() const { return BaseVector<length_d>::qVector.GetY(); }
auto GetZ() const { return BaseVector<length_d>::qVector.GetZ(); }
/// this always returns a QuantityVector as triple
auto GetCoordinates(CoordinateSystem const& pCS) const {
if (&pCS == BaseVector<length_d>::cs) {
return BaseVector<length_d>::qVector;
} else {
return QuantityVector<length_d>(
CoordinateSystem::GetTransformation(*BaseVector<length_d>::cs, pCS) *
BaseVector<length_d>::qVector.eVector);
}
}
/*!
* transforms the Point into another CoordinateSystem by changing its
* coordinates interally
*/
void rebase(CoordinateSystem const& pCS) {
BaseVector<length_d>::qVector = GetCoordinates(pCS);
BaseVector<length_d>::cs = &pCS;
}
Point operator+(Vector<length_d> const& pVec) const {
return Point(*BaseVector<length_d>::cs,
GetCoordinates() + pVec.GetComponents(*BaseVector<length_d>::cs));
}
/*!
* returns the distance Vector between two points
*/
Vector<length_d> operator-(Point const& pB) const {
auto& cs = *BaseVector<length_d>::cs;
return Vector<length_d>(cs, GetCoordinates() - pB.GetCoordinates(cs));
}
};
} // namespace corsika::geometry
#endif
/*
* (c) Copyright 2018 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.
*/
#ifndef _include_QUANTITYVECTOR_H_
#define _include_QUANTITYVECTOR_H_
#include <corsika/units/PhysicalUnits.h>
#include <Eigen/Dense>
#include <iostream>
#include <utility>
namespace corsika::geometry {
/*!
* A QuantityVector is a three-component container based on Eigen::Vector3d
* with a phys::units::si::dimension. Arithmethic operators are defined that
* propagate the dimensions by dimensional analysis.
*/
template <typename dim>
class QuantityVector {
public:
using Quantity = phys::units::quantity<dim, double>; //< the phys::units::quantity
// corresponding to the dimension
public:
Eigen::Vector3d eVector; //!< the actual container where the raw numbers are stored
typedef dim dimension; //!< should be a phys::units::dimension
QuantityVector(Quantity a, Quantity b, Quantity c)
: eVector{a.magnitude(), b.magnitude(), c.magnitude()} {}
QuantityVector(double a, double b, double c)
: eVector{a, b, c} {
static_assert(
std::is_same_v<dim, phys::units::dimensionless_d>,
"initialization of dimensionful QuantityVector with pure numbers not allowed!");
}
QuantityVector(Eigen::Vector3d pBareVector)
: eVector(pBareVector) {}
auto operator[](size_t index) const {
return Quantity(phys::units::detail::magnitude_tag, eVector[index]);
}
auto GetX() const { return Quantity(phys::units::detail::magnitude_tag, eVector[0]); }
auto GetY() const { return Quantity(phys::units::detail::magnitude_tag, eVector[1]); }
auto GetZ() const { return Quantity(phys::units::detail::magnitude_tag, eVector[2]); }
auto norm() const {
return Quantity(phys::units::detail::magnitude_tag, eVector.norm());
}
auto squaredNorm() const {
using QuantitySquared =
decltype(std::declval<Quantity>() * std::declval<Quantity>());
return QuantitySquared(phys::units::detail::magnitude_tag, eVector.squaredNorm());
}
auto operator+(QuantityVector<dim> const& pQVec) const {
return QuantityVector<dim>(eVector + pQVec.eVector);
}
auto operator-(QuantityVector<dim> const& pQVec) const {
return QuantityVector<dim>(eVector - pQVec.eVector);
}
template <typename ScalarDim>
auto operator*(phys::units::quantity<ScalarDim, double> const p) const {
using ResQuantity = phys::units::detail::Product<ScalarDim, dim, double, double>;
if constexpr (std::is_same<ResQuantity, double>::value) // result dimensionless, not
// a "Quantity" anymore
{
return QuantityVector<phys::units::dimensionless_d>(eVector * p.magnitude());
} else {
return QuantityVector<typename ResQuantity::dimension_type>(eVector *
p.magnitude());
}
}
template <typename ScalarDim>
auto operator/(phys::units::quantity<ScalarDim, double> const p) const {
return (*this) * (1 / p);
}
auto operator*(double const p) const { return QuantityVector<dim>(eVector * p); }
auto operator/(double const p) const { return QuantityVector<dim>(eVector / p); }
auto& operator/=(double const p) {
eVector /= p;
return *this;
}
auto& operator*=(double const p) {
eVector *= p;
return *this;
}
auto& operator+=(QuantityVector<dim> const& pQVec) {
eVector += pQVec.eVector;
return *this;
}
auto& operator-=(QuantityVector<dim> const& pQVec) {
eVector -= pQVec.eVector;
return *this;
}
auto& operator-() const { return QuantityVector<dim>(-eVector); }
auto normalized() const { return QuantityVector<dim>(eVector.normalized()); }
auto operator==(QuantityVector<dim> const& p) const { return eVector == p.eVector; }
};
template <typename dim>
auto& operator<<(std::ostream& os, corsika::geometry::QuantityVector<dim> qv) {
using Quantity = phys::units::quantity<dim, double>;
os << '(' << qv.eVector(0) << ' ' << qv.eVector(1) << ' ' << qv.eVector(2) << ") "
<< phys::units::to_unit_symbol<dim, double>(
Quantity(phys::units::detail::magnitude_tag, 1));
return os;
}
} // namespace corsika::geometry
#endif
/*
* (c) Copyright 2018 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.
*/
#ifndef _include_corsika_geometry_rootcoordinatesystem_h_
#define _include_corsika_geometry_rootcoordinatesystem_h_
#include <corsika/utl/Singleton.h>
#include <corsika/geometry/CoordinateSystem.h>
/*!
* This is the only way to get a root-coordinate system, and it is a
* singleton. All other CoordinateSystems must be relative to the
* RootCoordinateSystem
*/
namespace corsika::geometry {
class RootCoordinateSystem : public corsika::utl::Singleton<RootCoordinateSystem> {
friend class corsika::utl::Singleton<RootCoordinateSystem>;
protected:
RootCoordinateSystem() {}
public:
corsika::geometry::CoordinateSystem& GetRootCoordinateSystem() { return fRootCS; }
const corsika::geometry::CoordinateSystem& GetRootCoordinateSystem() const {
return fRootCS;
}
private:
corsika::geometry::CoordinateSystem fRootCS; // THIS IS IT
};
} // namespace corsika::geometry
#endif
/*
* (c) Copyright 2018 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.
*/
#ifndef _include_SPHERE_H_
#define _include_SPHERE_H_
#include <corsika/geometry/Point.h>
#include <corsika/geometry/Volume.h>
#include <corsika/units/PhysicalUnits.h>
namespace corsika::geometry {
class Sphere : public Volume {
Point const fCenter;
LengthType const fRadius;
public:
Sphere(Point const& pCenter, LengthType const pRadius)
: fCenter(pCenter)
, fRadius(pRadius) {}
//! returns true if the Point p is within the sphere
bool Contains(Point const& p) const override {
return fRadius * fRadius > (fCenter - p).squaredNorm();
}
auto& GetCenter() const { return fCenter; }
auto GetRadius() const { return fRadius; }
};
} // namespace corsika::geometry
#endif
/*
* (c) Copyright 2018 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.
*/
#ifndef _include_TRAJECTORY_H
#define _include_TRAJECTORY_H
#include <corsika/geometry/Line.h>
#include <corsika/geometry/Point.h>
#include <corsika/units/PhysicalUnits.h>
namespace corsika::geometry {
template <typename T>
class Trajectory : public T {
corsika::units::si::TimeType fTimeLength;
public:
using T::ArcLength;
using T::GetPosition;
Trajectory(T const& theT, corsika::units::si::TimeType timeLength)
: T(theT)
, fTimeLength(timeLength) {}
/*Point GetPosition(corsika::units::si::TimeType t) const {
return fTraj.GetPosition(t + fTStart);
}*/
Point GetPosition(double u) const { return T::GetPosition(fTimeLength * u); }
corsika::units::si::TimeType GetDuration() const { return fTimeLength; }
corsika::units::si::LengthType GetLength() const { return GetDistance(fTimeLength); }
corsika::units::si::LengthType GetDistance(corsika::units::si::TimeType t) const {
assert(t <= fTimeLength);
assert(t >= 0 * corsika::units::si::second);
return T::ArcLength(0 * corsika::units::si::second, t);
}
void LimitEndTo(corsika::units::si::LengthType limit) {
fTimeLength = T::TimeFromArclength(limit);
}
auto NormalizedDirection() const {
static_assert(std::is_same_v<T, corsika::geometry::Line>);
return T::GetV0().normalized();
}
};
} // namespace corsika::geometry
#endif
/*
* (c) Copyright 2018 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.
*/
#ifndef _include_VECTOR_H_
#define _include_VECTOR_H_
#include <corsika/geometry/BaseVector.h>
#include <corsika/geometry/QuantityVector.h>
#include <corsika/units/PhysicalUnits.h>
/*!
* A Vector represents a 3-vector in Euclidean space. It is defined by components
* given in a specific CoordinateSystem. It has a physical dimension ("unit")
* as part of its type, so you cannot mix up e.g. electric with magnetic fields
* (but you could calculate their cross-product to get an energy flux vector).
*
* When transforming coordinate systems, a Vector is subject to the rotational
* part only and invariant under translations.
*/
namespace corsika::geometry {
template <typename dim>
class Vector : public BaseVector<dim> {
public:
using Quantity = phys::units::quantity<dim, double>;
public:
Vector(CoordinateSystem const& pCS, QuantityVector<dim> pQVector)
: BaseVector<dim>(pCS, pQVector) {}
Vector(CoordinateSystem const& cs, Quantity x, Quantity y, Quantity z)
: BaseVector<dim>(cs, QuantityVector<dim>(x, y, z)) {}
/*!
* returns a QuantityVector with the components given in the "home"
* CoordinateSystem of the Vector
*/
auto GetComponents() const { return BaseVector<dim>::qVector; }
/*!
* returns a QuantityVector with the components given in an arbitrary
* CoordinateSystem
*/
auto GetComponents(CoordinateSystem const& pCS) const {
if (&pCS == BaseVector<dim>::cs) {
return BaseVector<dim>::qVector;
} else {
return QuantityVector<dim>(
CoordinateSystem::GetTransformation(*BaseVector<dim>::cs, pCS).linear() *
BaseVector<dim>::qVector.eVector);
}
}
/*!
* transforms the Vector into another CoordinateSystem by changing
* its components internally
*/
void rebase(CoordinateSystem const& pCS) {
BaseVector<dim>::qVector = GetComponents(pCS);
BaseVector<dim>::cs = &pCS;
}
/*!
* returns the norm/length of the Vector. Before using this method,
* think about whether squaredNorm() might be cheaper for your computation.
*/
auto norm() const { return BaseVector<dim>::qVector.norm(); }
auto GetNorm() const { return BaseVector<dim>::qVector.norm(); }
/*!
* returns the squared norm of the Vector. Before using this method,
* think about whether norm() might be cheaper for your computation.
*/
auto squaredNorm() const { return BaseVector<dim>::qVector.squaredNorm(); }
auto GetSquaredNorm() const { return BaseVector<dim>::qVector.squaredNorm(); }
/*!
* returns a Vector \f$ \vec{v}_{\parallel} \f$ which is the parallel projection
* of this vector \f$ \vec{v}_1 \f$ along another Vector \f$ \vec{v}_2 \f$ given by
* \f[
* \vec{v}_{\parallel} = \frac{\vec{v}_1 \cdot \vec{v}_2}{\vec{v}_2^2} \vec{v}_2
* \f]
*/
template <typename dim2>
auto parallelProjectionOnto(Vector<dim2> const& pVec,
CoordinateSystem const& pCS) const {
auto const ourCompVec = GetComponents(pCS);
auto const otherCompVec = pVec.GetComponents(pCS);
auto const& a = ourCompVec.eVector;
auto const& b = otherCompVec.eVector;
return Vector<dim>(pCS, QuantityVector<dim>(b * ((a.dot(b)) / b.squaredNorm())));
}
template <typename dim2>
auto parallelProjectionOnto(Vector<dim2> const& pVec) const {
return parallelProjectionOnto<dim2>(pVec, *BaseVector<dim>::cs);
}
auto operator+(Vector<dim> const& pVec) const {
auto const components =
GetComponents(*BaseVector<dim>::cs) + pVec.GetComponents(*BaseVector<dim>::cs);
return Vector<dim>(*BaseVector<dim>::cs, components);
}
auto operator-(Vector<dim> const& pVec) const {
auto const components = GetComponents() - pVec.GetComponents(*BaseVector<dim>::cs);
return Vector<dim>(*BaseVector<dim>::cs, components);
}
auto& operator*=(double const p) {
BaseVector<dim>::qVector *= p;
return *this;
}
template <typename ScalarDim>
auto operator*(phys::units::quantity<ScalarDim, double> const p) const {
using ProdDim = phys::units::detail::product_d<dim, ScalarDim>;
return Vector<ProdDim>(*BaseVector<dim>::cs, BaseVector<dim>::qVector * p);
}
template <typename ScalarDim>
auto operator/(phys::units::quantity<ScalarDim, double> const p) const {
return (*this) * (1 / p);
}
auto operator*(double const p) const {
return Vector<dim>(*BaseVector<dim>::cs, BaseVector<dim>::qVector * p);
}
auto operator/(double const p) const {
return Vector<dim>(*BaseVector<dim>::cs, BaseVector<dim>::qVector / p);
}
auto& operator+=(Vector<dim> const& pVec) {
BaseVector<dim>::qVector += pVec.GetComponents(*BaseVector<dim>::cs);
return *this;
}
auto& operator-=(Vector<dim> const& pVec) {
BaseVector<dim>::qVector -= pVec.GetComponents(*BaseVector<dim>::cs);
return *this;
}
auto& operator-() const {
return Vector<dim>(*BaseVector<dim>::cs, -BaseVector<dim>::qVector);
}
auto normalized() const { return (*this) * (1 / norm()); }
template <typename dim2>
auto cross(Vector<dim2> pV) const {
auto const c1 = GetComponents().eVector;
auto const c2 = pV.GetComponents(*BaseVector<dim>::cs).eVector;
auto const bareResult = c1.cross(c2);
using ProdDim = phys::units::detail::product_d<dim, dim2>;
return Vector<ProdDim>(*BaseVector<dim>::cs, bareResult);
}
template <typename dim2>
auto dot(Vector<dim2> pV) const {
auto const c1 = GetComponents().eVector;
auto const c2 = pV.GetComponents(*BaseVector<dim>::cs).eVector;
auto const bareResult = c1.dot(c2);
using ProdDim = phys::units::detail::product_d<dim, dim2>;
return phys::units::quantity<ProdDim, double>(phys::units::detail::magnitude_tag,
bareResult);
}
};
} // namespace corsika::geometry
#endif
/*
* (c) Copyright 2018 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.
*/
#ifndef _include_VOLUME_H_
#define _include_VOLUME_H_
#include <corsika/geometry/Point.h>
namespace corsika::geometry {
class Volume {
public:
//! returns true if the Point p is within the volume
virtual bool Contains(Point const& p) const = 0;
virtual ~Volume() = default;
};
} // namespace corsika::geometry
#endif
/*
* (c) Copyright 2018 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.
*/
#define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one
// cpp file
#include <catch2/catch.hpp>
#include <corsika/geometry/CoordinateSystem.h>
#include <corsika/geometry/FourVector.h>
#include <corsika/geometry/RootCoordinateSystem.h>
#include <corsika/geometry/Vector.h>
#include <corsika/units/PhysicalUnits.h>
#include <cmath>
#include <boost/type_index.hpp>
using boost::typeindex::type_id_with_cvr;
using namespace corsika::geometry;
using namespace corsika::units::si;
TEST_CASE("four vectors") {
// this is just needed as a baseline
CoordinateSystem& rootCS =
RootCoordinateSystem::GetInstance().GetRootCoordinateSystem();
/*
Test: P2 = E2 - p2 all in [GeV]
This is the typical HEP application
*/
SECTION("Energy momentum in hep-units") {
HEPEnergyType E0 = 10_GeV;
Vector<hepmomentum_d> P0(rootCS, {10_GeV, 10_GeV, 10_GeV});
FourVector p0(E0, P0);
REQUIRE(p0.GetNormSqr() == -200_GeV * 1_GeV);
REQUIRE(p0.GetNorm() == sqrt(200_GeV * 1_GeV));
}
/*
Check space/time-like
*/
SECTION("Space/time likeness") {
HEPEnergyType E0 = 20_GeV;
Vector<hepmomentum_d> P0(rootCS, {10_GeV, 0_GeV, 0_GeV});
Vector<hepmomentum_d> P1(rootCS, {10_GeV, 10_GeV, 20_GeV});
Vector<hepmomentum_d> P2(rootCS, {0_GeV, 20_GeV, 0_GeV});
FourVector p0(E0, P0);
FourVector p1(E0, P1);
FourVector p2(E0, P2);
CHECK(p0.IsSpacelike());
CHECK(!p0.IsTimelike());
// CHECK(!p0.IsPhotonlike());
CHECK(!p1.IsSpacelike());
CHECK(p1.IsTimelike());
// CHECK(!p1.IsPhotonlike());
CHECK(!p2.IsSpacelike());
CHECK(!p2.IsTimelike());
// CHECK(p2.IsPhotonlike());
}
/*
Test: P2 = E2/c2 - p2 with E in [GeV/c] and P in [GeV]
This requires additional factors of c
*/
SECTION("Energy momentum in SI-units") {
auto E1 = 100_GeV / corsika::units::constants::c;
Vector<hepmomentum_d> P1(rootCS, {10_GeV, 5_GeV, 15_GeV});
FourVector p1(E1, P1);
const double check = 100 * 100 - 10 * 10 - 5 * 5 - 15 * 15; // for dummies...
REQUIRE(p1.GetNormSqr() / 1_GeV / 1_GeV == Approx(check));
REQUIRE(p1.GetNorm() / 1_GeV == Approx(sqrt(check)));
}
/**
Test: P2 = T2/c2 - r2 with T in [s] and r in [m]
This requires additional factors of c
*/
SECTION("Spacetime in SI-units") {
TimeType T2 = 10_m / corsika::units::constants::c;
Vector<length_d> P2(rootCS, {10_m, 5_m, 5_m});
const double check = 10 * 10 - 10 * 10 - 5 * 5 - 5 * 5; // for dummies...
FourVector p2(T2, P2);
REQUIRE(p2.GetNormSqr() == check * 1_m * 1_m);
REQUIRE(p2.GetNorm() == sqrt(abs(check)) * 1_m);
}
/**
Testing the math operators
*/
SECTION("Operators and comutions") {
HEPEnergyType E1 = 100_GeV;
Vector<hepmomentum_d> P1(rootCS, {0_GeV, 0_GeV, 0_GeV});
HEPEnergyType E2 = 0_GeV;
Vector<hepmomentum_d> P2(rootCS, {10_GeV, 0_GeV, 0_GeV});
FourVector p1(E1, P1);
const FourVector p2(E2, P2);
REQUIRE(p1.GetNorm() / 1_GeV == Approx(100.));
REQUIRE(p2.GetNorm() / 1_GeV == Approx(10.));
SECTION("product") {
FourVector p3 = p1 + p2;
REQUIRE(p3.GetNorm() / 1_GeV == Approx(sqrt(100. * 100. - 100.)));
p3 -= p2;
REQUIRE(p3.GetNorm() / 1_GeV == Approx(100.));
REQUIRE(p1.GetNorm() / 1_GeV == Approx(100.));
REQUIRE(p2.GetNorm() / 1_GeV == Approx(10.));
}
SECTION("difference") {
FourVector p3 = p1 - p2;
REQUIRE(p3.GetNorm() / 1_GeV == Approx(sqrt(100. * 100. - 100.)));
p3 += p2;
REQUIRE(p3.GetNorm() / 1_GeV == Approx(100.));
REQUIRE(p1.GetNorm() / 1_GeV == Approx(100.));
REQUIRE(p2.GetNorm() / 1_GeV == Approx(10.));
}
SECTION("scale") {
double s = 10;
FourVector p3 = p1 * s;
REQUIRE(p3.GetNorm() / 1_GeV == Approx(sqrt(100. * 100. * s * s)));
p3 /= 10;
REQUIRE(p3.GetNorm() / 1_GeV == Approx(sqrt(100. * 100.)));
REQUIRE(p1.GetNorm() / 1_GeV == Approx(100.));
REQUIRE(p2.GetNorm() / 1_GeV == Approx(10.));
}
}
/**
The FourVector class can be used with reference template
arguments. In this configuration it does not hold any data
itself, but rather just refers to data located elsewhere. Thus,
it merely provides the physical/mathematical wrapper around the
data.
*/
SECTION("Use as wrapper") {
TimeType T = 10_m / corsika::units::constants::c;
Vector<length_d> P(rootCS, {10_m, 5_m, 5_m});
const TimeType T_c = 10_m / corsika::units::constants::c;
const Vector<length_d> P_c(rootCS, {10_m, 5_m, 5_m});
// FourVector<TimeType&, Vector<length_d>&> p0(T_c, P_c); // this does not compile,
// and it shoudn't!
FourVector<TimeType&, Vector<length_d>&> p1(T, P);
FourVector<const TimeType&, const Vector<length_d>&> p2(T, P);
FourVector<const TimeType&, const Vector<length_d>&> p3(T_c, P_c);
std::cout << type_id_with_cvr<decltype(p1)>().pretty_name() << std::endl;
std::cout << type_id_with_cvr<decltype(p2)>().pretty_name() << std::endl;
std::cout << type_id_with_cvr<decltype(p3)>().pretty_name() << std::endl;
p1 *= 10;
// p2 *= 10; // this does not compile, and it shoudn't !
// p3 *= 10; // this does not compile, and it shoudn't !!
const double check = 10 * 10 - 10 * 10 - 5 * 5 - 5 * 5; // for dummies...
REQUIRE(p1.GetNormSqr() / (1_m * 1_m) == Approx(10. * 10. * check));
REQUIRE(p2.GetNorm() / 1_m == Approx(10 * sqrt(abs(check))));
REQUIRE(p3.GetNorm() / 1_m == Approx(sqrt(abs(check))));
}
}
/*
* (c) Copyright 2018 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.
*/
#define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one
// cpp file
#include <catch2/catch.hpp>
#include <corsika/geometry/CoordinateSystem.h>
#include <corsika/geometry/Helix.h>
#include <corsika/geometry/Line.h>
#include <corsika/geometry/Point.h>
#include <corsika/geometry/RootCoordinateSystem.h>
#include <corsika/geometry/Sphere.h>
#include <corsika/geometry/Trajectory.h>
#include <corsika/units/PhysicalUnits.h>
#include <cmath>
using namespace corsika::geometry;
using namespace corsika::units::si;
double constexpr absMargin = 1.0e-8;
TEST_CASE("transformations between CoordinateSystems") {
CoordinateSystem& rootCS =
RootCoordinateSystem::GetInstance().GetRootCoordinateSystem();
REQUIRE(CoordinateSystem::GetTransformation(rootCS, rootCS)
.isApprox(EigenTransform::Identity()));
QuantityVector<length_d> const coordinates{0_m, 0_m, 0_m};
Point p1(rootCS, coordinates);
QuantityVector<magnetic_flux_density_d> components{1. * tesla, 0. * tesla, 0. * tesla};
Vector<magnetic_flux_density_d> v1(rootCS, components);
REQUIRE((p1.GetCoordinates() - coordinates).norm().magnitude() ==
Approx(0).margin(absMargin));
REQUIRE((p1.GetCoordinates(rootCS) - coordinates).norm().magnitude() ==
Approx(0).margin(absMargin));
/*
SECTION("unconnected CoordinateSystems") {
CoordinateSystem rootCS2 = CoordinateSystem::CreateRootCS();
REQUIRE_THROWS(CoordinateSystem::GetTransformation(rootCS, rootCS2));
}*/
SECTION("translations") {
QuantityVector<length_d> const translationVector{0_m, 4_m, 0_m};
CoordinateSystem translatedCS = rootCS.translate(translationVector);
REQUIRE(translatedCS.GetReference() == &rootCS);
REQUIRE((p1.GetCoordinates(translatedCS) + translationVector).norm().magnitude() ==
Approx(0).margin(absMargin));
// Vectors are not subject to translations
REQUIRE(
(v1.GetComponents(rootCS) - v1.GetComponents(translatedCS)).norm().magnitude() ==
Approx(0).margin(absMargin));
Point p2(translatedCS, {0_m, 0_m, 0_m});
REQUIRE(((p2 - p1).GetComponents() - translationVector).norm().magnitude() ==
Approx(0).margin(absMargin));
}
SECTION("multiple translations") {
QuantityVector<length_d> const tv1{0_m, 5_m, 0_m};
CoordinateSystem cs2 = rootCS.translate(tv1);
QuantityVector<length_d> const tv2{3_m, 0_m, 0_m};
CoordinateSystem cs3 = rootCS.translate(tv2);
QuantityVector<length_d> const tv3{0_m, 0_m, 2_m};
CoordinateSystem cs4 = cs3.translate(tv3);
REQUIRE(cs4.GetReference()->GetReference() == &rootCS);
REQUIRE(CoordinateSystem::GetTransformation(cs3, cs2).isApprox(
rootCS.translate({3_m, -5_m, 0_m}).GetTransform()));
REQUIRE(CoordinateSystem::GetTransformation(cs2, cs3).isApprox(
rootCS.translate({-3_m, +5_m, 0_m}).GetTransform()));
}
SECTION("rotations") {
QuantityVector<length_d> const axis{0_m, 0_m, 1_km};
double const angle = 90. / 180. * M_PI;
CoordinateSystem rotatedCS = rootCS.rotate(axis, angle);
REQUIRE(rotatedCS.GetReference() == &rootCS);
REQUIRE(v1.GetComponents(rotatedCS)[1].magnitude() ==
Approx((-1. * tesla).magnitude()));
// vector norm invariant under rotation
REQUIRE(v1.GetComponents(rotatedCS).norm().magnitude() ==
Approx(v1.GetComponents(rootCS).norm().magnitude()));
}
SECTION("multiple rotations") {
QuantityVector<length_d> const zAxis{0_m, 0_m, 1_km};
QuantityVector<length_d> const yAxis{0_m, 7_nm, 0_m};
QuantityVector<length_d> const xAxis{2_m, 0_nm, 0_m};
QuantityVector<magnetic_flux_density_d> components{1. * tesla, 2. * tesla,
3. * tesla};
Vector<magnetic_flux_density_d> v1(rootCS, components);
double const angle = 90. / 180. * M_PI;
CoordinateSystem rotated1 = rootCS.rotate(zAxis, angle);
CoordinateSystem rotated2 = rotated1.rotate(yAxis, angle);
CoordinateSystem rotated3 = rotated2.rotate(zAxis, -angle);
CoordinateSystem combined = rootCS.rotate(xAxis, -angle);
auto comp1 = v1.GetComponents(rotated3);
auto comp3 = v1.GetComponents(combined);
REQUIRE((comp1 - comp3).norm().magnitude() == Approx(0).margin(absMargin));
}
}
TEST_CASE("Sphere") {
CoordinateSystem& rootCS =
RootCoordinateSystem::GetInstance().GetRootCoordinateSystem();
Point center(rootCS, {0_m, 3_m, 4_m});
Sphere sphere(center, 5_m);
SECTION("GetCenter") {
CHECK((sphere.GetCenter().GetCoordinates(rootCS) -
QuantityVector<length_d>(0_m, 3_m, 4_m))
.norm()
.magnitude() == Approx(0).margin(absMargin));
CHECK(sphere.GetRadius() / 5_m == Approx(1));
}
SECTION("Contains") {
REQUIRE_FALSE(sphere.Contains(Point(rootCS, {100_m, 0_m, 0_m})));
REQUIRE(sphere.Contains(Point(rootCS, {2_m, 3_m, 4_m})));
}
}
TEST_CASE("Trajectories") {
CoordinateSystem& rootCS =
RootCoordinateSystem::GetInstance().GetRootCoordinateSystem();
Point r0(rootCS, {0_m, 0_m, 0_m});
SECTION("Line") {
Vector<SpeedType::dimension_type> v0(rootCS,
{3_m / second, 0_m / second, 0_m / second});
Line const line(r0, v0);
CHECK(
(line.GetPosition(2_s).GetCoordinates() - QuantityVector<length_d>(6_m, 0_m, 0_m))
.norm()
.magnitude() == Approx(0).margin(absMargin));
CHECK((line.PositionFromArclength(4_m).GetCoordinates() -
QuantityVector<length_d>(4_m, 0_m, 0_m))
.norm()
.magnitude() == Approx(0).margin(absMargin));
CHECK((line.GetPosition(7_s) - line.PositionFromArclength(line.ArcLength(0_s, 7_s)))
.norm()
.magnitude() == Approx(0).margin(absMargin));
auto const t = 1_s;
Trajectory<Line> base(line, t);
CHECK(line.GetPosition(t).GetCoordinates() == base.GetPosition(1.).GetCoordinates());
CHECK(base.ArcLength(1_s, 2_s) / 1_m == Approx(3));
CHECK((base.NormalizedDirection().GetComponents(rootCS) -
QuantityVector<dimensionless_d>{1, 0, 0})
.eVector.norm() == Approx(0).margin(absMargin));
}
SECTION("Helix") {
Vector<SpeedType::dimension_type> const vPar(
rootCS, {0_m / second, 0_m / second, 4_m / second});
Vector<SpeedType::dimension_type> const vPerp(
rootCS, {3_m / second, 0_m / second, 0_m / second});
auto const T = 1_s;
auto const omegaC = 2 * M_PI / T;
Helix const helix(r0, omegaC, vPar, vPerp);
CHECK((helix.GetPosition(1_s).GetCoordinates() -
QuantityVector<length_d>(0_m, 0_m, 4_m))
.norm()
.magnitude() == Approx(0).margin(absMargin));
CHECK((helix.GetPosition(0.25_s).GetCoordinates() -
QuantityVector<length_d>(-3_m / (2 * M_PI), -3_m / (2 * M_PI), 1_m))
.norm()
.magnitude() == Approx(0).margin(absMargin));
CHECK(
(helix.GetPosition(7_s) - helix.PositionFromArclength(helix.ArcLength(0_s, 7_s)))
.norm()
.magnitude() == Approx(0).margin(absMargin));
auto const t = 1234_s;
Trajectory<Helix> const base(helix, t);
CHECK(helix.GetPosition(t).GetCoordinates() == base.GetPosition(1.).GetCoordinates());
CHECK(base.ArcLength(0_s, 1_s) / 1_m == Approx(5));
}
}
/*
* (c) Copyright 2018 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.
*/
#ifndef _include_BufferedSink_h_
#define _include_BufferedSink_h_
namespace corsika::logging {
namespace sink {
/**
Output buffer template. NoBuffer does nothingk.
*/
/*
struct NoBuffer {
inline bool Test(const std::string&) const { return false; }
inline std::string GetString() const { return std::string(""); }
inline void Clear() {}
inline void Add(const std::string&) {}
};
*/
/**
Output buffer template. StdBuffer records fSize characters in
local memeory before passing it on to further output stages.
*/
struct StdBuffer {
StdBuffer(const int size)
: fSize(size) {}
inline bool Test(const std::string& s) {
return int(fBuffer.tellp()) + s.length() < fSize;
}
inline std::string GetString() const { return fBuffer.str(); }
inline void Clear() { fBuffer.str(""); }
inline void Add(const std::string& s) { fBuffer << s; }
private:
unsigned int fSize;
std::ostringstream fBuffer;
};
/**
Definition of Sink for log output.
*/
template <typename TStream, typename TBuffer = StdBuffer>
class BufferedSink {
public:
BufferedSink(TStream& out, TBuffer buffer = {})
: fOutput(out)
, fBuffer(std::move(buffer)) {}
void operator<<(const std::string& msg) {
if (!fBuffer.Test(msg)) {
fOutput << fBuffer.GetString();
fBuffer.Clear();
}
if (!fBuffer.Test(msg))
fOutput << msg;
else
fBuffer.Add(msg);
}
void Close() { fOutput << fBuffer.GetString(); }
private:
TStream& fOutput;
TBuffer fBuffer;
};
typedef BufferedSink<std::ostream, StdBuffer> BufferedSinkStream;
} // namespace sink
} // namespace corsika::logging
#endif
add_library (CORSIKAlogging INTERFACE)
# namespace of library -> location of header files
set (
CORSIKAlogging_NAMESPACE
corsika/logging
)
# header files of this library
set (
CORSIKAlogging_HEADERS
Logger.h
Sink.h
MessageOn.h
MessageOff.h
NoSink.h
Sink.h
BufferedSink.h
)
CORSIKA_COPY_HEADERS_TO_NAMESPACE (CORSIKAlogging ${CORSIKAlogging_NAMESPACE} ${CORSIKAlogging_HEADERS})
# include directive for upstream code
target_include_directories (
CORSIKAlogging
INTERFACE
$<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include>
$<INSTALL_INTERFACE:include/>
${Boost_INCLUDE_DIRS}
)
# install library
install (
FILES ${CORSIKAlogging_HEADERS}
DESTINATION include/${CORSIKAlogging_NAMESPACE}
)
# ----------------
# code unit testing
add_executable (
testLogging
testLogging.cc
)
target_link_libraries (
testLogging
CORSIKAlogging
CORSIKAthirdparty # for catch2
)
CORSIKA_ADD_TEST (testLogging)
/*
* (c) Copyright 2018 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.
*/
/**
@File Logger.h
Everything around logfile generation and text output.
*/
#ifndef _include_logger_h_
#define _include_logger_h_
#include <iosfwd>
#include <sstream>
#include <string>
#include <typeinfo>
#include <boost/format.hpp>
#include <corsika/logging/BufferedSink.h>
#include <corsika/logging/MessageOff.h>
#include <corsika/logging/MessageOn.h>
#include <corsika/logging/NoSink.h>
#include <corsika/logging/Sink.h>
namespace corsika::logging {
/**
@class Logger
Defines one stream to accept messages, and to wrote those into
TSink. The helper class MessageOn will convert input at
compile-time into message strings. The helper class MessageOff,
will just do nothing and will be optimized away at compile time.
*/
template <typename MSG = MessageOn, typename TSink = sink::NoSink>
class Logger : private MSG {
using MSG::Message;
public:
// Logger() : fName("") {}
Logger(const std::string color, const std::string name, TSink& sink)
: fSink(sink)
, fName(color + "[" + name + "]\033[39m ") {}
~Logger() { fSink.Close(); }
// Logger(const Logger&) = delete;
/**
Function to add string-concatenation of all inputs to output
sink.
*/
template <typename... Strings>
void Log(const Strings&... inputs) {
fSink << MSG::Message(inputs...);
}
const std::string& GetName() const { return fName; }
private:
TSink& fSink;
std::string fName;
};
} // namespace corsika::logging
/**
* @def LOG(...)
*
* This is the main interface to the logging facilities. If Logger
* object are defined (e.g. log1) use as
* @example LOG(log1, "var1=", variable1int, "var2=", variabl2double)
* for arbitrary long sequence
* of arguments. This may also include boost::format objects the
* output is concatenated, if log1 is switched off at compile time,
* the whole LOG command is optimized away by the compiler.
*/
#define LOG(__LOGGER, ...) \
__LOGGER.Log(__LOGGER.GetName(), __FILE__, ":", __LINE__, " (", __func__, ") -> ", \
__VA_ARGS__);
#endif
/*
* (c) Copyright 2018 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.
*/
#ifndef _include_MessageOff_h_
#define _include_MessageOff_h_
namespace corsika::logging {
/**
Helper class to ignore all arguments to MessagesOn::Message and
always return empty string "".
*/
class MessageOff {
protected:
template <typename First, typename... Strings>
std::string Message(const First&, const Strings&...) {
return "";
}
};
} // namespace corsika::logging
#endif
/*
* (c) Copyright 2018 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.
*/
#ifndef _include_MessageOn_h_
#define _include_MessageOn_h_
namespace corsika::logging {
/**
Helper class to convert all input arguments of MessageOn::Message
into string-concatenated version and return this as string.
*/
class MessageOn {
protected:
std::string Message() { return "\n"; }
template <typename First, typename... Strings>
std::string Message(const First& arg, const Strings&... rest) {
std::ostringstream ss;
ss << arg << Message(rest...);
return ss.str();
}
template <typename... Strings>
std::string Message(const int& arg, const Strings&... rest) {
return std::to_string(arg) + Message(rest...);
}
template <typename... Strings>
std::string Message(const double& arg, const Strings&... rest) {
return std::to_string(arg) + Message(rest...);
}
template <typename... Strings>
std::string Message(char const* arg, const Strings&... rest) {
return std::string(arg) + Message(rest...);
}
template <typename... Strings>
std::string Message(const std::string& arg, const Strings&... rest) {
return arg + Message(rest...);
}
// ----------------------
// boost format
template <typename... Strings>
std::string Message(const boost::format& fmt, const Strings&... rest) {
boost::format FMT(fmt);
return bformat(FMT, rest...);
}
template <typename Arg, typename... Strings>
std::string bformat(boost::format& fmt, const Arg& arg, const Strings&... rest) {
fmt % arg;
return bformat(fmt, rest...);
}
std::string bformat(boost::format& fmt) { return fmt.str() + "\n"; }
};
} // namespace corsika::logging
#endif
/*
* (c) Copyright 2018 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.
*/
#ifndef _include_NoSink_h_
#define _include_NoSink_h_
namespace corsika::logging {
namespace sink {
struct NoSink {
inline void operator<<(const std::string&) {}
inline void Close() {}
};
} // namespace sink
} // namespace corsika::logging
#endif
/*
* (c) Copyright 2018 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.
*/
#ifndef _include_Sink_h_
#define _include_Sink_h_
namespace corsika::logging {
/**
a sink for the logger must implement the two functions
operator<<(const std::string&)
and
Close()
See example: NoSink
*/
namespace sink {
/**
Definition of Sink for log output.
*/
template <typename TStream>
class Sink {
public:
Sink(TStream& out)
: fOutput(out) {}
void operator<<(const std::string& msg) { fOutput << msg; }
void Close() {}
private:
TStream& fOutput;
};
typedef Sink<std::ostream> SinkStream;
} // namespace sink
} // namespace corsika::logging
#endif
/*
* (c) Copyright 2018 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/logging/Logger.h>
#define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one
// cpp file
#include <catch2/catch.hpp>
TEST_CASE("Logging", "[Logging]") {
SECTION("sectionOne") {}
SECTION("sectionTwo") {}
SECTION("sectionThree") {}
}