astroceleste-engine 0.0.3__tar.gz → 0.0.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. {astroceleste_engine-0.0.3 → astroceleste_engine-0.0.4}/Cargo.lock +3 -3
  2. {astroceleste_engine-0.0.3 → astroceleste_engine-0.0.4}/Cargo.toml +1 -1
  3. {astroceleste_engine-0.0.3 → astroceleste_engine-0.0.4}/PKG-INFO +1 -1
  4. {astroceleste_engine-0.0.3 → astroceleste_engine-0.0.4}/crates/astroceleste-engine/README.md +4 -3
  5. {astroceleste_engine-0.0.3 → astroceleste_engine-0.0.4}/crates/astroceleste-engine/src/almanac.rs +49 -1
  6. {astroceleste_engine-0.0.3 → astroceleste_engine-0.0.4}/crates/astroceleste-engine/src/chart.rs +57 -11
  7. astroceleste_engine-0.0.4/crates/astroceleste-engine/src/election.rs +1688 -0
  8. {astroceleste_engine-0.0.3 → astroceleste_engine-0.0.4}/crates/astroceleste-engine/src/horary.rs +77 -20
  9. {astroceleste_engine-0.0.3 → astroceleste_engine-0.0.4}/crates/astroceleste-engine/src/houses.rs +14 -2
  10. {astroceleste_engine-0.0.3 → astroceleste_engine-0.0.4}/crates/astroceleste-engine/src/lib.rs +8 -0
  11. {astroceleste_engine-0.0.3 → astroceleste_engine-0.0.4}/crates/astroceleste-engine/src/planets.rs +10 -1
  12. {astroceleste_engine-0.0.3 → astroceleste_engine-0.0.4}/crates/astroceleste-engine-py/Cargo.toml +1 -1
  13. {astroceleste_engine-0.0.3 → astroceleste_engine-0.0.4}/crates/astroceleste-engine-py/astroceleste_engine.pyi +24 -0
  14. {astroceleste_engine-0.0.3 → astroceleste_engine-0.0.4}/crates/astroceleste-engine-py/src/lib.rs +74 -2
  15. {astroceleste_engine-0.0.3 → astroceleste_engine-0.0.4}/crates/astroceleste-engine-py/tests/test_engine.py +26 -0
  16. {astroceleste_engine-0.0.3 → astroceleste_engine-0.0.4}/LICENSE-APACHE +0 -0
  17. {astroceleste_engine-0.0.3 → astroceleste_engine-0.0.4}/LICENSE-MIT +0 -0
  18. {astroceleste_engine-0.0.3 → astroceleste_engine-0.0.4}/NOTICE +0 -0
  19. {astroceleste_engine-0.0.3 → astroceleste_engine-0.0.4}/README.md +0 -0
  20. {astroceleste_engine-0.0.3 → astroceleste_engine-0.0.4}/crates/astroceleste-engine/Cargo.toml +0 -0
  21. {astroceleste_engine-0.0.3 → astroceleste_engine-0.0.4}/crates/astroceleste-engine/LICENSE-APACHE +0 -0
  22. {astroceleste_engine-0.0.3 → astroceleste_engine-0.0.4}/crates/astroceleste-engine/LICENSE-MIT +0 -0
  23. {astroceleste_engine-0.0.3 → astroceleste_engine-0.0.4}/crates/astroceleste-engine/NOTICE +0 -0
  24. {astroceleste_engine-0.0.3 → astroceleste_engine-0.0.4}/crates/astroceleste-engine/src/aspects.rs +0 -0
  25. {astroceleste_engine-0.0.3 → astroceleste_engine-0.0.4}/crates/astroceleste-engine/src/catalog/derived_meanings.rs +0 -0
  26. {astroceleste_engine-0.0.3 → astroceleste_engine-0.0.4}/crates/astroceleste-engine/src/catalog/fixed_stars.rs +0 -0
  27. {astroceleste_engine-0.0.3 → astroceleste_engine-0.0.4}/crates/astroceleste-engine/src/catalog/mansions.rs +0 -0
  28. {astroceleste_engine-0.0.3 → astroceleste_engine-0.0.4}/crates/astroceleste-engine/src/catalog/mod.rs +0 -0
  29. {astroceleste_engine-0.0.3 → astroceleste_engine-0.0.4}/crates/astroceleste-engine/src/chiron.rs +0 -0
  30. {astroceleste_engine-0.0.3 → astroceleste_engine-0.0.4}/crates/astroceleste-engine/src/constants.rs +0 -0
  31. {astroceleste_engine-0.0.3 → astroceleste_engine-0.0.4}/crates/astroceleste-engine/src/data/chiron.bin +0 -0
  32. {astroceleste_engine-0.0.3 → astroceleste_engine-0.0.4}/crates/astroceleste-engine/src/data/deltat.bin +0 -0
  33. {astroceleste_engine-0.0.3 → astroceleste_engine-0.0.4}/crates/astroceleste-engine/src/derived.rs +0 -0
  34. {astroceleste_engine-0.0.3 → astroceleste_engine-0.0.4}/crates/astroceleste-engine/src/ephemeris/kernels.rs +0 -0
  35. {astroceleste_engine-0.0.3 → astroceleste_engine-0.0.4}/crates/astroceleste-engine/src/ephemeris/mod.rs +0 -0
  36. {astroceleste_engine-0.0.3 → astroceleste_engine-0.0.4}/crates/astroceleste-engine/src/ephemeris/observe.rs +0 -0
  37. {astroceleste_engine-0.0.3 → astroceleste_engine-0.0.4}/crates/astroceleste-engine/src/ephemeris/spk.rs +0 -0
  38. {astroceleste_engine-0.0.3 → astroceleste_engine-0.0.4}/crates/astroceleste-engine/src/error.rs +0 -0
  39. {astroceleste_engine-0.0.3 → astroceleste_engine-0.0.4}/crates/astroceleste-engine/src/fixed_stars.rs +0 -0
  40. {astroceleste_engine-0.0.3 → astroceleste_engine-0.0.4}/crates/astroceleste-engine/src/frames/mod.rs +0 -0
  41. {astroceleste_engine-0.0.3 → astroceleste_engine-0.0.4}/crates/astroceleste-engine/src/frames/nutation.rs +0 -0
  42. {astroceleste_engine-0.0.3 → astroceleste_engine-0.0.4}/crates/astroceleste-engine/src/frames/nutation_data.rs +0 -0
  43. {astroceleste_engine-0.0.3 → astroceleste_engine-0.0.4}/crates/astroceleste-engine/src/frames/precession.rs +0 -0
  44. {astroceleste_engine-0.0.3 → astroceleste_engine-0.0.4}/crates/astroceleste-engine/src/frames/sidereal.rs +0 -0
  45. {astroceleste_engine-0.0.3 → astroceleste_engine-0.0.4}/crates/astroceleste-engine/src/instant.rs +0 -0
  46. {astroceleste_engine-0.0.3 → astroceleste_engine-0.0.4}/crates/astroceleste-engine/src/lots.rs +0 -0
  47. {astroceleste_engine-0.0.3 → astroceleste_engine-0.0.4}/crates/astroceleste-engine/src/lunar.rs +0 -0
  48. {astroceleste_engine-0.0.3 → astroceleste_engine-0.0.4}/crates/astroceleste-engine/src/pyfloat.rs +0 -0
  49. {astroceleste_engine-0.0.3 → astroceleste_engine-0.0.4}/crates/astroceleste-engine/src/symbolic.rs +0 -0
  50. {astroceleste_engine-0.0.3 → astroceleste_engine-0.0.4}/crates/astroceleste-engine/src/temperament.rs +0 -0
  51. {astroceleste_engine-0.0.3 → astroceleste_engine-0.0.4}/crates/astroceleste-engine/src/time.rs +0 -0
  52. {astroceleste_engine-0.0.3 → astroceleste_engine-0.0.4}/crates/astroceleste-engine/src/zodiac.rs +0 -0
  53. {astroceleste_engine-0.0.3 → astroceleste_engine-0.0.4}/crates/astroceleste-engine-py/LICENSE-APACHE +0 -0
  54. {astroceleste_engine-0.0.3 → astroceleste_engine-0.0.4}/crates/astroceleste-engine-py/LICENSE-MIT +0 -0
  55. {astroceleste_engine-0.0.3 → astroceleste_engine-0.0.4}/crates/astroceleste-engine-py/NOTICE +0 -0
  56. {astroceleste_engine-0.0.3 → astroceleste_engine-0.0.4}/crates/astroceleste-engine-py/README.md +0 -0
  57. {astroceleste_engine-0.0.3 → astroceleste_engine-0.0.4}/pyproject.toml +0 -0
@@ -4,7 +4,7 @@ version = 4
4
4
 
5
5
  [[package]]
6
6
  name = "astroceleste-engine"
7
- version = "0.0.3"
7
+ version = "0.0.4"
8
8
  dependencies = [
9
9
  "serde",
10
10
  "serde_json",
@@ -12,7 +12,7 @@ dependencies = [
12
12
 
13
13
  [[package]]
14
14
  name = "astroceleste-engine-py"
15
- version = "0.0.3"
15
+ version = "0.0.4"
16
16
  dependencies = [
17
17
  "astroceleste-engine",
18
18
  "pyo3",
@@ -22,7 +22,7 @@ dependencies = [
22
22
 
23
23
  [[package]]
24
24
  name = "astroceleste-engine-wasm"
25
- version = "0.0.3"
25
+ version = "0.0.4"
26
26
  dependencies = [
27
27
  "astroceleste-engine",
28
28
  "js-sys",
@@ -3,7 +3,7 @@ resolver = "2"
3
3
  members = ["crates/astroceleste-engine", "crates/astroceleste-engine-py"]
4
4
 
5
5
  [workspace.package]
6
- version = "0.0.3"
6
+ version = "0.0.4"
7
7
  edition = "2021"
8
8
  rust-version = "1.85"
9
9
  license = "MIT OR Apache-2.0"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: astroceleste-engine
3
- Version: 0.0.3
3
+ Version: 0.0.4
4
4
  Classifier: Development Status :: 2 - Pre-Alpha
5
5
  Classifier: Programming Language :: Rust
6
6
  Classifier: Programming Language :: Python :: 3
@@ -39,14 +39,15 @@ println!("{}", serde_json::to_string_pretty(&chart)?);
39
39
  ```
40
40
 
41
41
  Charts serialize to the same JSON as the Astroceleste API. Other entry points:
42
- `calculate_horary_chart`, `calculate_transit_chart`, `calculate_synastry` and
43
- `calculate_derived_chart`. In the browser or on mobile, load the kernel from memory with
42
+ `calculate_horary_chart`, `calculate_transit_chart`, `calculate_synastry`,
43
+ `calculate_derived_chart`, and for electional astrology `calculate_election_chart` and
44
+ `search_elections` (the best moments over a span of time). In the browser or on mobile, load the kernel from memory with
44
45
  `Spk::from_bytes`.
45
46
 
46
47
  ## Bindings
47
48
 
48
49
  - **Python** (`pip install astroceleste-engine`, CPython ≥ 3.12): `Engine([kernel paths])`
49
- with `.chart()`, `.horary()`, `.transit()`, plus `synastry()` and `derived_chart()`,
50
+ with `.chart()`, `.horary()`, `.transit()`, `.election()`, `.elections()`, plus `synastry()` and `derived_chart()`,
50
51
  returning plain dicts. See [crates/astroceleste-engine-py](https://github.com/ffalcinelli/astroceleste-engine/tree/main/crates/astroceleste-engine-py).
51
52
  - **JavaScript / WebAssembly** (`npm install astroceleste-engine`): the same API over
52
53
  kernels loaded from memory, for browsers and Node. See
@@ -155,7 +155,55 @@ pub fn rise_set(
155
155
  let t_start = Time::from_ut1(jd_utc - 1.5);
156
156
  let t_end = Time::from_ut1(jd_utc + 1.5);
157
157
  let events = find_transitions(kernel, &site, t_start.tt(), t_end.tt())?;
158
+ Ok(select_rise_set(&events, jd_utc))
159
+ }
160
+
161
+ /// Sunrises and sunsets over a span, found once for all the days in it.
162
+ pub(crate) struct SunEvents {
163
+ /// (TT Julian date, whether it is a sunrise), in time order.
164
+ events: Vec<(f64, bool)>,
165
+ /// TT span searched.
166
+ span: (f64, f64),
167
+ }
168
+
169
+ impl SunEvents {
170
+ /// Every sunrise and sunset from UT1 Julian date `jd0` to `jd1`, when a single kernel
171
+ /// covers the whole span (`None` otherwise).
172
+ pub fn find(
173
+ kernels: &KernelSet,
174
+ jd0: f64,
175
+ jd1: f64,
176
+ latitude: f64,
177
+ longitude: f64,
178
+ ) -> Result<Option<Self>, EngineError> {
179
+ let kernel = require_kernel(kernels, jd0)?;
180
+ if !(kernel.start_jd..=kernel.end_jd).contains(&jd1) {
181
+ return Ok(None);
182
+ }
183
+ let site = Site::new(latitude, longitude);
184
+ let span = (Time::from_ut1(jd0).tt(), Time::from_ut1(jd1).tt());
185
+ let events = find_transitions(kernel, &site, span.0, span.1)?;
186
+ Ok(Some(SunEvents { events, span }))
187
+ }
188
+
189
+ /// [`rise_set`] for `jd_utc`, from the events of its ±1.5 day window; `None` when
190
+ /// the window is not inside the span searched.
191
+ pub fn rise_set(&self, jd_utc: f64) -> Option<(f64, f64, f64)> {
192
+ let (t0, t1) = (
193
+ Time::from_ut1(jd_utc - 1.5).tt(),
194
+ Time::from_ut1(jd_utc + 1.5).tt(),
195
+ );
196
+ if t0 < self.span.0 || t1 > self.span.1 {
197
+ return None;
198
+ }
199
+ let from = self.events.partition_point(|e| e.0 < t0);
200
+ let to = self.events.partition_point(|e| e.0 <= t1);
201
+ Some(select_rise_set(&self.events[from..to], jd_utc))
202
+ }
203
+ }
158
204
 
205
+ /// The day's sunrise, sunset and next sunrise among the events around `jd_utc`.
206
+ fn select_rise_set(events: &[(f64, bool)], jd_utc: f64) -> (f64, f64, f64) {
159
207
  let ut1 = |jd_tt: f64| Time::from_tt_jd(jd_tt).ut1();
160
208
  let sunrises: Vec<f64> = events.iter().filter(|e| e.1).map(|e| ut1(e.0)).collect();
161
209
  let sunsets: Vec<f64> = events.iter().filter(|e| !e.1).map(|e| ut1(e.0)).collect();
@@ -176,5 +224,5 @@ pub fn rise_set(
176
224
  .copied()
177
225
  .find(|&s| s > rise + 0.1)
178
226
  .unwrap_or(rise + 1.0);
179
- Ok((rise, set, next_rise))
227
+ (rise, set, next_rise)
180
228
  }
@@ -8,7 +8,7 @@ use crate::aspects::{natal_aspects, Aspect, OrbSettings, Point};
8
8
  use crate::ephemeris::KernelSet;
9
9
  use crate::error::EngineError;
10
10
  use crate::fixed_stars::{fixed_stars, FixedStarPosition};
11
- use crate::houses::{calculate_houses, HouseSystem};
11
+ use crate::houses::{calculate_houses, HouseSystem, Houses};
12
12
  use crate::instant::UtcInstant;
13
13
  use crate::lots::{arabic_parts, Lot};
14
14
  use crate::lunar::{lunar_status, LunarStatus};
@@ -16,7 +16,7 @@ use crate::planets::calculate_planets;
16
16
  use crate::symbolic::symbolic_degree_number;
17
17
  use crate::temperament::{temperament, Temperament};
18
18
  use crate::zodiac::{
19
- ayanamsa, ayanamsa_info, determine_house, longitude_to_zodiac, DEFAULT_AYANAMSA,
19
+ ayanamsa, ayanamsa_info, determine_house, longitude_to_zodiac, AyanamsaInfo, DEFAULT_AYANAMSA,
20
20
  };
21
21
 
22
22
  /// Every body a complete chart carries; missing ones are reported in
@@ -200,15 +200,28 @@ fn symbol_of(name: &str) -> &'static str {
200
200
  }
201
201
  }
202
202
 
203
- /// Compute a chart (`calculate_chart_data`).
204
- pub fn calculate_chart(kernels: &KernelSet, req: &ChartRequest) -> Result<Chart, EngineError> {
203
+ /// The positions part of a chart: placements and house cusps, without aspects, fixed
204
+ /// stars, lots or temperament. Electional searches evaluate thousands of these.
205
+ pub(crate) struct Sky {
206
+ pub zodiac_type: &'static str,
207
+ pub is_sidereal: bool,
208
+ pub ayanamsa_code: &'static str,
209
+ pub info: AyanamsaInfo,
210
+ pub jd: f64,
211
+ /// Cusps 1-12, degrees.
212
+ pub cusps: Vec<f64>,
213
+ pub house_cusps: Vec<HouseCusp>,
214
+ pub planets: Vec<Placement>,
215
+ }
216
+
217
+ /// Placements and houses of a request (the first half of [`calculate_chart`]).
218
+ pub(crate) fn sky(kernels: &KernelSet, req: &ChartRequest) -> Result<Sky, EngineError> {
205
219
  let zodiac_type = match req.zodiac_type.trim().to_lowercase().as_str() {
206
220
  "sidereal" => "sidereal",
207
221
  _ => "tropical",
208
222
  };
209
223
  let is_sidereal = zodiac_type == "sidereal";
210
224
  let ayanamsa_code = ayanamsa(req.ayanamsa).code;
211
- let orbs = OrbSettings::merge(req.orb_settings)?;
212
225
 
213
226
  let jd = req.instant.julian_day();
214
227
  let info = ayanamsa_info(jd, ayanamsa_code);
@@ -242,16 +255,34 @@ pub fn calculate_chart(kernels: &KernelSet, req: &ChartRequest) -> Result<Chart,
242
255
  })
243
256
  .collect();
244
257
 
245
- let mut raw: Vec<(&'static str, f64, f64, bool)> = engine
258
+ let raw: Vec<(&'static str, f64, f64, bool)> = engine
246
259
  .bodies
247
260
  .iter()
248
261
  .map(|b| (b.name, b.longitude, b.speed, b.is_retrograde))
249
262
  .collect();
263
+ let planets = placements(raw, &houses);
264
+
265
+ Ok(Sky {
266
+ zodiac_type,
267
+ is_sidereal,
268
+ ayanamsa_code,
269
+ info,
270
+ jd,
271
+ cusps,
272
+ house_cusps,
273
+ planets,
274
+ })
275
+ }
276
+
277
+ /// Placements of bodies given as (name, longitude, speed, retrograde), followed by the
278
+ /// Ascendant and the Midheaven, with their houses.
279
+ pub(crate) fn placements(
280
+ mut raw: Vec<(&'static str, f64, f64, bool)>,
281
+ houses: &Houses,
282
+ ) -> Vec<Placement> {
250
283
  raw.push(("Ascendant", houses.ascendant, 0.0, false));
251
284
  raw.push(("Midheaven", houses.midheaven, 0.0, false));
252
-
253
- let planets: Vec<Placement> = raw
254
- .iter()
285
+ raw.iter()
255
286
  .map(|&(name, lon, speed, is_retrograde)| {
256
287
  let z = longitude_to_zodiac(lon);
257
288
  Placement {
@@ -262,13 +293,28 @@ pub fn calculate_chart(kernels: &KernelSet, req: &ChartRequest) -> Result<Chart,
262
293
  degree: z.degree,
263
294
  minute: z.minute,
264
295
  ecliptic_longitude: lon,
265
- house: determine_house(lon, &cusps),
296
+ house: determine_house(lon, &houses.cusps),
266
297
  speed,
267
298
  is_retrograde,
268
299
  symbolic_degree: symbolic_degree_number(z.degree, z.minute),
269
300
  }
270
301
  })
271
- .collect();
302
+ .collect()
303
+ }
304
+
305
+ /// Compute a chart (`calculate_chart_data`).
306
+ pub fn calculate_chart(kernels: &KernelSet, req: &ChartRequest) -> Result<Chart, EngineError> {
307
+ let orbs = OrbSettings::merge(req.orb_settings)?;
308
+ let Sky {
309
+ zodiac_type,
310
+ is_sidereal,
311
+ ayanamsa_code,
312
+ info,
313
+ jd,
314
+ cusps,
315
+ house_cusps,
316
+ planets,
317
+ } = sky(kernels, req)?;
272
318
  let points: Vec<Point> = planets
273
319
  .iter()
274
320
  .map(|p| Point {