Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
T
ToolBox
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Model registry
Operate
Environments
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
SCS
ToolBox
Compare revisions
3911af0c2648b3b90de050b1402ba371beb353fe to 87b28ade8fb3d1be31a69a9928397b084b2743f5
Compare revisions
Changes are shown as if the
source
revision was being merged into the
target
revision.
Learn more about comparing revisions.
Source
SCS/ToolBox
Select target project
No results found
87b28ade8fb3d1be31a69a9928397b084b2743f5
Select Git revision
Swap
Target
SCS/ToolBox
Select target project
SCS/ToolBox
kluyvert/ToolBox
2 results
3911af0c2648b3b90de050b1402ba371beb353fe
Select Git revision
Show changes
Only incoming changes from source
Include changes to target since source was created
Compare
Commits on Source (2)
Use knife edge base in routine function
· b193c40e
Laurent Mercadier
authored
3 years ago
b193c40e
Merge branch 'use-knife-edge-base' into 'knife-edge-base'
· 87b28ade
Laurent Mercadier
authored
3 years ago
Use knife edge base in routine function See merge request
!114
87b28ade
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
src/toolbox_scs/base/knife_edge.py
+13
-7
13 additions, 7 deletions
src/toolbox_scs/base/knife_edge.py
src/toolbox_scs/routines/knife_edge.py
+48
-92
48 additions, 92 deletions
src/toolbox_scs/routines/knife_edge.py
with
61 additions
and
99 deletions
src/toolbox_scs/base/knife_edge.py
View file @
87b28ade
...
...
@@ -107,13 +107,7 @@ def prepare_arrays(arrX: np.ndarray, arrY: np.ndarray,
3. Retrieve finite values.
"""
# Convert both arrays to 1D of the same size
assert
arrX
.
shape
[
0
]
==
arrY
.
shape
[
0
]
arrX
=
arrX
.
flatten
()
arrY
=
arrY
.
flatten
()
if
len
(
arrX
)
>
len
(
arrY
):
arrY
=
np
.
repeat
(
arrY
,
len
(
arrX
)
//
len
(
arrY
))
else
:
arrX
=
np
.
repeat
(
arrX
,
len
(
arrY
)
//
len
(
arrX
))
arrX
,
arrY
=
arrays_to1d
(
arrX
,
arrY
)
# Select ranges
if
xRange
is
not
None
:
...
...
@@ -133,6 +127,18 @@ def prepare_arrays(arrX: np.ndarray, arrY: np.ndarray,
return
arrX
,
arrY
def
arrays_to1d
(
arrX
:
np
.
ndarray
,
arrY
:
np
.
ndarray
):
"""
Flatten two arrays and matches their sizes
"""
assert
arrX
.
shape
[
0
]
==
arrY
.
shape
[
0
]
arrX
,
arrY
=
arrX
.
flatten
(),
arrY
.
flatten
()
if
len
(
arrX
)
>
len
(
arrY
):
arrY
=
np
.
repeat
(
arrY
,
len
(
arrX
)
//
len
(
arrY
))
else
:
arrX
=
np
.
repeat
(
arrX
,
len
(
arrY
)
//
len
(
arrX
))
return
arrX
,
arrY
def
range_mask
(
array
,
minimum
=
None
,
maximum
=
None
):
"""
Retrieve the resulting array from the given minimum and maximum
"""
...
...
This diff is collapsed.
Click to expand it.
src/toolbox_scs/routines/knife_edge.py
View file @
87b28ade
"""
Toolbox for SCS.
Various utilities function to quickly process data measured
at the SCS instrument
s
.
at the SCS instrument.
Copyright (2019-) SCS Team.
"""
import
matplotlib.pyplot
as
plt
import
numpy
as
np
from
scipy.special
import
erfc
from
scipy.optimize
import
curve_fit
import
bisect
from
toolbox_scs.base.knife_edge
import
knife_edge_base
,
erfc
,
arrays_to1d
__all__
=
[
'
knife_edge
'
]
def
knife_edge
(
ds
,
axisKey
=
'
scannerX
'
,
signalKey
=
'
FastADC4peaks
'
,
axisRange
=
[
None
,
None
],
p0
=
None
,
full
=
False
,
plot
=
False
):
def
knife_edge
(
ds
,
axisKey
=
'
scannerX
'
,
signalKey
=
'
FastADC4peaks
'
,
axisRange
=
[
None
,
None
],
p0
=
None
,
full
=
False
,
plot
=
False
,
display
=
False
):
"""
Calculates the beam radius at 1/e^2 from a knife-edge scan by
fitting with erfc function:
f(a,b,u) = a*erfc(u) + b or
f(
a,b,u) = a*erfc(-u) + b where u =
sqrt(2)*(x-x0)/w0
with w0
the beam radius at 1/e^2 and x0 the beam center.
fitting with erfc function:
f(
x, x0, w0, a, b) = a*erfc(np.
sqrt(2)*(x-x0)/w0
) + b
with w0
the beam radius at 1/e^2 and x0 the beam center.
Parameters
----------
...
...
@@ -38,103 +35,62 @@ def knife_edge(ds, axisKey='scannerX',
edges of the scanning axis between which to apply the fit.
p0: list of floats, numpy 1D array
initial parameters used for the fit: x0, w0, a, b. If None, a beam
radius of 100
u
m is assumed.
radius of 100 m
icrometers
is assumed.
full: bool
If False, returns the beam radius and standard error.
If True, returns the popt, pcov list of parameters and covariance
matrix from scipy.optimize.curve_fit
as well as the fitting function
.
matrix from scipy.optimize.curve_fit.
plot: bool
If True, plots the data and the result of the fit.
If True, plots the data and the result of the fit. Default is False.
display: bool
If True, displays info on the fit. True when plot is True, default is
False.
Returns
-------
If full is False,
ndarray
with beam radius at 1/e^2 in mm and standard
If full is False,
tuple
with beam radius at 1/e^2 in mm and standard
error from the fit in mm. If full is True, returns parameters and
covariance matrix from scipy.optimize.curve_fit function.
"""
def
stepUp
(
x
,
x0
,
w0
,
a
,
b
):
return
a
*
erfc
(
-
np
.
sqrt
(
2
)
*
(
x
-
x0
)
/
w0
)
+
b
def
stepDown
(
x
,
x0
,
w0
,
a
,
b
):
return
a
*
erfc
(
np
.
sqrt
(
2
)
*
(
x
-
x0
)
/
w0
)
+
b
# get the number of pulses per train from the signal source:
dim
=
[
k
for
k
in
ds
[
signalKey
].
dims
if
k
!=
'
trainId
'
][
0
]
# duplicate motor position values to match signal shape
# this is faster than using ds.stack()
positions
=
np
.
repeat
(
ds
[
axisKey
].
values
,
len
(
ds
[
dim
])).
astype
(
ds
[
signalKey
].
dtype
)
# sort the data to decide which fitting function to use
sortIdx
=
np
.
argsort
(
positions
)
positions
=
positions
[
sortIdx
]
intensities
=
ds
[
signalKey
].
values
.
flatten
()[
sortIdx
]
if
axisRange
[
0
]
is
None
or
axisRange
[
0
]
<
positions
[
0
]:
idxMin
=
0
else
:
if
axisRange
[
0
]
>=
positions
[
-
1
]:
raise
ValueError
(
'
The minimum value of axisRange is too large
'
)
idxMin
=
bisect
.
bisect
(
positions
,
axisRange
[
0
])
if
axisRange
[
1
]
is
None
or
axisRange
[
1
]
>
positions
[
-
1
]:
idxMax
=
None
else
:
if
axisRange
[
1
]
<=
positions
[
0
]:
raise
ValueError
(
'
The maximum value of axisRange is too small
'
)
idxMax
=
bisect
.
bisect
(
positions
,
axisRange
[
1
])
+
1
pos_sel
=
positions
[
idxMin
:
idxMax
]
int_sel
=
intensities
[
idxMin
:
idxMax
]
no_nan
=
~
np
.
isnan
(
int_sel
)
pos_sel
=
pos_sel
[
no_nan
]
int_sel
=
int_sel
[
no_nan
]
popt
,
pcov
=
knife_edge_base
(
ds
[
axisKey
].
values
,
ds
[
signalKey
].
values
,
axisRange
=
axisRange
,
p0
=
p0
)
if
plot
:
positions
,
intensities
=
arrays_to1d
(
ds
[
axisKey
].
values
,
ds
[
signalKey
].
values
)
plot_knife_edge
(
positions
,
intensities
,
popt
,
pcov
[
1
,
1
]
**
0.5
,
ds
.
attrs
[
'
runFolder
'
],
axisKey
,
signalKey
)
display
=
True
# estimate a linear slope fitting the data to determine which function
# to fit
slope
=
np
.
cov
(
pos_sel
,
int_sel
)[
0
][
1
]
/
np
.
var
(
pos_sel
)
if
slope
<
0
:
func
=
stepDown
if
display
:
funcStr
=
'
a*erfc(np.sqrt(2)*(x-x0)/w0) + b
'
else
:
func
=
stepUp
funcStr
=
'
a*erfc(-np.sqrt(2)*(x-x0)/w0) + b
'
if
p0
is
None
:
p0
=
[
np
.
mean
(
pos_sel
),
0.1
,
np
.
max
(
int_sel
)
/
2
,
0
]
try
:
popt
,
pcov
=
curve_fit
(
func
,
pos_sel
,
int_sel
,
p0
=
p0
)
print
(
'
fitting function:
'
,
funcStr
)
print
(
'
w0 = (%.1f +/- %.1f) um
'
%
(
popt
[
1
]
*
1e3
,
pcov
[
1
,
1
]
**
0.5
*
1e3
))
print
(
'
w0 = (%.1f +/- %.1f) um
'
%
(
np
.
abs
(
popt
[
1
])
*
1e3
,
pcov
[
1
,
1
]
**
0.5
*
1e3
))
print
(
'
x0 = (%.3f +/- %.3f) mm
'
%
(
popt
[
0
],
pcov
[
0
,
0
]
**
0.5
))
print
(
'
a = %e +/- %e
'
%
(
popt
[
2
],
pcov
[
2
,
2
]
**
0.5
))
print
(
'
b = %e +/- %e
'
%
(
popt
[
3
],
pcov
[
3
,
3
]
**
0.5
))
fitSuccess
=
True
except
Exception
as
e
:
print
(
f
'
Could not fit the data with erfc function:
{
e
}
.
'
+
'
Try adjusting the axisRange and the initial parameters p0
'
)
fitSuccess
=
False
if
plot
:
plt
.
figure
(
figsize
=
(
7
,
4
))
plt
.
scatter
(
positions
,
intensities
,
color
=
'
C1
'
,
label
=
'
exp
'
,
s
=
2
,
alpha
=
0.1
)
if
fitSuccess
:
xfit
=
np
.
linspace
(
positions
.
min
(),
positions
.
max
(),
1000
)
yfit
=
func
(
xfit
,
*
popt
)
plt
.
plot
(
xfit
,
yfit
,
color
=
'
C4
'
,
label
=
r
'
fit $\rightarrow$ $w_0=$(%.1f $\pm$ %.1f) $\mu$m
'
%
(
popt
[
1
]
*
1e3
,
pcov
[
1
,
1
]
**
0.5
*
1e3
))
leg
=
plt
.
legend
()
for
lh
in
leg
.
legendHandles
:
lh
.
set_alpha
(
1
)
plt
.
ylabel
(
signalKey
)
plt
.
xlabel
(
axisKey
+
'
position [mm]
'
)
plt
.
title
(
ds
.
attrs
[
'
runFolder
'
])
plt
.
tight_layout
()
if
full
:
if
fitSuccess
:
return
popt
,
pcov
,
func
else
:
return
np
.
zeros
(
4
),
np
.
zeros
(
2
),
None
return
popt
,
pcov
else
:
if
fitSuccess
:
return
np
.
array
([
popt
[
1
],
pcov
[
1
,
1
]
**
0.5
])
else
:
return
np
.
zeros
(
2
)
return
np
.
abs
(
popt
[
1
]),
pcov
[
1
,
1
]
**
0.5
def
plot_knife_edge
(
positions
,
intensities
,
fit_params
,
rel_err
,
title
,
axisKey
,
signalKey
):
plt
.
figure
(
figsize
=
(
7
,
4
))
plt
.
scatter
(
positions
,
intensities
,
color
=
'
C1
'
,
label
=
'
measured
'
,
s
=
2
,
alpha
=
0.1
)
xfit
=
np
.
linspace
(
positions
.
min
(),
positions
.
max
(),
1000
)
yfit
=
erfc
(
xfit
,
*
fit_params
)
plt
.
plot
(
xfit
,
yfit
,
color
=
'
C4
'
,
label
=
r
'
fit $\rightarrow$ $w_0=$(%.1f $\pm$ %.1f) $\mu$m
'
%
(
np
.
abs
(
fit_params
[
1
])
*
1e3
,
rel_err
*
1e3
))
leg
=
plt
.
legend
()
for
lh
in
leg
.
legendHandles
:
lh
.
set_alpha
(
1
)
plt
.
ylabel
(
signalKey
)
plt
.
xlabel
(
axisKey
+
'
position [mm]
'
)
plt
.
title
(
title
)
plt
.
tight_layout
()
This diff is collapsed.
Click to expand it.