-
Notifications
You must be signed in to change notification settings - Fork 131
/
ParticleData.h
1656 lines (1396 loc) · 61.3 KB
/
ParticleData.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// Copyright (c) 2009-2024 The Regents of the University of Michigan.
// Part of HOOMD-blue, released under the BSD 3-Clause License.
/*! \file ParticleData.h
\brief Defines the ParticleData class and associated utilities
*/
#ifdef __HIPCC__
#error This header cannot be compiled by nvcc
#endif
#ifndef __PARTICLE_DATA_H__
#define __PARTICLE_DATA_H__
#include "GPUVector.h"
#include "GlobalArray.h"
#include "HOOMDMath.h"
#include "PythonLocalDataAccess.h"
#ifdef ENABLE_HIP
#include "GPUPartition.cuh"
#include "ParticleData.cuh"
#endif
#include "BoxDim.h"
#include "ExecutionConfiguration.h"
#include "HOOMDMPI.h"
#include <hoomd/extern/nano-signal-slot/nano_signal_slot.hpp>
#include <memory>
#ifndef __HIPCC__
#include <pybind11/numpy.h>
#include <pybind11/pybind11.h>
#endif
#ifdef ENABLE_MPI
#include "Index1D.h"
#endif
#include "DomainDecomposition.h"
#include <bitset>
#include <stack>
#include <stdlib.h>
#include <string>
#include <vector>
/*! \ingroup hoomd_lib
@{
*/
/*! \defgroup data_structs Data structures
\brief All classes that are related to the fundamental data
structures for storing particles.
\details See \ref page_dev_info for more information
*/
/*! @}
*/
//! Feature-define for HOOMD API
#define HOOMD_SUPPORTS_ADD_REMOVE_PARTICLES
namespace hoomd
{
//! List of optional fields that can be enabled in ParticleData
struct pdata_flag
{
//! The enum
enum Enum
{
pressure_tensor = 0, //!< Bit id in PDataFlags for the full virial
rotational_kinetic_energy, //!< Bit id in PDataFlags for the rotational kinetic energy
external_field_virial //!< Bit id in PDataFlags for the external virial contribution of
//!< volume change
};
};
//! flags determines which optional fields in in the particle data arrays are to be computed / are
//! valid
typedef std::bitset<32> PDataFlags;
//! Defines a simple structure to deal with complex numbers
/*! This structure is useful to deal with complex numbers for such situations
as Fourier transforms. Note that we do not need any to define any operations and the
default constructor is good enough
*/
struct CScalar
{
Scalar r; //!< Real part
Scalar i; //!< Imaginary part
};
//! Sentinel value in \a body to signify that this particle does not belong to a body
const unsigned int NO_BODY = 0xffffffff;
//! Unsigned value equivalent to a sign flip in a signed int. All larger values of the \a body flag
//! indicate a floppy body (forces between are ignored, but they are integrated independently).
const unsigned int MIN_FLOPPY = 0x80000000;
//! Sentinel value in \a r_tag to signify that this particle is not currently present on the local
//! processor
const unsigned int NOT_LOCAL = 0xffffffff;
} // end namespace hoomd
namespace hoomd
{
namespace detail
{
/// Get a default type name given a type id
std::string getDefaultTypeName(unsigned int id);
} // end namespace detail
//! Handy structure for passing around per-particle data
/*! A snapshot is used for two purposes:
* - Initializing the ParticleData
* - inside an Analyzer to iterate over the current ParticleData
*
* Initializing the ParticleData is accomplished by first filling the particle data arrays with
* default values (such as type, mass, diameter). Then a snapshot of this initial state is taken and
* passed to the ParticleDataInitializer, which may modify any of the fields of the snapshot. It
* then returns it to ParticleData, which in turn initializes its internal arrays from the snapshot
* using ParticleData::initializeFromSnapshot().
*
* To support the second scenario it is necessary that particles can be accessed in global tag
* order. Therefore, the data in a snapshot is stored in global tag order. \ingroup data_structs
*/
template<class Real> struct PYBIND11_EXPORT SnapshotParticleData
{
//! Empty snapshot
SnapshotParticleData() : size(0), is_accel_set(false) { }
//! constructor
/*! \param N number of particles to allocate memory for
*/
SnapshotParticleData(unsigned int N);
//! Resize the snapshot
/*! \param N number of particles in snapshot
*/
void resize(unsigned int N);
unsigned int getSize()
{
return size;
}
//! Insert n elements at position i
void insert(unsigned int i, unsigned int n);
//! Validate the snapshot
/*! Throws an exception when the snapshot contains invalid data.
*/
void validate() const;
#ifdef ENABLE_MPI
//! Broadcast the snapshot using MPI
/*! \param root the processor to send from
\param mpi_comm The MPI communicator
*/
void bcast(unsigned int root, MPI_Comm mpi_comm);
#endif
//! Replicate this snapshot
/*! \param nx Number of times to replicate the system along the x direction
* \param ny Number of times to replicate the system along the y direction
* \param nz Number of times to replicate the system along the z direction
* \param old_box Old box dimensions
* \param new_box Dimensions of replicated box
*/
void replicate(unsigned int nx,
unsigned int ny,
unsigned int nz,
std::shared_ptr<const BoxDim> old_box,
std::shared_ptr<const BoxDim> new_box);
//! Replicate this snapshot
/*! \param nx Number of times to replicate the system along the x direction
* \param ny Number of times to replicate the system along the y direction
* \param nz Number of times to replicate the system along the z direction
* \param old_box Old box dimensions
* \param new_box Dimensions of replicated box
*/
void replicate(unsigned int nx,
unsigned int ny,
unsigned int nz,
const BoxDim& old_box,
const BoxDim& new_box);
//! Get pos as a Python object
static pybind11::object getPosNP(pybind11::object self);
//! Get vel as a Python object
static pybind11::object getVelNP(pybind11::object self);
//! Get accel as a Python object
static pybind11::object getAccelNP(pybind11::object self);
//! Get type as a Python object
static pybind11::object getTypeNP(pybind11::object self);
//! Get mass as a Python object
static pybind11::object getMassNP(pybind11::object self);
//! Get charge as a Python object
static pybind11::object getChargeNP(pybind11::object self);
//! Get diameter as a Python object
static pybind11::object getDiameterNP(pybind11::object self);
//! Get image as a Python object
static pybind11::object getImageNP(pybind11::object self);
//! Get body as a Python object
static pybind11::object getBodyNP(pybind11::object self);
//! Get orientation as a Python object
static pybind11::object getOrientationNP(pybind11::object self);
//! Get moment of inertia as a numpy array
static pybind11::object getMomentInertiaNP(pybind11::object self);
//! Get angular momentum as a numpy array
static pybind11::object getAngmomNP(pybind11::object self);
//! Get the type names for python
pybind11::list getTypes();
//! Set the type names from python
void setTypes(pybind11::list types);
std::vector<vec3<Real>> pos; //!< positions
std::vector<vec3<Real>> vel; //!< velocities
std::vector<vec3<Real>> accel; //!< accelerations
std::vector<unsigned int> type; //!< types
std::vector<Real> mass; //!< masses
std::vector<Real> charge; //!< charges
std::vector<Real> diameter; //!< diameters
std::vector<int3> image; //!< images
std::vector<unsigned int> body; //!< body ids
std::vector<quat<Real>> orientation; //!< orientations
std::vector<quat<Real>> angmom; //!< angular momentum quaternion
std::vector<vec3<Real>> inertia; //!< principal moments of inertia
unsigned int size; //!< number of particles in this snapshot
std::vector<std::string> type_mapping; //!< Mapping between particle type ids and names
bool is_accel_set; //!< Flag indicating if accel is set
};
namespace detail
{
//! Structure to store packed particle data
/* pdata_element is used for compact storage of particle data, mainly for communication.
*/
struct pdata_element
{
Scalar4 pos; //!< Position
Scalar4 vel; //!< Velocity
Scalar3 accel; //!< Acceleration
Scalar charge; //!< Charge
Scalar diameter; //!< Diameter
int3 image; //!< Image
unsigned int body; //!< Body id
Scalar4 orientation; //!< Orientation
Scalar4 angmom; //!< Angular momentum
Scalar3 inertia; //!< Principal moments of inertia
unsigned int tag; //!< global tag
Scalar4 net_force; //!< net force
Scalar4 net_torque; //!< net torque
Scalar net_virial[6]; //!< net virial
};
} // end namespace detail
//! Manages all of the data arrays for the particles
/*! <h1> General </h1>
ParticleData stores and manages particle coordinates, velocities, accelerations, type,
and tag information. This data must be available both via the CPU and GPU memories.
All copying of data back and forth from the GPU is accomplished transparently by GlobalArray.
For performance reasons, data is stored as simple arrays. Once a handle to the particle data
GlobalArrays has been acquired, the coordinates of the particle with
<em>index</em> \c i can be accessed with <code>pos_array_handle.data[i].x</code>,
<code>pos_array_handle.data[i].y</code>, and <code>pos_array_handle.data[i].z</code>
where \c i runs from 0 to <code>getN()</code>.
Velocities and other properties can be accessed in a similar manner.
\note Position and type are combined into a single Scalar4 quantity. x,y,z specifies the
position and w specifies the type. Use __scalar_as_int() / __int_as_scalar() (or __int_as_float()
/ __float_as_int()) to extract / set this integer that is masquerading as a scalar.
\note Velocity and mass are combined into a single Scalar4 quantity. x,y,z specifies the
velocity and w specifies the mass.
\warning Local particles can and will be rearranged in the arrays throughout a simulation.
So, a particle that was once at index 5 may be at index 123 the next time the data
is acquired. Individual particles can be tracked through all these changes by their (global)
tag. The tag of a particle is stored in the \c m_tag array, and the ith element contains the tag
of the particle with index i. Conversely, the the index of a particle with tag \c tag can be read
from the element at position \c tag in the a \c m_rtag array.
In a parallel simulation, the global tag is unique among all processors.
In order to help other classes deal with particles changing indices, any class that
changes the order must call notifyParticleSort(). Any class interested in being notified
can subscribe to the signal by calling connectParticleSort().
Some fields in ParticleData are not computed and assigned by default because they require
additional processing time. PDataFlags is a bitset that lists which flags (enumerated in
pdata_flag) are enable/disabled. Computes should call getFlags() and compute the requested
quantities whenever the corresponding flag is set. Updaters and Analyzers can request flags be
computed via their getRequestedPDataFlags() methods. A particular updater or analyzer should
return a bitset PDataFlags with only the bits set for the flags that it needs. During a run,
System will query the updaters and analyzers that are to be executed on the current step. All of
the flag requests are combined with the binary or operation into a single set of flag requests.
System::run() then sets the flags by calling setPDataFlags so that the computes produce the
requested values during that step.
These fields are:
- pdata_flag::pressure_tensor - specify that the full virial tensor is valid
- pdata_flag::external_field_virial - specify that an external virial contribution is valid
If these flags are not set, these arrays can still be read but their values may be incorrect.
If any computation is unable to supply the appropriate values (i.e. rigid body virial can not be
computed until the second step of the simulation), then it should remove the flag to signify that
the values are not valid. Any analyzer/updater that expects the value to be set should check the
flags that are actually set.
\note Particles are not checked if their position is actually inside the local box. In fact,
when using spatial domain decomposition, particles may temporarily move outside the boundaries.
\ingroup data_structs
## Parallel simulations
In a parallel simulation, the ParticleData contains he local particles only, and getN() returns
the current number of \a local particles. The method getNGlobal() can be used to query the \a
global number of particles on all processors.
During the simulation particles may enter or leave the box, therefore the number of \a local
particles may change. To account for this, the size of the particle data arrays is dynamically
updated using amortized doubling of the array sizes. To add particles to the domain, the
addParticles() method is called, and the arrays are resized if necessary. Particles are retrieved
and removed from the local particle data arrays using removeParticles(). To flag particles for
removal, set the communication flag (m_comm_flags) for that particle to a non-zero value.
In addition, since many other classes maintain internal arrays holding data for every particle
(such as neighbor lists etc.), these arrays need to be resized, too, if the particle number
changes. Every time the particle data arrays are reallocated, a maximum particle number change
signal is triggered. Other classes can subscribe to this signal using
connectMaxParticleNumberChange(). They may use the current maximum size of the particle arrays,
which is returned by getMaxN(). This size changes only infrequently (by amortized array
resizing). Note that getMaxN() can return a higher number than the actual number of particles.
Particle data also stores temporary particles ('ghost atoms'). These are added after the local
particle data (i.e. with indices starting at getN()). It keeps track of those particles using the
addGhostParticles() and removeAllGhostParticles() methods. The caller is responsible for updating
the particle data arrays with the ghost particle information.
## Anisotropic particles
Anisotropic particles are handled by storing an orientation quaternion for every particle in the
simulation. Similarly, a net torque is computed and stored for each particle. The design decision
made is to not duplicate efforts already made to enable composite bodies of anisotropic
particles. So the particle orientation is a read only quantity when used by most of HOOMD. To
integrate this degree of freedom forward, the particle must be part of a composite body (there
can be single-particle bodies, of course) where integration methods like NVERigid will handle
updating the degrees of freedom of the composite body and then set the constrained position,
velocity, and orientation of the constituent particles.
Particles that are part of a floppy body will have the same value of the body flag, but that
value must be a negative number less than -1 (which is reserved as NO_BODY). Such particles do
not need to be treated specially by the integrator; they are integrated independently of one
another, but they do not interact. This lack of interaction is enforced through the neighbor
list, in which particles that belong to the same body are excluded by default.
To enable correct initialization of the composite body moment of inertia, each particle is also
assigned an individual moment of inertia which is summed up correctly to determine the composite
body's total moment of inertia.
Access the orientation quaternion of each particle with the GlobalArray gotten from
getOrientationArray(), the net torque with getTorqueArray(). Individual inertia tensor values can
be accessed with getMomentsOfInertia() and setMomentsOfInertia()
The current maximum diameter of all composite particles is stored in ParticleData and can be
requested by the NeighborList or other classes to compute rigid body interactions correctly. The
maximum value is updated by querying all classes that compute rigid body forces for updated
values whenever needed.
## Origin shifting
Parallel MC simulations randomly translate all particles by a fixed vector at periodic
intervals. This motion is not physical, it is merely the easiest way to shift the origin of the
cell list / domain decomposition boundaries. Analysis routines (i.e. MSD) and movies are
complicated by the random motion of all particles.
ParticleData can track this origin and subtract it from all particles. This subtraction is done
when taking a snapshot. Putting the subtraction there naturally puts the correction there for all
analysis routines and file I/O while leaving the shifted particles in place for computes,
updaters, and integrators. On the restoration from a snapshot, the origin needs to be cleared.
Two routines support this: translateOrigin() and resetOrigin(). The position of the origin is
tracked by ParticleData internally. translateOrigin() moves it by a given vector. resetOrigin()
zeroes it. TODO: This might not be sufficient for simulations where the box size changes. We'll
see in testing.
## Acceleration data
Most initialization routines do not provide acceleration data. In this case, the integrator
needs to compute appropriate acceleration data before time step 0 for integration to be correct.
However, the acceleration data is valid on taking/restoring a snapshot or executing additional
run() commands and there is no need for the integrator to provide acceleration. Doing so produces
incorrect results with some integrators (see issue #252). Future updates to gsd may enable
restarting with acceleration data from a file.
The solution is to store a flag in the particle data (and in the snapshot) indicating if the
acceleration data is valid. When it is not valid, the integrator will compute accelerations and
make it valid in prepRun(). When it is valid, the integrator will do nothing. On initialization
from a snapshot, ParticleData will inherit its valid flag.
*/
class PYBIND11_EXPORT ParticleData
{
public:
//! Construct with N particles in the given box
ParticleData(unsigned int N,
const std::shared_ptr<const BoxDim> global_box,
unsigned int n_types,
std::shared_ptr<ExecutionConfiguration> exec_conf,
std::shared_ptr<DomainDecomposition> decomposition
= std::shared_ptr<DomainDecomposition>());
//! Construct using a ParticleDataSnapshot
template<class Real>
ParticleData(const SnapshotParticleData<Real>& snapshot,
const std::shared_ptr<const BoxDim> global_box,
std::shared_ptr<ExecutionConfiguration> exec_conf,
std::shared_ptr<DomainDecomposition> decomposition
= std::shared_ptr<DomainDecomposition>());
//! Destructor
virtual ~ParticleData();
//! Get the simulation box
const BoxDim getBox() const;
//! Set the global simulation box
void setGlobalBox(const BoxDim& box);
//! Set the global simulation box
void setGlobalBox(const std::shared_ptr<const BoxDim> box);
//! Set the global simulation box Lengths
void setGlobalBoxL(const Scalar3& L)
{
auto box = BoxDim(L);
setGlobalBox(box);
}
//! Get the global simulation box
const BoxDim getGlobalBox() const;
//! Access the execution configuration
std::shared_ptr<const ExecutionConfiguration> getExecConf() const
{
return m_exec_conf;
}
//! Get the number of particles
/*! \return Number of particles in the box
*/
inline unsigned int getN() const
{
return m_nparticles;
}
//! Get the current maximum number of particles
/*\ return Maximum number of particles that can be stored in the particle array
* this number has to be larger than getN() + getNGhosts()
*/
inline unsigned int getMaxN() const
{
return m_max_nparticles;
}
//! Get current number of ghost particles
/*\ return Number of ghost particles
*/
inline unsigned int getNGhosts() const
{
return m_nghosts;
}
//! Get the global number of particles in the simulation
/*!\ return Global number of particles
*/
inline unsigned int getNGlobal() const
{
return m_nglobal;
}
//! Set global number of particles
/*! \param nglobal Global number of particles
*/
void setNGlobal(unsigned int nglobal);
//! Get the accel set flag
/*! \returns true if the acceleration has already been set
*/
inline bool isAccelSet()
{
return m_accel_set;
}
//! Set the accel set flag to true
inline void notifyAccelSet()
{
m_accel_set = true;
}
//! Get the number of particle types
/*! \return Number of particle types
\note Particle types are indexed from 0 to NTypes-1
*/
unsigned int getNTypes() const
{
return (unsigned int)(m_type_mapping.size());
}
//! Get the origin for the particle system
/*! \return origin of the system
*/
Scalar3 getOrigin()
{
return m_origin;
}
//! Get the origin image for the particle system
/*! \return image of the origin of the system
*/
int3 getOriginImage()
{
return m_o_image;
}
//! Get the maximum diameter of the particle set
/*! \return Maximum Diameter Value
*/
Scalar getMaxDiameter() const
{
Scalar maxdiam = 0;
ArrayHandle<Scalar> h_diameter(getDiameters(), access_location::host, access_mode::read);
for (unsigned int i = 0; i < m_nparticles; i++)
if (h_diameter.data[i] > maxdiam)
maxdiam = h_diameter.data[i];
#ifdef ENABLE_MPI
if (m_decomposition)
{
MPI_Allreduce(MPI_IN_PLACE,
&maxdiam,
1,
MPI_HOOMD_SCALAR,
MPI_MAX,
m_exec_conf->getMPICommunicator());
}
#endif
return maxdiam;
}
/*! Returns true if there are bodies in the system
*/
bool hasBodies() const
{
unsigned int has_bodies = 0;
ArrayHandle<unsigned int> h_body(getBodies(), access_location::host, access_mode::read);
for (unsigned int i = 0; i < getN(); ++i)
{
if (h_body.data[i] != NO_BODY)
{
has_bodies = 1;
break;
}
}
#ifdef ENABLE_MPI
if (m_decomposition)
{
MPI_Allreduce(MPI_IN_PLACE,
&has_bodies,
1,
MPI_UNSIGNED,
MPI_MAX,
m_exec_conf->getMPICommunicator());
}
#endif
return has_bodies;
}
//! Return positions and types
const GlobalArray<Scalar4>& getPositions() const
{
return m_pos;
}
//! Return velocities and masses
const GlobalArray<Scalar4>& getVelocities() const
{
return m_vel;
}
//! Return accelerations
const GlobalArray<Scalar3>& getAccelerations() const
{
return m_accel;
}
//! Return charges
const GlobalArray<Scalar>& getCharges() const
{
return m_charge;
}
//! Return diameters
const GlobalArray<Scalar>& getDiameters() const
{
return m_diameter;
}
//! Return images
const GlobalArray<int3>& getImages() const
{
return m_image;
}
//! Return tags
const GlobalArray<unsigned int>& getTags() const
{
return m_tag;
}
//! Return reverse-lookup tags
const GlobalVector<unsigned int>& getRTags() const
{
return m_rtag;
}
//! Return body ids
const GlobalArray<unsigned int>& getBodies() const
{
return m_body;
}
/*!
* Access methods to stand-by arrays for fast swapping in of reordered particle data
*
* \warning An array that is swapped in has to be completely initialized.
* In parallel simulations, the ghost data needs to be initialized as well,
* or all ghosts need to be removed and re-initialized before and after reordering.
*
* USAGE EXAMPLE:
* \code
* m_comm->migrateParticles(); // migrate particles and remove all ghosts
* {
* ArrayHandle<Scalar4> h_pos_alt(m_pdata->getAltPositions(), access_location::host,
* access_mode::overwrite) ArrayHandle<Scalar4> h_pos(m_pdata->getPositions(),
* access_location::host, access_mode::read); for (int i=0; i < getN(); ++i) h_pos_alt.data[i] =
* h_pos.data[permutation[i]]; // apply some permutation
* }
* m_pdata->swapPositions(); // swap in reordered data at no extra cost
* notifyParticleSort(); // ensures that ghosts will be restored at next communication step
* \endcode
*/
//! Return positions and types (alternate array)
const GlobalArray<Scalar4>& getAltPositions() const
{
return m_pos_alt;
}
//! Swap in positions
inline void swapPositions()
{
m_pos.swap(m_pos_alt);
}
//! Return velocities and masses (alternate array)
const GlobalArray<Scalar4>& getAltVelocities() const
{
return m_vel_alt;
}
//! Swap in velocities
inline void swapVelocities()
{
m_vel.swap(m_vel_alt);
}
//! Return accelerations (alternate array)
const GlobalArray<Scalar3>& getAltAccelerations() const
{
return m_accel_alt;
}
//! Swap in accelerations
inline void swapAccelerations()
{
m_accel.swap(m_accel_alt);
}
//! Return charges (alternate array)
const GlobalArray<Scalar>& getAltCharges() const
{
return m_charge_alt;
}
//! Swap in accelerations
inline void swapCharges()
{
m_charge.swap(m_charge_alt);
}
//! Return diameters (alternate array)
const GlobalArray<Scalar>& getAltDiameters() const
{
return m_diameter_alt;
}
//! Swap in diameters
inline void swapDiameters()
{
m_diameter.swap(m_diameter_alt);
}
//! Return images (alternate array)
const GlobalArray<int3>& getAltImages() const
{
return m_image_alt;
}
//! Swap in images
inline void swapImages()
{
m_image.swap(m_image_alt);
}
//! Return tags (alternate array)
const GlobalArray<unsigned int>& getAltTags() const
{
return m_tag_alt;
}
//! Swap in tags
inline void swapTags()
{
m_tag.swap(m_tag_alt);
}
//! Return body ids (alternate array)
const GlobalArray<unsigned int>& getAltBodies() const
{
return m_body_alt;
}
//! Swap in bodies
inline void swapBodies()
{
m_body.swap(m_body_alt);
}
//! Get the net force array (alternate array)
const GlobalArray<Scalar4>& getAltNetForce() const
{
return m_net_force_alt;
}
//! Swap in net force
inline void swapNetForce()
{
m_net_force.swap(m_net_force_alt);
}
//! Get the net virial array (alternate array)
const GlobalArray<Scalar>& getAltNetVirial() const
{
return m_net_virial_alt;
}
//! Swap in net virial
inline void swapNetVirial()
{
m_net_virial.swap(m_net_virial_alt);
}
//! Get the net torque array (alternate array)
const GlobalArray<Scalar4>& getAltNetTorqueArray() const
{
return m_net_torque_alt;
}
//! Swap in net torque
inline void swapNetTorque()
{
m_net_torque.swap(m_net_torque_alt);
}
//! Get the orientations (alternate array)
const GlobalArray<Scalar4>& getAltOrientationArray() const
{
return m_orientation_alt;
}
//! Swap in orientations
inline void swapOrientations()
{
m_orientation.swap(m_orientation_alt);
}
//! Get the angular momenta (alternate array)
const GlobalArray<Scalar4>& getAltAngularMomentumArray() const
{
return m_angmom_alt;
}
//! Get the moments of inertia array (alternate array)
const GlobalArray<Scalar3>& getAltMomentsOfInertiaArray() const
{
return m_inertia_alt;
}
//! Swap in angular momenta
inline void swapAngularMomenta()
{
m_angmom.swap(m_angmom_alt);
}
//! Swap in moments of inertia
inline void swapMomentsOfInertia()
{
m_inertia.swap(m_inertia_alt);
}
//! Connects a function to be called every time the particles are rearranged in memory
Nano::Signal<void()>& getParticleSortSignal()
{
return m_sort_signal;
}
//! Notify listeners that the particles have been rearranged in memory
void notifyParticleSort();
//! Connects a function to be called every time the box size is changed
Nano::Signal<void()>& getBoxChangeSignal()
{
return m_boxchange_signal;
}
//! Connects a function to be called every time the global number of particles changes
Nano::Signal<void()>& getGlobalParticleNumberChangeSignal()
{
return m_global_particle_num_signal;
}
//! Connects a function to be called every time the local maximum particle number changes
Nano::Signal<void()>& getMaxParticleNumberChangeSignal()
{
return m_max_particle_num_signal;
}
//! Connects a function to be called every time the ghost particles become invalid
Nano::Signal<void()>& getGhostParticlesRemovedSignal()
{
return m_ghost_particles_removed_signal;
}
#ifdef ENABLE_MPI
//! Connects a function to be called every time a single particle migration is requested
Nano::Signal<void(unsigned int, unsigned int, unsigned int)>& getSingleParticleMoveSignal()
{
return m_ptl_move_signal;
}
#endif
//! Notify listeners that ghost particles have been removed
void notifyGhostParticlesRemoved();
//! Gets the particle type index given a name
unsigned int getTypeByName(const std::string& name) const;
//! Gets the name of a given particle type index
std::string getNameByType(unsigned int type) const;
/// Get the complete type mapping
const std::vector<std::string>& getTypeMapping() const
{
return m_type_mapping;
}
//! Get the types for python
pybind11::list getTypesPy()
{
pybind11::list types;
for (unsigned int i = 0; i < getNTypes(); i++)
types.append(pybind11::str(m_type_mapping[i]));
return types;
}
//! Rename a type
void setTypeName(unsigned int type, const std::string& name);
//! Get the net force array
const GlobalArray<Scalar4>& getNetForce() const
{
return m_net_force;
}
//! Get the net virial array
const GlobalArray<Scalar>& getNetVirial() const
{
return m_net_virial;
}
//! Get the net torque array
const GlobalArray<Scalar4>& getNetTorqueArray() const
{
return m_net_torque;
}
//! Get the orientation array
const GlobalArray<Scalar4>& getOrientationArray() const
{
return m_orientation;
}
//! Get the angular momentum array
const GlobalArray<Scalar4>& getAngularMomentumArray() const
{
return m_angmom;
}
//! Get the angular momentum array
const GlobalArray<Scalar3>& getMomentsOfInertiaArray() const
{
return m_inertia;
}
//! Get the communication flags array
const GlobalArray<unsigned int>& getCommFlags() const
{
return m_comm_flags;
}
#ifdef ENABLE_MPI
//! Find the processor that owns a particle
unsigned int getOwnerRank(unsigned int tag) const;
#endif
//! Get the current position of a particle
Scalar3 getPosition(unsigned int tag) const;
//! Get the current velocity of a particle
Scalar3 getVelocity(unsigned int tag) const;
//! Get the current acceleration of a particle
Scalar3 getAcceleration(unsigned int tag) const;
//! Get the current image flags of a particle
int3 getImage(unsigned int tag) const;
//! Get the current mass of a particle
Scalar getMass(unsigned int tag) const;
//! Get the current diameter of a particle
Scalar getDiameter(unsigned int tag) const;
//! Get the current charge of a particle
Scalar getCharge(unsigned int tag) const;
//! Get the body id of a particle
unsigned int getBody(unsigned int tag) const;
//! Get the current type of a particle
unsigned int getType(unsigned int tag) const;
//! Get the current index of a particle with a given global tag
inline unsigned int getRTag(unsigned int tag) const
{
assert(tag < m_rtag.size());
ArrayHandle<unsigned int> h_rtag(m_rtag, access_location::host, access_mode::read);
unsigned int idx = h_rtag.data[tag];
#ifdef ENABLE_MPI
assert(m_decomposition || idx < getN());
#endif
assert(idx < getN() + getNGhosts() || idx == NOT_LOCAL);
return idx;
}
//! Return true if particle is local (= owned by this processor)
bool isParticleLocal(unsigned int tag) const
{
assert(tag < m_rtag.size());
ArrayHandle<unsigned int> h_rtag(m_rtag, access_location::host, access_mode::read);
return h_rtag.data[tag] < getN();
}