roche/sphere_eclipse.rs
1use crate::Vec3;
2use pyo3::prelude::*;
3use std::f64::consts::TAU;
4
5///
6/// sphere_eclipse tells you whether or not a given sphere will eclipse a
7/// given point or not. If the answer is yes, it will also return with four
8/// parameters to define the phase range and the multiplier range delimiting the
9/// region within which the spheres surface is crossed.
10/// These can then be used as the starting point for later computation.
11/// (The line of sight is described as the point in question plus a scalar multiplier
12/// times a unit vector pointing towards Earth -- this is the "multiplier" referred to above
13/// and below). The multiplier must be positive: in other words the routine does not
14/// project backwards. If the point in inside the sphere, phi1 will be set = 0, phi2 = 1,
15/// lam1 = 0, and lam2 = the largest value of the multiplier lambda
16///
17/// Arguments:
18///
19/// * `cosi`: cosine of orbital inclination
20/// * `sini`: sine of orbital inclination
21/// * `r`: the position vector of the point in question (units of binary separation)
22/// * `c`: the centre of the sphere enclosing the star (units of binary separation)
23/// * `rsphere`: the radius defining the sphere enclosing the star (units of binary separation)
24/// * `phi1`: if eclipse by the sphere, this is the start of the phase range (0 -1)
25/// * `phi2`: if eclipse by the sphere, this is the end of the phase range (least amount > phi1)
26/// * `lam1`: if eclipse by the sphere, this is the start of the multiplier range (>=0)
27/// * `lam2`: if eclipse by the sphere, this is the end of the multiplier range (>lam1)
28///
29/// Returns:
30///
31/// * false = not eclipsed; true = eclipsed.
32///
33pub fn sphere_eclipse(
34 cosi: f64,
35 sini: f64,
36 r: &Vec3,
37 c: &Vec3,
38 rsphere: f64,
39 phi1: &mut f64,
40 phi2: &mut f64,
41 lam1: &mut f64,
42 lam2: &mut f64,
43) -> bool {
44 let d: Vec3 = *r - *c;
45
46 let pdist: f64 = (d.x * d.x + d.y * d.y).sqrt();
47 let bquad: f64 = d.z * cosi - pdist * sini;
48 if bquad >= 0. {
49 return false;
50 }
51 let cquad: f64 = d.sqr() - rsphere * rsphere;
52
53 let mut fac: f64 = bquad * bquad - cquad;
54 if fac <= 0.0 {
55 return false;
56 }
57 fac = fac.sqrt();
58
59 *lam2 = -bquad + fac;
60 *lam1 = 0_f64.max(cquad / (*lam2));
61
62 if cquad < 0. {
63 *phi1 = 0.;
64 *phi2 = 1.
65 } else {
66 let delta: f64 = ((cosi * d.z + cquad.sqrt()) / (sini * pdist)).acos();
67 let phi: f64 = d.y.atan2(-d.x);
68 *phi1 = (phi - delta) / TAU;
69 *phi1 -= phi1.floor();
70 *phi2 = *phi1 + 2. * delta / TAU;
71 }
72 true
73}
74
75// wrapper of the above function to use with Python (e.g avoiding mutable arguments)
76
77///
78/// sphere_eclipse tells you whether or not a given sphere will eclipse a
79/// given point or not. If the answer is yes, it will also return with four
80/// parameters to define the phase range and the multiplier range delimiting the
81/// region within which the spheres surface is crossed.
82/// These can then be used as the starting point for later computation.
83/// (The line of sight is described as the point in question plus a scalar multiplier
84/// times a unit vector pointing towards Earth -- this is the "multiplier" referred to above
85/// and below). The multiplier must be positive: in other words the routine does not
86/// project backwards. If the point in inside the sphere, phi1 will be set = 0, phi2 = 1,
87/// lam1 = 0, and lam2 = the largest value of the multiplier lambda
88///
89/// Arguments:
90///
91/// * `cosi`: cosine of orbital inclination
92/// * `sini`: sine of orbital inclination
93/// * `r (Vec3)`: the position vector of the point in question (units of binary separation)
94/// * `c (Vec3)`: the centre of the sphere enclosing the star (units of binary separation)
95/// * `rsphere`: the radius defining the sphere enclosing the star (units of binary separation)
96///
97/// Returns:
98///
99/// * (eclipsed, phi1, phi2, lam1, lam2)
100///
101#[pyfunction]
102#[pyo3(name = "sphere_eclipse")]
103pub fn sphere_eclipse_wrapper(
104 cosi: f64,
105 sini: f64,
106 r: &Vec3,
107 c: &Vec3,
108 rsphere: f64,
109) -> (bool, f64, f64, f64, f64) {
110 let mut phi1 = 0.0;
111 let mut phi2 = 0.0;
112 let mut lam1 = 0.0;
113 let mut lam2 = 0.0;
114
115 let eclipsed = sphere_eclipse(
116 cosi, sini, r, c, rsphere, &mut phi1, &mut phi2, &mut lam1, &mut lam2,
117 );
118
119 (eclipsed, phi1, phi2, lam1, lam2)
120}
121
122///
123/// This version of sphere_eclipse tells you whether or not a given sphere will eclipse
124/// a given point at a particular phase or not. If the answer is yes,
125/// it will also return with the multiplier values giving the cut points. These can then
126/// be used as starting points for Roche lobe computations. These can then be used as the
127/// starting point for later computation. Points inside the sphere are regarded as being
128/// eclipsed with the lower mulitplier set = 0
129///
130/// Arguments:
131///
132/// * `earth`: vector towards Earth
133/// * `r`: the position vector of the point in question (units of binary separation)
134/// * `c`: the centre of the sphere (units of binary separation)
135/// * `rsphere`: the radius defining the sphere enclosing the star (units of binary separation)
136/// * `lam1`: if eclipse by the sphere, this is the start of the multiplier range
137/// * `lam2`: if eclipse by the sphere, this is the end of the multiplier range
138///
139/// Returns:
140///
141/// * false = not eclipsed; true = eclipsed.
142///
143pub fn sphere_eclipse_vector(
144 earth: &Vec3,
145 r: &Vec3,
146 c: &Vec3,
147 rsphere: f64,
148 lam1: &mut f64,
149 lam2: &mut f64,
150) -> bool {
151 let d: Vec3 = *r - *c;
152
153 let bquad: f64 = earth.dot(&d);
154 if bquad >= 0. {
155 return false;
156 }
157 let cquad: f64 = d.sqr() - rsphere * rsphere;
158
159 let mut fac: f64 = bquad * bquad - cquad;
160 if fac <= 0. {
161 return false;
162 }
163 fac = fac.sqrt();
164
165 *lam2 = -bquad + fac;
166 *lam1 = 0_f64.max(cquad / (*lam2));
167 true
168}
169
170// wrapper of the above function to use with Python (e.g avoiding mutable arguments)
171
172///
173/// This version of sphere_eclipse tells you whether or not a given sphere will eclipse
174/// a given point at a particular phase or not. If the answer is yes,
175/// it will also return with the multiplier values giving the cut points. These can then
176/// be used as starting points for Roche lobe computations. These can then be used as the
177/// starting point for later computation. Points inside the sphere are regarded as being
178/// eclipsed with the lower multiplier set = 0.
179/// The multiplier along the line of sight is lambda, with the smallest and largest values
180/// returned as lam1 and lam2, respectively.
181///
182/// Arguments:
183///
184/// * `earth`: vector towards Earth
185/// * `r`: the position vector of the point in question (units of binary separation)
186/// * `c`: the centre of the sphere (units of binary separation)
187/// * `rsphere`: the radius defining the sphere enclosing the star (units of binary separation)
188///
189/// Returns:
190///
191/// * (eclipsed, lam1, lam2)
192///
193#[pyfunction]
194#[pyo3(name = "sphere_eclipse_vector")]
195pub fn sphere_eclipse_vector_wrapper(
196 earth: &Vec3,
197 r: &Vec3,
198 c: &Vec3,
199 rsphere: f64,
200) -> (bool, f64, f64) {
201 let mut lam1 = 0.0;
202 let mut lam2 = 0.0;
203
204 let eclipsed = sphere_eclipse_vector(earth, r, c, rsphere, &mut lam1, &mut lam2);
205
206 (eclipsed, lam1, lam2)
207}