Skip to main content

roche/
vstream_physics.rs

1use crate::errors::RocheError;
2use crate::x_l1;
3use crate::{Vec3, vel_transform, strinit, stradv, strmnx, rocacc, OrbitalSystem};
4use bulirsch::{self, Integrator};
5use pyo3::prelude::*;
6
7
8
9///
10/// vstream computes the path of the gas stream in a binary in velocity space.
11/// There are a few different options for the type of stream velocities produced.
12/// vstream works by integrating the equations of motion for the Roche
13/// potential using Burlisch-Stoer integration. Every time the speed
14/// changes by step, it interpolates and stores a new point.
15///
16/// The velocities are inertial frame velocities for comparison with Doppler maps.
17/// 
18/// Arguments:
19/// 
20/// * `q`: mass ratio = M2/M1. Stream flows from star 2 to 1.
21/// * `step`: step between points (units of K1+K2).
22/// * `n_points`: number of points to compute.
23/// * `transform_type`: type of velocity, see !!ref{vtrans.html}{vtrans} for supported types.
24/// 
25/// Returns:
26/// 
27/// * `vx`: array of x velocities.
28/// * `vy`: array of y velocities.
29/// * `rad`: array of radii equivalent to velocities (units of a)
30///
31pub fn vstream(q: f64, step: f64, n_points: usize, transform_type: i32) -> Result<(Vec<f64>, Vec<f64>, Vec<f64>), RocheError> {
32
33    if n_points < 2 {
34        return Err(RocheError::ParameterError(
35            "Need at least 2 points in the stream.".to_string(),
36        ));
37    }
38
39    let mut vx_arr: Vec<f64> = vec![];
40    let mut vy_arr: Vec<f64> = vec![];
41    let mut r_arr: Vec<f64> = vec![];
42    let mut frac: f64;
43    let mut acc: f64;
44    let mut ar_x: f64;
45    let mut ar_y: f64;
46
47    // Initialise stream
48    let rl1: f64 = x_l1(q)?;
49    let (mut r, mut v) = strinit(q)?;
50
51    // Store L1 as first point
52    let (mut tvx, mut tvy) = vel_transform(q, transform_type, rl1, 0.0, 0.0, 0.0)?;
53    vx_arr.push(tvx);
54    vy_arr.push(tvy);
55    let mut lp: usize = 0;
56
57    // Store interpolation between L1 and initial point if step
58    // has been set small enough
59
60    (tvx, tvy) = vel_transform(q, transform_type, r.x, r.y, v.x, v.y)?;
61    let mut delta_velocity: f64 = (tvx - vx_arr[0]).hypot(tvy - vy_arr[0]);
62    if delta_velocity > step {
63        frac = step / delta_velocity;
64        vx_arr.push(vx_arr[0] + (tvx - vx_arr[0]) * frac);
65        vy_arr.push(vy_arr[0] + (tvy - vy_arr[0]) * frac);
66        r_arr.push(r.x.hypot(r.y));
67        lp += 1;
68    }
69
70    let mut delta_t: f64 = 1.0e-3;
71    let smax: f64 = 1.0e-3_f64.min(step / 2.0);
72
73    // set up Bulirsch-Stoer integrator
74    let system = OrbitalSystem { q };
75    let mut integrator = Integrator::default()
76        .with_abs_tol(1.0e-8)
77        .with_rel_tol(1.0e-8)
78        .into_adaptive();
79    // Initialise arrays
80    let mut y = ndarray::array![r.x, r.y, r.z, v.x, v.y, v.z];
81    let mut y_next = ndarray::Array::zeros(y.raw_dim());
82
83    while lp < n_points - 1 {
84
85        integrator
86            .step(&system, delta_t, y.view(), y_next.view_mut())
87            .unwrap();
88        y.assign(&y_next);
89
90        r.set(y[0], y[1], y[2]);
91        v.set(y[3], y[4], y[5]);
92        (tvx, tvy) = vel_transform(q, transform_type, r.x, r.y, v.x, v.y)?;
93        delta_velocity = (tvx - vx_arr[lp]).hypot(tvy - vy_arr[lp]);
94        if delta_velocity > step {
95            frac = step / delta_velocity;
96            vx_arr.push(vx_arr[lp] + (tvx - vx_arr[lp]) * frac);
97            vy_arr.push(vy_arr[lp] + (tvy - vy_arr[lp]) * frac);
98            r_arr.push(r.x.hypot(r.y));
99            lp += 1;
100        }
101        (ar_x, ar_y, _) = rocacc(q, &r, &v);
102        acc = ar_x.hypot(ar_y);
103        delta_t = delta_t.min(smax/acc);
104    }
105
106    Ok((vx_arr, vy_arr, r_arr))
107
108}
109
110#[pyfunction]
111#[pyo3(name = "vstream")]
112#[pyo3(signature = (q, step=0.01, n_points=60, transform_type=1))]
113pub fn vstream_reg(q: f64, step: f64, n_points: usize, transform_type: i32) -> Result<(Vec<f64>, Vec<f64>), RocheError> {
114
115    const TLOC: f64 = 1.0e-8;
116    const RLOC: f64 = 1.0e-8;
117
118    if n_points < 2 {
119        return Err(RocheError::ParameterError(
120            "Need at least 2 points in the stream.".to_string(),
121        ));
122    }
123
124    let mut rm: Vec3;
125    let mut vm: Vec3;
126    let mut r_end: f64;
127    let mut vx_arr: Vec<f64> = vec![];
128    let mut vy_arr: Vec<f64> = vec![];
129
130    let rl1: f64 = x_l1(q)?;
131
132    let (mut tvx, mut tvy) = vel_transform(q, transform_type, rl1, 0.0, 0.0, 0.0)?;
133    vx_arr.push(tvx);
134    vy_arr.push(tvy);
135
136    let mut np: usize = 1;
137    let mut r_next: f64 = rl1 * (1.0 - step);
138    let mut r_decreasing: bool = true;
139
140    // Initialise stream
141    let (mut r, mut v) = strinit(q)?;
142
143    while np < n_points {
144
145        // Advance one step
146        stradv(q, &mut r, &mut v, r_next, RLOC, 1.0e-3);
147        (tvx, tvy) = vel_transform(q, transform_type, r.x, r.y, v.x, v.y)?;
148        vx_arr.push(tvx);
149        vy_arr.push(tvy);
150        np += 1;
151        r_next = if r_decreasing {r_next - rl1 * step} else {r_next + rl1 * step};
152
153        // Locate and store next turning point
154        rm = r;
155        vm = v;
156        strmnx(q, &mut rm, &mut vm, TLOC)?;
157        r_end = rm.length();
158
159        // Loop over all radii wanted before next turning point
160        while np < n_points && ((r_decreasing && r_next > r_end) || (!r_decreasing && r_next < r_end)) {
161            stradv(q, &mut r, &mut v, r_next, RLOC, 1.0e-3);
162            (tvx, tvy) = vel_transform(q, transform_type, r.x, r.y, v.x, v.y)?;
163            vx_arr.push(tvx);
164            vy_arr.push(tvy);
165            np += 1;
166            r_next = if r_decreasing {r_next - rl1 * step} else {r_next + rl1 * step};
167        }
168
169        // Change direction of search and move it to start at turning point
170        r = rm;
171        v = vm;
172        r_decreasing = !r_decreasing;
173        r_next = if r_decreasing {r_next - rl1 * step} else {r_next + rl1 * step};
174    }
175    
176    Ok((vx_arr, vy_arr))
177}
178
179