8 #include <Eigen/Geometry> 9 #include <spdlog/spdlog.h> 10 #include <range/v3/view/cartesian_product.hpp> 11 #include <range/v3/view/zip.hpp> 61 NLOHMANN_JSON_SERIALIZE_ENUM(
VolumeMethod, {{VolumeMethod::INVALID,
nullptr},
62 {VolumeMethod::ISOTROPIC,
"isotropic"},
63 {VolumeMethod::ISOCHORIC,
"isochoric"},
64 {VolumeMethod::XY,
"xy"},
65 {VolumeMethod::Z,
"z"}})
67 enum class Coordinates
74 enum class Boundary : int
88 using BoundaryXYZ = Eigen::Matrix<Boundary, 3, 1>;
92 Coordinates coordinates;
93 BoundaryXYZ direction;
95 [[nodiscard]] Eigen::Matrix<bool, 3, 1> isPeriodic()
const;
98 BoundaryXYZ boundary = {Boundary::FIXED, Boundary::FIXED,
100 : coordinates(coordinates)
101 , direction(std::move(boundary)) {};
110 [[nodiscard]]
virtual double getVolume(
int = 3)
const = 0;
111 virtual void boundary(
Point&)
const = 0;
112 [[nodiscard]]
virtual bool collision(
const Point&)
const = 0;
114 [[nodiscard]]
virtual Point vdist(
const Point& a,
115 const Point& b)
const = 0;
116 [[nodiscard]]
virtual Point getLength()
const = 0;
118 virtual void to_json(
json& j)
const = 0;
119 virtual void from_json(
const json& j) = 0;
123 return [
this](
Point& i) { boundary(i); };
128 return [
this](
const Point& i,
const Point& j) {
return vdist(i, j); };
132 template <
typename T =
double> [[nodiscard]]
inline int anint(
T x)
const 134 return int(x > 0.0 ? x + 0.5 : x - 0.5);
150 [[nodiscard]]
virtual std::unique_ptr<GeometryImplementation> clone()
const = 0;
160 Point box, box_half, box_inv;
163 Point getLength()
const override;
164 double getVolume(
int dim = 3)
const final;
165 void setLength(
const Point& len);
166 Point setVolume(
double volume,
VolumeMethod method = VolumeMethod::ISOTROPIC)
override;
168 void boundary(
Point& a)
const override;
169 bool collision(
const Point& a)
const override;
170 void randompos(
Point& m,
Random& rand)
const override;
171 void from_json(
const json& j)
override;
172 void to_json(
json& j)
const override;
176 [[nodiscard]] std::unique_ptr<GeometryImplementation>
177 clone()
const override;
192 Slit(
double x,
double y,
double z);
193 explicit Slit(
double x = 0.0);
195 [[nodiscard]] std::unique_ptr<GeometryImplementation>
196 clone()
const override;
208 Point getLength()
const override;
209 double getVolume(
int dim = 3)
const override;
210 Point setVolume(
double volume,
VolumeMethod method = VolumeMethod::ISOTROPIC)
override;
213 inline static double sqdist(
const Point& a,
const Point& b) {
return (a - b).squaredNorm(); };
215 void boundary(
Point& a)
const override;
216 bool collision(
const Point& point)
const override;
217 void randompos(
Point& m,
Random& rand)
const override;
218 void from_json(
const json& j)
override;
219 void to_json(
json& j)
const override;
220 explicit Sphere(
double radius = 0.0);
221 double getRadius()
const;
223 std::unique_ptr<GeometryImplementation>
224 clone()
const override;
231 bool collision(
const Point& a)
const override;
232 void randompos(
Point& m,
Random& rand)
const override;
235 std::unique_ptr<GeometryImplementation>
236 clone()
const override;
246 double radius, height;
249 Point getLength()
const override;
250 double getVolume(
int dim = 3)
const override;
251 Point setVolume(
double volume,
VolumeMethod method = VolumeMethod::ISOTROPIC)
override;
253 void boundary(
Point& a)
const override;
254 bool collision(
const Point& a)
const override;
255 void randompos(
Point& m,
Random& rand)
const override;
256 void from_json(
const json& j)
override;
257 void to_json(
json& j)
const override;
258 explicit Cylinder(
double radius = 0.0,
double height = 0.0);
260 [[nodiscard]] std::unique_ptr<GeometryImplementation>
261 clone()
const override;
276 static const Eigen::Matrix3d rhombic2cartesian;
277 static const Eigen::Matrix3d cartesian2rhombic;
280 void set_box(
double side,
double height);
283 Point getLength()
const override;
284 double getVolume(
int dim = 3)
const override;
285 Point setVolume(
double volume,
VolumeMethod method = VolumeMethod::ISOTROPIC)
override;
287 void boundary(
Point& a)
const override;
288 bool collision(
const Point& a)
const override;
289 void randompos(
Point& m,
Random& rand)
const override;
290 void from_json(
const json& j)
override;
291 void to_json(
json& j)
const override;
294 [[nodiscard]] std::unique_ptr<GeometryImplementation>
295 clone()
const override;
297 [[nodiscard]]
double innerRadius()
const;
298 [[nodiscard]]
double outerRadius()
const;
299 [[nodiscard]]
double height()
const;
310 Point getLength()
const override;
311 double getVolume(
int dim = 3)
const override;
312 Point setVolume(
double volume,
VolumeMethod method = VolumeMethod::ISOTROPIC)
override;
314 void boundary(
Point& a)
const override;
315 bool collision(
const Point& a)
const override;
316 void randompos(
Point& pos,
Random& rand)
const override;
317 void from_json(
const json& j)
override;
318 void to_json(
json& j)
const override;
321 [[nodiscard]] std::unique_ptr<GeometryImplementation>
322 clone()
const override;
345 Point len_or_zero = {0, 0, 0};
348 std::unique_ptr<GeometryImplementation> geometry =
353 makeGeometry(
const Variant type =
355 void _setLength(
const Point& l);
359 const std::string& name = _name;
360 double getVolume(
int dim = 3)
const override;
362 Point getLength()
const override;
364 void setLength(
const Point&);
365 void boundary(
Point&)
const override;
367 double sqdist(
const Point&,
370 bool collision(
const Point&)
const override;
371 void from_json(
const json&)
override;
372 void to_json(
json& j)
const override;
376 static const std::map<std::string, Variant>
names;
377 typedef std::pair<std::string, Variant> VariantName;
379 static VariantName variantName(
const std::string& name);
381 static VariantName variantName(
const json& j);
392 [[nodiscard]] std::shared_ptr<GeometryImplementation> asSimpleGeometry()
const;
398 geometry->randompos(m, rand);
404 return geometry->collision(a);
409 const auto& boundary_conditions = geometry->boundary_conditions;
410 if (boundary_conditions.coordinates == Coordinates::ORTHOGONAL) {
411 if (boundary_conditions.direction.x() == Boundary::PERIODIC) {
412 if (std::fabs(a.x()) > len_half.x())
413 a.x() -= len.x() * anint(a.x() * len_inv.x());
415 if (boundary_conditions.direction.y() == Boundary::PERIODIC) {
416 if (std::fabs(a.y()) > len_half.y())
417 a.y() -= len.y() * anint(a.y() * len_inv.y());
419 if (boundary_conditions.direction.z() == Boundary::PERIODIC) {
420 if (std::fabs(a.z()) > len_half.z())
421 a.z() -= len.z() * anint(a.z() * len_inv.z());
425 geometry->boundary(a);
432 const auto& boundary_conditions = geometry->boundary_conditions;
433 if (boundary_conditions.coordinates == Coordinates::ORTHOGONAL) {
435 if (boundary_conditions.direction.x() == Boundary::PERIODIC) {
436 if (distance.x() > len_half.x())
437 distance.x() -= len.x();
438 else if (distance.x() < -len_half.x())
439 distance.x() += len.x();
441 if (boundary_conditions.direction.y() == Boundary::PERIODIC) {
442 if (distance.y() > len_half.y())
443 distance.y() -= len.y();
444 else if (distance.y() < -len_half.y())
445 distance.y() += len.y();
447 if (boundary_conditions.direction.z() == Boundary::PERIODIC) {
448 if (distance.z() > len_half.z())
449 distance.z() -= len.z();
450 else if (distance.z() < -len_half.z())
451 distance.z() += len.z();
455 distance = geometry->vdist(a, b);
462 if (geometry->boundary_conditions.coordinates == Coordinates::ORTHOGONAL) {
463 if constexpr (
true) {
464 Point d((a - b).cwiseAbs());
465 return (d - (d.array() > len_half.array())
468 .cwiseProduct(len_or_zero))
473 for (
int i = 0; i < 3; ++i) {
474 d[i] = std::fabs(d[i]);
477 static_cast<double>(d[i] > len_half[i]);
479 return d[0] * d[0] + d[1] * d[1] + d[2] * d[2];
483 return geometry->vdist(a, b).squaredNorm();
504 template <std::ranges::range Positions, std::ranges::range Weights>
506 const Positions& positions,
const Weights& weights,
509 double weight_sum = 0.0;
510 Point center(0.0, 0.0, 0.0);
511 for (
const auto& [position, weight] : ranges::views::zip(positions, weights)) {
512 Point shifted_position = position + shift;
513 boundary(shifted_position);
514 center += weight * shifted_position;
515 weight_sum += weight;
517 if (std::fabs(weight_sum) > pc::epsilon_dbl) {
518 center = center / weight_sum - shift;
523 faunus_logger->warn(
"warning: sum of weights is zero! setting center to (0,0,0)");
524 return Point::Zero();
540 template <RequireParticleIterator iterator>
542 std::function<
double(
const Particle&)> weight_function,
543 const Point& shift = Point::Zero())
545 namespace rv = std::views;
546 auto particles = std::ranges::subrange(begin, end);
548 auto weights = particles | rv::transform(weight_function);
562 template <RequireParticleIterator iterator>
565 const Point& shift = {0.0, 0.0, 0.0})
567 auto particle_mass = [](
const auto& particle) ->
double {
return particle.traits().mw; };
568 return weightedCenter(begin, end, apply_boundary, particle_mass, shift);
577 template <RequireParticleIterator iterator>
579 iterator begin, iterator end,
const Point& displacement,
582 std::for_each(begin, end, [&](
auto& particle) {
583 particle.pos += displacement;
584 apply_boundary(particle.pos);
594 template <RequireParticleIterator iterator>
598 translate(begin, end, -cm, apply_boundary);
613 template <RequireParticleIterator iterator>
615 iterator begin, iterator end,
const Eigen::Quaterniond& quaternion,
618 const auto rotation_matrix = quaternion.toRotationMatrix();
619 std::for_each(begin, end, [&](
auto& particle) {
620 particle.rotate(quaternion, rotation_matrix);
621 particle.pos += shift;
622 apply_boundary(particle.pos);
623 particle.pos = (quaternion * particle.pos) - shift;
624 apply_boundary(particle.pos);
633 template <
class Tspace,
class GroupIndex>
635 const std::vector<int>& dir = {0, 1, 2})
637 if (dir.empty() || dir.size() > 3) {
638 throw std::out_of_range(
"invalid directions");
640 namespace rv = std::views;
641 using std::views::join;
642 auto positions = indices | rv::transform([&](
auto i) {
return spc.groups.at(i); }) | join |
646 Point theta(0, 0, 0);
649 const auto q = 2.0 * pc::pi / spc.geometry.getLength()[k];
651 std::for_each(positions.begin(), positions.end(), [&](
auto& position) {
652 theta[k] = position[k] * q;
653 zeta[k] += std::sin(theta[k]);
654 xhi[k] += std::cos(theta[k]);
658 std::atan2(-zeta[k] / static_cast<double>(cnt), -xhi[k] / static_cast<double>(cnt)) +
660 com[k] = spc.geometry.getLength()[k] * theta[k] / (2.0 * pc::pi);
662 spc.geometry.boundary(com);
688 template <RequirePo
intIterator position_iterator, std::forward_iterator mass_iterator>
690 position_iterator begin, position_iterator end, mass_iterator mass,
const Point& mass_center,
693 Tensor S = Tensor::Zero();
694 double total_mass = 0.0;
695 std::for_each(begin, end, [&](
const Point& position) {
696 Point r = position - mass_center;
698 S += (*mass) * r * r.transpose();
699 total_mass += (*mass);
700 std::advance(mass, 1);
702 if (total_mass > pc::epsilon_dbl) {
703 return S / total_mass;
705 throw std::runtime_error(
"gyration tensor: total mass must be positive");
726 template <RequireParticleIterator iterator>
728 iterator begin, iterator end,
const Point& mass_center,
731 namespace rv = std::views;
732 auto particles = std::ranges::subrange(begin, end);
734 auto masses = particles | rv::transform(&
Particle::traits) | rv::transform(&AtomData::mw);
735 return gyration(positions.begin(), positions.end(), masses.begin(), mass_center, boundary);
746 double gyration_radius_squared = 0.0;
747 double asphericity = 0.0;
748 double acylindricity = 0.0;
749 double relative_shape_anisotropy =
770 template <RequireParticleIterator iterator>
772 iterator begin, iterator end,
const Point origin = Point::Zero(),
775 Tensor I = Tensor::Zero();
776 std::for_each(begin, end, [&](
const Particle& particle) {
779 I += particle.
traits().mw * (t.squaredNorm() * Tensor::Identity() - t * t.transpose());
790 template <std::forward_iterator InputIt1, std::forward_iterator InputIt2,
typename BinaryOperation>
792 BinaryOperation diff_squared_func)
794 assert(std::distance(begin, end) > 0);
796 for (InputIt1 i = begin; i != end; ++i) {
797 sq_sum += diff_squared_func(*i, *d_begin);
800 return std::sqrt(sq_sum / std::distance(begin, end));
833 cuboid_particles.reserve(2 * std::distance(particles.begin(), particles.end()));
834 std::copy(particles.begin(), particles.end(),
835 std::back_inserter(cuboid_particles));
837 std::transform(particles.begin(), particles.end(), std::back_inserter(cuboid_particles),
840 hexagon.
innerRadius() * (particle.pos.x() > 0.0 ? -1.0 : 1.0);
842 hexagon.
outerRadius() * (particle.pos.y() > 0.0 ? -1.5 : 1.5);
843 assert(cuboid.collision(particle.pos) ==
false);
846 assert(std::fabs(cuboid.getVolume() - 2.0 * hexagon.
getVolume()) <= pc::epsilon_dbl);
847 return {cuboid, cuboid_particles};
877 static std::vector<Point> fibonacciSphere(
size_t);
896 namespace rv = ranges::views;
897 auto product = [](
const auto& pair) -> Eigen::Quaterniond {
898 const auto& [q2, q_dihedral] = pair;
899 return q_dihedral * q2;
901 auto second_body_quaternions =
902 rv::cartesian_product(quaternions_2, dihedrals) | std::views::transform(product);
903 return rv::cartesian_product(quaternions_1, second_body_quaternions);
931 using QuaternionIter = std::vector<Eigen::Quaterniond>::const_iterator;
932 QuaternionIter q_euler1;
933 QuaternionIter q_euler2;
934 QuaternionIter q_dihedral;
940 std::optional<std::pair<Eigen::Quaterniond, Eigen::Quaterniond>>
nlohmann::json json
JSON object.
Definition: json_support.h:10
void translateToOrigin(iterator begin, iterator end, BoundaryFunction apply_boundary=[](auto &) {})
Move the mass center of the particle range to origin (0,0,0) and wrap to PBC.
Definition: geometry.h:595
Eigen::Vector3d Point
3D vector used for positions, velocities, forces etc.
Definition: coordinates.h:7
std::vector< Eigen::Quaterniond > quaternions_1
Quaternions to explore all Euler angles.
Definition: geometry.h:880
Definition: geometry.h:227
BoundaryFunction getBoundaryFunc() const
Lambda for applying boundary conditions on a point.
Definition: geometry.h:121
Tensor class Tensor class.
Definition: tensor.h:10
std::function< Point(const Point &, const Point &)> DistanceFunction
Function to calculate the (minimum) distance between two points.
Definition: core.h:32
Point pos
Particle position vector.
Definition: particle.h:227
Random number generator.
Definition: random.h:34
double getVolume(int dim=3) const override
Get volume.
Definition: geometry.cpp:345
A base class for various geometries implementations.
Definition: geometry.h:142
Geometry class for spheres, cylinders, cuboids, hexagonal prism, truncated octahedron, slits.
Definition: geometry.h:342
Point weightedCenter(const Positions &positions, const Weights &weights, Geometry::BoundaryFunction boundary=[](auto &) {}, const Point &shift=Point::Zero())
Calculates the (weighted) center for a set of positions.
Definition: geometry.h:505
Tensor gyration(position_iterator begin, position_iterator end, mass_iterator mass, const Point &mass_center, const BoundaryFunction boundary=[](auto &) {})
Calculates a gyration tensor of a range of particles.
Definition: geometry.h:689
The spherical geometry where no periodic boundary condition could be applied.
Definition: geometry.h:202
void rotate(iterator begin, iterator end, const Eigen::Quaterniond &quaternion, BoundaryFunction apply_boundary=[](auto &) {}, const Point &shift=Point::Zero())
Rotate range of particles using a Quaternion.
Definition: geometry.h:614
double T
floating point size
Definition: units.h:73
concept RequireParticles
Concept for a range of particles.
Definition: particle.h:257
DistanceFunction getDistanceFunc() const
Lambda for calculating the (minimum) distance vector between two positions.
Definition: geometry.h:126
Tensor inertia(iterator begin, iterator end, const Point origin=Point::Zero(), const BoundaryFunction boundary=[](auto &) {})
Calculates an inertia tensor of a molecular group.
Definition: geometry.h:771
void randompos(Point &, Random &) const override
Generate random position.
Definition: geometry.h:395
double innerRadius() const
Inner hexagonal radius.
Definition: geometry.cpp:465
The cuboid geometry with periodic boundary conditions possibly applied in all three directions...
Definition: geometry.h:157
double sqdist(const Point &, const Point &) const
(Minimum) squared distance between two points
Definition: geometry.h:460
The hexagonal prism geometry with periodic boundary conditions.
Definition: geometry.h:271
An interface for all geometries.Base class for all geometries.
Definition: geometry.h:107
A legacy class for the cuboid geometry with periodic boundary conditions only in xy directions...
Definition: geometry.h:186
std::vector< Particle > ParticleVector
Storage type for collections of particles.
Definition: particle.h:253
std::function< void(Point &)> BoundaryFunction
Function to apply PBC to a position.
Definition: core.h:30
void boundary(Point &) const override
Apply boundary conditions.
Definition: geometry.h:407
A structure containing a type of boundary condition in each direction.
Definition: geometry.h:85
std::vector< Eigen::Quaterniond > dihedrals
Quaternions to explore all dihedral angles.
Definition: geometry.h:882
double outerRadius() const
Outer radius / side-length.
Definition: geometry.cpp:470
Variant
Geometry variant used for Chameleon.
Definition: geometry.h:40
Particle class for storing positions, id, and other properties.
Definition: particle.h:220
Structure for exploring a discrete, uniform angular space between two rigid bodies.
Definition: geometry.h:875
static const std::map< std::string, Variant > names
Geometry names.
Definition: geometry.h:376
const AtomData & traits() const
get properties from AtomData
Definition: particle.cpp:183
double rootMeanSquareDeviation(InputIt1 begin, InputIt1 end, InputIt2 d_begin, BinaryOperation diff_squared_func)
Root-mean-square deviation of two data sets represented by iterators.
Definition: geometry.h:791
int anint(T x) const
Round to int.
Definition: geometry.h:132
Shape descriptors derived from gyration tensor.
Definition: geometry.h:744
Cell list class templates.
Definition: actions.cpp:11
auto distance(T1 first, T2 last)
Distance between two arbitrary contiguous iterators.
Definition: iteratorsupport.h:7
The cylindrical geometry with periodic boundary conditions in z-axis (the height of the cylinder)...
Definition: geometry.h:243
bool collision(const Point &) const override
Overlap with boundaries.
Definition: geometry.h:401
double height() const
Prism height.
Definition: geometry.cpp:475
The truncated octahedron geoemtry with periodic boundary conditions in all directions.
Definition: geometry.h:305
Structure for exploring a discrete, uniform angular space between two rigid bodies.
Definition: geometry.h:928
Point massCenter(iterator begin, iterator end, BoundaryFunction apply_boundary=[](Point &) {}, const Point &shift={0.0, 0.0, 0.0})
Calculates mass center of range of particles.
Definition: geometry.h:563
Point vdist(const Point &, const Point &) const override
Minimum distance vector b->a.
Definition: geometry.h:429
auto quaternionPairs() const
Iterator to loop over pairs of quaternions to rotate two rigid bodies against each other...
Definition: geometry.h:894
std::vector< Eigen::Quaterniond > quaternions_2
Quaternions to explore all Euler angles.
Definition: geometry.h:881
Point trigoCom(const Tspace &spc, const GroupIndex &indices, const std::vector< int > &dir={0, 1, 2})
Calculate mass center of cluster of particles in unbounded environment.
Definition: geometry.h:634
std::pair< Cuboid, ParticleVector > hexagonalPrismToCuboid(const HexagonalPrism &hexagon, const RequireParticles auto &particles)
Convert particles in hexagonal prism to space-filled cuboid.
Definition: geometry.h:828
void translate(iterator begin, iterator end, const Point &displacement, BoundaryFunction apply_boundary=[](auto &) {})
Definition: geometry.h:578
VolumeMethod
Various methods of volume scaling,.
Definition: geometry.h:52
ParticleVector mapParticlesOnSphere(const ParticleVector &source)
Scale particles to the surface of a sphere.
Definition: geometry.cpp:766