1use crate::errors::RocheError;
2use crate::{Star, Vec3};
3use crate::{drpot1, drpot2, rpot1, rpot2};
4use pyo3::prelude::*;
5
6#[pyfunction]
31pub fn face(
32 q: f64,
33 star: Star,
34 spin: f64,
35 direction: Vec3,
36 rref: f64,
37 pref: f64,
38 acc: f64,
39) -> Result<(Vec3, Vec3, f64, f64), RocheError> {
40 let mut pvec: Vec3;
41 let mut r: f64;
42
43 let cofm: Vec3 = match star {
44 Star::Primary => Vec3::cofm1(),
45 Star::Secondary => Vec3::cofm2(),
46 };
47
48 let rp: fn(f64, f64, &Vec3) -> Result<f64, RocheError> = match star {
49 Star::Primary => rpot1,
50 Star::Secondary => rpot2,
51 };
52
53 let drp: fn(f64, f64, &Vec3) -> Result<Vec3, RocheError> = match star {
54 Star::Primary => drpot1,
55 Star::Secondary => drpot2,
56 };
57
58 let mut tref: f64 = rp(q, spin, &(cofm + rref * direction))?;
59 if tref < pref {
60 let message = format!(
61 "point at reference radius {} appears to be at lower potential {} than the reference potential {}",
62 rref, tref, pref
63 );
64 return Err(RocheError::FaceError(message));
65 }
66
67 let mut r1: f64 = rref / 2.;
68 let mut r2: f64 = rref;
69 tref = pref + 1.;
70
71 const MAXSEARCH: i32 = 30;
72 let mut i: i32 = 0;
73 while i < MAXSEARCH && tref > pref {
74 r1 = r2 / 2.;
75 tref = rp(q, spin, &(cofm + r1 * direction))?;
76 if tref > pref {
77 r2 = r1;
78 }
79 i += 1;
80 }
81 if tref > pref {
82 let message = "could not find a radius with a potential below the reference potential; probably bad inputs.";
83 return Err(RocheError::FaceError(message.to_string()));
84 }
85
86 const MAXCHOP: i32 = 100;
87 let mut nchop: i32 = 0;
88 while r2 - r1 > acc && nchop < MAXCHOP {
89 r = (r1 + r2) / 2.;
90 pvec = cofm + r * direction;
91 if rp(q, spin, &pvec)? < pref {
92 r1 = r;
93 } else {
94 r2 = r;
95 }
96 nchop += 1;
97 }
98 if nchop == MAXCHOP {
99 return Err(RocheError::FaceError(
100 "reached maximum number of binary chops".to_string(),
101 ));
102 }
103 r = (r1 + r2) / 2.;
104 pvec = cofm + r * direction;
105 let mut dvec: Vec3 = drp(q, spin, &pvec)?;
106 let g = dvec.length();
107 dvec /= g;
108 Ok((pvec, dvec, r, g))
109}
110
111#[cfg(test)]
112mod tests {
113 use crate::ref_sphere::ref_sphere;
114
115 use super::*;
116
117 #[test]
118 fn face_test() -> Result<(), RocheError> {
119 let dirn = Vec3::new(1.0, 1.0, 1.0).norm();
121 let (rref, pref) = ref_sphere(0.2, Star::Secondary, 1.0, 0.8)?;
122 let (pvec, dvec, r, g) = face(0.2, Star::Secondary, 1.0, dirn, rref, pref, 1.0e-5)?;
123 assert_eq!(
124 pvec,
125 Vec3::new(1.1352429036726859, 0.13524290367268593, 0.13524290367268593)
126 );
127 assert_eq!(
128 dvec,
129 Vec3::new(0.49134297246922065, 0.5917653290265037, 0.6390585878988438)
130 );
131 assert_eq!(r, 0.2342475805242355);
132 assert_eq!(g, 2.8596655611690993);
133 Ok(())
134 }
135}