def test_04(): from mpmath import mp, mpc import random from weierstrass_elliptic import weierstrass_elliptic as we random.seed(0) mp.dps = 50 # Generate 20 random objects. l = [we(random.uniform(-10,20),random.uniform(-10,10)) for _ in range(0,20)] # Add a couple of cases wih g2/g3 = 0. l.append(we(random.uniform(-10,20),0.)) l.append(we(random.uniform(-10,20),0.)) l.append(we(random.uniform(-10,20),0.)) l.append(we(0.,random.uniform(-10,10))) l.append(we(0.,random.uniform(-10,10))) l.append(we(0.,random.uniform(-10,10))) retval = [] for w in l: p1,p2 = w.periods g2,g3 = w.invariants # Generate 100 random values within 10 real periods. z_list = [p1.real*random.uniform(-10,10) for _ in range(0,100)] values_list = [w.Pprime(_) for _ in z_list] res = [(t[0],t[1].real) for t in zip(z_list,values_list)] retval.append([str(g2),str(g3)] + [str(item) for sublist in res for item in sublist]) # Build the string. return "\n".join([','.join(t) for t in retval])
def test_01(): from mpmath import mp, mpc import random from weierstrass_elliptic import weierstrass_elliptic as we random.seed(0) mp.dps = 50 l = [we(random.uniform(-10,20),random.uniform(-10,10)) for _ in range(0,1000)] # Add a couple of cases wih g2/g3 = 0. l.append(we(random.uniform(-10,20),0.)) l.append(we(random.uniform(-10,20),0.)) l.append(we(random.uniform(-10,20),0.)) l.append(we(0.,random.uniform(-10,10))) l.append(we(0.,random.uniform(-10,10))) l.append(we(0.,random.uniform(-10,10))) # Extract the tuple of interest. lt = [(w.invariants[0],w.invariants[1],w.roots[0],w.roots[1],w.roots[2],w.periods[0],w.periods[1]) for w in l] # Fix negative periods and put everything in mpc (apart from invariants). lt = [[t[0],t[1],mpc(t[2]),mpc(t[3]),mpc(t[4]),mpc(t[5]),t[6] if t[6].real > 0 else mpc(-t[6].real,t[6].imag)] for t in lt] # Add also eta. lt = [t[1] + [t[0].zeta(t[0].periods[0]/2)] for t in zip(l,lt)] # Convert to suitable string format. lt = [[str(t[0]),str(t[1]),str(t[2].real),str(t[2].imag),str(t[3].real),str(t[3].imag),str(t[4].real),str(t[4].imag),str(t[5].real),str(t[5].imag), str(t[6].real),str(t[6].imag),str(t[7].real),str(t[7].imag)] for t in lt] # Build the string. return "\n".join([','.join(t) for t in lt])
def __set_params(self,d): from copy import deepcopy from mpmath import mpf, sqrt, polyroots, cos, acos, pi, mp from weierstrass_elliptic import weierstrass_elliptic as we names = ['m2','r2','rot2','r1','rot1','i_a','ht','a','e','i','h'] if not all([s in d for s in names]): raise ValueError('invalid set of parameters') # Convert all the values to mpf and introduce convenience shortcuts. m2, r2, rot2, r1, rot1, i_a, ht_in, a, e, i, h_in = [mpf(d[s]) for s in names] L_in = sqrt(self.__GG_val * m2 * a) G_in = L_in * sqrt(1. - e**2) H_in = G_in * cos(i) Gt_in = (2 * r1**2 * rot1) / 5 Ht_in = Gt_in * cos(i_a) Hts_in = H_in + Ht_in hs_in = h_in - ht_in Gxy_in = sqrt(G_in**2 - H_in**2) Gtxys_in = sqrt(Gt_in**2 - Ht_in**2) J2 = (2 * m2 * r2**2 * rot2) / 5 II_1 = mpf(5) / (2 * r1**2) # Evaluation dictionary. eval_names = ['L','G','H','\\tilde{G}','\\tilde{H}_\\ast','h_\\ast','\\tilde{G}_{xy\\ast}','m_2','\\mathcal{G}','J_2','G_{xy}',\ '\\varepsilon','\\mathcal{I}_1'] eval_values = [L_in,G_in,H_in,Gt_in,Hts_in,hs_in,Gtxys_in,m2,self.__GG_val,J2,Gxy_in,self.__eps_val,II_1] d_eval = dict(zip(eval_names,eval_values)) # Evaluate Hamiltonian with initial conditions. HHp_val = self.__HHp.trim().evaluate(d_eval) # Add the value of the Hamiltonian to the eval dictionary. d_eval['\\mathcal{H}^\\prime'] = HHp_val # Evaluate g2 and g3. g2_val, g3_val = self.__g2.trim().evaluate(d_eval), self.__g3.trim().evaluate(d_eval) # Create the Weierstrass object. wp = we(g2_val,g3_val) # Store the period. self.__wp_period = wp.periods[0] # Now let's find the roots of the quartic polynomial. # NOTE: in theory here we could use the exact solution for the quartic. p4coeffs = [t[0] * t[1].trim().evaluate(d_eval) for t in zip([1,4,6,4,1],self.__f4_cf)] p4roots, err = polyroots(p4coeffs,error = True, maxsteps = 1000) # Find a reachable root. Hr, H_max, n_lobes, lobe_idx = self.__reachable_root(p4roots,H_in) # Determine t_r t_r = self.__compute_t_r(n_lobes,lobe_idx,H_in,Hr,d_eval,p4roots,p4coeffs[0]) # Now evaluate the derivatives of the polynomial. We will need to replace H_in with Hr in the eval dict. d_eval['H'] = Hr _, f4Hp, f4Hpp, _, _ = self.__f4 f4p_eval = f4Hp.trim().evaluate(d_eval) f4pp_eval = f4Hpp.trim().evaluate(d_eval) # Build and store the expression for H(t). self.__H_time = lambda t: Hr + f4p_eval / (4 * (wp.P(t - t_r) - f4pp_eval / 24)) # H will not be needed any more, replace with H_r del d_eval['H'] d_eval['H_r'] = Hr # Inject the invariants and the other two constants into the evaluation dictionary. d_eval['g_2'] = g2_val d_eval['g_3'] = g3_val d_eval['A'] = f4p_eval / 4 d_eval['B'] = f4pp_eval / 24 # Verify the formulae in solutions.py self.__verify_solutions(d_eval) # Assuming g = 0 as initial angle. self.__g_time = spin_gr_theory.__get_g_time(d_eval,t_r,0.) self.__hs_time = spin_gr_theory.__get_hs_time(d_eval,t_r,hs_in) self.__ht_time = spin_gr_theory.__get_ht_time(d_eval,t_r,ht_in) def obliquity(t): from mpmath import acos, cos, sqrt H = self.__H_time(t) hs = self.__hs_time(t) G = d_eval['G'] Gt = d_eval['\\tilde{G}'] Hts = d_eval['\\tilde{H}_\\ast'] Gxy = sqrt(G**2-H**2) Gtxys = sqrt(Gt**2-(Hts-H)**2) retval = (Gxy*Gtxys*cos(hs)+H*(Hts-H))/(G*Gt) return acos(retval) self.__obliquity_time = obliquity def spin_vector(t): import numpy ht = self.__ht_time(t) H = self.__H_time(t) G = d_eval['G'] Gt = d_eval['\\tilde{G}'] Hts = d_eval['\\tilde{H}_\\ast'] Gtxys = sqrt(Gt**2-(Hts-H)**2) return numpy.array([x.real for x in [Gtxys*sin(ht),-Gtxys*cos(ht),Hts-H]]) self.__spin_vector_time = spin_vector def orbit_vector(t): import numpy ht = self.__ht_time(t) hs = self.__hs_time(t) h = hs + ht H = self.__H_time(t) G = d_eval['G'] Gxy = sqrt(G**2-H**2) return numpy.array([x.real for x in [Gxy*sin(h),-Gxy*cos(h),H]]) self.__orbit_vector_time = orbit_vector # Store the params of the system. self.__params = dict(zip(names,[mpf(d[s]) for s in names])) # Final report. rad_conv = 360 / (2 * pi()) print("\x1b[31mAccuracy in the identification of the poly roots:\x1b[0m") print(err) print("\n\x1b[31mPeriod (years):\x1b[0m") print(wp.periods[0] / (3600*24*365)) print("\n\x1b[31mMin and max orbital inclination (deg):\x1b[0m") print(acos(Hr/G_in) * rad_conv,acos(H_max/G_in) * rad_conv) print("\n\x1b[31mMin and max axial inclination (deg):\x1b[0m") print(acos((Hts_in - Hr)/Gt_in) * rad_conv,acos((Hts_in-H_max)/Gt_in) * rad_conv) print("\n\x1b[31mNumber of lobes:\x1b[0m " + str(n_lobes)) print("\n\x1b[31mLobe idx:\x1b[0m " + str(lobe_idx)) # Report the known results for simplified system for comparison. H = H_in HHp,G,L,GG,eps,m2,Hts,Gt,J2 = [d_eval[s] for s in ['\\mathcal{H}^\\prime','G','L','\\mathcal{G}',\ '\\varepsilon','m_2','\\tilde{H}_\\ast','\\tilde{G}','J_2']] print("\n\x1b[31mEinstein (g):\x1b[0m " + str((3 * eps * GG**4 * m2**4/(G**2*L**3)))) print("\n\x1b[31mLense-Thirring (g):\x1b[0m " + str(((eps * ((-6*H*J2*GG**4*m2**3)/(G**4*L**3)+3*GG**4*m2**4/(G**2*L**3)))))) print("\n\x1b[31mLense-Thirring (h):\x1b[0m " + str((2*eps*J2*GG**4*m2**3/(G**3*L**3)))) # These are the Delta_ constants of quasi-periodicity. f_period = self.wp_period print("\n\x1b[31mDelta_g:\x1b[0m " + str(self.g_time(f_period) - self.g_time(0))) print("\n\x1b[31mg_rate:\x1b[0m " + str((self.g_time(f_period) - self.g_time(0))/f_period)) Delta_hs = self.hs_time(f_period) - self.hs_time(0) print("\n\x1b[31mDelta_hs:\x1b[0m " + str(Delta_hs)) print("\n\x1b[31mhs_rate:\x1b[0m " + str(Delta_hs/f_period)) Delta_ht = self.ht_time(f_period) - self.ht_time(0) print("\n\x1b[31mDelta_ht:\x1b[0m " + str(Delta_ht)) print("\n\x1b[31mht_rate:\x1b[0m " + str(Delta_ht/f_period)) print("\n\x1b[31mDelta_h:\x1b[0m " + str(Delta_ht+Delta_hs)) print("\n\x1b[31mh_rate:\x1b[0m " + str((Delta_ht+Delta_hs)/f_period)) print("\n\n")
def __init__(self, eps, x0, v0): from numpy import dot from mpmath import polyroots, mpf, mpc, sqrt, atan2, polyval from weierstrass_elliptic import weierstrass_elliptic as we if eps <= 0: raise ValueError('thrust must be strictly positive') eps = mpf(eps) # Unitary grav. parameter. mu = mpf(1.) x, y, z = [mpf(x) for x in x0] vx, vy, vz = [mpf(v) for v in v0] r = sqrt(x**2 + y**2 + z**2) xi = sqrt(r + z) eta = sqrt(r - z) phi = atan2(y, x) vr = dot(v0, x0) / r vxi = (vr + vz) / (2 * sqrt(r + z)) veta = (vr - vz) / (2 * sqrt(r - z)) vphi = (vy * x - vx * y) / (x**2 + y**2) pxi = (xi**2 + eta**2) * vxi peta = (xi**2 + eta**2) * veta pphi = xi**2 * eta**2 * vphi if pphi == 0: raise ValueError('bidimensional case') # Energy constant. h = (pxi**2 + peta**2) / (2 * (xi**2 + eta**2)) + pphi**2 / ( 2 * xi**2 * eta**2) - (2 * mu) / (xi**2 + eta**2) - eps * (xi**2 - eta**2) / 2 # Alpha constants. alpha1 = -eps * xi**4 / 2 - h * xi**2 + pxi**2 / 2 + pphi**2 / (2 * xi**2) alpha2 = eps * eta**4 / 2 - h * eta**2 + peta**2 / 2 + pphi**2 / ( 2 * eta**2) # Analysis of the cubic polynomials in the equations for pxi and peta. roots_xi, _ = polyroots([8 * eps, 8 * h, 4 * alpha1, -pphi**2], error=True, maxsteps=100) roots_eta, _ = polyroots([-8 * eps, 8 * h, 4 * alpha2, -pphi**2], error=True, maxsteps=100) # NOTE: these are all paranoia checks that could go away if we used the exact cubic formula. if not (all([isinstance(x, mpf) for x in roots_xi]) or (isinstance(roots_xi[0], mpf) and isinstance(roots_xi[1], mpc) and isinstance(roots_xi[2], mpc))): raise ValueError('invalid xi roots detected: ' + str(roots_xi)) if not (all([isinstance(x, mpf) for x in roots_eta]) or (isinstance(roots_eta[0], mpf) and isinstance( roots_eta[1], mpc) and isinstance(roots_eta[2], mpc))): raise ValueError('invalid eta roots detected: ' + str(roots_eta)) # For xi we need to understand which of the real positive roots will be or was reached # given the initial condition. rp_roots_extract = lambda x: isinstance(x, mpf) and x > 0 rp_roots_xi = [sqrt(2 * _) for _ in filter(rp_roots_extract, roots_xi)] rp_roots_eta = [ sqrt(2. * _) for _ in filter(rp_roots_extract, roots_eta) ] # Paranoia. if not len(rp_roots_xi) in [1, 3]: raise ValueError('invalid xi roots detected: ' + str(roots_xi)) if len(rp_roots_eta) != 2: raise ValueError('invalid eta roots detected: ' + str(roots_eta)) # We choose as reachable/reached roots always those corresponding to the "pericentre" # for the two coordinates. if len(rp_roots_xi) == 1: # Here there's really no choice, only 1 root available. rr_xi = rp_roots_xi[0] else: # If motion is unbound, take the only root, otherwise take the smallest of the # two roots of the bound motion. rr_xi = rp_roots_xi[-1] if xi >= rp_roots_xi[-1] else rp_roots_xi[0] # No choice to be made here. rr_eta = rp_roots_eta[0] # Store parameters and constants. self.__init_coordinates = [xi, eta, phi] self.__init_momenta = [pxi, peta, pphi] self.__eps = eps self.__h = h self.__alpha1 = alpha1 self.__alpha2 = alpha2 self.__rp_roots_xi = rp_roots_xi self.__rp_roots_eta = rp_roots_eta self.__rr_xi = rr_xi self.__rr_eta = rr_eta self.__roots_xi = roots_xi self.__roots_eta = roots_eta # Create the Weierstrass objects for xi and eta. a1, a2, a3, a4 = 2 * eps, (4 * h) / 3, alpha1, -pphi**2 g2 = -4 * a1 * a3 + 3 * a2**2 g3 = 2 * a1 * a2 * a3 - a2**3 - a1**2 * a4 self.__f_xi = [4 * a1, 6 * a2, 4 * a3, a4] self.__fp_xi = [12 * a1, 12 * a2, 4 * a3] self.__fpp_xi = [24 * a1, 12 * a2] self.__w_xi = we(g2, g3) # Eta. a1, a3 = -a1, alpha2 g2 = -4 * a1 * a3 + 3 * a2**2 g3 = 2 * a1 * a2 * a3 - a2**3 - a1**2 * a4 self.__f_eta = [4 * a1, 6 * a2, 4 * a3, a4] self.__fp_eta = [12 * a1, 12 * a2, 4 * a3] self.__fpp_eta = [24 * a1, 12 * a2] self.__w_eta = we(g2, g3) # Compute the taus. tau_xi = self.__compute_tau_xi() tau_eta = self.__compute_tau_eta() self.__tau_xi = tau_xi self.__tau_eta = tau_eta # Store the real periods. self.__T_xi = self.__w_xi.periods[0] self.__T_eta = self.__w_eta.periods[0] # Delta bound (for debugging). xi_roots = self.__w_xi.roots # Determine the root corresponding to the real half-period. e_R = min(xi_roots, key=lambda x: abs(self.__w_xi.P(self.__T_xi / 2) - x)) self.__Dbound = e_R - polyval(self.__fpp_xi, xi**2 / 2) / 24
def __init__(self,eps,x0,v0): from numpy import dot from mpmath import polyroots, mpf, mpc, sqrt, atan2, polyval from weierstrass_elliptic import weierstrass_elliptic as we if eps <= 0: raise ValueError('thrust must be strictly positive') eps = mpf(eps) # Unitary grav. parameter. mu = mpf(1.) x,y,z = [mpf(x) for x in x0] vx,vy,vz = [mpf(v) for v in v0] r = sqrt(x**2 + y**2 + z**2) xi = sqrt(r+z) eta = sqrt(r-z) phi = atan2(y,x) vr = dot(v0,x0) / r vxi = (vr + vz) / (2 * sqrt(r+z)) veta = (vr - vz) / (2 * sqrt(r-z)) vphi = (vy*x - vx*y) / (x**2 + y**2) pxi = (xi**2 + eta**2) * vxi peta = (xi**2 + eta**2) * veta pphi = xi**2*eta**2*vphi if pphi == 0: raise ValueError('bidimensional case') # Energy constant. h = (pxi**2 + peta**2) / (2*(xi**2 + eta**2)) + pphi**2 / (2*xi**2*eta**2) - (2 * mu) / (xi**2+eta**2) - eps * (xi**2 - eta**2) / 2 # Alpha constants. alpha1 = -eps * xi**4 / 2 - h * xi**2 + pxi**2 / 2 + pphi**2 / (2*xi**2) alpha2 = eps * eta**4 / 2 - h * eta**2 + peta**2 / 2 + pphi**2 / (2*eta**2) # Analysis of the cubic polynomials in the equations for pxi and peta. roots_xi, _ = polyroots([8*eps,8*h,4*alpha1,-pphi**2],error=True,maxsteps=100) roots_eta, _ = polyroots([-8*eps,8*h,4*alpha2,-pphi**2],error=True,maxsteps=100) # NOTE: these are all paranoia checks that could go away if we used the exact cubic formula. if not (all([isinstance(x,mpf) for x in roots_xi]) or (isinstance(roots_xi[0],mpf) and isinstance(roots_xi[1],mpc) and isinstance(roots_xi[2],mpc))): raise ValueError('invalid xi roots detected: ' + str(roots_xi)) if not (all([isinstance(x,mpf) for x in roots_eta]) or (isinstance(roots_eta[0],mpf) and isinstance(roots_eta[1],mpc) and isinstance(roots_eta[2],mpc))): raise ValueError('invalid eta roots detected: ' + str(roots_eta)) # For xi we need to understand which of the real positive roots will be or was reached # given the initial condition. rp_roots_extract = lambda x: isinstance(x,mpf) and x > 0 rp_roots_xi = [sqrt(2 * _) for _ in filter(rp_roots_extract,roots_xi)] rp_roots_eta = [sqrt(2. * _) for _ in filter(rp_roots_extract,roots_eta)] # Paranoia. if not len(rp_roots_xi) in [1,3]: raise ValueError('invalid xi roots detected: ' + str(roots_xi)) if len(rp_roots_eta) != 2: raise ValueError('invalid eta roots detected: ' + str(roots_eta)) # We choose as reachable/reached roots always those corresponding to the "pericentre" # for the two coordinates. if len(rp_roots_xi) == 1: # Here there's really no choice, only 1 root available. rr_xi = rp_roots_xi[0] else: # If motion is unbound, take the only root, otherwise take the smallest of the # two roots of the bound motion. rr_xi = rp_roots_xi[-1] if xi >= rp_roots_xi[-1] else rp_roots_xi[0] # No choice to be made here. rr_eta = rp_roots_eta[0] # Store parameters and constants. self.__init_coordinates = [xi,eta,phi] self.__init_momenta = [pxi,peta,pphi] self.__eps = eps self.__h = h self.__alpha1 = alpha1 self.__alpha2 = alpha2 self.__rp_roots_xi = rp_roots_xi self.__rp_roots_eta = rp_roots_eta self.__rr_xi = rr_xi self.__rr_eta = rr_eta self.__roots_xi = roots_xi self.__roots_eta = roots_eta # Create the Weierstrass objects for xi and eta. a1, a2, a3, a4 = 2*eps, (4 * h)/3, alpha1, -pphi**2 g2 = -4 * a1 * a3 + 3 * a2**2 g3 = 2 * a1 * a2 * a3 - a2**3 - a1**2*a4 self.__f_xi = [4*a1,6*a2,4*a3,a4] self.__fp_xi = [12*a1,12*a2,4*a3] self.__fpp_xi = [24*a1,12*a2] self.__w_xi = we(g2,g3) # Eta. a1,a3 = -a1,alpha2 g2 = -4 * a1 * a3 + 3 * a2**2 g3 = 2 * a1 * a2 * a3 - a2**3 - a1**2*a4 self.__f_eta = [4*a1,6*a2,4*a3,a4] self.__fp_eta = [12*a1,12*a2,4*a3] self.__fpp_eta = [24*a1,12*a2] self.__w_eta = we(g2,g3) # Compute the taus. tau_xi = self.__compute_tau_xi() tau_eta = self.__compute_tau_eta() self.__tau_xi = tau_xi self.__tau_eta = tau_eta # Store the real periods. self.__T_xi = self.__w_xi.periods[0] self.__T_eta = self.__w_eta.periods[0] # Delta bound (for debugging). xi_roots = self.__w_xi.roots # Determine the root corresponding to the real half-period. e_R = min(xi_roots,key = lambda x: abs(self.__w_xi.P(self.__T_xi/2) - x)) self.__Dbound = e_R - polyval(self.__fpp_xi,xi**2/2) / 24