Skip to content

Commit a057b86

Browse files
authored
Merge pull request #8 from cyclotron-bonn/ion_rework_v2
Ion rework v2
2 parents 87c20a5 + 436570c commit a057b86

File tree

14 files changed

+314
-166
lines changed

14 files changed

+314
-166
lines changed

Diff for: irrad_control/analysis/constants.py

+9
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,12 @@
88

99
# nano prefix
1010
nano = 1e-9
11+
12+
# Conversion factor for MeV/g to Mrad, 1 eV = 1.602e-19 J, 1 rad = 0.01 J/kg
13+
# -> MeV / g = 1e6 * 1.602e-19 J / 1e-3 kg
14+
# -> MeV / g = 1e9 * 1.602e-19 J / kg
15+
# -> MeV / g = 1e9 * 1.602e-19 * 1e2 rad
16+
# -> MeV / g = 1e11 * 1.602e-19 rad
17+
# -> Mev / g = 1e5 * 1.602e-19 Mrad
18+
# -> Mev / g = 1e5 * elementary_charge * Mrad
19+
MEV_PER_GRAM_TO_MRAD = 1e5 * elementary_charge

Diff for: irrad_control/analysis/damage.py

+22-19
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,15 @@ def analyse_radiation_damage(data, config=None):
1313
bins = (100, 100)
1414

1515
# Dict that holds results and error maps; bin centers
16-
results = {r: None for r in ('proton', 'neq', 'tid')}
16+
results = {r: None for r in ('primary', 'neq', 'tid')}
1717
errors = {e: None for e in results}
1818
bin_centers = {'x': None, 'y': None}
1919

2020
# We have a multipart irradiation with mulitple datafiles
2121
if config is None:
2222

2323
server = None # Only allow files with exactly one server for multipart to avoid adding unrelated fluence maps
24+
ion_name = None
2425

2526
# Loop over generator and get partial data files
2627
for nfile, data_part, config_part, session_basename in data:
@@ -35,28 +36,29 @@ def analyse_radiation_damage(data, config=None):
3536
# Only allow one fixed server for multipart
3637
if server is None:
3738
server = server_config['name']
39+
ion_name = server_config['daq']['ion']
3840

3941
if server not in data_part:
4042
raise KeyError(f"Server '{server}' not present in file {session_basename}!")
4143

4244
# Initialize damage and error maps
4345
if nfile == 0:
4446

45-
results['proton'], errors['proton'], bin_centers['x'], bin_centers['y'] = fluence.generate_fluence_map(beam_data=data_part[server]['Beam'],
47+
results['primary'], errors['primary'], bin_centers['x'], bin_centers['y'] = fluence.generate_fluence_map(beam_data=data_part[server]['Beam'],
4648
scan_data=data_part[server]['Scan'],
4749
beam_sigma=beam_sigma,
4850
bins=bins)
4951
# Generate eqivalent fluence map as well as TID map
5052
if server_config['daq']['kappa'] is None:
5153
del results['neq']
5254
else:
53-
results['neq'] = results['proton'] * server_config['daq']['kappa']['nominal']
55+
results['neq'] = results['primary'] * server_config['daq']['kappa']['nominal']
5456

5557
print(server_config['daq']['stopping_power'], type(server_config['daq']['stopping_power']))
5658
if server_config['daq']['stopping_power'] is None:
5759
del results['tid']
5860
else:
59-
results['tid'] = formulas.tid_scan(proton_fluence=results['proton'], stopping_power=server_config['daq']['stopping_power'])
61+
results['tid'] = formulas.tid_per_scan(primary_fluence=results['primary'], stopping_power=server_config['daq']['stopping_power'])
6062

6163
continue
6264

@@ -65,38 +67,39 @@ def analyse_radiation_damage(data, config=None):
6567
beam_sigma=beam_sigma,
6668
bins=bins)
6769
# Add to overall map
68-
results['proton'] += fluence_map_part
69-
errors['proton'] = (errors['proton']**2 + fluence_map_part_error**2)**.5
70+
results['primary'] += fluence_map_part
71+
errors['primary'] = (errors['primary']**2 + fluence_map_part_error**2)**.5
7072

7173
# Add to eqivalent fluence map
7274
if 'neq' in results:
73-
results['neq'] += results['proton'] * server_config['daq']['kappa']['nominal']
74-
errors['neq'] = ((server_config['daq']['kappa']['nominal'] * errors['proton'])**2 + (results['proton'] * server_config['daq']['kappa']['sigma'])**2)**0.5
75+
results['neq'] += results['primary'] * server_config['daq']['kappa']['nominal']
76+
errors['neq'] = ((server_config['daq']['kappa']['nominal'] * errors['primary'])**2 + (results['primary'] * server_config['daq']['kappa']['sigma'])**2)**0.5
7577

7678
if 'tid' in results:
77-
results['tid'] += formulas.tid_scan(proton_fluence=results['proton'], stopping_power=server_config['daq']['stopping_power'])
78-
errors['tid'] = formulas.tid_scan(proton_fluence=errors['proton'], stopping_power=server_config['daq']['stopping_power'])
79+
results['tid'] += formulas.tid_per_scan(primary_fluence=results['primary'], stopping_power=server_config['daq']['stopping_power'])
80+
errors['tid'] = formulas.tid_per_scan(primary_fluence=errors['primary'], stopping_power=server_config['daq']['stopping_power'])
7981

8082
else:
8183

8284
server = config['name']
85+
ion_name = config['daq']['ion']
8386

84-
results['proton'], errors['proton'], bin_centers['x'], bin_centers['y'] = fluence.generate_fluence_map(beam_data=data[server]['Beam'],
87+
results['primary'], errors['primary'], bin_centers['x'], bin_centers['y'] = fluence.generate_fluence_map(beam_data=data[server]['Beam'],
8588
scan_data=data[server]['Scan'],
8689
beam_sigma=beam_sigma,
8790
bins=bins)
8891
# Generate eqivalent fluence map as well as TID map
8992
if config['daq']['kappa'] is None:
9093
del results['neq']
9194
else:
92-
results['neq'] = results['proton'] * config['daq']['kappa']['nominal']
93-
errors['neq'] = ((config['daq']['kappa']['nominal'] * errors['proton'])**2 + (results['proton'] * config['daq']['kappa']['sigma'])**2)**.5
95+
results['neq'] = results['primary'] * config['daq']['kappa']['nominal']
96+
errors['neq'] = ((config['daq']['kappa']['nominal'] * errors['primary'])**2 + (results['primary'] * config['daq']['kappa']['sigma'])**2)**.5
9497

9598
if config['daq']['stopping_power'] is None:
9699
del results['tid']
97100
else:
98-
results['tid'] = formulas.tid_scan(proton_fluence=results['proton'], stopping_power=config['daq']['stopping_power'])
99-
errors['tid'] = formulas.tid_scan(proton_fluence=errors['proton'], stopping_power=config['daq']['stopping_power'])
101+
results['tid'] = formulas.tid_per_scan(primary_fluence=results['primary'], stopping_power=config['daq']['stopping_power'])
102+
errors['tid'] = formulas.tid_per_scan(primary_fluence=errors['primary'], stopping_power=config['daq']['stopping_power'])
100103

101104
if any(a is None for a in (list(bin_centers.values()) + list(results.values()))):
102105
raise ValueError('Uninitialized values! Something went wrong - maybe files not found?')
@@ -123,16 +126,16 @@ def analyse_radiation_damage(data, config=None):
123126

124127
is_dut = damage_map.shape == dut_map.shape
125128

126-
fig, _ = plotting.plot_damage_map_3d(damage_map=damage_map, map_centers_x=centers_x, map_centers_y=centers_y, contour=not is_dut, damage=damage, server=server, dut=is_dut)
129+
fig, _ = plotting.plot_damage_map_3d(damage_map=damage_map, map_centers_x=centers_x, map_centers_y=centers_y, contour=not is_dut, damage=damage, ion_name=ion_name, server=server, dut=is_dut)
127130
figs.append(fig)
128131

129-
fig, _ = plotting.plot_damage_error_3d(damage_map=damage_map, error_map=errors[damage] if not is_dut else dut_error_map, map_centers_x=centers_x, map_centers_y=centers_y, contour=not is_dut, damage=damage, server=server, dut=is_dut)
132+
fig, _ = plotting.plot_damage_error_3d(damage_map=damage_map, error_map=errors[damage] if not is_dut else dut_error_map, map_centers_x=centers_x, map_centers_y=centers_y, contour=not is_dut, damage=damage, ion_name=ion_name, server=server, dut=is_dut)
130133
figs.append(fig)
131134

132-
fig, _ = plotting.plot_damage_map_2d(damage_map=damage_map, map_centers_x=centers_x, map_centers_y=centers_y, damage=damage, server=server, dut=is_dut)
135+
fig, _ = plotting.plot_damage_map_2d(damage_map=damage_map, map_centers_x=centers_x, map_centers_y=centers_y, damage=damage, ion_name=ion_name, server=server, dut=is_dut)
133136
figs.append(fig)
134137

135-
fig, _ = plotting.plot_damage_map_contourf(damage_map=damage_map, map_centers_x=centers_x, map_centers_y=centers_y, damage=damage, server=server, dut=is_dut)
138+
fig, _ = plotting.plot_damage_map_contourf(damage_map=damage_map, map_centers_x=centers_x, map_centers_y=centers_y, damage=damage, ion_name=ion_name, server=server, dut=is_dut)
136139
figs.append(fig)
137140

138141
logging.info("Finished plotting.")

Diff for: irrad_control/analysis/dtype.py

+11-11
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@
3737
('row_stop_y', '<f4'), # # y component of the stopping position of currently-scanned row [mm]
3838
('row_mean_beam_current', '<f4'), # Mean of the beam current during scanning current row [A]
3939
('row_mean_beam_current_error', '<f4'), # Error of the beam current; quadratic addition of std of beam current and measurement error [A]
40-
('row_proton_fluence', '<f8'), # The proton fluence during scanning current row [protons/cm^2]
41-
('row_proton_fluence_error', '<f8'), # Error of the proton fluence during scanning current row [protons/cm^2]
40+
('row_primary_fluence', '<f8'), # The ion fluence during scanning current row [ions/cm^2]
41+
('row_primary_fluence_error', '<f8'), # Error of the ion fluence during scanning current row [ions/cm^2]
4242
('row_tid', '<f4'), # The TID during scanning current row [Mrad]
4343
('row_tid_error', '<f4'), # Error of the tid [Mrad]
4444
('row_scan_speed', '<f4'), # Speed with which the sample is scanned [mm/s]
@@ -48,8 +48,8 @@
4848
_irrad_dtype = [('timestamp', '<f8'), # Posix-timestamp of init [s]
4949
('row_separation', '<f4'), # Row separation e.g. step size of scan, spacing in between scanned rows [mm]
5050
('n_rows', '<i2'), # Number of total rows in scan
51-
('aim_damage', 'S4'), # Either NIEL or TID
52-
('aim_value', '<f4'), # Nominal value of damage to be induced, either in neq/cm^2 or Mrad
51+
('aim_damage', 'S8'), # Either neq, tid or primary
52+
('aim_value', '<f4'), # Nominal value of damage to be induced, either in neq/cm^2, Mrad or ions / cm^2
5353
('min_scan_current', '<f4'), # Minimum current for scanning [A]
5454
('scan_origin_x', '<f4'), # x component of the scan origin from where the rel. coord. system is constructed [mm]
5555
('scan_origin_y', '<f4'), # y component of the scan origin from where the rel. coord. system is constructed [mm]
@@ -61,18 +61,18 @@
6161
# Damage data dtype; contains NIEL and TID damage data on a per-scan basis
6262
_damage_dtype = [('timestamp', '<f8'), # Timestamp [s]
6363
('scan', '<i2'), # Number of *completed* scans,
64-
('scan_proton_fluence', '<f8'), # Proton fluence delivered in this scan [protons/cm^2]
65-
('scan_proton_fluence_error', '<f8'), # Error of proton fluence delivered in this scan [protons/cm^2]
64+
('scan_primary_fluence', '<f8'), # ion fluence delivered in this scan [ions/cm^2]
65+
('scan_primary_fluence_error', '<f8'), # Error of ion fluence delivered in this scan [ions/cm^2]
6666
('scan_tid', '<f8'), # Total-ionizing dose delivered in this scan [Mrad]
6767
('scan_tid_error', '<f8')] # Error of total-ionizing dose delivered in this scan [Mrad]
6868

6969

70-
# Result data type: contains proton as well as neutron fluence and scaling factor
70+
# Result data type: contains ion as well as neutron fluence and scaling factor
7171
_result_dtype = [('timestamp', '<f8'),
72-
('proton_fluence', '<f8'),
73-
('proton_fluence_error', '<f8'),
74-
('neutron_fluence', '<f8'),
75-
('neutron_fluence_error', '<f8'),
72+
('primary_fluence', '<f8'),
73+
('primary_fluence_error', '<f8'),
74+
('neq_fluence', '<f8'),
75+
('neq_fluence_error', '<f8'),
7676
('tid', '<f4'),
7777
('tid_error', '<f4')]
7878

Diff for: irrad_control/analysis/fluence.py

+17-17
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ def generate_fluence_map(beam_data, scan_data, beam_sigma, bins=(100, 100)):
8888
# Take sqrt of error map squared
8989
fluence_map_error = np.sqrt(fluence_map_error)
9090

91-
# Scale from protons / mm² (intrinsic unit) to protons / cm²
91+
# Scale from ions / mm² (intrinsic unit) to ions / cm²
9292
fluence_map *= 100
9393
fluence_map_error *= 100
9494

@@ -403,7 +403,7 @@ def _process_row_wait(row_data, wait_beam_data, fluence_map, fluence_map_error,
403403
wait_mu_y = row_data['row_start_y'] - scan_y_offset
404404

405405
# Add variation to the uncertainty
406-
wait_protons_std = np.std(wait_beam_data['beam_current'])
406+
wait_ions_std = np.std(wait_beam_data['beam_current'])
407407

408408
# Loop over currents and apply Gauss kernel at given position
409409
for i in range(wait_beam_data.shape[0] - 1):
@@ -415,16 +415,16 @@ def _process_row_wait(row_data, wait_beam_data, fluence_map, fluence_map_error,
415415
# Calculate how many seconds this current was present while waiting
416416
wait_interval = wait_beam_data[i+1]['timestamp'] - wait_beam_data[i]['timestamp']
417417

418-
# Integrate over *wait_interval* to obtain number of protons induced
419-
wait_protons = wait_current * wait_interval / elementary_charge
420-
wait_protons_error = wait_current_error * wait_interval / elementary_charge
421-
wait_protons_error = (wait_protons_error**2 + wait_protons_std**2)**.5
418+
# Integrate over *wait_interval* to obtain number of ions induced
419+
wait_ions = wait_current * wait_interval / elementary_charge
420+
wait_ions_error = wait_current_error * wait_interval / elementary_charge
421+
wait_ions_error = (wait_ions_error**2 + wait_ions_std**2)**.5
422422

423-
# Apply Gaussian kernel for protons
423+
# Apply Gaussian kernel for ions
424424
apply_gauss_2d_kernel(map_2d=fluence_map,
425425
map_2d_error=fluence_map_error,
426-
amplitude=wait_protons,
427-
amplitude_error=wait_protons_error,
426+
amplitude=wait_ions,
427+
amplitude_error=wait_ions_error,
428428
bin_centers_x=map_bin_centers_x,
429429
bin_centers_y=map_bin_centers_y,
430430
mu_x=wait_mu_x,
@@ -479,23 +479,23 @@ def _process_row_scan(row_data, row_beam_data, fluence_map, fluence_map_error, r
479479
row_bin_center_currents = np.interp(row_bin_center_timestamps, row_beam_data['timestamp'], row_beam_data['beam_current'])
480480
row_bin_center_current_errors = np.interp(row_bin_center_timestamps, row_beam_data['timestamp'], row_beam_data['beam_current_error'])
481481

482-
# Integrate the current measurements with the times spent in each bin to calculate the amount of protons in the bin
483-
row_bin_center_protons = (row_bin_center_currents * row_bin_transit_times) / elementary_charge
484-
row_bin_center_proton_errors = (row_bin_center_current_errors * row_bin_transit_times) / elementary_charge
485-
row_bin_center_proton_errors = (row_bin_center_proton_errors**2 + np.std(row_bin_center_protons)**2)**.5
482+
# Integrate the current measurements with the times spent in each bin to calculate the amount of ions in the bin
483+
row_bin_center_ions = (row_bin_center_currents * row_bin_transit_times) / elementary_charge
484+
row_bin_center_ion_errors = (row_bin_center_current_errors * row_bin_transit_times) / elementary_charge
485+
row_bin_center_ion_errors = (row_bin_center_ion_errors**2 + np.std(row_bin_center_ions)**2)**.5
486486

487487
# Loop over row times
488-
for i in range(row_bin_center_protons.shape[0]):
488+
for i in range(row_bin_center_ions.shape[0]):
489489

490490
# Update mean location of the distribution
491491
mu_x = map_bin_centers_x[(-(i+1) if row_data['row'] % 2 else i)]
492492
mu_y = row_data['row_start_y'] - scan_y_offset
493493

494-
# Apply Gaussian kernel for protons
494+
# Apply Gaussian kernel for ions
495495
apply_gauss_2d_kernel(map_2d=fluence_map,
496496
map_2d_error=fluence_map_error,
497-
amplitude=row_bin_center_protons[i],
498-
amplitude_error=row_bin_center_proton_errors[i],
497+
amplitude=row_bin_center_ions[i],
498+
amplitude_error=row_bin_center_ion_errors[i],
499499
bin_centers_x=map_bin_centers_x,
500500
bin_centers_y=map_bin_centers_y,
501501
mu_x=mu_x,

0 commit comments

Comments
 (0)