Commit f6256af2 by Samuel Rowlinson

### adding function for finite aperture clipping loss for LG modes

parent 05e0a752
 ... ... @@ -10,6 +10,7 @@ from math import factorial from pykat.math.hermite import hermite from scipy.misc import comb from scipy.integrate import newton_cotes from scipy.special import gammainc from pykat.math import newton_weights import time ... ... @@ -33,237 +34,237 @@ def makeCouplingMatrix(max_order, Neven=True, Nodd=True, Meven=True, Modd=True): for i in c: row = [] for j in c: e = list(i) e.extend(j) if not Neven and (e[0]-e[2]) % 2 == 0: continue if not Nodd and (e[0]-e[2]) % 2 == 1: continue if not Meven and (e[1]-e[3]) % 2 == 0: continue if not Modd and (e[1]-e[3]) % 2 == 1: continue row.append(e) M.append(np.array(row).squeeze()) return np.array(M) def adaptive_knm(mode_in, mode_out, q1, q2, q1y=None, q2y=None, smap=None, delta=(0,0), params={}): if q1y is None: q1y = q1 if q2y is None: q2y = q2 if "epsabs" not in params: params["epsabs"] = 1e-6 if "epsrel" not in params: params["epsrel"] = 1e-6 if "usepolar" not in params: params["usepolar"] = False if len(mode_in) != 2 or len(mode_out) != 2: raise BasePyKatException("Both mode in and out should be a container with modes [n m]") Hg_in = HG_mode(qx=q1, qy=q1y, n=mode_in[0], m=mode_in[1]) Hg_out = HG_mode(qx=q2, qy=q2y, n=mode_out[0], m=mode_out[1]) Nfuncs = [] Nfuncs.append(0) if smap is not None: if not params["usepolar"]: xlims = (min(smap.x), max(smap.x)) ylims = (min(smap.y), max(smap.y)) def Rfunc(y,x): #Nfuncs[-1] += len(x) return (Hg_in.Unm(x+delta[0], y+delta[1]) * smap.z_xy(x=x,y=y) * Hg_out.Unm(x, y).conjugate()).real def Ifunc(y,x): #Nfuncs[-1] += len(x) return (Hg_in.Unm(x+delta[0], y+delta[1]) * smap.z_xy(x=x,y=y) * Hg_out.Unm(x, y).conjugate()).imag else: xlims = (0, 2*math.pi) ylims = (0, params["aperture"]) def Rfunc(r, phi): #Nfuncs[-1] += len(phi) x = r*np.cos(phi) y = r*np.sin(phi) return (r * Hg_in.Unm(x, y) * smap.z_xy(x=x,y=y) * Hg_out.Unm(x, y).conjugate()).real def Ifunc(r, phi): #Nfuncs[-1] += len(x) x = r*np.cos(phi) y = r*np.sin(phi) return (r * Hg_in.Unm(x, y) * smap.z_xy(x=x,y=y) * Hg_out.Unm(x, y).conjugate()).imag else: if not params["usepolar"]: _x = 4 * math.sqrt(1+max(mode_in[0],mode_in[1])) * q1.w _y = 4 * math.sqrt(1+max(mode_in[0],mode_in[1])) * q1y.w xlims = (-_x, _x) ylims = (-_y, _y) def Rfunc(y, x): Nfuncs[-1] += len(r) return (Hg_in.Unm(x+delta[0], y+delta[1]) * Hg_out.Unm(x, y).conjugate()).real def Ifunc(y,x): Nfuncs[-1] += len(r) return (Hg_in.Unm(x+delta[0], y+delta[1]) * Hg_out.Unm(x, y).conjugate()).imag else: xlims = (0, 2*math.pi) ylims = (0, params["aperture"]) def Rfunc(r, phi): if hasattr(r, "__len__"): Nfuncs[-1] += len(r) else: Nfuncs[-1] += 1 x = r*np.cos(phi) y = r*np.sin(phi) return (r * Hg_in.Unm(x, y) * Hg_out.Unm(x, y).conjugate()).real def Ifunc(r, phi): if hasattr(r, "__len__"): Nfuncs[-1] += len(r) else: Nfuncs[-1] += 1 x = r*np.cos(phi) y = r*np.sin(phi) return (r * Hg_in.Unm(x, y) * Hg_out.Unm(x, y).conjugate()).imag R, errR = dblquad(Rfunc, xlims[0], xlims[1], lambda y: ylims[0], lambda y: ylims[1], epsabs=params["epsabs"], epsrel=params["epsrel"]) I, errI = dblquad(Ifunc, xlims[0], xlims[1], lambda y: ylims[0], lambda y: ylims[1], epsabs=params["epsabs"], epsrel=params["epsrel"]) params["Nfuncs"] = Nfuncs[0] params["errors"] = (errR, errI) return R + 1j * I def riemann_HG_knm(x, y, mode_in, mode_out, q1, q2, q1y=None, q2y=None, Axy=None, cache=None, delta=(0,0), params={}, newtonCotesOrder=0): if Axy is None: Axy == np.ones((len(x), len(y))) if q1y is None: q1y = q1 if q2y is None: q2y = q2 if len(mode_in) != 2 or len(mode_out) != 2: raise BasePyKatException("Both mode in and out should be a container with modes [n m]") raise BasePyKatException("Both mode in and out should be a container with modes [n m]") dx = abs(x[1] - x[0]) dy = abs(y[1] - y[0]) dy = abs(y[1] - y[0]) if cache is None: Hg_in = HG_mode(qx=q1, qy=q1y, n=mode_in[0], m=mode_in[1]) Hg_out = HG_mode(qx=q2, qy=q2y, n=mode_out[0], m=mode_out[1]) U1 = Hg_in.Unm(x+delta[0], y+delta[1]) U2 = Hg_out.Unm(x,y).conjugate() if newtonCotesOrder > 0: W = newton_cotes(newtonCotesOrder, 1)[0] if newtonCotesOrder > 1: if (len(x) - len(W)) % newtonCotesOrder != 0: raise ValueError("To use Newton-Cotes order {0} the number of data points in x must ensure: (N_x - ({0}+1)) mod {0} == 0".format(newtonCotesOrder) ) if (len(y) - len(W)) % newtonCotesOrder != 0: raise ValueError("To use Newton-Cotes order {0} the number of data points in y must ensure: (N_y - ({0}+1)) mod {0} == 0".format(newtonCotesOrder) ) wx = np.zeros(x.shape, dtype=np.float64) wx = np.zeros(x.shape, dtype=np.float64) wy = np.zeros(y.shape, dtype=np.float64) N = len(W) for i in range(0, (len(wx)-1)/newtonCotesOrder): wx[(i*(N-1)):(i*(N-1)+N)] += W for i in range(0, (len(wy)-1)/newtonCotesOrder): wy[(i*(N-1)):(i*(N-1)+N)] += W Wxy = np.outer(wx, wy) if newtonCotesOrder == 0: return dx * dy * np.einsum('ij,ij', Axy, U1*U2) else: return dx * dy * np.einsum('ij,ij', Axy, U1*U2*Wxy) else: strx = "u1[%i,%i]" % (mode_in[0], mode_out[0]) stry = "u2[%i,%i]" % (mode_in[1], mode_out[1]) return dx * dy * np.einsum('ij,ij', Axy, np.outer(cache[strx], cache[stry])) def __gen_riemann_knm_cache(x, y, couplings, q1, q2, q1y=None, q2y=None, delta=(0,0), params={}): if q1y is None: q1y = q1 if q2y is None: q2y = q2 #it = np.nditer(couplings, flags=['refs_ok','f_index']) cache = {} #while not it.finished: # try: # mode_in = [int(it.next()), int(it.next())] # mode_out = [int(it.next()), int(it.next())] couplings = couplings.copy() couplings.resize(int(couplings.size/4), 4) for _ in couplings: mode_in = _[:2] mode_out = _[2:] mode_out = _[2:] strx = "u1[%i,%i]" % (mode_in[0], mode_out[0]) stry = "u2[%i,%i]" % (mode_in[1], mode_out[1]) #Hg_in = HG_beam(qx=q1, qy=q1y, n=mode_in[0], m=mode_in[1]) #Hg_out = HG_beam(qx=q2, qy=q2y, n=mode_out[0], m=mode_out[1]) if strx not in cache: cache[strx] = u_star_u(q1.z, q2.z, q1.w0, q2.w0, mode_in[0], mode_out[0], x, x+delta[0]) #Hg_in.Un(x) * Hg_out.Un(x).conjugate() cache[strx] = u_star_u(q1.z, q2.z, q1.w0, q2.w0, mode_in[0], mode_out[0], x, x+delta[0]) #Hg_in.Un(x) * Hg_out.Un(x).conjugate() if stry not in cache: cache[stry] = u_star_u(q1y.z, q2y.z, q1y.w0, q2y.w0, mode_in[1], mode_out[1], y, y+delta[1]) cache[stry] = u_star_u(q1y.z, q2y.z, q1y.w0, q2y.w0, mode_in[1], mode_out[1], y, y+delta[1]) #Hg_in.Um(y) * Hg_out.Um(y).conjugate() # except StopIteration: # break return cache def __gen_ROM_HG_knm_cache(weights, couplings, q1, q2, q1y=None, q2y=None): if q1y is None: q1y = q1 if q2y is None: q2y = q2 cache = {} cache["w_ij_Q1Q3"] = weights.w_ij_Q1 + weights.w_ij_Q3 cache["w_ij_Q2Q4"] = weights.w_ij_Q2 + weights.w_ij_Q4 cache["w_ij_Q1Q2"] = weights.w_ij_Q1 + weights.w_ij_Q2 ... ... @@ -274,7 +275,7 @@ def __gen_ROM_HG_knm_cache(weights, couplings, q1, q2, q1y=None, q2y=None): couplings = couplings.copy() couplings.resize(int(couplings.size/4), 4) for _ in couplings: mode_in = _[:2] mode_out = _[2:] ... ... @@ -287,7 +288,7 @@ def __gen_ROM_HG_knm_cache(weights, couplings, q1, q2, q1y=None, q2y=None): if stry not in cache: cache[stry] = u_star_u(q1y.z, q2y.z, q1y.w0, q2y.w0, mode_in[1], mode_out[1], weights.EI["ym"].nodes) # it = np.nditer(couplings, flags=['refs_ok','f_index']) # while not it.finished: # try: ... ... @@ -305,7 +306,7 @@ def __gen_ROM_HG_knm_cache(weights, couplings, q1, q2, q1y=None, q2y=None): # # except StopIteration: # break return cache ... ... @@ -316,7 +317,7 @@ def ROM_HG_knm(weights, mode_in, mode_out, q1, q2, q1y=None, q2y=None, cache=Non if q2y is None: q2y = q2 # x modes n = mode_in[0] m = mode_out[0] ... ... @@ -324,12 +325,12 @@ def ROM_HG_knm(weights, mode_in, mode_out, q1, q2, q1y=None, q2y=None, cache=Non # y modes npr = mode_in[1] mpr = mode_out[1] if isinstance(weights, ROMWeights): if cache is None: u_x_nodes = u_star_u(q1.z, q2.z, q1.w0, q2.w0, n, m, weights.EIx.nodes) u_y_nodes = u_star_u(q1y.z, q2y.z, q1y.w0, q2y.w0, npr, mpr, weights.EIy.nodes) w_ij_Q1Q3 = weights.w_ij_Q1 + weights.w_ij_Q3 w_ij_Q2Q4 = weights.w_ij_Q2 + weights.w_ij_Q4 w_ij_Q1Q2 = weights.w_ij_Q1 + weights.w_ij_Q2 ... ... @@ -337,14 +338,14 @@ def ROM_HG_knm(weights, mode_in, mode_out, q1, q2, q1y=None, q2y=None, cache=Non w_ij_Q2Q3 = weights.w_ij_Q2 + weights.w_ij_Q3 w_ij_Q3Q4 = weights.w_ij_Q3 + weights.w_ij_Q4 w_ij_Q1Q2Q3Q4 = weights.w_ij_Q1 + weights.w_ij_Q2 + weights.w_ij_Q3 + weights.w_ij_Q4 else: strx = "x[%i,%i]" % (mode_in[0], mode_out[0]) stry = "y[%i,%i]" % (mode_in[1], mode_out[1]) u_x_nodes = cache[strx] u_y_nodes = cache[stry] w_ij_Q1Q3 = cache["w_ij_Q1Q3"] w_ij_Q2Q4 = cache["w_ij_Q2Q4"] w_ij_Q1Q2 = cache["w_ij_Q1Q2"] ... ... @@ -352,7 +353,7 @@ def ROM_HG_knm(weights, mode_in, mode_out, q1, q2, q1y=None, q2y=None, cache=Non w_ij_Q2Q3 = cache["w_ij_Q2Q3"] w_ij_Q3Q4 = cache["w_ij_Q3Q4"] w_ij_Q1Q2Q3Q4 = cache["w_ij_Q1Q2Q3Q4"] u_xy_nodes = np.outer(u_x_nodes, u_y_nodes) n_mod_2 = n % 2 ... ... @@ -374,12 +375,12 @@ def ROM_HG_knm(weights, mode_in, mode_out, q1, q2, q1y=None, q2y=None, cache=Non k_ROQ = np.einsum('ij,ij', u_xy_nodes, w_ij_Q3Q4) - np.einsum('ij,ij', u_xy_nodes, w_ij_Q1Q2) else: k_ROQ = np.einsum('ij,ij', u_xy_nodes, w_ij_Q2Q4) - np.einsum('ij,ij', u_xy_nodes, w_ij_Q1Q3) else: if cache is None: u_x_nodes = u_star_u(q1.z, q2.z, q1.w0, q2.w0, n, m, weights.EIx.nodes) u_y_nodes = u_star_u(q1y.z, q2y.z, q1y.w0, q2y.w0, npr, mpr, weights.EIy.nodes) w_ij = weights.w_ij else: strx = "x[%i,%i]" % (mode_in[0], mode_out[0]) ... ... @@ -387,11 +388,11 @@ def ROM_HG_knm(weights, mode_in, mode_out, q1, q2, q1y=None, q2y=None, cache=Non u_x_nodes = cache[strx] u_y_nodes = cache[stry] u_xy_nodes = np.outer(u_x_nodes, u_y_nodes) k_ROQ = np.einsum('ij,ij', u_xy_nodes, w_ij) return k_ROQ __fac_cache = [] ... ... @@ -418,56 +419,56 @@ def __sum_lim(m): def __Sg(m, _m, X, _X, F, _F): lim1 = __sum_lim(m) lim2 = __sum_lim(_m) lim2 = __sum_lim(_m) r = 0 def __Ss(u, _u, F, _F): r = 0 for s in range(0, min(u,_u)+1): r += m_1_pow(s) * _F ** (u-s) * F ** (_u-s) / (fac(2*s)*fac(u-s)*fac(_u-s)) return r for u in range(0, lim1+1): for _u in range(0, lim2+1): r += m_1_pow(u) * _X**(m - 2*u) * X**(_m - 2*_u) / ( fac(m - 2*u) * fac(_m - 2*_u) ) * __Ss(u, _u, F, _F) return r def __Su(m, _m, X, _X, F, _F): lim1 = __sum_lim(m-1) lim2 = __sum_lim(_m-1) r = 0 def __Ss(u, _u, F, _F): r = 0 for s in range(0, min(u,_u)+1): r += m_1_pow(s) * _F ** (u-s) * F ** (_u-s) / (fac(2*s+1)*fac(u-s)*fac(_u-s)) return r for u in range(0, lim1+1): for _u in range(0, lim2+1): r += m_1_pow(u) * _X**(m-2*u-1) * X**(_m-2*_u-1) / ( fac(m-2*u-1)*fac(_m-2*_u-1) ) * __Ss(u, _u, F, _F) return r def __bayerhelms_kn(n, _n, q1, q2, gamma=0.0): K0 = (q1.zr - q2.zr)/q2.zr K2 = (q1.z - q2.z)/q2.zr K = (K0 + 1j*K2)/2.0 Ktilde = abs(K / (1+K)) if gamma != 0: ... ... @@ -480,9 +481,9 @@ def __bayerhelms_kn(n, _n, q1, q2, gamma=0.0): _X = 0.0 X = 0.0 Ex = 1.0 _F = K / (2.0 * (1.0+K0)) F = K.conjugate() / 2.0 F = K.conjugate() / 2.0 Sg = __Sg(n, _n, X, _X, F, _F) ... ... @@ -490,20 +491,20 @@ def __bayerhelms_kn(n, _n, q1, q2, gamma=0.0): Su = 0 else: Su = __Su(n, _n, X, _X, F, _F) b = ((-1)**(_n) * b = ((-1)**(_n) * np.sqrt(fac(n) * fac(_n)) * (1.0 + K0)**(n/2.0 + 1/4.0) * (1.0 + K.conjugate()) ** (-(n+_n+1)/2.0)) #print(K, F, _F, b, Ex, Sg, Su) return b * Ex * (Sg - Su) def bayerhelms_HG_knm(mode_in, mode_out, q1, q2, q1y=None, q2y=None, gamma=(0,0)): """ """ if q1y is None: q1y = q1 ... ... @@ -527,23 +528,23 @@ def __sqaure_knm_int(n, _n, R): # thus making it easier to solve expR = math.exp(-(R**2)) S = 0 for j in range(0, min(n, _n)+1): _j1 = _n + n - 2*j - 1 if _j1+1 == 0: # for the zeroth order we just have the gaussian integral to solve L = math.sqrt(math.pi) * math.erf(R) L = math.sqrt(math.pi) * math.erf(R) elif (_j1+1) % 2 == 1: # if the Hermite is odd then the integral is always 0 as its anti-symmetric L = 0 else: L = 2 * hermite(_j1, 0) - expR * (hermite(_j1, R) - hermite(_j1, -R)) I = 2**j * factorial(j) * comb(n, j) * comb(_n, j) S += I * L return S ... ... @@ -558,22 +559,22 @@ def square_aperture_HG_knm(mode_in, mode_out, q, R): # y modes m = mode_in[1] _m = mode_out[1] hg1 = HG_mode(q, n=n, m=m) hg2 = HG_mode(q, n=_n, m=_m) kx = hg1.constant_x * hg2.constant_x.conjugate() ky = hg1.constant_y * hg2.constant_y.conjugate() f = q.w / math.sqrt(2) R = R / (q.w / math.sqrt(2)) kx *= f kx *= __sqaure_knm_int(n, _n, R) ky *= f ky *= __sqaure_knm_int(m, _m, R) return kx * ky ... ... @@ -586,15 +587,15 @@ def knmHG(couplings, q1, q2, surface_map=None, q1y=None, q2y=None, method="riema - Surface maps (b) - Tilts (c) - Displacements (d) Mathematically the 2D overlap integral between an incoming and outgoing beam is solved in the Hermite-Gaussian mode basis. Some of these effects aren't supported in all solvers. The markers a,b,c, and d are listed below next to each method. Parameters: couplings - Matrix of coupling couplings - Matrix of coupling q1 - Incoming mode parameter (x and y direction if q1y not specified) q2 - Outgoing mode parameter (x and y direction if q2y not specified) q1y - Incoming mode parameter (y direction) ... ... @@ -605,11 +606,11 @@ def knmHG(couplings, q1, q2, surface_map=None, q1y=None, q2y=None, method="riema adaptive - Numerical adaptive quadrature solver (a,b,c,d) romhom - Reduced order modelling solver (a,b,c) verbose - If true outputs more debug and timing information gamma - Tuple of misalignment tilts of incoming and outgoing mode, e.g. (x_tilt, y_tilt) delta - Tuple of displacements between incoming and outgoing mode, e.g. (dx, dy) gamma - Tuple of misalignment tilts of incoming and outgoing mode, e.g. (x_tilt, y_tilt) delta - Tuple of displacements between incoming and outgoing mode, e.g. (dx, dy) cache - If True caching of certain results are used to speed up calculations