Skip to content

Commit 6c11ab7

Browse files
committed
Merge branch 'master' of https://github.com/like2000/PyHEADTAIL
2 parents dbb54cb + 160c72b commit 6c11ab7

File tree

5 files changed

+168
-65
lines changed

5 files changed

+168
-65
lines changed

_version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
__version__ = "1.0.3"
1+
__version__ = "1.0.4"
22

monitors/monitors.py

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -341,10 +341,10 @@ def __init__(self, filename, stride=1, parameters_dict=None,
341341

342342
self._create_file_structure(parameters_dict)
343343

344-
def dump(self, bunch):
344+
def dump(self, bunch, arrays_dict=None):
345345
""" Write particle data to file. See docstring of method
346346
self._write_data_to_file . """
347-
self._write_data_to_file(bunch)
347+
self._write_data_to_file(bunch, arrays_dict)
348348
self.i_steps += 1
349349

350350
def _create_file_structure(self, parameters_dict):
@@ -362,11 +362,12 @@ def _create_file_structure(self, parameters_dict):
362362
self.filename + 'failed. \n')
363363
raise
364364

365-
def _write_data_to_file(self, bunch):
365+
def _write_data_to_file(self, bunch, arrays_dict):
366366
""" Write macroparticle data (x, xp, y, yp, z, dp, id) of a
367-
selection of particles to the HDF5 file. The file is opened and
368-
closed every time to prevent from loss of data in case of a
369-
crash.
367+
selection of particles to the HDF5 file. Optionally, data in
368+
additional_quantities can also be added if provided in the
369+
constructor. The file is opened and closed every time to prevent
370+
from loss of data in case of a crash.
370371
For each simulation step, a new group with name 'Step#..' is
371372
created. It contains one dataset for each of the quantities
372373
given in self.quantities_to_store. """
@@ -375,9 +376,16 @@ def _write_data_to_file(self, bunch):
375376
dims = (bunch.macroparticlenumber // self.stride,)
376377

377378
# resorting_indices = np.argsort(bunch.id)[::self.stride]
379+
all_quantities = {}
378380
for quant in self.quantities_to_store:
379381
quant_values = getattr(bunch, quant)
382+
all_quantities[quant] = quant_values
383+
all_quantities.update(arrays_dict)
384+
385+
for quant in all_quantities.keys():
386+
quant_values = all_quantities[quant]
380387
h5group.create_dataset(quant, shape=dims, compression='gzip',
381388
compression_opts=9, dtype=quant_values.dtype)
382389
h5group[quant][:] = quant_values[::self.stride]
390+
383391
h5file.close()

particles/generators.py

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -411,11 +411,17 @@ class CutRFBucket6D(ParticleGenerator):
411411
bi-gaussian with given sigma_z and sigma_dp which is then cut along
412412
the funciotn given by is_accepted which is typically the function
413413
is_in_separatrix of the rfbucket instance of the longitudinal map.
414+
To avoid bucket leakage and particle losses as a consequence of the
415+
unmatched initialisation, a margin / tolerance can be specified by
416+
the argument margin (in % of RFBucket.Hmax, 5% by default), so that
417+
particles will not be initialised too close to the separatrix. The
418+
boundary will in fact be given by the equihamiltonian with a value
419+
of margin*Hmax.
414420
'''
415421
def __init__(self, macroparticlenumber, intensity, charge, mass,
416422
circumference, gamma_reference,
417423
transverse_map, epsn_x, epsn_y,
418-
sigma_z, sigma_dp, is_accepted=None,
424+
sigma_z, sigma_dp, is_accepted, margin=0.05,
419425
*args, **kwargs):
420426
'''Uses the transverse_map to extract the optics parameters
421427
and the rf_bucket to match the longitudinal distribution.
@@ -427,7 +433,7 @@ def __init__(self, macroparticlenumber, intensity, charge, mass,
427433
self._rf_bucket_matcher = CutRFBucket2D(
428434
macroparticlenumber, intensity, charge, mass,
429435
circumference, gamma_reference,
430-
sigma_z, sigma_dp, is_accepted, *args, **kwargs)
436+
sigma_z, sigma_dp, is_accepted, margin, *args, **kwargs)
431437
super(CutRFBucket6D, self).__init__(
432438
macroparticlenumber, intensity, charge, mass, circumference,
433439
gamma_reference, HEADTAILcoords.coordinates, *args, **kwargs)
@@ -474,39 +480,46 @@ def distribute(self):
474480
class CutRFBucket2D(ParticleGenerator):
475481
'''
476482
For HEADTAIL style matching into RF bucket.
483+
The argument is_accepted takes a function (usually
484+
RFBucket.is_in_separatrix) defining the separatrix of the rf bucket.
485+
To avoid bucket leakage and particle losses as a consequence of the
486+
unmatched initialisation, a margin / tolerance can be specified by
487+
the argument margin (in % of RFBucket.Hmax, 5% by default), so that
488+
particles will not be initialised too close to the separatrix. The
489+
boundary will in fact be given by the equihamiltonian with a value
490+
of margin*Hmax.
491+
477492
BY KEVIN: NEEDS TO BE CLEANED UP BY ADRIAN!
478493
'''
479494
def __init__(self, macroparticlenumber, intensity, charge, mass,
480495
circumference, gamma_reference, sigma_z, sigma_dp,
481-
is_accepted=None, generator_seed=None, *args, **kwargs):
496+
is_accepted, margin=0.05, *args, **kwargs):
482497

483498
self.sigma_z = sigma_z
484499
self.sigma_dp = sigma_dp
485500
self.is_accepted = is_accepted
486-
self.random_state = RandomState()
487-
self.random_state.seed(generator_seed)
501+
self.margin = margin
488502

489503
super(CutRFBucket2D, self).__init__(
490504
macroparticlenumber, intensity, charge, mass, circumference,
491505
gamma_reference, HEADTAILcoords.longitudinal, *args, **kwargs)
492506

493507
def distribute(self):
494508

495-
z = self.sigma_z * self.random_state.randn(self.macroparticlenumber)
496-
dp = self.sigma_dp * self.random_state.randn(self.macroparticlenumber)
497-
if self.is_accepted:
498-
self._redistribute(z, dp)
509+
z = normal(0, self.sigma_z, self.macroparticlenumber)
510+
dp = normal(0, self.sigma_dp, self.macroparticlenumber)
511+
self._redistribute(z, dp)
499512

500513
return {'z': z, 'dp': dp}
501514

502515
def _redistribute(self, z, dp):
503516

504-
mask_out = ~self.is_accepted(z, dp)
517+
mask_out = ~self.is_accepted(z, dp, self.margin)
505518
while mask_out.any():
506519
n_gen = np.sum(mask_out)
507-
z[mask_out] = self.sigma_z * self.random_state.randn(n_gen)
508-
dp[mask_out] = self.sigma_dp * self.random_state.randn(n_gen)
509-
mask_out = ~self.is_accepted(z, dp)
520+
z[mask_out] = normal(0, self.sigma_z, n_gen)
521+
dp[mask_out] = normal(0, self.sigma_dp, n_gen)
522+
mask_out = ~self.is_accepted(z, dp, self.margin)
510523
self.prints('Reiterate on non-accepted particles')
511524

512525

testing/interactive-tests/GeneratorTest.ipynb

Lines changed: 122 additions & 41 deletions
Large diffs are not rendered by default.

trackers/rf_bucket.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -261,13 +261,14 @@ def p_max(self, zc):
261261
f = self.equihamiltonian(zc)
262262
return np.amax(f(self.zs))
263263

264-
def is_in_separatrix(self, z, dp):
264+
def is_in_separatrix(self, z, dp, margin):
265265
"""
266-
Returns boolean whether this coordinate is located
267-
strictly inside the separatrix.
266+
Returns boolean whether this coordinate is located inside the
267+
separatrix. A margin, in % of self.Hmax, can be specified so
268+
that the separatrix is not strictly followed.
268269
"""
269270
return np.logical_and(np.logical_and(self.zleft < z, z < self.zright),
270-
self.hamiltonian(z, dp) > 0.01 * self.Hmax)
271+
self.hamiltonian(z, dp) > margin * self.Hmax)
271272

272273
def bucket_area(self):
273274
xmin, xmax = self.zleft, self.zright

0 commit comments

Comments
 (0)