Skip to content

Commit 3862eff

Browse files
authored
Merge pull request #40 from pluflou/multiwire_scan
Multiwire scan feature -- base.
2 parents a193a5c + 6e1e723 commit 3862eff

27 files changed

+1649
-103
lines changed
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
name: "Run TODO to Issue"
2+
on: ["push"]
3+
jobs:
4+
build:
5+
runs-on: "ubuntu-latest"
6+
steps:
7+
- uses: "actions/checkout@v3"
8+
- name: "TODO to Issue"
9+
uses: "alstr/todo-to-issue-action@v4"

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,6 @@ Or install by running:
1313

1414
pip install pyemittance
1515

16+
## Setting the correct configuration parameters for the machine/simulation
17+
18+
For examples on how to set the correct configuration and how to use the new API, please see [`examples/tutorial.ipynb`](https://github.com/pluflou/PyEmittance/blob/main/docs/examples/tutorial.ipynb).

docs/examples/multiwire_scan.ipynb

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "code",
5+
"execution_count": 1,
6+
"id": "d42f27c2-676d-44af-a548-9531666931a3",
7+
"metadata": {},
8+
"outputs": [],
9+
"source": [
10+
"from pyemittance import PyEmittance\n",
11+
"import numpy as np"
12+
]
13+
},
14+
{
15+
"cell_type": "code",
16+
"execution_count": 2,
17+
"id": "879b56fa-2759-4f44-9227-349f88fd23aa",
18+
"metadata": {},
19+
"outputs": [],
20+
"source": [
21+
"meas = PyEmittance(config_name=\"LCLS_WS02\")"
22+
]
23+
},
24+
{
25+
"cell_type": "code",
26+
"execution_count": 3,
27+
"id": "dc8046f2-b673-41d6-aae5-cdc11315a44b",
28+
"metadata": {},
29+
"outputs": [],
30+
"source": [
31+
"meas.emit_calc_type = 'multiwire'"
32+
]
33+
},
34+
{
35+
"cell_type": "code",
36+
"execution_count": 4,
37+
"id": "d5b19318-7d3c-43bd-b2d4-2af9ac430f53",
38+
"metadata": {},
39+
"outputs": [],
40+
"source": [
41+
"meas.plot = True\n",
42+
"meas.save_runs = True"
43+
]
44+
},
45+
{
46+
"cell_type": "code",
47+
"execution_count": 5,
48+
"id": "003d34ba-aa3b-4614-9b0e-952d0fa90725",
49+
"metadata": {},
50+
"outputs": [
51+
{
52+
"name": "stdout",
53+
"output_type": "stream",
54+
"text": [
55+
"Running offline.\n",
56+
"Savepaths not set. Please set them in 'configs/savepaths.json'\n",
57+
"Using docs examples directory: /Users/smiskov/Documents/SLAC/PyEmittance/docs/examples\n"
58+
]
59+
}
60+
],
61+
"source": [
62+
"meas.measure_emittance()"
63+
]
64+
}
65+
],
66+
"metadata": {
67+
"kernelspec": {
68+
"display_name": "Python 3",
69+
"language": "python",
70+
"name": "python3"
71+
},
72+
"language_info": {
73+
"codemirror_mode": {
74+
"name": "ipython",
75+
"version": 3
76+
},
77+
"file_extension": ".py",
78+
"mimetype": "text/x-python",
79+
"name": "python",
80+
"nbconvert_exporter": "python",
81+
"pygments_lexer": "ipython3",
82+
"version": "3.7.11"
83+
}
84+
},
85+
"nbformat": 4,
86+
"nbformat_minor": 5
87+
}

docs/examples/quad_scan_tutorial.ipynb

Lines changed: 484 additions & 0 deletions
Large diffs are not rendered by default.

docs/examples/tutorial.ipynb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -773,7 +773,7 @@
773773
"name": "python",
774774
"nbconvert_exporter": "python",
775775
"pygments_lexer": "ipython3",
776-
"version": "3.8.13"
776+
"version": "3.7.11"
777777
},
778778
"vscode": {
779779
"interpreter": {
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
timestamp,varx_cur,vary_cur,varz_cur,bact_cur,xrms,yrms,xrms_err,yrms_err
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
timestamp,ncol,nrow,roi_xmin,roi_xmax,roi_ymin,roi_ymax,resolution,bact,x_size,y_size,xrms,yrms,xrms_err,yrms_err]

pyemittance/__init__.py

Lines changed: 239 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,243 @@
11
from . import _version
22
__version__ = _version.get_versions()['version']
33

4-
from pyemittance.pyemittance import PyEmittance
4+
from pyemittance.observer import Observer
5+
from pyemittance.data_handler import adapt_range, check_symmetry, find_inflection_pnt, add_measurements_btwn_pnts
6+
from pyemittance.emittance_calc import EmitCalc
7+
from pyemittance.emittance_calc_multiwire import MultiWireCalc
8+
from pyemittance.load_json_configs import load_configs
59

10+
# For setting quad back after scan is done
11+
from pyemittance.beam_io import MachineIO
12+
13+
# TODO: set up unit testing for PyEmittance, EmitCalc, MultiWireCalc, Observer
14+
class PyEmittance:
15+
16+
def __init__(self,
17+
emit_calc_type='quadscan', # 'quadscan' or 'multiwire'
18+
config_name='LCLS_OTR2', # make sure config corresponds to calc type
19+
config_dict=None, # supersedes json configs
20+
meas_type='OTRS', # only relevant if emit_calc_type=='quadscan'
21+
use_model=False,
22+
online=False
23+
):
24+
25+
self.emit_calc_type = emit_calc_type
26+
self.meas_type = meas_type
27+
28+
# if config is not provided, use LCLS-OTR2 as default
29+
self.config_name = config_name
30+
self.config_dict = config_dict if config_dict else load_configs(self.config_name)
31+
32+
# if running on machine, use_model=False
33+
self.use_model = use_model
34+
# only True if setting PVs
35+
self.online = online
36+
self.verbose = True
37+
38+
# injector settings (SOL, CQ, SQ) if optimizing
39+
# TODO: remove injector settings options from all modules (optimizer needs to be separate)
40+
self.inj_config = None
41+
if self.emit_calc_type == 'quadscan':
42+
# initial rough quad scan
43+
self.quad_init = [-6, -4, -2, 0]
44+
45+
# pyemittance method options
46+
self.save_runs = False
47+
self.calc_bmag = False
48+
self.show_plots = True
49+
if self.emit_calc_type == 'quadscan':
50+
self.adapt_ranges = True
51+
self.num_points = 7
52+
self.check_sym = True
53+
self.infl_check = True
54+
self.add_pnts = True
55+
self.use_prev_meas = True
56+
self.quad_tol = 0.05
57+
58+
# simulation/model options
59+
# beamsize function from model
60+
self.get_bs_model = None
61+
self.add_noise = False
62+
63+
# to save total number of points queried
64+
self.return_num_points = False
65+
66+
def measure_emittance(self):
67+
if self.emit_calc_type == 'quadscan':
68+
self.measure_emittance_quad_scan()
69+
elif self.emit_calc_type == 'multiwire':
70+
self.measure_emittance_multiwire()
71+
else:
72+
raise Exception("Cannot perform measurement. 'emit_calc_type' needs to be 'quadscan' or 'multiwire'.")
73+
74+
def measure_emittance_quad_scan(self):
75+
# get initial points from the observer
76+
o = Observer([], {'x': [], 'y': []}, {'x': [], 'y': []})
77+
o.use_model = self.use_model
78+
o.inj_config = self.inj_config
79+
o.online = self.online
80+
o.meas_type = self.meas_type
81+
o.use_prev_meas = self.use_prev_meas
82+
o.tolerance = self.quad_tol
83+
o.config_name = self.config_name
84+
o.config_dict = self.config_dict
85+
86+
# print warning
87+
if self.online and self.verbose:
88+
print("Running online!")
89+
else:
90+
print("Running offline.")
91+
92+
# if using sim
93+
# set beamsize fn
94+
o.get_beamsizes_model = self.get_bs_model
95+
o.add_noise = self.add_noise
96+
97+
energy = o.config_dict['beamline_info']['energy']
98+
l_quad = o.config_dict['beamline_info']['l']
99+
100+
# Save init quad value
101+
io = MachineIO(self.config_name, self.config_dict, self.meas_type)
102+
io.online = self.online
103+
quad_init = io.getquad()
104+
105+
# Get initial beamsizes (rough scan)
106+
bs_x_list, bs_y_list, bs_x_list_err, bs_y_list_err = o.measure_beam(self.quad_init)
107+
108+
quad_range_x = self.quad_init
109+
quad_range_y = self.quad_init
110+
111+
if self.adapt_ranges:
112+
quad_range_x = adapt_range(quad_range_x,
113+
bs_x_list,
114+
'x',
115+
w=bs_x_list_err,
116+
energy=energy,
117+
l_eff=l_quad,
118+
num_points=self.num_points
119+
)
120+
quad_range_y = adapt_range(quad_range_y,
121+
bs_y_list,
122+
'y',
123+
w=bs_y_list_err,
124+
energy=energy,
125+
l_eff=l_quad,
126+
num_points=self.num_points
127+
)
128+
129+
new_beamsize_x = o.measure_beam(quad_range_x)
130+
bs_x_list, bs_x_list_err = new_beamsize_x[0], new_beamsize_x[2]
131+
new_beamsize_y = o.measure_beam(quad_range_y)
132+
bs_y_list, bs_y_list_err = new_beamsize_y[1], new_beamsize_y[3]
133+
134+
if self.check_sym:
135+
add_points_x = check_symmetry(quad_range_x, bs_x_list, bs_x_list_err, 'x',
136+
bs_fn=o.measure_beam, add_meas=True)
137+
add_points_y = check_symmetry(quad_range_y, bs_y_list, bs_y_list_err, 'y',
138+
bs_fn=o.measure_beam, add_meas=True)
139+
140+
if add_points_x is not None:
141+
quad_range_x = add_points_x[0]
142+
bs_x_list = add_points_x[1]
143+
bs_x_list_err = add_points_x[2]
144+
145+
if add_points_y is not None:
146+
quad_range_y = add_points_y[0]
147+
bs_y_list = add_points_y[1]
148+
bs_y_list_err = add_points_y[2]
149+
150+
if self.infl_check:
151+
left_x, right_x = find_inflection_pnt(quad_range_x,
152+
bs_x_list,
153+
show_plots=self.show_plots
154+
)
155+
left_y, right_y = find_inflection_pnt(quad_range_y,
156+
bs_y_list,
157+
show_plots=self.show_plots
158+
)
159+
160+
# truncate data
161+
quad_range_x = quad_range_x[left_x:right_x]
162+
bs_x_list = bs_x_list[left_x:right_x]
163+
bs_x_list_err = bs_x_list_err[left_x:right_x]
164+
165+
quad_range_y = quad_range_y[left_y:right_y]
166+
bs_y_list = bs_y_list[left_y:right_y]
167+
bs_y_list_err = bs_y_list_err[left_y:right_y]
168+
169+
if self.add_pnts:
170+
quad_range_x, bs_x_list, bs_x_list_err = add_measurements_btwn_pnts(quad_range_x,
171+
bs_x_list,
172+
bs_x_list_err,
173+
self.num_points,
174+
'x',
175+
bs_fn=o.measure_beam
176+
)
177+
quad_range_y, bs_y_list, bs_y_list_err = add_measurements_btwn_pnts(quad_range_y,
178+
bs_y_list,
179+
bs_y_list_err,
180+
self.num_points,
181+
'y',
182+
bs_fn=o.measure_beam
183+
)
184+
185+
# Put quad back
186+
io.online = self.online
187+
io.setquad(quad_init)
188+
189+
# Finally get emittance
190+
ef = EmitCalc({'x': quad_range_x, 'y': quad_range_y},
191+
{'x': bs_x_list, 'y': bs_y_list},
192+
{'x': bs_x_list_err, 'y': bs_y_list_err},
193+
config_dict=o.config_dict,
194+
config_name=o.config_name
195+
)
196+
ef.plot = self.show_plots
197+
ef.save_runs = self.save_runs
198+
ef.calc_bmag = self.calc_bmag
199+
200+
# get normalized transverse emittance
201+
ef.get_emit()
202+
203+
# save total number of points queried
204+
if self.return_num_points:
205+
ef.out_dict["total_points_measured"] = len(o.quad_meas)
206+
207+
return ef.out_dict
208+
209+
def measure_emittance_multiwire(self):
210+
# get wire measurements from the observer
211+
o = Observer()
212+
o.inj_config = self.inj_config
213+
o.online = self.online
214+
o.meas_type = 'WIRE'
215+
o.emit_calc_type = self.emit_calc_type
216+
o.config_name = self.config_name
217+
o.config_dict = self.config_dict
218+
219+
# print warning
220+
if self.online and self.verbose:
221+
print("Running online!")
222+
else:
223+
print("Running offline.")
224+
225+
# Get beamsizes
226+
# This returns lists now: xrms, yrms, xrms_err, yrms_err
227+
# TODO: implement error dict to be returned to track multiwire success
228+
bs_x_list, bs_y_list, bs_x_list_err, bs_y_list_err = o.multiwire_measure_beam()
229+
230+
# Finally get emittance
231+
ef = MultiWireCalc(beam_vals={'x': bs_x_list, 'y': bs_y_list},
232+
beam_vals_err={'x': bs_x_list_err, 'y': bs_y_list_err},
233+
config_dict=o.config_dict,
234+
config_name=o.config_name
235+
)
236+
ef.plot = self.show_plots
237+
ef.save_runs = self.save_runs
238+
ef.calc_bmag = self.calc_bmag
239+
240+
# get normalized transverse emittance
241+
ef.get_emit()
242+
243+
return ef.out_dict

0 commit comments

Comments
 (0)