@@ -127,30 +127,8 @@ void synchronize_bank()
127127 " No fission sites banked on MPI rank " + std::to_string (mpi::rank));
128128 }
129129
130- // Make sure all processors start at the same point for random sampling. Then
131- // skip ahead in the sequence using the starting index in the 'global'
132- // fission bank for each processor.
133-
134- int64_t id = simulation::total_gen + overall_generation ();
135- uint64_t seed = init_seed (id, STREAM_TRACKING);
136- advance_prn_seed (start, &seed);
137-
138- // Determine how many fission sites we need to sample from the source bank
139- // and the probability for selecting a site.
140-
141- int64_t sites_needed;
142- if (total < settings::n_particles) {
143- sites_needed = settings::n_particles % total;
144- } else {
145- sites_needed = settings::n_particles;
146- }
147- double p_sample = static_cast <double >(sites_needed) / total;
148-
149130 simulation::time_bank_sample.start ();
150131
151- // ==========================================================================
152- // SAMPLE N_PARTICLES FROM FISSION BANK AND PLACE IN TEMP_SITES
153-
154132 // Allocate temporary source bank -- we don't really know how many fission
155133 // sites were created, so overallocate by a factor of 3
156134 int64_t index_temp = 0 ;
@@ -165,33 +143,38 @@ void synchronize_bank()
165143 temp_delayed_groups, temp_lifetimes, 3 * simulation::work_per_rank);
166144 }
167145
168- for ( int64_t i = 0 ; i < simulation::fission_bank. size (); i++) {
169- const auto & site = simulation::fission_bank[i];
146+ // ==========================================================================
147+ // SAMPLE N_PARTICLES FROM FISSION BANK AND PLACE IN TEMP_SITES
170148
171- // If there are less than n_particles particles banked, automatically add
172- // int(n_particles/total) sites to temp_sites. For example, if you need
173- // 1000 and 300 were banked, this would add 3 source sites per banked site
174- // and the remaining 100 would be randomly sampled.
175- if (total < settings::n_particles) {
176- for (int64_t j = 1 ; j <= settings::n_particles / total; ++j) {
177- temp_sites[index_temp] = site;
178- if (settings::ifp_on) {
179- copy_ifp_data_from_fission_banks (
180- i, temp_delayed_groups[index_temp], temp_lifetimes[index_temp]);
181- }
182- ++index_temp;
183- }
184- }
149+ // We use Uniform Combing method to exactly get the targeted particle size
150+ // [https://doi.org/10.1080/00295639.2022.2091906]
185151
186- // Randomly sample sites needed
187- if (prn (&seed) < p_sample) {
188- temp_sites[index_temp] = site;
189- if (settings::ifp_on) {
190- copy_ifp_data_from_fission_banks (
191- i, temp_delayed_groups[index_temp], temp_lifetimes[index_temp]);
192- }
193- ++index_temp;
152+ // Make sure all processors use the same random number seed.
153+ int64_t id = simulation::total_gen + overall_generation ();
154+ uint64_t seed = init_seed (id, STREAM_TRACKING);
155+
156+ // Comb specification
157+ double teeth_distance = static_cast <double >(total) / settings::n_particles;
158+ double teeth_offset = prn (&seed) * teeth_distance;
159+
160+ // First and last hitting tooth
161+ int64_t end = start + simulation::fission_bank.size ();
162+ int64_t tooth_start = std::ceil ((start - teeth_offset) / teeth_distance);
163+ int64_t tooth_end = std::floor ((end - teeth_offset) / teeth_distance) + 1 ;
164+
165+ // Locally comb particles in fission_bank
166+ double tooth = tooth_start * teeth_distance + teeth_offset;
167+ for (int64_t i = tooth_start; i < tooth_end; i++) {
168+ int64_t idx = std::floor (tooth) - start;
169+ temp_sites[index_temp] = simulation::fission_bank[idx];
170+ if (settings::ifp_on) {
171+ copy_ifp_data_from_fission_banks (
172+ idx, temp_delayed_groups[index_temp], temp_lifetimes[index_temp]);
194173 }
174+ ++index_temp;
175+
176+ // Next tooth
177+ tooth += teeth_distance;
195178 }
196179
197180 // At this point, the sampling of source sites is done and now we need to
@@ -217,37 +200,6 @@ void synchronize_bank()
217200 finish = index_temp;
218201#endif
219202
220- // Now that the sampling is complete, we need to ensure that we have exactly
221- // n_particles source sites. The way this is done in a reproducible manner is
222- // to adjust only the source sites on the last processor.
223-
224- if (mpi::rank == mpi::n_procs - 1 ) {
225- if (finish > settings::n_particles) {
226- // If we have extra sites sampled, we will simply discard the extra
227- // ones on the last processor
228- index_temp = settings::n_particles - start;
229-
230- } else if (finish < settings::n_particles) {
231- // If we have too few sites, repeat sites from the very end of the
232- // fission bank
233- sites_needed = settings::n_particles - finish;
234- // TODO: sites_needed > simulation::fission_bank.size() or other test to
235- // make sure we don't need info from other proc
236- for (int i = 0 ; i < sites_needed; ++i) {
237- int i_bank = simulation::fission_bank.size () - sites_needed + i;
238- temp_sites[index_temp] = simulation::fission_bank[i_bank];
239- if (settings::ifp_on) {
240- copy_ifp_data_from_fission_banks (i_bank,
241- temp_delayed_groups[index_temp], temp_lifetimes[index_temp]);
242- }
243- ++index_temp;
244- }
245- }
246-
247- // the last processor should not be sending sites to right
248- finish = simulation::work_index[mpi::rank + 1 ];
249- }
250-
251203 simulation::time_bank_sample.stop ();
252204 simulation::time_bank_sendrecv.start ();
253205
0 commit comments