Grid 0.7.0
Lattice_rng.h
Go to the documentation of this file.
1/*************************************************************************************
2
3 Grid physics library, www.github.com/paboyle/Grid
4
5 Source file: ./lib/lattice/Lattice_rng.h
6
7 Copyright (C) 2015
8
9 Author: Peter Boyle <paboyle@ph.ed.ac.uk>
10 Author: Guido Cossu <guido.cossu@ed.ac.uk>
11
12 This program is free software; you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation; either version 2 of the License, or
15 (at your option) any later version.
16
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
21
22 You should have received a copy of the GNU General Public License along
23 with this program; if not, write to the Free Software Foundation, Inc.,
24 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25
26 See the full license in the file "LICENSE" in the top level distribution directory
27*************************************************************************************/
28/* END LEGAL */
29#ifndef GRID_LATTICE_RNG_H
30#define GRID_LATTICE_RNG_H
31
32#include <random>
33
34#ifdef RNG_SITMO
35#include <Grid/sitmo_rng/sitmo_prng_engine.hpp>
36#endif
37
38#if defined(RNG_SITMO)
39#define RNG_FAST_DISCARD
40#else
41#undef RNG_FAST_DISCARD
42#endif
43
45
47// Allow the RNG state to be less dense than the fine grid
49inline int RNGfillable(GridBase *coarse,GridBase *fine)
50{
51
52 int rngdims = coarse->_ndimension;
53
54 // trivially extended in higher dims, with locality guaranteeing RNG state is local to node
55 int lowerdims = fine->_ndimension - coarse->_ndimension;
56 assert(lowerdims >= 0);
57 for(int d=0;d<lowerdims;d++){
58 assert(fine->_simd_layout[d]==1);
59 assert(fine->_processors[d]==1);
60 }
61
62 int multiplicity=1;
63 for(int d=0;d<lowerdims;d++){
64 multiplicity=multiplicity*fine->_rdimensions[d];
65 }
66 // local and global volumes subdivide cleanly after SIMDization
67 for(int d=0;d<rngdims;d++){
68 int fd= d+lowerdims;
69 assert(coarse->_processors[d] == fine->_processors[fd]);
70 assert(coarse->_simd_layout[d] == fine->_simd_layout[fd]);
71 assert(((fine->_rdimensions[fd] / coarse->_rdimensions[d])* coarse->_rdimensions[d])==fine->_rdimensions[fd]);
72
73 multiplicity = multiplicity *fine->_rdimensions[fd] / coarse->_rdimensions[d];
74 }
75 return multiplicity;
76}
77
78
79// merge of April 11 2017
80// this function is necessary for the LS vectorised field
81inline int RNGfillable_general(GridBase *coarse,GridBase *fine)
82{
83 int rngdims = coarse->_ndimension;
84
85 // trivially extended in higher dims, with locality guaranteeing RNG state is local to node
86 int lowerdims = fine->_ndimension - coarse->_ndimension; assert(lowerdims >= 0);
87 // assumes that the higher dimensions are not using more processors
88 // all further divisions are local
89 for(int d=0;d<lowerdims;d++) assert(fine->_processors[d]==1);
90 for(int d=0;d<rngdims;d++) assert(coarse->_processors[d] == fine->_processors[d+lowerdims]);
91
92 // then divide the number of local sites
93 // check that the total number of sims agree, meanse the iSites are the same
94 assert(fine->Nsimd() == coarse->Nsimd());
95
96 // check that the two grids divide cleanly
97 assert( (fine->lSites() / coarse->lSites() ) * coarse->lSites() == fine->lSites() );
98
99 return fine->lSites() / coarse->lSites();
100}
101
102// real scalars are one component
103template<class scalar,class distribution,class generator>
104void fillScalar(scalar &s,distribution &dist,generator & gen)
105{
106 s=dist(gen);
107}
108template<class distribution,class generator>
109void fillScalar(ComplexF &s,distribution &dist, generator &gen)
110{
111 // s=ComplexF(dist(gen),dist(gen));
112 s.real(dist(gen));
113 s.imag(dist(gen));
114}
115template<class distribution,class generator>
116void fillScalar(ComplexD &s,distribution &dist,generator &gen)
117{
118 // s=ComplexD(dist(gen),dist(gen));
119 s.real(dist(gen));
120 s.imag(dist(gen));
121}
122
124public:
125 // One generator per site.
126 // Uniform and Gaussian distributions from these generators.
127#ifdef RNG_RANLUX
128 typedef std::ranlux48 RngEngine;
129 typedef uint64_t RngStateType;
130 static const int RngStateCount = 15;
131#endif
132#ifdef RNG_MT19937
133 typedef std::mt19937 RngEngine;
134 typedef uint32_t RngStateType;
135 static const int RngStateCount = std::mt19937::state_size;
136#endif
137#ifdef RNG_SITMO
138 typedef sitmo::prng_engine RngEngine;
139 typedef uint64_t RngStateType;
140 static const int RngStateCount = 13;
141#endif
142
143 std::vector<RngEngine> _generators;
144 std::vector<std::uniform_real_distribution<RealD> > _uniform;
145 std::vector<std::normal_distribution<RealD> > _gaussian;
146 std::vector<std::discrete_distribution<int32_t> > _bernoulli;
147 std::vector<std::uniform_int_distribution<uint32_t> > _uid;
148
150 // support for parallel init
152#ifdef RNG_FAST_DISCARD
153 static void Skip(RngEngine &eng,uint64_t site)
154 {
155#if 0
157 // Skip by 2^40 elements between successive lattice sites
158 // This goes by 10^12.
159 // Consider quenched updating; likely never exceeding rate of 1000 sweeps
160 // per second on any machine. This gives us of order 10^9 seconds, or 100 years
161 // skip ahead.
162 // For HMC unlikely to go at faster than a solve per second, and
163 // tens of seconds per trajectory so this is clean in all reasonable cases,
164 // and margin of safety is orders of magnitude.
165 // We could hack Sitmo to skip in the higher order words of state if necessary
166 //
167 // Replace with 2^30 ; avoid problem on large volumes
168 //
170 // uint64_t skip = site+1; // Old init Skipped then drew. Checked compat with faster init
171 const int shift = 30;
172
174 // Weird compiler bug in Intel 2018.1 under O3 was generating 32bit and not 64 bit left shift.
176 volatile uint64_t skip = site;
177
178 skip = skip<<shift;
179
180 assert((skip >> shift)==site); // check for overflow
181
182 eng.discard(skip);
183#else
184 eng.discardhi(site);
185#endif
186 // std::cout << " Engine " <<site << " state " <<eng<<std::endl;
187 }
188#endif
189 static RngEngine Reseed(RngEngine &eng)
190 {
191 std::vector<uint32_t> newseed;
192 std::uniform_int_distribution<uint32_t> uid;
193 return Reseed(eng,newseed,uid);
194 }
195 static RngEngine Reseed(RngEngine &eng,std::vector<uint32_t> & newseed,
196 std::uniform_int_distribution<uint32_t> &uid)
197 {
198 const int reseeds=4;
199
200 newseed.resize(reseeds);
201 for(int i=0;i<reseeds;i++){
202 newseed[i] = uid(eng);
203 }
204 std::seed_seq sseq(newseed.begin(),newseed.end());
205 return RngEngine(sseq);
206 }
207
208 void GetState(std::vector<RngStateType> & saved,RngEngine &eng) {
209 saved.resize(RngStateCount);
210 std::stringstream ss;
211 ss<<eng;
212 ss.seekg(0,ss.beg);
213 for(int i=0;i<RngStateCount;i++){
214 ss>>saved[i];
215 }
216 }
217 void GetState(std::vector<RngStateType> & saved,int gen) {
218 GetState(saved,_generators[gen]);
219 }
220 void SetState(std::vector<RngStateType> & saved,RngEngine &eng){
221 assert(saved.size()==RngStateCount);
222 std::stringstream ss;
223 for(int i=0;i<RngStateCount;i++){
224 ss<< saved[i]<<" ";
225 }
226 ss.seekg(0,ss.beg);
227 ss>>eng;
228 }
229 void SetState(std::vector<RngStateType> & saved,int gen){
230 SetState(saved,_generators[gen]);
231 }
232 void SetEngine(RngEngine &Eng, int gen){
233 _generators[gen]=Eng;
234 }
235 void GetEngine(RngEngine &Eng, int gen){
236 Eng=_generators[gen];
237 }
238 template<class source> void Seed(source &src, int gen)
239 {
240 _generators[gen] = RngEngine(src);
241 }
242};
243
245public:
246
248 _generators.resize(1);
249 _uniform.resize(1,std::uniform_real_distribution<RealD>{0,1});
250 _gaussian.resize(1,std::normal_distribution<RealD>(0.0,1.0) );
251 _bernoulli.resize(1,std::discrete_distribution<int32_t>{1,1});
252 _uid.resize(1,std::uniform_int_distribution<uint32_t>() );
253 }
254
255 template <class sobj,class distribution> inline void fill(sobj &l,std::vector<distribution> &dist){
256
257 typedef typename sobj::scalar_type scalar_type;
258
259 int words = sizeof(sobj)/sizeof(scalar_type);
260
261 scalar_type *buf = (scalar_type *) & l;
262
263 dist[0].reset();
264 for(int idx=0;idx<words;idx++){
265 fillScalar(buf[idx],dist[0],_generators[0]);
266 }
267
268 CartesianCommunicator::BroadcastWorld(0,(void *)&l,sizeof(l));
269
270 }
271
272 template <class distribution> inline void fill(ComplexF &l,std::vector<distribution> &dist){
273 dist[0].reset();
274 fillScalar(l,dist[0],_generators[0]);
275 CartesianCommunicator::BroadcastWorld(0,(void *)&l,sizeof(l));
276 }
277 template <class distribution> inline void fill(ComplexD &l,std::vector<distribution> &dist){
278 dist[0].reset();
279 fillScalar(l,dist[0],_generators[0]);
280 CartesianCommunicator::BroadcastWorld(0,(void *)&l,sizeof(l));
281 }
282 template <class distribution> inline void fill(RealF &l,std::vector<distribution> &dist){
283 dist[0].reset();
284 fillScalar(l,dist[0],_generators[0]);
285 CartesianCommunicator::BroadcastWorld(0,(void *)&l,sizeof(l));
286 }
287 template <class distribution> inline void fill(RealD &l,std::vector<distribution> &dist){
288 dist[0].reset();
289 fillScalar(l,dist[0],_generators[0]);
290 CartesianCommunicator::BroadcastWorld(0,(void *)&l,sizeof(l));
291 }
292 // vector fill
293 template <class distribution> inline void fill(vComplexF &l,std::vector<distribution> &dist){
294 RealF *pointer=(RealF *)&l;
295 dist[0].reset();
296 for(int i=0;i<2*vComplexF::Nsimd();i++){
297 fillScalar(pointer[i],dist[0],_generators[0]);
298 }
299 CartesianCommunicator::BroadcastWorld(0,(void *)&l,sizeof(l));
300 }
301 template <class distribution> inline void fill(vComplexD &l,std::vector<distribution> &dist){
302 RealD *pointer=(RealD *)&l;
303 dist[0].reset();
304 for(int i=0;i<2*vComplexD::Nsimd();i++){
305 fillScalar(pointer[i],dist[0],_generators[0]);
306 }
307 CartesianCommunicator::BroadcastWorld(0,(void *)&l,sizeof(l));
308 }
309 template <class distribution> inline void fill(vRealF &l,std::vector<distribution> &dist){
310 RealF *pointer=(RealF *)&l;
311 dist[0].reset();
312 for(int i=0;i<vRealF::Nsimd();i++){
313 fillScalar(pointer[i],dist[0],_generators[0]);
314 }
315 CartesianCommunicator::BroadcastWorld(0,(void *)&l,sizeof(l));
316 }
317 template <class distribution> inline void fill(vRealD &l,std::vector<distribution> &dist){
318 RealD *pointer=(RealD *)&l;
319 dist[0].reset();
320 for(int i=0;i<vRealD::Nsimd();i++){
321 fillScalar(pointer[i],dist[0],_generators[0]);
322 }
323 CartesianCommunicator::BroadcastWorld(0,(void *)&l,sizeof(l));
324 }
325
326 void SeedFixedIntegers(const std::vector<int> &seeds){
327 CartesianCommunicator::BroadcastWorld(0,(void *)&seeds[0],sizeof(int)*seeds.size());
328 std::seed_seq src(seeds.begin(),seeds.end());
329 Seed(src,0);
330 }
331
332 void SeedUniqueString(const std::string &s){
333 std::vector<int> seeds;
334 std::stringstream sha;
336 for(int i=0;i<seeds.size();i++) {
337 sha << std::hex << seeds[i];
338 }
339 std::cout << GridLogMessage << "Intialising serial RNG with unique string '"
340 << s << "'" << std::endl;
341 std::cout << GridLogMessage << "Seed SHA256: " << sha.str() << std::endl;
342 SeedFixedIntegers(seeds);
343 }
344};
345
347private:
350 unsigned int _vol;
351
352public:
353 GridBase *Grid(void) const { return _grid; }
354 int generator_idx(int os,int is) {
355 return is*_grid->oSites()+os;
356 }
357
359 _grid = grid;
360 _vol =_grid->iSites()*_grid->oSites();
361
362 _generators.resize(_vol);
363 _uniform.resize(_vol,std::uniform_real_distribution<RealD>{0,1});
364 _gaussian.resize(_vol,std::normal_distribution<RealD>(0.0,1.0) );
365 _bernoulli.resize(_vol,std::discrete_distribution<int32_t>{1,1});
366 _uid.resize(_vol,std::uniform_int_distribution<uint32_t>() );
367 }
368 template <class vobj,class distribution> inline void fill(Lattice<vobj> &l,std::vector<distribution> &dist)
369 {
370 if ( l.Grid()->_isCheckerBoarded ) {
371 Lattice<vobj> tmp(_grid);
372 fill(tmp,dist);
374 return;
375 }
376 typedef typename vobj::scalar_object scalar_object;
377 typedef typename vobj::scalar_type scalar_type;
378 typedef typename vobj::vector_type vector_type;
379
380 double inner_time_counter = usecond();
381
382 int multiplicity = RNGfillable_general(_grid, l.Grid()); // l has finer or same grid
383 int Nsimd = _grid->Nsimd(); // guaranteed to be the same for l.Grid() too
384 int osites = _grid->oSites(); // guaranteed to be <= l.Grid()->oSites() by a factor multiplicity
385 int words = sizeof(scalar_object) / sizeof(scalar_type);
386
387 autoView(l_v, l, CpuWrite);
388 thread_for( ss, osites, {
390 for (int m = 0; m < multiplicity; m++) { // Draw from same generator multiplicity times
391
392 int sm = multiplicity * ss + m; // Maps the generator site to the fine site
393
394 for (int si = 0; si < Nsimd; si++) {
395
396 int gdx = generator_idx(ss, si); // index of generator state
397 scalar_type *pointer = (scalar_type *)&buf[si];
398 dist[gdx].reset();
399 for (int idx = 0; idx < words; idx++)
400 fillScalar(pointer[idx], dist[gdx], _generators[gdx]);
401 }
402 // merge into SIMD lanes, FIXME suboptimal implementation
403 merge(l_v[sm], buf);
404 }
405 });
406 // });
407
408 _time_counter += usecond()- inner_time_counter;
409 }
410
411 void SeedUniqueString(const std::string &s){
412 std::vector<int> seeds;
414 std::cout << GridLogMessage << "Intialising parallel RNG with unique string '"
415 << s << "'" << std::endl;
416 std::cout << GridLogMessage << "Seed SHA256: " << GridChecksum::sha256_string(seeds) << std::endl;
417 SeedFixedIntegers(seeds);
418 }
419 void SeedFixedIntegers(const std::vector<int> &seeds, int britney=0){
420
421 // Everyone generates the same seed_seq based on input seeds
422 CartesianCommunicator::BroadcastWorld(0,(void *)&seeds[0],sizeof(int)*seeds.size());
423
424 std::seed_seq source(seeds.begin(),seeds.end());
425
426 RngEngine master_engine(source);
427
428#ifdef RNG_FAST_DISCARD
430 // Skip ahead through a single stream.
431 // Applicable to SITMO and other has based/crypto RNGs
432 // Should be applicable to Mersenne Twister, but the C++11
433 // MT implementation does not implement fast discard even though
434 // in principle this is possible
436 thread_for( lidx, _grid->lSites(), {
437
438 int64_t gidx;
439 int o_idx;
440 int i_idx;
441 int rank;
442 Coordinate pcoor;
443 Coordinate lcoor;
444 Coordinate gcoor;
445 _grid->LocalIndexToLocalCoor(lidx,lcoor);
446 pcoor=_grid->ThisProcessorCoor();
447 _grid->ProcessorCoorLocalCoorToGlobalCoor(pcoor,lcoor,gcoor);
448 _grid->GlobalCoorToGlobalIndex(gcoor,gidx);
449
450 _grid->GlobalCoorToRankIndex(rank,o_idx,i_idx,gcoor);
451
452 assert(rank == _grid->ThisRank() );
453
454 int l_idx=generator_idx(o_idx,i_idx);
455 _generators[l_idx] = master_engine;
456 if ( britney ) {
457 Skip(_generators[l_idx],l_idx); // Skip to next RNG sequence
458 } else {
459 Skip(_generators[l_idx],gidx); // Skip to next RNG sequence
460 }
461 });
462#else
464 // Machine and thread decomposition dependent seeding is efficient
465 // and maximally parallel; but NOT reproducible from machine to machine.
466 // Not ideal, but fastest way to reseed all nodes.
468 {
469 // Obtain one Reseed per processor
470 int Nproc = _grid->ProcessorCount();
471 std::vector<RngEngine> seeders(Nproc);
472 int me= _grid->ThisRank();
473 for(int p=0;p<Nproc;p++){
474 seeders[p] = Reseed(master_engine);
475 }
476 master_engine = seeders[me];
477 }
478
479 {
480 // Obtain one reseeded generator per thread
481 int Nthread = 32; // Hardwire a good level or parallelism
482 std::vector<RngEngine> seeders(Nthread);
483 for(int t=0;t<Nthread;t++){
484 seeders[t] = Reseed(master_engine);
485 }
486
487 thread_for( t, Nthread, {
488 // set up one per local site in threaded fashion
489 std::vector<uint32_t> newseeds;
490 std::uniform_int_distribution<uint32_t> uid;
491 for(int l=0;l<_grid->lSites();l++) {
492 if ( (l%Nthread)==t ) {
493 _generators[l] = Reseed(seeders[t],newseeds,uid);
494 }
495 }
496 });
497 }
498#endif
499 }
500
501 void Report(){
502 std::cout << GridLogMessage << "Time spent in the fill() routine by GridParallelRNG: "<< _time_counter/1e3 << " ms" << std::endl;
503 }
504
505
507 // Support for rigorous test of RNG's
508 // Return uniform random uint32_t from requested site generator
510 uint32_t GlobalU01(int gsite){
511
512 uint32_t the_number;
513 // who
514 int rank,o_idx,i_idx;
515 Coordinate gcoor;
516 _grid->GlobalIndexToGlobalCoor(gsite,gcoor);
517 _grid->GlobalCoorToRankIndex(rank,o_idx,i_idx,gcoor);
518
519 // draw
520 int l_idx=generator_idx(o_idx,i_idx);
521 if( rank == _grid->ThisRank() ){
522 the_number = _uid[l_idx](_generators[l_idx]);
523 }
524
525 // share & return
526 _grid->Broadcast(rank,(void *)&the_number,sizeof(the_number));
527 return the_number;
528 }
529
530};
531
532template <class vobj> inline void random(GridParallelRNG &rng,Lattice<vobj> &l) { rng.fill(l,rng._uniform); }
533template <class vobj> inline void gaussian(GridParallelRNG &rng,Lattice<vobj> &l) { rng.fill(l,rng._gaussian); }
534template <class vobj> inline void bernoulli(GridParallelRNG &rng,Lattice<vobj> &l){ rng.fill(l,rng._bernoulli);}
535
536template <class sobj> inline void random(GridSerialRNG &rng,sobj &l) { rng.fill(l,rng._uniform ); }
537template <class sobj> inline void gaussian(GridSerialRNG &rng,sobj &l) { rng.fill(l,rng._gaussian ); }
538template <class sobj> inline void bernoulli(GridSerialRNG &rng,sobj &l){ rng.fill(l,rng._bernoulli); }
539
541#endif
AcceleratorVector< int, MaxDims > Coordinate
Definition Coordinate.h:95
Grid_simd< complex< float >, SIMD_Ftype > vComplexF
Grid_simd< float, SIMD_Ftype > vRealF
Grid_simd< complex< double >, SIMD_Dtype > vComplexD
Grid_simd< double, SIMD_Dtype > vRealD
void gaussian(GridParallelRNG &rng, Lattice< vobj > &l)
int RNGfillable(GridBase *coarse, GridBase *fine)
Definition Lattice_rng.h:49
void fillScalar(scalar &s, distribution &dist, generator &gen)
void bernoulli(GridParallelRNG &rng, Lattice< vobj > &l)
int RNGfillable_general(GridBase *coarse, GridBase *fine)
Definition Lattice_rng.h:81
void random(GridParallelRNG &rng, Lattice< vobj > &l)
void pickCheckerboard(int cb, Lattice< vobj > &half, const Lattice< vobj > &full)
#define autoView(l_v, l, mode)
GridLogger GridLogMessage(1, "Message", GridLogColours, "NORMAL")
@ CpuWrite
#define NAMESPACE_BEGIN(A)
Definition Namespace.h:35
#define NAMESPACE_END(A)
Definition Namespace.h:36
std::complex< RealF > ComplexF
Definition Simd.h:78
float RealF
Definition Simd.h:60
std::complex< RealD > ComplexD
Definition Simd.h:79
double RealD
Definition Simd.h:61
static void generator(int lieIndex, iGroupMatrix< cplx > &ta, GroupName::Sp)
Definition Sp2n.impl.h:27
AcceleratorVector< __T,GRID_MAX_SIMD > ExtractBuffer
accelerator void merge(vobj &vec, ExtractBuffer< sobj > &extracted)
#define thread_for(i, num,...)
Definition Threads.h:60
double usecond(void)
Definition Timer.h:50
static void BroadcastWorld(int root, void *data, int bytes)
int lSites(void) const
bool _isCheckerBoarded
Coordinate _rdimensions
Coordinate _simd_layout
int Nsimd(void) const
static std::vector< int > sha256_seeds(const std::string &s)
Definition Sha.h:78
static std::string sha256_string(const std::vector< T > &hash)
Definition Sha.h:58
uint32_t GlobalU01(int gsite)
int generator_idx(int os, int is)
void SeedFixedIntegers(const std::vector< int > &seeds, int britney=0)
GridParallelRNG(GridBase *grid)
unsigned int _vol
void fill(Lattice< vobj > &l, std::vector< distribution > &dist)
GridBase * _grid
GridBase * Grid(void) const
void SeedUniqueString(const std::string &s)
void SetEngine(RngEngine &Eng, int gen)
void GetState(std::vector< RngStateType > &saved, int gen)
std::vector< std::discrete_distribution< int32_t > > _bernoulli
std::vector< RngEngine > _generators
std::vector< std::uniform_int_distribution< uint32_t > > _uid
void GetState(std::vector< RngStateType > &saved, RngEngine &eng)
void SetState(std::vector< RngStateType > &saved, RngEngine &eng)
static RngEngine Reseed(RngEngine &eng)
void SetState(std::vector< RngStateType > &saved, int gen)
std::vector< std::uniform_real_distribution< RealD > > _uniform
void GetEngine(RngEngine &Eng, int gen)
void Seed(source &src, int gen)
std::vector< std::normal_distribution< RealD > > _gaussian
static RngEngine Reseed(RngEngine &eng, std::vector< uint32_t > &newseed, std::uniform_int_distribution< uint32_t > &uid)
void fill(ComplexF &l, std::vector< distribution > &dist)
void fill(vComplexF &l, std::vector< distribution > &dist)
void fill(ComplexD &l, std::vector< distribution > &dist)
void fill(vRealD &l, std::vector< distribution > &dist)
void SeedUniqueString(const std::string &s)
void fill(RealF &l, std::vector< distribution > &dist)
void fill(vComplexD &l, std::vector< distribution > &dist)
void SeedFixedIntegers(const std::vector< int > &seeds)
void fill(RealD &l, std::vector< distribution > &dist)
void fill(vRealF &l, std::vector< distribution > &dist)
void fill(sobj &l, std::vector< distribution > &dist)
static accelerator_inline constexpr int Nsimd(void)
accelerator_inline int Checkerboard(void) const
GridBase * Grid(void) const