Skip to main content

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}