Source code for torx.analysis.fit_eich_profile_m
"""Routines to fit Eich-type heat flux profiles."""
import warnings
import numpy as np
import matplotlib.pyplot as plt
import scipy.special
from scipy.optimize import curve_fit
from torx import Quantity
from torx.autodoc_decorators_m import autodoc_function
def erfc(x):
"""
Complimentary error function.
Handles quantity objects.
Only dimensionless quantities can be handled.
erfc(x) = 1 - erf(x)
"""
if isinstance(x, Quantity):
return scipy.special.erfc(x.to("").magnitude)
else:
return scipy.special.erfc(x)
[docs]
@autodoc_function
def eich_profile(s_bar, q0, lambda_q, S, q_BG):
"""
Fit an Eich profile to heat flux data.
Using upstream-mapped distance to remove the explicit flux-expansion factor.
See Eich Nuclear Fusion 2013. Using s_bar as the OMP-mapped distance
means that you should set 'f_x' = 1.
You can pass the parameters to the profile either all as dimensional
Quantity objects, or all as dimensionless raw arrays/floats.
s_bar: OMP-mapped radial distance [mm]
q0: peak heat flux [MW/m^2]
lambda_q: heat flux decay width [mm]
S: spreading factor [mm]
q_BG: background heat-flux [MW/m^2]
"""
with warnings.catch_warnings():
warnings.simplefilter("ignore", category=RuntimeWarning)
return (
q0
/ 2.0
* np.exp((S / (2.0 * lambda_q)) ** 2 - s_bar / lambda_q)
* erfc(S / (2.0 * lambda_q) - s_bar / S)
+ q_BG
)
[docs]
@autodoc_function
def fit_eich_profile(position, heat_flux, p0, plot=False):
"""
Use non-linear least-squares to fit an Eich profile to heat flux data.
Should pass
position: OMP-mapped radial distance [mm]
heat_flux: total heat flux [MW/m^2]
p0: initial guess off the parameters for the Eich profile
We use the Levenberg-Marquardt algorithm (default) for curve fitting.
Note that the algorithm takes into account the error if provided.
"""
heat_flux_loc = heat_flux * heat_flux.attrs["norm"].to("MW/m^2").magnitude
position_loc = position * position.attrs["norm"].to("mm").magnitude
# Transform p0 to the correct units
p0_loc = [
p0[0].to("MW/m^2").magnitude,
p0[1].to("mm").magnitude,
p0[2].to("mm").magnitude,
p0[3].to("MW/m^2").magnitude,
]
popt, pcov = curve_fit(
eich_profile,
xdata=position_loc,
ydata=heat_flux_loc,
p0=p0_loc,
method="lm",
xtol=1e-6,
)
q0, lambda_q, S, q_BG = popt
perr = np.sqrt(np.diag(pcov))
q0_err, lambda_q_err, S_err, q_BG_err = perr
if plot:
plt.plot(position_loc, heat_flux_loc, label="signal")
plt.plot(position, eich_profile(position_loc, *popt), label="eich")
plt.legend()
plt.title("Eich profile fitting")
return (
Quantity((q0, q0_err), "MW/m^2"),
Quantity((lambda_q, lambda_q_err), "mm"),
Quantity((S, S_err), "mm"),
Quantity((q_BG, q_BG_err), "MW/m^2"),
)