Project

General

Profile

Task #1153 » target_bpm.py

Hall C target beam position python script - Sanghwa Park, 01/16/2026 01:06 PM

 
1
#!/usr/bin/python3
2
import sys,os
3
import gc
4
import math
5
import time
6
import numpy as np
7
#import pylab as py
8
import matplotlib.pyplot as plt
9
from scipy.odr import *
10
from subprocess import call
11
from subprocess import check_output
12

    
13
# Nominal beam position - not corrected for offsets
14
# these were for XEM2/CaFe part 1
15
#NOMINAL_POS = {
16
#'3H07A': {'XPOS': -0.09,
17
#          'YPOS': -0.30},
18
#'3H07B': {'XPOS': -0.49,
19
#          'YPOS': -0.41},
20
#'3H07C': {'XPOS': -0.68,
21
#          'YPOS': -0.49},
22
#'target': {'XPOS': -0.38,
23
#           'YPOS': -0.18}
24
#}
25

    
26
#updated feb. 22,2023 for CaFe part 2, deuteron
27
#NOMINAL_POS = {
28
#'3H07A': {'XPOS': 0.8,
29
#          'YPOS': -0.6},
30
#'3H07B': {'XPOS': 0.27,
31
#          'YPOS': -0.65},
32
#'3H07C': {'XPOS': 0.2,
33
#          'YPOS': -0.8},
34
#'target': {'XPOS': 0.5,
35
#           'YPOS': -0.45}
36
#}
37

    
38
#updated sept. 15,2023 for NPS
39
#NOMINAL_POS = {
40
#'3H07A': {'XPOS': 1.7,
41
#          'YPOS': 0.3},
42
#'3H07B': {'XPOS': 0.82,
43
#          'YPOS': -0.02},
44
#'3H07C': {'XPOS': 0.7,
45
#          'YPOS': 0.3},
46
#'target': {'XPOS': 1.34,
47
#           'YPOS': 0.04}
48
#}
49

    
50
#updated Feb. 21, 2024 after 3H07B work on Feb. 20
51
#NOMINAL_POS = {
52
#'3H07A': {'XPOS': 1.7,
53
#          'YPOS': 0.3},
54
#'3H07B': {'XPOS': 0.62,
55
#          'YPOS': 0.35},
56
#'3H07C': {'XPOS': 0.7,
57
#          'YPOS': 0.3},
58
#'target': {'XPOS': 1.27,
59
#           'YPOS': 0.06}
60
#}
61

    
62
#updated April 3, 2024 after long down - target moved a little
63
#NOMINAL_POS = {
64
#'3H07A': {'XPOS': 1.85,
65
#          'YPOS': 0.3},
66
#'3H07B': {'XPOS': 0.77,
67
#          'YPOS': 0.35},
68
#'3H07C': {'XPOS': 0.75,
69
#          'YPOS': 0.3},
70
#'target': {'XPOS': 1.27,
71
#           'YPOS': 0.06}
72
#}
73

    
74
#updated April 7, 2025
75
#NOMINAL_POS = {
76
#'3H07A': {'XPOS': 0,
77
#          'YPOS': 0},
78
#'3H07B': {'XPOS': -1.05,
79
#          'YPOS': 0.23},
80
#'3H07C': {'XPOS': -1.0,
81
#          'YPOS': 0.},
82
#'target': {'XPOS': -0.4,
83
#           'YPOS': 0.1}
84
#}
85

    
86
#updated April 10, 2025 Per JP 
87
NOMINAL_POS = {
88
'3H07A': {'XPOS': 0.25,
89
          'YPOS': 0.80},
90
'3H07B': {'XPOS': -0.47,
91
          'YPOS': 0.82},
92
'3H07C': {'XPOS': -0.7,
93
          'YPOS': 0.8},
94
'target': {'XPOS': -0.06,
95
           'YPOS': 0.29}
96
}
97

    
98
NOMINAL_POS_REF = {
99
'3H07A': {'XPOS':  0.0,
100
          'YPOS':  0.0},
101
'3H07B': {'XPOS':  0.0,
102
          'YPOS':  0.0},
103
'3H07C': {'XPOS':  0.0,
104
          'YPOS':  0.0},
105
'target': {'XPOS': 0.0,
106
           'YPOS': 0.0}
107
}
108

    
109
plt.ion()
110
fig=plt.figure()
111
# Aargh - used old, wrong value
112
#AZ=-370.83 
113
AZ=-320.42
114
BZ=-224.97
115
CZ=-129.314
116

    
117
ZVEC=np.array([AZ,BZ,CZ])
118

    
119
NOMINAL_ZVEC=np.array([AZ,BZ,CZ])
120
NOMINAL_XVEC=np.array([NOMINAL_POS['3H07A']['XPOS'],NOMINAL_POS['3H07B']['XPOS'],NOMINAL_POS['3H07C']['XPOS']])
121
NOMINAL_YVEC=np.array([NOMINAL_POS['3H07A']['YPOS'],NOMINAL_POS['3H07B']['YPOS'],NOMINAL_POS['3H07C']['YPOS']])
122

    
123
NOMINAL_XVEC_REF=np.array([NOMINAL_POS_REF['3H07A']['XPOS'],NOMINAL_POS_REF['3H07B']['XPOS'],NOMINAL_POS_REF['3H07C']['XPOS']])
124
NOMINAL_YVEC_REF=np.array([NOMINAL_POS_REF['3H07A']['YPOS'],NOMINAL_POS_REF['3H07B']['YPOS'],NOMINAL_POS_REF['3H07C']['YPOS']])
125

    
126
## Use slopes from BPM calibration 
127

    
128
## Slopes from DEC 2019 - keep same coordinate system as BPMs
129
#XSLOPE=np.array([0.973,1.096,0.956])
130
#YSLOPE=np.array([0.957,1.16,0.855])
131

    
132
# calibration, Sept.-Oct. 2023
133
XSLOPE=np.array([1.02,1.06,0.86])
134
YSLOPE=np.array([0.99,1.18,0.88])
135

    
136

    
137
#########################
138
# Target Lock Positions #
139
#########################
140
## offsets fom survey Summer 2021
141
#XOFF=np.array([0.05,-0.13,-0.16])
142
#YOFF=np.array([0.17,0.52,-0.17])
143
# from first harp scans, 9/5/2021 23:52
144
#XOFF=np.array([0.293,-0.189,-0.354])
145
#YOFF=np.array([-0.063,0.415,0.321])
146
# January 2022
147
#XOFF=np.array([0.07,-0.33,0.51])
148
#YOFF=np.array([-0.13,0.424,0.27])
149

    
150
#June 9, 2022
151
#Xoff=np.array([0.17,0.87,1.40])
152
#YOFF=np.array([-0.05,0.204,0.17])
153

    
154
#Harp survey corrected
155
#XOFF=np.array([0.08,0.39,0.54])
156
#YOFF=np.array([-0.05,0.204,0.17])
157
#XOFF=np.array([-0.17,0.21,0.37])
158
#YOFF=np.array([0.09,0.24,0.23])
159

    
160
# Offsets from harp scans, September 12, 2023
161
#XOFF=np.array([-0.18,0.51,0.73])
162
#YOFF=np.array([-0.13,0.17,-0.13])
163

    
164
# calibration, sept-oct 2023
165
#XOFF=np.array([-0.17,0.57,0.82])
166
#YOFF=np.array([-0.12,0.21,-0.16])
167

    
168
# Harp scan, Feb. 2024 - after BPM 3H07B fix
169
#XOFF=np.array([-0.17,0.78,0.82])
170
#YOFF=np.array([-0.12,-0.27,-0.16])
171

    
172
# Harp scan, April 3 2025 - needed to set BPM XSOFs back to old values
173
XOFF=np.array([-0.195,0.708,0.549])
174
YOFF=np.array([0.131,-0.091,0.089])
175

    
176
# Harp scan, July 15 2025 - needed to set BPM XSOFs back to old values
177
#XOFF=np.array([-0.066,0.756,0.937])
178
#YOFF=np.array([0.0885,-0.0322,-0.370])
179

    
180
# Harp scan, April 10 2025 - after IHA3H07B limit switch issue addressed
181
#XOFF=np.array([-0.196,1.12,1.45])
182
#YOFF=np.array([0.304,-0.553,-0.800])
183

    
184
# Harp scan, April 11 2025 - after harp check w/calipers
185
#XOFF=np.array([-0.180,0.858,0.915])
186
#YOFF=np.array([0.143,-0.320,-0.291])
187

    
188
# Harp scan, April 11 2025 - after harp check w/calipers
189
# test
190
#XOFF=np.array([-0.185,0.900,0.817])
191
#YOFF=np.array([0.146,-0.418,-0.255])
192

    
193

    
194
#raw variables June 9, 2022
195
#XOFF=np.array([0.114,0.919,1.617])
196
#YOFF=np.array([-0.318,-0.012,0.312])
197
# raw - w/corrected harp survey
198
#XOFF=np.array([0.012,0.445,0.543])
199
#YOFF=np.array([-0.318,-0.012,0.312])
200

    
201

    
202
# Jan 2022
203
#XOFF=np.array([0.07,-0.33,0.51])
204
#YOFF=np.array([-0.13,0.424,0.27])
205

    
206
#XSLOPE=np.array([-0.9843,-1.116,-0.9645])
207
#XOFF=np.array([-0.05355,0.08082,-0.893])
208

    
209
#YSLOPE=np.array([0.9687,1.166,0.8703])
210
#YOFF=np.array([-0.2027,0.3751,0.466])
211

    
212
# Fit fro DEC 2019 BPm vs HARP scan . In fit made the Harp X coordinate the same sign as the X epics
213
#XSLOPE=np.array([0.973,1.096,0.956])
214
#XOFF=np.array([-.076,-0.200,0.937])
215

    
216
#YSLOPE=np.array([0.957,1.16,0.855])
217
#YOFF=np.array([0.17,-0.06,-0.94])
218

    
219
# changes to offsets from most recent survey
220
#DY=np.array([0.25,0.22,0.20])
221
#DX=np.array([-0.02,-0.09,-0.24])
222

    
223
# survey numbers above were updated..
224
#DY=np.array([0.21,0.21,0.29])
225
#DX=np.array([0.06,0.05,-0.09])
226

    
227
# from comparison to harp scans 8/29
228
#DY=np.array([0.36,0.36,0.31])
229
#DX=np.array([0.21,0.12,-0.29])
230

    
231
# from survey, Feb. 2019 (note these are relative to 2017 survey offsets)
232
#DY=np.array([-0.05,-0.23,-0.46])
233
#DX=np.array([0.16,0.21,0.03])
234

    
235
# from HARP/BPM comparison, Feb. 9 2019 !!!!WRONG!!!
236
#DY=np.array([0.26,0.21,0.06])
237
#DX=np.array([0.32,0.38,0.09])
238

    
239
# from HARP/BPM comparison, Feb. 9 2019
240
#DY=np.array([0.38,-0.13,-0.76])
241
#DX=np.array([0.32,0.38,0.09])
242

    
243
# For Dec 2019 comparison did a BPM vs HARP scan so do not need offset
244

    
245
#additional, incremental corrections, not fit from full calibration
246
DY=np.array([0.,0.,0.])
247
DX=np.array([0.,0.,0.])
248

    
249

    
250
XOFF=XOFF+DX
251
YOFF=YOFF+DY
252

    
253
EX=np.array([0.1,0.1,0.1])
254
EY=np.array([0.1,0.1,0.1])
255

    
256
# fit line using both x and y errors
257
def f(B, x):
258
    '''Linear function y = m*x + b'''
259
    # B is a vector of the parameters.
260
    # x is an array of the current x values.
261
    # x is in the same format as the x passed to Data or RealData.
262
    #
263
    # Return an array in the same format as y passed to Data or RealData.
264
    return B[0]*x + B[1]
265

    
266

    
267
ax1 = fig.add_subplot(221)
268

    
269
ax2 = fig.add_subplot(223)
270
ax2.set_xlabel('Distance from target (cm)',fontsize=15)
271

    
272
ax3 = fig.add_subplot(122)
273

    
274
while True:
275
    time.sleep(0.5)
276
    #read in epics stuff
277
    ibcm=float(check_output("caget -w5 IBC3H00CRCUR4 | awk \ '{print $2}'", shell=True).strip())
278

    
279
    AXraw=float(check_output("caget -w5 IPM3H07A.XPOS | awk \ '{print $2}'", shell=True).strip())
280
    BXraw=float(check_output("caget -w5 IPM3H07B.XPOS | awk \ '{print $2}'", shell=True).strip())
281
    CXraw=float(check_output("caget -w5 IPM3H07C.XPOS | awk \ '{print $2}'", shell=True).strip())
282

    
283
    AYraw=float(check_output("caget -w5 IPM3H07A.YPOS | awk \ '{print $2}'", shell=True).strip())
284
    BYraw=float(check_output("caget -w5 IPM3H07B.YPOS | awk \ '{print $2}'", shell=True).strip())
285
    CYraw=float(check_output("caget -w5 IPM3H07C.YPOS | awk \ '{print $2}'", shell=True).strip())
286

    
287
    XRAW=np.array([AXraw,BXraw,CXraw])
288
    YRAW=np.array([AYraw,BYraw,CYraw])
289
# test
290
#    XRAW=np.array([0.03,-0.57,-0.97])
291
#    YRAW=np.array([-1.21,-1.26,-1.48])
292
# test
293
#    XRAW=np.array([0.8,0.27,0.2])
294
#    YRAW=np.array([-0.6,-0.65,-0.8])
295
#    XVEC=(XRAW*XSLOPE+XOFF)
296
#    YVEC=YRAW*YSLOPE+YOFF
297

    
298
    if ibcm > 0.1:
299
        XVEC=(XRAW*XSLOPE+XOFF)
300
        YVEC=YRAW*YSLOPE+YOFF
301
    else:
302
        XVEC=XRAW*0.0
303
        YVEC=YRAW*0.0
304

    
305
    # Show X Position
306
    # define 1st plot -x vs. z
307
    ax1.plot(NOMINAL_ZVEC, NOMINAL_XVEC, marker='s', color='black', markersize=8,
308
            fillstyle='none', linestyle='none')
309
    ax1.errorbar(ZVEC, XRAW, yerr=EX, fmt='o', color='blue', markersize=4)
310
    ax1.errorbar(ZVEC, XVEC, yerr=EX, fmt='o', color='red', markersize=4)
311
    linear = Model(f)
312
    mydata = RealData(ZVEC, XVEC, sy=EX)
313
    myodr = ODR(mydata, linear, beta0=[1., 2.])
314
    myoutput = myodr.run()
315
    #    myoutput.pprint()
316
    # get errors from covariance matrix - standard errors from ODR amplify by chi-squared
317
    xtar=round(myoutput.beta[1],2)
318

    
319
    # Plot the fit
320
    x_fit = np.linspace(-400, 50, 1000)
321
    y_fit = f(myoutput.beta, x_fit)
322
    ax1.plot(x_fit, y_fit, color='black')
323

    
324
    #plt.xlabel('Distance from target (cm)',fontsize=15)
325
    #ax1.text(-400,2,"X Pos @ tgt: "+str(xtar))
326
    ax1.set_ylim([-2,2])
327

    
328
    # Show Y Position
329
    ax2.plot(NOMINAL_ZVEC, NOMINAL_YVEC, marker='s', color='black', markersize=8,
330
            fillstyle='none', linestyle='none')
331
    yraw = ax2.errorbar(ZVEC, YRAW, yerr=EX, fmt='o', color='blue', markersize=4)
332
    ycor = ax2.errorbar(ZVEC, YVEC, yerr=EX, fmt='o', color='red', markersize=4)
333
    linear = Model(f)
334
    mydata = RealData(ZVEC, YVEC, sy=EX)
335
    myodr = ODR(mydata, linear, beta0=[1., 2.])
336
    myoutput = myodr.run()
337
    #    myoutput.pprint()
338
    # get errors from covariance matrix - standard errors from ODR amplify by chi-squared
339
    ytar=round(myoutput.beta[1],2)
340

    
341
    # Plot the fit
342
    x_fit = np.linspace(-400, 50, 1000)
343
    y_fit = f(myoutput.beta, x_fit)
344
    ax2.plot(x_fit, y_fit, color='black')
345

    
346
    #ax2.text(-400,2,"Y Pos @ tgt: "+str(ytar));
347
    ax2.set_ylim([-2,2])
348
    ax1.set_ylabel('X (mm)',fontsize=15)
349
    ax2.set_ylabel('Y (mm)',fontsize=15)
350
    ax2.set_xlabel('Z (cm)',fontsize=15)
351
    ax3.set_xlabel('Target X (mm)',fontsize=15)
352
    ax3.set_ylabel('Target Y (mm)',fontsize=15)
353

    
354
    # BPM monitoring-like Output
355
    cPos, =ax3.plot([NOMINAL_POS['target']['XPOS']], [NOMINAL_POS['target']['YPOS']], marker='s', color='black', markersize=8, fillstyle='none', linestyle='none')
356
    ax3.errorbar(xtar,ytar, yerr=0, fmt='o', color='red', markersize=4)
357
    ax3.yaxis.tick_right()
358
    ax3.yaxis.set_label_position('right')
359
    ax3.yaxis.grid(color='grey')
360
    ax3.xaxis.grid(color='grey')
361
    ax3.text(-3,1.2,"Beam position on target:\n"+8*" "+str(xtar)+" , "+str(ytar));
362
    ax3.set_xlim([-4,4])
363
    ax3.set_ylim([-2,2])
364
#    ax3.legend((yraw, ycor), ('Raw BPM', 'corrected'), loc='lower left')
365
    ax3.legend((yraw,ycor,cPos), ('Raw BPM position', 'True position','Nominal position'), loc='lower left')
366
#    print('xtar is', xtar, 'ytar', ytar)
367

    
368
    ax3.text(-11.5, 2.5,"Nominal 3H07A: ({}, {}), 3H07C: ({}, {})".format(
369
        NOMINAL_POS['3H07A']['XPOS'],
370
        NOMINAL_POS['3H07A']['YPOS'],
371
        NOMINAL_POS['3H07C']['XPOS'],
372
        NOMINAL_POS['3H07C']['YPOS']),
373
        size=10)
374

    
375
    fig.canvas.draw()
376
    fig.canvas.flush_events()
377
    plt.pause(1)
378
    ax1.clear()
379
    ax2.clear()
380
    ax3.clear()
381
    gc.collect()
(1-1/2)