Skip to content

Commit 024d73f

Browse files
committed
Add option to save timestamp positions [WIP]
1 parent dfd43d3 commit 024d73f

3 files changed

Lines changed: 90 additions & 46 deletions

File tree

notebooks/PyBroMo - 1. Simulate 3D trajectories - single core.ipynb

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,8 @@
9595
"psf = pbm.NumericPSF()\n",
9696
"\n",
9797
"# Particles definition\n",
98-
"P = pbm.Particles(num_particles=20, D=D1, box=box, rs=rs)\n",
99-
"P.add(num_particles=15, D=D2)\n",
98+
"P = pbm.Particles(num_particles=2, D=D1, box=box, rs=rs)\n",
99+
"P.add(num_particles=2, D=D2)\n",
100100
"\n",
101101
"# Simulation time step (seconds)\n",
102102
"t_step = 0.5e-6\n",
@@ -214,7 +214,8 @@
214214
},
215215
"outputs": [],
216216
"source": [
217-
"S.simulate_diffusion(total_emission=False, save_pos=True, verbose=True, rs=rs, chunksize=2**19, chunkslice='times')"
217+
"S.simulate_diffusion(total_emission=False, save_pos=True, verbose=True, \n",
218+
" rs=rs, chunksize=2**19, chunkslice='times')"
218219
]
219220
},
220221
{
@@ -228,6 +229,16 @@
228229
"print('Current random state:', pbm.hash_(rs.get_state()))"
229230
]
230231
},
232+
{
233+
"cell_type": "code",
234+
"execution_count": null,
235+
"metadata": {},
236+
"outputs": [],
237+
"source": [
238+
"position = S.position[:]\n",
239+
"position.shape, position.dtype"
240+
]
241+
},
231242
{
232243
"cell_type": "markdown",
233244
"metadata": {},
@@ -273,7 +284,7 @@
273284
},
274285
"outputs": [],
275286
"source": [
276-
"S = pbm.ParticlesSimulation.from_datafile('0168') # Read-only by default"
287+
"S = pbm.ParticlesSimulation.from_datafile('9125') # Read-only by default"
277288
]
278289
},
279290
{
@@ -352,7 +363,6 @@
352363
"cell_type": "code",
353364
"execution_count": null,
354365
"metadata": {
355-
"collapsed": true,
356366
"scrolled": true
357367
},
358368
"outputs": [],
@@ -415,9 +425,7 @@
415425
{
416426
"cell_type": "code",
417427
"execution_count": null,
418-
"metadata": {
419-
"collapsed": true
420-
},
428+
"metadata": {},
421429
"outputs": [],
422430
"source": [
423431
"S.store.close()"
@@ -440,7 +448,7 @@
440448
"metadata": {},
441449
"outputs": [],
442450
"source": [
443-
"S = pbm.ParticlesSimulation.from_datafile('0168', mode='w')"
451+
"S = pbm.ParticlesSimulation.from_datafile('9125', mode='w')"
444452
]
445453
},
446454
{
@@ -459,7 +467,7 @@
459467
"outputs": [],
460468
"source": [
461469
"S.simulate_timestamps_mix(max_rates=(200e3,), \n",
462-
" populations=(slice(0, 35),),\n",
470+
" populations=(slice(None),),\n",
463471
" bg_rate=1e3)"
464472
]
465473
},
@@ -547,19 +555,17 @@
547555
{
548556
"cell_type": "code",
549557
"execution_count": null,
550-
"metadata": {
551-
"collapsed": true
552-
},
558+
"metadata": {},
553559
"outputs": [],
554560
"source": []
555561
}
556562
],
557563
"metadata": {
558564
"anaconda-cloud": {},
559565
"kernelspec": {
560-
"display_name": "Python 3.6 (pybromo_env)",
566+
"display_name": "Python 3.6 (py36)",
561567
"language": "python",
562-
"name": "pybromo_env"
568+
"name": "py36"
563569
},
564570
"language_info": {
565571
"codemirror_mode": {
@@ -571,7 +577,7 @@
571577
"name": "python",
572578
"nbconvert_exporter": "python",
573579
"pygments_lexer": "ipython3",
574-
"version": "3.6.3"
580+
"version": "3.6.7"
575581
},
576582
"toc": {
577583
"nav_menu": {},

pybromo/diffusion.py

Lines changed: 54 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -686,7 +686,7 @@ def timestamp_names(self):
686686
return names
687687

688688
def _sim_timestamps(self, max_rate, bg_rate, emission, i_start, rs,
689-
ip_start=0, scale=10, sort=True):
689+
ip_start=0, scale=10, sort=True, save_pos=False):
690690
"""Simulate timestamps from emission trajectories.
691691
692692
Uses attributes: `.t_step`.
@@ -702,7 +702,9 @@ def _sim_timestamps(self, max_rate, bg_rate, emission, i_start, rs,
702702
assert counts_chunk.shape == (nrows, emission.shape[1])
703703
max_counts = counts_chunk.max()
704704
if max_counts == 0:
705-
return np.array([], dtype=np.int64), np.array([], dtype=np.int64)
705+
return (np.array([], dtype=np.int64), # timestamps
706+
np.array([], dtype=np.int64), # particles
707+
np.array([], dtype=np.float32)) # positions
706708

707709
time_start = i_start * scale
708710
time_stop = time_start + counts_chunk.shape[1] * scale
@@ -722,51 +724,67 @@ def _sim_timestamps(self, max_rate, bg_rate, emission, i_start, rs,
722724
# Append current particle
723725
times_chunk_p.append(t)
724726
par_index_chunk_p.append(np.full(t.size, ip + ip_start, dtype='u1'))
727+
if save_pos:
728+
# TODO compute and asve positions
729+
pass
725730

726731
# Merge the arrays of different particles
727732
times_chunk = np.hstack(times_chunk_p)
728733
par_index_chunk = np.hstack(par_index_chunk_p)
734+
positions_chunk = np.zeros(
735+
(times_chunk.shape[-1], self.positions.shape[1]), dtype=np.float32)
729736

730737
if sort:
731738
# Sort timestamps inside the merged chunk
732739
index_sort = times_chunk.argsort(kind='mergesort')
733740
times_chunk = times_chunk[index_sort]
734741
par_index_chunk = par_index_chunk[index_sort]
742+
if save_pos:
743+
# TODO: reorder positions
744+
pass
735745

736-
return times_chunk, par_index_chunk
746+
return times_chunk, par_index_chunk, positions_chunk
737747

738748
def _sim_timestamps_populations(self, emission, max_rates, populations,
739-
bg_rates, i_start, rs, scale=10):
749+
bg_rates, i_start, rs, scale=10,
750+
save_pos=False):
740751
if populations is None:
741752
populations = [slice(0, self.num_particles)]
742753

743754
# Loop for each population
744755
ts_chunk_pop_list, par_index_chunk_pop_list = [], []
756+
positions_chunk_pop_list = []
745757
# Loop through populations
746758
for rate, pop, bg in zip(max_rates, populations, bg_rates):
747759
emission_pop = emission[pop]
748-
ts_chunk_pop, par_index_chunk_pop = \
760+
ts_chunk_pop, par_index_chunk_pop, positions_pop = \
749761
self._sim_timestamps(
750762
rate, bg, emission_pop, i_start, ip_start=pop.start,
751-
rs=rs, scale=scale, sort=False)
763+
rs=rs, scale=scale, sort=False, save_pos=save_pos)
752764

753765
ts_chunk_pop_list.append(ts_chunk_pop)
754766
par_index_chunk_pop_list.append(par_index_chunk_pop)
767+
if save_pos:
768+
positions_chunk_pop_list.append(positions_pop)
755769

756770
# Merge populations
757771
times_chunk_s = np.hstack(ts_chunk_pop_list)
758772
par_index_chunk_s = np.hstack(par_index_chunk_pop_list)
773+
if save_pos:
774+
positions_chunk_s = np.hstack(positions_chunk_pop_list)
759775

760776
# Sort timestamps inside the merged chunk
761777
index_sort = times_chunk_s.argsort(kind='mergesort')
762778
times_chunk_s = times_chunk_s[index_sort]
763779
par_index_chunk_s = par_index_chunk_s[index_sort]
764-
return times_chunk_s, par_index_chunk_s
780+
if save_pos:
781+
positions_chunk_s = positions_chunk_s[index_sort]
782+
return times_chunk_s, par_index_chunk_s, positions_chunk_s
765783

766784
def simulate_timestamps_mix(self, max_rates, populations, bg_rate,
767785
rs=None, seed=1, chunksize=2**16,
768786
comp_filter=None, overwrite=False,
769-
skip_existing=False, scale=10,
787+
skip_existing=False, scale=10, save_pos=False,
770788
path=None, t_chunksize=None, timeslice=None):
771789
"""Compute one timestamps array for a mixture of N populations.
772790
@@ -797,6 +815,8 @@ def simulate_timestamps_mix(self, max_rates, populations, bg_rate,
797815
scale (int): `self.t_step` is multiplied by `scale` to obtain the
798816
timestamps units in seconds.
799817
path (string): folder where to save the data.
818+
save_pos (bool): if True save 3D position of each particle
819+
for each emitted photon.
800820
timeslice (float or None): timestamps are simulated until
801821
`timeslice` seconds. If None, simulate until `self.t_max`.
802822
"""
@@ -814,13 +834,16 @@ def simulate_timestamps_mix(self, max_rates, populations, bg_rate,
814834
max_rates=max_rates, bg_rate=bg_rate, populations=populations,
815835
num_particles=self.num_particles,
816836
bg_particle=self.num_particles,
817-
overwrite=overwrite, chunksize=chunksize
837+
overwrite=overwrite, chunksize=chunksize,
838+
save_pos=save_pos,
818839
)
840+
if save_pos:
841+
kw.update(spatial_dims=self.positions.shape[1])
819842
if comp_filter is not None:
820843
kw.update(comp_filter=comp_filter)
821844
try:
822-
self._timestamps, self._tparticles = (self.ts_store
823-
.add_timestamps(**kw))
845+
self._timestamps, self._tparticles, self._tpositions = (
846+
self.ts_store.add_timestamps(**kw))
824847
except ExistingArrayError as e:
825848
if skip_existing:
826849
print(' - Skipping already present timestamps array.')
@@ -832,7 +855,7 @@ def simulate_timestamps_mix(self, max_rates, populations, bg_rate,
832855
self._timestamps.attrs['init_random_state'] = rs.get_state()
833856
self._timestamps.attrs['PyBroMo'] = __version__
834857

835-
ts_list, part_list = [], []
858+
ts_list, part_list, pos_list = [], [], []
836859
# Load emission in chunks, and save only the final timestamps
837860
bg_rates = [None] * (len(max_rates) - 1) + [bg_rate]
838861
prev_time = 0
@@ -846,18 +869,21 @@ def simulate_timestamps_mix(self, max_rates, populations, bg_rate,
846869

847870
em_chunk = self.emission[:, i_start:i_end]
848871

849-
times_chunk_s, par_index_chunk_s = \
872+
times_chunk_s, par_index_chunk_s, positions_chunk_s = \
850873
self._sim_timestamps_populations(
851874
em_chunk, max_rates, populations, bg_rates, i_start,
852-
rs, scale)
875+
rs, scale, save_pos=save_pos)
853876

854-
# Save sorted timestamps (suffix '_s') and corresponding particles
877+
# Save sorted "photons" (suffix '_s')
855878
ts_list.append(times_chunk_s)
856879
part_list.append(par_index_chunk_s)
880+
pos_list.append(positions_chunk_s) # it may be a list of None
857881

858-
for ts, part in zip(ts_list, part_list):
882+
for ts, part, pos in zip(ts_list, part_list, pos_list):
859883
self._timestamps.append(ts)
860884
self._tparticles.append(part)
885+
if save_pos:
886+
self._tpositions.append(pos)
861887

862888
# Save current random state so it can be resumed in the next session
863889
self.ts_group._v_attrs['last_random_state'] = rs.get_state()
@@ -933,8 +959,8 @@ def simulate_timestamps_mix_da(self, max_rates_d, max_rates_a,
933959

934960
kw.update(name=name_d, max_rates=max_rates_d, bg_rate=bg_rate_d)
935961
try:
936-
self._timestamps_d, self._tparticles_d = (self.ts_store
937-
.add_timestamps(**kw))
962+
self._timestamps_d, self._tparticles_d, _ = (
963+
self.ts_store.add_timestamps(**kw))
938964
except ExistingArrayError as e:
939965
if skip_existing:
940966
print(' - Skipping already present timestamps array.')
@@ -944,8 +970,8 @@ def simulate_timestamps_mix_da(self, max_rates_d, max_rates_a,
944970

945971
kw.update(name=name_a, max_rates=max_rates_a, bg_rate=bg_rate_a)
946972
try:
947-
self._timestamps_a, self._tparticles_a = (self.ts_store
948-
.add_timestamps(**kw))
973+
self._timestamps_a, self._tparticles_a, _ = (
974+
self.ts_store.add_timestamps(**kw))
949975
except ExistingArrayError as e:
950976
if skip_existing:
951977
print(' - Skipping already present timestamps array.')
@@ -972,12 +998,12 @@ def simulate_timestamps_mix_da(self, max_rates_d, max_rates_a,
972998

973999
em_chunk = self.emission[:, i_start:i_end]
9741000

975-
times_chunk_s_d, par_index_chunk_s_d = \
1001+
times_chunk_s_d, par_index_chunk_s_d, _ = \
9761002
self._sim_timestamps_populations(
9771003
em_chunk, max_rates_d, populations, bg_rates_d, i_start,
9781004
rs, scale)
9791005

980-
times_chunk_s_a, par_index_chunk_s_a = \
1006+
times_chunk_s_a, par_index_chunk_s_a, _ = \
9811007
self._sim_timestamps_populations(
9821008
em_chunk, max_rates_a, populations, bg_rates_a, i_start,
9831009
rs, scale)
@@ -1057,8 +1083,8 @@ def simulate_timestamps_mix_da_online(self, max_rates_d, max_rates_a,
10571083

10581084
kw.update(name=name_d, max_rates=max_rates_d, bg_rate=bg_rate_d)
10591085
try:
1060-
self._timestamps_d, self._tparticles_d = (self.ts_store
1061-
.add_timestamps(**kw))
1086+
self._timestamps_d, self._tparticles_d, _ = (
1087+
self.ts_store.add_timestamps(**kw))
10621088
except ExistingArrayError as e:
10631089
if skip_existing:
10641090
print(' - Skipping, timestamps array already present.')
@@ -1068,8 +1094,8 @@ def simulate_timestamps_mix_da_online(self, max_rates_d, max_rates_a,
10681094

10691095
kw.update(name=name_a, max_rates=max_rates_a, bg_rate=bg_rate_a)
10701096
try:
1071-
self._timestamps_a, self._tparticles_a = (self.ts_store
1072-
.add_timestamps(**kw))
1097+
self._timestamps_a, self._tparticles_a, _ = (
1098+
self.ts_store.add_timestamps(**kw))
10731099
except ExistingArrayError as e:
10741100
if skip_existing:
10751101
print(' - Skipping, timestamps array already present.')
@@ -1103,12 +1129,12 @@ def simulate_timestamps_mix_da_online(self, max_rates_d, max_rates_a,
11031129
save_pos=False, radial=False,
11041130
wrap_func=wrap_periodic)
11051131

1106-
times_chunk_s_d, par_index_chunk_s_d = \
1132+
times_chunk_s_d, par_index_chunk_s_d, _ = \
11071133
self._sim_timestamps_populations(
11081134
em_chunk, max_rates_d, populations, bg_rates_d, i_start,
11091135
rs, scale)
11101136

1111-
times_chunk_s_a, par_index_chunk_s_a = \
1137+
times_chunk_s_a, par_index_chunk_s_a, _ = \
11121138
self._sim_timestamps_populations(
11131139
em_chunk, max_rates_a, populations, bg_rates_a, i_start,
11141140
rs, scale)

pybromo/storage.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,8 @@ def __init__(self, datafile, path='./', nparams=dict(), attr_params=dict(),
255255
def add_timestamps(self, name, clk_p, max_rates, bg_rate,
256256
num_particles, bg_particle, populations=None,
257257
overwrite=False, chunksize=2**16,
258-
comp_filter=default_compression):
258+
comp_filter=default_compression, save_pos=False,
259+
spatial_dims=None):
259260
if name in self.h5file.root.timestamps:
260261
if overwrite:
261262
self.h5file.remove_node('/timestamps', name=name)
@@ -286,7 +287,18 @@ def add_timestamps(self, name, clk_p, max_rates, bg_rate,
286287
particles_array.set_attr('bg_particle', bg_particle)
287288
particles_array.set_attr('PyBroMo', __version__)
288289
particles_array.set_attr('creation_time', current_time())
289-
return times_array, particles_array
290+
positions_array = None
291+
if save_pos:
292+
assert spatial_dims is not None, 'You need to pass `spatial_dims`.'
293+
positions_array = self.h5file.create_earray(
294+
'/timestamps', name + '_pos', atom=tables.Float32Atom(),
295+
shape=(0, spatial_dims),
296+
chunkshape=(chunksize,),
297+
filters=comp_filter,
298+
title='Particle position for each timestamp')
299+
positions_array.set_attr('PyBroMo', __version__)
300+
positions_array.set_attr('creation_time', current_time())
301+
return times_array, particles_array, positions_array
290302

291303

292304
if __name__ == '__main__':

0 commit comments

Comments
 (0)