1use crate::errors::RocheError;
2use crate::potential::{drpot, rpot};
3use crate::x_lagrange::x_l1;
4use crate::{Star, Vec3};
5use pyo3::prelude::*;
6
7pub struct LineRoche {
9 pub q: f64,
11 pub star: Star,
13 pub dx: f64,
15 pub dy: f64,
17 pub cpot: f64,
19}
20
21impl LineRoche {
22 pub fn new(q: f64, star: Star, dx: f64, dy: f64, cpot: f64) -> Self {
23 Self {
24 q,
25 star,
26 dx,
27 dy,
28 cpot,
29 }
30 }
31
32 pub fn cost(&self, lam: f64) -> Result<(f64, f64), RocheError> {
33 let p: Vec3 = match self.star {
34 Star::Primary => Vec3::new(lam * self.dx, lam * self.dy, 0.0),
35 Star::Secondary => Vec3::new(1.0 + lam * self.dx, lam * self.dy, 0.0),
36 };
37 let f: f64 = rpot(self.q, &p)? - self.cpot;
39 let dp: Vec3 = drpot(self.q, &p)?;
41 let d: f64 = self.dx * dp.x + self.dy * dp.y;
43 Ok((f, d))
44 }
45}
46
47#[pyfunction]
55#[pyo3(signature = (q, n=200))]
56pub fn lobe1(q: f64, n: usize) -> Result<(Vec<f64>, Vec<f64>), RocheError> {
57 const FRAC: f64 = 1.0e-6;
59
60 let rl1: f64 = x_l1(q)?;
62 let p: Vec3 = Vec3::new(rl1, 0.0, 0.0);
63 let cpot: f64 = rpot(q, &p)?;
64
65 let mut xarr: Vec<f64> = Vec::with_capacity(n);
66 let mut yarr: Vec<f64> = Vec::with_capacity(n);
67 for i in 0..n {
68 if i == 0 || i == n - 1 {
69 xarr.push(rl1);
71 yarr.push(0.0);
72 } else {
73 let theta: f64 = (i as f64) * std::f64::consts::PI * 2.0 / ((n as f64) - 1.0);
74 let dx: f64 = theta.cos();
75 let dy: f64 = theta.sin();
76 let line: LineRoche = LineRoche::new(q, Star::Primary, dx, dy, cpot);
77 let lam: f64 = rtsafe(rl1 / 4.0, rl1, |lam| line.cost(lam), FRAC)?;
78 xarr.push(lam * dx);
79 yarr.push(lam * dy);
80 }
81 }
82 Ok((xarr, yarr))
83}
84
85#[pyfunction]
93#[pyo3(signature = (q, n=200))]
94pub fn lobe2(q: f64, n: usize) -> Result<(Vec<f64>, Vec<f64>), RocheError> {
95 const FRAC: f64 = 1.0e-6;
97
98 let rl1: f64 = x_l1(q)?;
100 let p: Vec3 = Vec3::new(rl1, 0.0, 0.0);
101 let cpot: f64 = rpot(q, &p)?;
102 let upper: f64 = 1.0 - rl1;
103 let lower: f64 = upper / 4.0;
104 let mut xarr: Vec<f64> = Vec::with_capacity(n);
105 let mut yarr: Vec<f64> = Vec::with_capacity(n);
106 for i in 0..n {
107 if i == 0 || i == n - 1 {
108 xarr.push(rl1);
110 yarr.push(0.0);
111 } else {
112 let theta: f64 = (i as f64) * std::f64::consts::PI * 2.0 / ((n as f64) - 1.0);
113 let dx: f64 = -theta.cos();
114 let dy: f64 = theta.sin();
115 let line: LineRoche = LineRoche::new(q, Star::Secondary, dx, dy, cpot);
116 let lam: f64 = rtsafe(lower, upper, |lam| line.cost(lam), FRAC)?;
117 xarr.push(1.0 + lam * dx);
118 yarr.push(lam * dy);
119 }
120 }
121 Ok((xarr, yarr))
122}
123
124#[pyfunction]
132#[pyo3(signature = (q, n=200))]
133pub fn vlobe1(q: f64, n: usize) -> Result<(Vec<f64>, Vec<f64>), RocheError> {
134
135 let mut tvx: f64;
136 let mut tvy: f64;
137
138 let mut vx_arr: Vec<f64> = vec![];
139 let mut vy_arr: Vec<f64> = vec![];
140
141 let (x, y) = lobe1(q, n)?;
142
143 let mu: f64 = q / (1.0 + q);
144 for i in 0..n {
145 tvx = -y[i];
146 tvy = x[i] - mu;
147 vx_arr.push(tvx);
148 vy_arr.push(tvy);
149 }
150 Ok((vx_arr, vy_arr))
151}
152
153#[pyfunction]
161#[pyo3(signature = (q, n=200))]
162pub fn vlobe2(q: f64, n: usize) -> Result<(Vec<f64>, Vec<f64>), RocheError> {
163
164 let mut tvx: f64;
165 let mut tvy: f64;
166
167 let mut vx_arr: Vec<f64> = vec![];
168 let mut vy_arr: Vec<f64> = vec![];
169
170 let (x, y) = lobe2(q, n)?;
171
172 let mu: f64 = q / (1.0 + q);
173 for i in 0..n {
174 tvx = -y[i];
175 tvy = x[i] - mu;
176 vx_arr.push(tvx);
177 vy_arr.push(tvy);
178 }
179 Ok((vx_arr, vy_arr))
180}
181
182pub fn rtsafe<F>(x1: f64, x2: f64, func: F, xacc: f64) -> Result<f64, RocheError>
190where
191 F: Fn(f64) -> Result<(f64, f64), RocheError>,
192{
193 let mut xlo = x1;
194 let mut xhi = x2;
195 let mut fl;
196 let mut fh;
197 let mut df;
198 const MAXITER: i32 = 100;
199 (fl, _) = func(xlo)?;
200 (fh, _) = func(xhi)?;
201
202 if (fl > 0.0 && fh > 0.0) || (fl < 0.0 && fh < 0.0) {
203 return Err(RocheError::RtsafeError(
204 "Root must be bracketed in rtsafe".to_string(),
205 ));
206 }
207
208 if fl == 0.0 {
210 return Ok(xlo);
211 } else if fh == 0.0 {
212 return Ok(xhi);
213 }
214
215 if fh < 0.0 {
217 std::mem::swap(&mut xlo, &mut xhi);
218 std::mem::swap(&mut fl, &mut fh);
219 }
220
221 let mut rts = 0.5 * (xlo + xhi);
222 let mut dxold = (xhi - xlo).abs();
223 let mut dx = dxold;
224 let mut f;
225 (f, df) = func(rts)?;
226 let mut iter = 0;
227 while iter < MAXITER {
228 if ((rts - xhi) * df - f) * ((rts - xlo) * df - f) >= 0.0
229 || ((2.0 * f).abs() > (dxold * df).abs())
230 {
231 dxold = dx;
233 dx = 0.5 * (xhi - xlo);
234 rts = xlo + dx;
235 if xlo == rts {
236 return Ok(rts);
237 }
238 } else {
239 dxold = dx;
241 dx = f / df;
242 let temp = rts;
243 rts -= dx;
244 if temp == rts {
245 return Ok(rts);
246 }
247 }
248
249 if dx.abs() < xacc {
250 return Ok(rts);
251 }
252
253 (f, df) = func(rts)?;
254 if f < 0.0 {
255 xlo = rts;
256 } else {
257 xhi = rts;
258 }
259 iter += 1;
260 }
261
262 Err(RocheError::RtsafeError(
263 "Maximum number of iterations exceeded in rtsafe".to_string(),
264 ))
265}