1use std::panic;
2use std::f64::consts::TAU;
3use crate::{Vec3, Star};
4use crate::{rpot_val, rpot_grad, rpot1, rpot2, drpot1, drpot2};
5use crate::{pot_min, dbrent, x_l1, x_l1_1, x_l1_2, x_l2, x_l3};
6use crate::{sphere_eclipse, sphere_eclipse_vector, set_earth};
7
8
9pub struct RocheContext {
18 pub q: f64,
19 pub star: Star,
20 pub spin: f64,
21 pub x_l1: f64,
22}
23
24impl RocheContext {
25
26 pub fn new(q: f64, star: Star, spin: f64) -> Self {
27 if q <= 0. {
28 panic!("q = {} <= 0", q);
29 }
30 let x_l1: f64 = match star {
31 Star::Primary => x_l1_1(q, spin),
32 Star::Secondary => x_l1_2(q, spin),
33 };
34 Self { q, star, spin, x_l1 }
35 }
36
37 pub fn potential(&self, earth: &Vec3, p:&Vec3, lam: f64) -> f64 {
38 rpot_val(self.q, self.star, self.spin, earth, p, lam)
39 }
40
41
42 pub fn gradient(&self, earth: &Vec3, p: &Vec3, lam: f64) -> (f64, f64) {
43 let (dp, dl) = rpot_grad(self.q, self.star, self.spin, earth, p, lam);
44 (dp, dl)
45 }
46
47
48 pub fn potential_grad(&self, earth: &Vec3, p: &Vec3, lam: f64) -> (f64, f64, f64) {
49 let f = self.potential(earth, p, lam);
50 let (dp, dl) = rpot_grad(self.q, self.star, self.spin, earth, p, lam);
51 (f, dp, dl)
52 }
53
54
55 pub fn ref_sphere(&self, ffac: f64) -> (f64, f64) {
56 let tref: f64;
57 let rref: f64;
58 let pref: f64;
59 if self.star == Star::Primary {
60 tref = self.x_l1;
61 rref = tref * 1.0_f64.min(1.001*ffac);
62 pref = rpot1(self.q, self.spin, &Vec3 { x: ffac*tref, y: 0.0, z: 0.0 });
63 (rref, pref)
64 } else if self.star == Star::Secondary {
65 tref = 1.0 - self.x_l1;
66 rref = tref * 1.0_f64.min(1.001*ffac);
67 pref = rpot2(self.q, self.spin, &Vec3 { x: 1.0 - ffac*tref, y: 0.0, z: 0.0 });
68 (rref, pref)
69 } else {
70 panic!("star is not an instance of Star")
71 }
72 }
73
74
75 pub fn fblink(&self, ffac: f64, acc: f64, earth: &Vec3, p: &Vec3) -> Result<bool, &'static str> {
76
77 let (rref, pref) = self.ref_sphere(ffac);
78
79 let cofm: Vec3 = match self.star {
80 Star::Primary => Vec3::cofm1(),
81 Star::Secondary => Vec3::cofm2(),
82 };
83
84 let mut lam1 = 0.0;
86 let mut lam2 = 0.0;
87 if !sphere_eclipse_vector(earth, p, &cofm, rref, &mut lam1, &mut lam2) {
88 return Ok(false);
89 }
90 if lam1 == 0.0 {
91 return Ok(true);
92 }
93
94 let func = |lam: f64| {
96 self.potential(earth, p, lam)
97 };
98
99 let mut nstep: i32 = 1;
103 let mut step: f64 = lam2 - lam1;
104
105 let mut f1: f64 = 0.0;
106 let mut f2: f64 = 0.0;
107 let mut flam: f64 = 1.0;
108 let mut lam: f64 = lam1;
109
110 while step > acc {
111
112 lam = lam1 + step/2.0;
113
114 for _ in 0..nstep {
115
116 flam = func(lam);
117 if flam <= pref {
118 return Ok(true);
119 }
120
121 if nstep == 1 {
123 f1 = func(lam1);
124 f2 = func(lam2);
125 }
126
127 if flam < f1 && flam < f2 {
128 break;
129 }
130
131 lam += step;
132 }
133 if flam < f1 && flam < f2 {
134 break;
135 }
136 step /= 2.0;
137 nstep *= 2;
138 }
139
140 if flam < f1 && flam < f2 {
141
142 let dfunc = |lam: f64| {
146 let (_dp, dl) = self.gradient(earth, p, lam);
147 dl
148 };
149
150 let (_xmin, flam) = dbrent(lam1, lam, lam2, |x| func(x), |x| dfunc(x), acc, true, pref)?;
151
152 Ok(flam < pref)
153 } else {
154 Ok(false)
157 }
158
159
160 }
161
162
163 pub fn face(&self, direction: Vec3, rref: f64, pref: f64, acc: f64) -> (Vec3, Vec3, f64, f64) {
164
165 let mut pvec: Vec3;
166 let mut r: f64;
167
168 let cofm: Vec3 = match self.star {
169 Star::Primary => Vec3::cofm1(),
170 Star::Secondary => Vec3::cofm2(),
171 };
172
173 let rp: fn(f64, f64, &Vec3) -> f64 = match self.star {
174 Star::Primary => rpot1,
175 Star::Secondary => rpot2,
176 };
177
178 let drp: fn(f64, f64, &Vec3) -> Vec3 = match self.star {
179 Star::Primary => drpot1,
180 Star::Secondary => drpot2,
181 };
182
183 let mut tref: f64 = rp(self.q, self.spin, &(cofm + rref*direction));
184 if tref < pref {
185 panic!("stuff")
186 }
187
188 let mut r1: f64 = rref/2.;
189 let mut r2: f64 = rref;
190 tref = pref + 1.;
191
192 const MAXSEARCH: i32 = 30;
193 let mut i: i32 = 0;
194 while i < MAXSEARCH && tref > pref {
195 r1 = r2/2.;
196 tref = rp(self.q, self.spin, &(cofm + r1*direction));
197 if tref > pref {
198 r2 = r1;
199 }
200 i+=1;
201 }
202 if tref > pref {
203 panic!("other stuff");
204 }
205
206 const MAXCHOP: i32 = 100;
207 let mut nchop: i32 = 0;
208 while r2 - r1 > acc && nchop < MAXCHOP {
209 r = (r1 + r2)/2.;
210 pvec = cofm + r*direction;
211 if rp(self.q, self.spin, &pvec) < pref {
212 r1 = r;
213 }else {
214 r2 = r;
215 }
216 nchop += 1;
217 }
218 if nchop == MAXCHOP {
219 panic!("even more stuff");
220 }
221 r = (r1 + r2)/2.;
222 pvec = cofm + r*direction;
223 let mut dvec: Vec3 = drp(self.q, self.spin, &pvec);
224 let g = dvec.length();
225 dvec /= g;
226 return (pvec, dvec, r, g)
227 }
228
229
230 pub fn ingress_egress(&self, ffac: f64, iangle: f64, delta: f64, r: &Vec3, ingress: &mut f64, egress: &mut f64) -> bool {
231 let rref: f64;
232 let pref: f64;
233 (rref, pref) = self.ref_sphere(ffac);
234 let ri: f64 = iangle.to_radians();
235 let (sini, cosi) = ri.sin_cos();
236
237 let cofm: Vec3 = match self.star {
238 Star::Primary => Vec3::cofm1(),
239 Star::Secondary => Vec3::cofm2(),
240 };
241
242 let mut phi1: f64 = 0.0;
243 let mut phi2: f64 = 0.0;
244 let mut lam1: f64 = 0.0;
245 let mut lam2: f64 = 0.0;
246 let mut phi: f64 = 0.0;
247 let mut lam: f64 = 0.0;
248
249 if sphere_eclipse(cosi, sini, r, &cofm, rref, &mut phi1, &mut phi2, &mut lam1, &mut lam2) {
250
251 let acc: f64 = 2.*(2.*TAU*(lam2 - lam1)*delta).sqrt();
252
253 if self.pot_min(cosi, sini, r, phi1, phi2, lam1, lam2, rref, pref, acc, &mut phi, &mut lam) {
254
255 let mut pin: f64 = phi;
256 let mut pout: f64 = phi1;
257 let mut pmid: f64;
258
259 while (pin - pout).abs() > delta {
260 pmid = (pin + pout)/2.0;
261 if self.fblink(ffac, acc, &set_earth(cosi, sini, pmid), r).unwrap() {
262 pin = pmid;
263 } else {
264 pout = pmid;
265 }
266 }
267 *ingress = (pin+pout)/2.0;
268 *ingress = *ingress - ingress.floor();
269
270 pin = phi;
271 pout = phi2;
272 while (pin-pout).abs() > delta {
273 pmid = (pin+pout)/2.;
274 if self.fblink(ffac, acc, &set_earth(cosi, sini, pmid), r).unwrap() {
275 pin = pmid;
276 } else {
277 pout = pmid;
278 }
279 }
280 *egress = (pin+pout)/2.0;
281 *egress = *egress - egress.floor();
282 if *egress < *ingress {
283 *egress += 1.0;
284 }
285 return true;
286 } else {
287 return false;
288 }
289 } else {
290 return false;
291 }
292
293 }
294
295
296 pub fn pot_min(&self, cosi: f64, sini: f64, p: &Vec3, phi1: f64, phi2: f64, lam1: f64, lam2: f64, rref: f64, pref: f64, acc: f64, phi: &mut f64, lam: &mut f64) -> bool {
297 pot_min(self.q, self.star, self.spin, cosi, sini, p, phi1, phi2, lam1, lam2, rref, pref, acc, phi, lam)
298 }
299
300
301 pub fn x_l1(&self) -> f64 {
302 x_l1(self.q)
303 }
304
305
306 pub fn x_l1_asyncronous(&self) -> f64 {
307 match self.star {
308 Star::Primary => x_l1_1(self.q, self.spin),
309 Star::Secondary => x_l1_2(self.q, self.spin),
310 }
311 }
312
313
314 pub fn x_l2(&self) -> f64 {
315 x_l2(self.q)
316 }
317
318
319 pub fn x_l3(&self) -> f64 {
320 x_l3(self.q)
321 }
322
323}
324