libaditya 0.3.4__py2.py3-none-any.whl

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 (131) hide show
  1. libaditya/__init__.py +46 -0
  2. libaditya/_version.py +29 -0
  3. libaditya/calc/__init__.py +9 -0
  4. libaditya/calc/api.py +30 -0
  5. libaditya/calc/avasthas.py +346 -0
  6. libaditya/calc/docs/jaimini-get-arch.md +95 -0
  7. libaditya/calc/hellenistic.py +37 -0
  8. libaditya/calc/jaimini.py +335 -0
  9. libaditya/calc/jaimini_get.py +240 -0
  10. libaditya/calc/kala.py +73 -0
  11. libaditya/calc/panchanga.py +422 -0
  12. libaditya/calc/rashi.py +612 -0
  13. libaditya/calc/returns.py +50 -0
  14. libaditya/calc/swe_functions.py +118 -0
  15. libaditya/calc/varga.py +287 -0
  16. libaditya/calc/vimshottari.py +431 -0
  17. libaditya/cards/__init__.py +5 -0
  18. libaditya/cards/card.py +90 -0
  19. libaditya/cards/cards_constants.py +115 -0
  20. libaditya/cards/cards_of_truth.py +323 -0
  21. libaditya/cards/cot.py +142 -0
  22. libaditya/cards/deck.py +52 -0
  23. libaditya/charts/__init__.py +6 -0
  24. libaditya/charts/api.py +36 -0
  25. libaditya/charts/bodygraph.py +80 -0
  26. libaditya/charts/chart.py +286 -0
  27. libaditya/charts/jaimini.py +83 -0
  28. libaditya/charts/tajika.py +36 -0
  29. libaditya/constants.py +1776 -0
  30. libaditya/draw/__init__.py +20 -0
  31. libaditya/draw/draw_bodygraph.py +489 -0
  32. libaditya/draw/draw_sbc.py +712 -0
  33. libaditya/draw/themes/default-theme.hd +53 -0
  34. libaditya/ephe/ast0/se00010s.se1 +0 -0
  35. libaditya/ephe/seas_00.se1 +0 -0
  36. libaditya/ephe/seas_06.se1 +0 -0
  37. libaditya/ephe/seas_12.se1 +0 -0
  38. libaditya/ephe/seas_18.se1 +0 -0
  39. libaditya/ephe/seas_24.se1 +0 -0
  40. libaditya/ephe/seas_30.se1 +0 -0
  41. libaditya/ephe/seas_36.se1 +0 -0
  42. libaditya/ephe/seas_42.se1 +0 -0
  43. libaditya/ephe/seas_48.se1 +0 -0
  44. libaditya/ephe/seasm06.se1 +0 -0
  45. libaditya/ephe/seasm12.se1 +0 -0
  46. libaditya/ephe/seasm18.se1 +0 -0
  47. libaditya/ephe/seasm24.se1 +0 -0
  48. libaditya/ephe/seasm30.se1 +0 -0
  49. libaditya/ephe/seasm36.se1 +0 -0
  50. libaditya/ephe/seasm42.se1 +0 -0
  51. libaditya/ephe/seasm48.se1 +0 -0
  52. libaditya/ephe/seasm54.se1 +0 -0
  53. libaditya/ephe/sefstars.txt +4245 -0
  54. libaditya/ephe/semo_00.se1 +0 -0
  55. libaditya/ephe/semo_06.se1 +0 -0
  56. libaditya/ephe/semo_12.se1 +0 -0
  57. libaditya/ephe/semo_18.se1 +0 -0
  58. libaditya/ephe/semo_24.se1 +0 -0
  59. libaditya/ephe/semo_30.se1 +0 -0
  60. libaditya/ephe/semo_36.se1 +0 -0
  61. libaditya/ephe/semo_42.se1 +0 -0
  62. libaditya/ephe/semo_48.se1 +0 -0
  63. libaditya/ephe/semom06.se1 +0 -0
  64. libaditya/ephe/semom12.se1 +0 -0
  65. libaditya/ephe/semom18.se1 +0 -0
  66. libaditya/ephe/semom24.se1 +0 -0
  67. libaditya/ephe/semom30.se1 +0 -0
  68. libaditya/ephe/semom36.se1 +0 -0
  69. libaditya/ephe/semom42.se1 +0 -0
  70. libaditya/ephe/semom48.se1 +0 -0
  71. libaditya/ephe/semom54.se1 +0 -0
  72. libaditya/ephe/sepl_00.se1 +0 -0
  73. libaditya/ephe/sepl_06.se1 +0 -0
  74. libaditya/ephe/sepl_12.se1 +0 -0
  75. libaditya/ephe/sepl_18.se1 +0 -0
  76. libaditya/ephe/sepl_24.se1 +0 -0
  77. libaditya/ephe/sepl_30.se1 +0 -0
  78. libaditya/ephe/sepl_36.se1 +0 -0
  79. libaditya/ephe/sepl_42.se1 +0 -0
  80. libaditya/ephe/sepl_48.se1 +0 -0
  81. libaditya/ephe/seplm06.se1 +0 -0
  82. libaditya/ephe/seplm12.se1 +0 -0
  83. libaditya/ephe/seplm18.se1 +0 -0
  84. libaditya/ephe/seplm24.se1 +0 -0
  85. libaditya/ephe/seplm30.se1 +0 -0
  86. libaditya/ephe/seplm36.se1 +0 -0
  87. libaditya/ephe/seplm42.se1 +0 -0
  88. libaditya/ephe/seplm48.se1 +0 -0
  89. libaditya/ephe/seplm54.se1 +0 -0
  90. libaditya/hd/__init__.py +8 -0
  91. libaditya/hd/calc.py +55 -0
  92. libaditya/hd/constants.py +127 -0
  93. libaditya/hd/longitude.py +271 -0
  94. libaditya/objects/__init__.py +19 -0
  95. libaditya/objects/celestial_object.py +206 -0
  96. libaditya/objects/context.py +133 -0
  97. libaditya/objects/cusps.py +259 -0
  98. libaditya/objects/julian_day.py +265 -0
  99. libaditya/objects/location.py +167 -0
  100. libaditya/objects/longitude.py +987 -0
  101. libaditya/objects/nakshatras.py +285 -0
  102. libaditya/objects/planets.py +2059 -0
  103. libaditya/objects/shadbala.py +398 -0
  104. libaditya/objects/signs.py +690 -0
  105. libaditya/objects/swe_functions.py +75 -0
  106. libaditya/print_functions.py +548 -0
  107. libaditya/read.py +665 -0
  108. libaditya/stars/__init__.py +4 -0
  109. libaditya/stars/fixed_star.py +216 -0
  110. libaditya/stars/make_swe_stars.py +407 -0
  111. libaditya/stars/star-sign-boundaries +44 -0
  112. libaditya/stars/stellarium/__init__.py +6 -0
  113. libaditya/stars/stellarium/remote_control/__init__.py +22 -0
  114. libaditya/stars/stellarium/remote_control/location.py +82 -0
  115. libaditya/stars/stellarium/remote_control/locationsearch.py +35 -0
  116. libaditya/stars/stellarium/remote_control/main.py +165 -0
  117. libaditya/stars/stellarium/remote_control/objects.py +63 -0
  118. libaditya/stars/stellarium/remote_control/scripts.py +81 -0
  119. libaditya/stars/stellarium/remote_control/simbad.py +20 -0
  120. libaditya/stars/stellarium/remote_control/stelaction.py +31 -0
  121. libaditya/stars/stellarium/remote_control/stelproperty.py +30 -0
  122. libaditya/stars/stellarium/remote_control/view.py +85 -0
  123. libaditya/stars/stellarium/stellarium.py +92 -0
  124. libaditya/stars/the_stars.py +522 -0
  125. libaditya/stars/utilities.py +539 -0
  126. libaditya/utils.py +431 -0
  127. libaditya/write.py +67 -0
  128. libaditya-0.3.4.dist-info/METADATA +588 -0
  129. libaditya-0.3.4.dist-info/RECORD +131 -0
  130. libaditya-0.3.4.dist-info/WHEEL +5 -0
  131. libaditya-0.3.4.dist-info/licenses/LICENSE +661 -0
@@ -0,0 +1,335 @@
1
+ # This file is part of libaditya.
2
+ #
3
+ # Copyright (c) 2025 Josh Harper <humanhaven@substack.com>
4
+ #
5
+ # libaditya is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU Affero General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # libaditya is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU Affero General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU Affero General Public License
16
+ # along with libaditya. If not, see <https://www.gnu.org/licenses/>.
17
+
18
+ from functools import cmp_to_key
19
+
20
+ from more_itertools import collapse
21
+
22
+ from libaditya import utils
23
+
24
+ from libaditya.objects import Planet, Planets, Sign
25
+
26
+ class Jaimini:
27
+ """
28
+ this is calc.Jaimini, it inherits unto calc.Varga
29
+
30
+ it is really a Varga, even though Varga inherits from here
31
+
32
+ these are calculations that can happen on any varga, in that varga
33
+
34
+ this class is a "Mixin"
35
+ it has no __init__ functions and cannot instantiate anything
36
+ it inherits unto Varga, giving Varga all of Jaimini capabilities
37
+
38
+ can use any Varga.methods()
39
+
40
+ this is really just to keep Varga from being humongeous by itself, so the functionality
41
+ will be split in modules that inherit into Varga
42
+
43
+ do not confuse with charts.Jaimini, which provides API functionality through Chart
44
+ that is the frontend
45
+ this is the backend
46
+ """
47
+
48
+
49
+ def pada(self) -> Sign:
50
+ """
51
+ the "foot" of the lagna
52
+ find how many signs the lagna lord is from the lagna
53
+ from the lord that many signs is the pada
54
+ if the lord is in the 4th or 10th from lagna, pada is in the 4th
55
+ if the lord is in the 1st or 7th from lagna, pada is in the 10th
56
+
57
+ return Sign class of the pada
58
+ """
59
+ lagna = self.signs().lagna()
60
+ return self._get_pada(lagna)
61
+
62
+ def upapada(self) -> Sign:
63
+ """
64
+ the "foot" of the 10th cusp
65
+ find how many signs the lagna lord is from the lagna
66
+ from the lord that many signs is the pada
67
+ if the lord is in the 4th or 10th from lagna, pada is in the 4th
68
+ if the lord is in the 1st or 7th from lagna, pada is in the 10th
69
+
70
+ return Sign class of the pada
71
+ """
72
+ # we need to find the Sign that has the 10th cusp
73
+ tenth = self.signs().where_is(10) # 10 means the 10th cusp
74
+ return self._get_pada(tenth)
75
+
76
+ def padas(self) -> {Sign:Sign}:
77
+ """
78
+ return a dictionary of all the padas
79
+ key is the Sign, value is the Sign of the pada
80
+ """
81
+ padas = {}
82
+ for sign in self.signs():
83
+ padas[sign] = self._get_pada(sign)
84
+ return padas
85
+
86
+ def _get_pada(self, sign: Sign) -> Sign:
87
+ """
88
+ a generic helper to get the pada Sign of Sign sign
89
+ """
90
+ # Planet class of the lord
91
+ lord = self.planets()[sign.lord()]
92
+ signs_apart = sign.astrological_signs_apart(lord.sign())
93
+
94
+ # check special pada rules
95
+ if signs_apart == 4 or signs_apart == 10:
96
+ return self.signs()[sign.astrological_signs_forward(4)]
97
+ if signs_apart == 1 or signs_apart == 7:
98
+ return self.signs()[sign.astrological_signs_forward(10)]
99
+ # otherwise signs_apart forward from lagna lord is the pada
100
+ lords_sign = self.signs()[lord.sign()]
101
+ return self.signs()[lords_sign.astrological_signs_forward(signs_apart)]
102
+
103
+ def bandhana_yogas(self) -> [([Planet],[Planet])]:
104
+ """
105
+ return a list of tuples of Planet lists that are forming bandhana yogas
106
+ tuple[0] and tuple[1] are planets are that in bandhana relationship to each other from the ascendant
107
+ if, in:
108
+ 2-12
109
+ 3-11
110
+ 4-10
111
+ 5-9
112
+ 6-8
113
+ relationship as signs from the ascedant
114
+ """
115
+ # pairs=((2,12),(3,11),(4,10),(5,9),(6,8))
116
+ # we go forwards and backwards by the same amount
117
+
118
+ pairs = (2,3,4,5,6)
119
+ lagna = self.lagna()
120
+
121
+ ret = []
122
+
123
+ for pair in pairs:
124
+ one = self.signs()[lagna.astrological_signs_forward(pair)]
125
+ two = self.signs()[lagna.astrological_signs_backward(pair)]
126
+ if one.how_many_grahas() == two.how_many_grahas():
127
+ if one.how_many_grahas() == 0:
128
+ continue
129
+ s1ls = []
130
+ s2ls = []
131
+ for sign1, sign2 in zip(one.grahas(),two.grahas()):
132
+ s1ls.append(sign1)
133
+ s2ls.append(sign2)
134
+ ret.append((s1ls,s2ls))
135
+
136
+ return ret
137
+
138
+ def argala(self, rashi: Sign) -> [[Planet], [Planet], [Planet]]:
139
+ """
140
+ get argala to rashi in this varga
141
+
142
+ returns three lists
143
+ [Planet classes forming argala from/to rashi]
144
+ [malefic Planet classes forming argala to 3rd from rashi]
145
+ [Planet classes being obstructed from/to rashi]
146
+ """
147
+ # will need the first strength of all the signs to determine argala
148
+ fs = self.jaimini_first_strength()
149
+ # if ketu is in this sign, we could backwards for argala
150
+ sign = -1 if self.where_is("Ketu").sign() == rashi.sign() else 1
151
+ # find our Sign classes of argala-virodhina pairs
152
+ # 2-12
153
+ # 11-3
154
+ # 4-10
155
+ # 9-5
156
+ # check third for more malefics than benefics
157
+ second = self.signs()[rashi.astrological_signs_forward(sign*2)]
158
+ twelfth = self.signs()[rashi.astrological_signs_forward(sign*12)]
159
+ eleventh = self.signs()[rashi.astrological_signs_forward(sign*11)]
160
+ third = self.signs()[rashi.astrological_signs_forward(sign*3)]
161
+ fourth = self.signs()[rashi.astrological_signs_forward(sign*4)]
162
+ tenth = self.signs()[rashi.astrological_signs_forward(sign*10)]
163
+ ninth = self.signs()[rashi.astrological_signs_forward(sign*9)]
164
+ fifth = self.signs()[rashi.astrological_signs_forward(sign*5)]
165
+ argala = []
166
+ obstructed = []
167
+ pairs = [(second,twelfth),(eleventh,third),(fourth,tenth),(ninth,fifth)]
168
+
169
+ # now we can loop on these pairs
170
+ for arg,vir in pairs:
171
+ if arg.how_many_grahas() > vir.how_many_grahas():
172
+ # if there are more grahas in arg, then they cause argala
173
+ # cant be obstructed by vir, so add them to argala list
174
+ argala.append(arg.grahas())
175
+ if arg.how_many_grahas() < vir.how_many_grahas():
176
+ # virodhina house has fewer planets, but are they are also weaker?
177
+ # if they are weaker than cannot obstruct; if not weaker, can obstruct
178
+ # how to determine how weak? sign strength
179
+ if fs.index(vir) > fs.index(arg):
180
+ # vir has a higher index, it is weaker, so it cannot obstruct
181
+ # argala sign is stronger, so the planets form argala
182
+ argala.append(arg.grahas())
183
+ else:
184
+ # if vir has a lower index, it is stronger, so it does obstruct
185
+ obstructed.append(arg.grahas())
186
+ if arg.how_many_grahas() == vir.how_many_grahas():
187
+ obstructed.append(arg.grahas())
188
+
189
+ # check malefic argala in 3rd
190
+ mal = [] # how many malefics in Three
191
+ ben = [] # how many benefics in Three
192
+ for graha in third.grahas():
193
+ if graha.nature() == "Malefic":
194
+ mal.append(graha)
195
+ else:
196
+ ben.append(graha)
197
+ third_argala = []
198
+ if len(mal) > len(ben):
199
+ # if there are more malefics in the 3rd than benefics, these malefics causes a special argala
200
+ third_argala.append(mal)
201
+
202
+ return list(collapse(argala)),list(collapse(third_argala)),list(collapse(obstructed))
203
+
204
+
205
+ def _compare_strength(self, sign1, sign2, kn_rao=False) -> int:
206
+ """
207
+ compare two signs by Jaimini first source of strength
208
+ returns 1 if sign1 is stronger, -1 if sign2 is stronger, 0 if equal
209
+
210
+ 8 tiebreaker levels per the Jaimini sutras:
211
+ 0: planet count in sign
212
+ 1: highest dignity among occupying planets (sorted comparison)
213
+ 2: modality (dual > fixed > movable)
214
+ 3: planet count in the lord's sign
215
+ 4: highest dignity in the lord's sign
216
+ 5: modality of the lord's sign
217
+ 6: distance from sign to its lord (greater distance wins)
218
+ 7: final tiebreaker (standard: higher rashi number; KN Rao: lord's degree in sign)
219
+ """
220
+ dignities = self.dignities()
221
+
222
+ # Level 0: planet count in sign
223
+ c1, c2 = sign1.how_many_karakas(), sign2.how_many_karakas()
224
+ if c1 != c2:
225
+ return 1 if c1 > c2 else -1
226
+
227
+ # Level 1: dignity comparison among occupying planets
228
+ result = utils.compare_signs_dignities(sign1, sign2, dignities)
229
+ if result == 1: return 1
230
+ if result == 2: return -1
231
+
232
+ # Level 2: modality
233
+ result = utils.compare_signs_modalities(sign1, sign2)
234
+ if result == 1: return 1
235
+ if result == 2: return -1
236
+
237
+ # Levels 3-5: same three checks on the lord's rashi
238
+ lord1 = self.planets()[sign1.lord()]
239
+ lord2 = self.planets()[sign2.lord()]
240
+ lord1_rashi = self.signs().where_is(sign1.lord())
241
+ lord2_rashi = self.signs().where_is(sign2.lord())
242
+
243
+ # Level 3: planet count in lord's sign
244
+ c1, c2 = lord1_rashi.how_many_karakas(), lord2_rashi.how_many_karakas()
245
+ if c1 != c2:
246
+ return 1 if c1 > c2 else -1
247
+
248
+ # Level 4: dignity comparison in lord's sign
249
+ result = utils.compare_signs_dignities(lord1_rashi, lord2_rashi, dignities)
250
+ if result == 1: return 1
251
+ if result == 2: return -1
252
+
253
+ # Level 5: modality of lord's sign
254
+ result = utils.compare_signs_modalities(lord1_rashi, lord2_rashi)
255
+ if result == 1: return 1
256
+ if result == 2: return -1
257
+
258
+ # Level 6: distance from sign to its lord (greater distance wins)
259
+ d1 = sign1.astrological_signs_apart(lord1.sign())
260
+ d2 = sign2.astrological_signs_apart(lord2.sign())
261
+ if d1 != d2:
262
+ return 1 if d1 > d2 else -1
263
+
264
+ # Level 7: final tiebreaker
265
+ if kn_rao:
266
+ deg1 = lord1.real_in_sign_longitude()
267
+ deg2 = lord2.real_in_sign_longitude()
268
+ if deg1 != deg2:
269
+ return 1 if deg1 > deg2 else -1
270
+ return 0
271
+ else:
272
+ # standard: higher rashi number wins
273
+ if sign1.sign() != sign2.sign():
274
+ return 1 if sign1.sign() > sign2.sign() else -1
275
+ return 0
276
+
277
+ def jaimini_first_strength(self, kn_rao=False) -> [Sign]:
278
+ """
279
+ calculate Jaiminis first source of strength for all signs
280
+ return a list of Sign classes from strongest (index 0) to weakest (index 11)
281
+
282
+ kn_rao=True uses KN Rao's tiebreaker at level 7 (lord's degree in sign)
283
+ kn_rao=False (default) uses standard tiebreaker (higher rashi number wins)
284
+ """
285
+ signs = list(self.signs())
286
+ signs.sort(key=cmp_to_key(lambda a, b: self._compare_strength(a, b, kn_rao)), reverse=True)
287
+ return signs
288
+
289
+ def jaimini_second_strength(self) -> {Sign: [Planets]}:
290
+ """
291
+ calculate Jaiminis second source of strength for all signs
292
+ return a dictionary of Sign: [Planet]
293
+ [Planet] is a list of Planet classes that provide a source of strength to Sign
294
+ due either to conjunctino or rashi aspect
295
+ this be either Mercury, Jupiter, or the Lord of Sign
296
+ """
297
+ sss = {}
298
+
299
+ jupiter_Sign = self.signs()[self.planets().jupiter().sign()]
300
+ mercury_Sign = self.signs()[self.planets().mercury().sign()]
301
+
302
+ for sign in self.signs():
303
+ sourcels = [] # list to hold Planet-s that are conjunct or aspect this sign
304
+ jup_aspects = self.signs().rashi_aspect_from_to(jupiter_Sign,sign)
305
+ # 1 means jupiter aspects sign from jupiter_Sign; 0 not
306
+ mer_aspects = self.signs().rashi_aspect_from_to(mercury_Sign,sign)
307
+ lord = self.planets()[sign.lord()]
308
+ lords_Sign = self.signs()[lord.sign()]
309
+ lord_aspects = self.signs().rashi_aspect_from_to(lords_Sign,sign)
310
+ if jupiter_Sign == sign or jup_aspects: # the object itself should be the same; means they are in the same sign
311
+ sourcels.append(self.planets().jupiter())
312
+ if mercury_Sign == sign or mer_aspects: # the object itself should be the same
313
+ sourcels.append(self.planets().mercury())
314
+ if lords_Sign == sign or lord_aspects:
315
+ sourcels.append(lord)
316
+ sss[sign] = sourcels
317
+ return sss
318
+
319
+ def jaimini_third_strength(self) -> {Sign: (int, str)}:
320
+ """
321
+ calculate Jaiminis third source of strength for all signs
322
+ classifies each sign by the distance from sign to its lord:
323
+ Kendra (distance % 3 == 1) = 2 (strong)
324
+ Panapara (distance % 3 == 2) = 1 (moderate)
325
+ Apoklima (distance % 3 == 0) = 0 (weak)
326
+
327
+ return a dictionary of Sign: (value, category_name)
328
+ """
329
+ categories = {1: (2, "Kendra"), 2: (1, "Panapara"), 0: (0, "Apoklima")}
330
+ ret = {}
331
+ for sign in self.signs():
332
+ lord = self.planets()[sign.lord()]
333
+ distance = sign.astrological_signs_apart(lord.sign())
334
+ ret[sign] = categories[distance % 3]
335
+ return ret
@@ -0,0 +1,240 @@
1
+ # This file is part of libaditya.
2
+ #
3
+ # Copyright (c) 2025 Josh Harper <humanhaven@substack.com>
4
+ #
5
+ # libaditya is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU Affero General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # libaditya is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU Affero General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU Affero General Public License
16
+ # along with libaditya. If not, see <https://www.gnu.org/licenses/>.
17
+
18
+
19
+
20
+ class Gets:
21
+ """
22
+ factor-s are all signs
23
+
24
+ it is the planets that are conjunct or aspect that provide the effect
25
+ but that is interpretive, what effect and to what extent
26
+
27
+ "gets" are just about calculations
28
+
29
+ to do the interpretation, we need to know which planets are conjunct or aspecting the ak,
30
+ which sign the ak is in in various vargas, etc.
31
+
32
+ that is what we are doing here.
33
+ """
34
+
35
+ karakamshas = {
36
+ # AK, i.e.,s the sign of the AK
37
+ "factor": ["AK"],
38
+ "vargas": ["9","1","7"]
39
+ }
40
+
41
+ spirituality = {
42
+ "factor": ["12 away AK"],
43
+ "vargas": ["9","24","1"]
44
+ }
45
+
46
+ mundane_deity = {
47
+ "factor": ["6 away AmK"],
48
+ "vargas": ["30","9","1"]
49
+ }
50
+
51
+ home = {
52
+ "factor": ["4 away AK"],
53
+ "vargas": ["4","16","1"]
54
+ }
55
+
56
+ dharma = {
57
+ "factor": ["9 away AK"],
58
+ # 12 for seeing if parental influence supports innate dharma
59
+ "vargas": ["9","1","12"]
60
+ }
61
+
62
+ farmer = {
63
+ "factor": ["3", "6", "9"],
64
+ # others?
65
+ "vargas": ["1"]
66
+ }
67
+
68
+ # this is interpretive actually
69
+ # this is will be covered by "karamashas" near the top
70
+ # adultery = {
71
+ # "factor": ["AK in Mars or AK in Venus"],
72
+ # "vargas": ["9","7","1"]
73
+ # }
74
+
75
+ spouse = {
76
+ "factor": ["7 away AK"],
77
+ "vargas": ["9","7","1"]
78
+ }
79
+
80
+ might = {
81
+ "factor": ["3 away AK"],
82
+ "vargas": ["9","3","1"]
83
+ }
84
+
85
+ conjurer = {
86
+ "factor": ["5 away AK", "9 away AK"],
87
+ "vargas": ["9","1"]
88
+ }
89
+
90
+ capability = {
91
+ # 1.2.40-42: how capable/successful a person is at their livelihood
92
+ "factor": ["10 away AK"],
93
+ "vargas": ["9","1","10"]
94
+ }
95
+
96
+ disease_foundation = {
97
+ # 1.2.88-90: chronic/glandular/skin disease (vitiligo, leprosy)
98
+ # same factor as home but different vargas — D30 changes the reading
99
+ "factor": ["4 away AK"],
100
+ "vargas": ["9","30","1"]
101
+ }
102
+
103
+ disease_vitality = {
104
+ # 1.2.91-95: bodily wasting, boils, water disease, poison
105
+ "factor": ["AK", "5 away AK"],
106
+ "vargas": ["9","30","1"]
107
+ }
108
+
109
+ authorship = {
110
+ # 1.2.102-104: writing ability (Moon+Jupiter = author, etc.)
111
+ "factor": ["AK", "5 away AK"],
112
+ "vargas": ["9","1","10"]
113
+ }
114
+
115
+ intelligence = {
116
+ # 1.2.105-114: type of intelligence (poet, logician, mathematician, etc.)
117
+ "factor": ["AK", "5 away AK"],
118
+ "vargas": ["9","24","1"]
119
+ }
120
+
121
+ speech = {
122
+ # 1.2.116-118: secondary skills; Ketu+malefic = speech problems
123
+ "factor": ["2 away AK"],
124
+ "vargas": ["9","1"]
125
+ }
126
+
127
+ # dont forget const.multi_vargas
128
+
129
+ # karaka abbreviations in jaimini_karakas() order
130
+ KARAKA_ORDER = ["AK", "AmK", "BK", "MK", "PiK", "GK", "DK"]
131
+
132
+
133
+ class JaiminiGet:
134
+ """
135
+ a inheritor for calc.Rashi
136
+ defines functions that get certain sets of planets
137
+
138
+ these are functions that may use any varga, so they are in the Rashi, which has access to all vargas
139
+
140
+ functions that work on the vargas themselves are defined in calc.Varga/calc.Jaimini
141
+ """
142
+
143
+ # default varga overrides: when a Gets spec says "24", use -240 (siddhamsha) by default
144
+ DEFAULT_VARGA_OVERRIDES = {"24": "-240"}
145
+
146
+ def _parse_factor(self, factor_str):
147
+ """
148
+ parse a factor string into (offset, karaka_abbrev) or (cusp_number, None)
149
+
150
+ "12 away AK" -> (12, "AK")
151
+ "AK" -> (0, "AK")
152
+ "6 away AmK" -> (6, "AmK")
153
+ "3" -> (3, None) # raw cusp number
154
+ """
155
+ parts = factor_str.split()
156
+ if len(parts) == 3 and parts[1] == "away":
157
+ return int(parts[0]), parts[2]
158
+ elif factor_str in Gets.KARAKA_ORDER:
159
+ return 0, factor_str
160
+ else:
161
+ return int(factor_str), None
162
+
163
+ def _resolve_vargas(self, spec_vargas, varga_overrides=None):
164
+ """
165
+ apply varga overrides to the spec's varga list
166
+
167
+ varga_overrides is a dict like {"24": "-240"} that replaces
168
+ varga numbers with alternate divisional chart variants
169
+ """
170
+ overrides = dict(self.DEFAULT_VARGA_OVERRIDES)
171
+ if varga_overrides:
172
+ overrides.update(varga_overrides)
173
+ return [overrides.get(v, v) for v in spec_vargas]
174
+
175
+ def _get_influences(self, varga, sign):
176
+ """
177
+ given a varga and a Sign, return the conjunction and aspect planets
178
+ """
179
+ aspecting = varga.rashi_aspects_given_to(sign)
180
+ aspecting = [this_one.grahas() for this_one in aspecting]
181
+ conjunction = [p.jaimini_info() for p in sign.grahas()]
182
+ aspect_list = []
183
+ for sign_planets in aspecting:
184
+ aspect_list.append([p.jaimini_info() for p in sign_planets])
185
+ return {"conjunction": conjunction, "aspecting": aspect_list}
186
+
187
+ def _find_factor_sign(self, varga, factor_str, karakas):
188
+ """
189
+ given a varga, a factor string, and the karaka list,
190
+ return the Sign that the factor points to
191
+ """
192
+ offset, karaka_abbrev = self._parse_factor(factor_str)
193
+
194
+ if karaka_abbrev is None:
195
+ # raw cusp number
196
+ cusp_sign_num = varga.cusps()[offset].sign()
197
+ return varga.signs()[cusp_sign_num]
198
+
199
+ karaka_index = Gets.KARAKA_ORDER.index(karaka_abbrev)
200
+ karaka = karakas[karaka_index]
201
+ karaka_sign = varga.where_is(karaka.identity())
202
+
203
+ if offset == 0:
204
+ return karaka_sign
205
+
206
+ direction = 1 if karaka_sign.sign() % 2 == 1 else -1
207
+ return varga.signs()[karaka_sign.astrological_signs_forward(offset * direction)]
208
+
209
+ def jaimini_get(self, spec, varga_overrides=None):
210
+ """
211
+ general method to get influences for any Gets spec
212
+
213
+ spec is a dict from Gets, e.g. Gets.spirituality, Gets.karakamshas
214
+ varga_overrides is an optional dict like {"24": "-240"} to choose
215
+ alternate varga variants
216
+
217
+ returns a dictionary that can be converted to toml
218
+ """
219
+ factors = spec["factor"]
220
+ vargas = self._resolve_vargas(spec["vargas"], varga_overrides)
221
+ karakas = self.planets().jaimini_karakas()
222
+
223
+ ret = {
224
+ "aspect_type": self.context.rashi_aspects,
225
+ }
226
+
227
+ for factor_str in factors:
228
+ factor_ret = {}
229
+ for amsha in vargas:
230
+ varga = self.master.varga(int(amsha))
231
+ sign = self._find_factor_sign(varga, factor_str, karakas)
232
+ factor_ret[amsha] = self._get_influences(varga, sign)
233
+ ret[factor_str] = factor_ret
234
+
235
+ return ret
236
+
237
+ def get_spiritual_planets(self, d24=-240):
238
+ """get spiritual planets using the general get() method"""
239
+ return self.jaimini_get(Gets.spirituality, varga_overrides={"24": str(d24)})
240
+
libaditya/calc/kala.py ADDED
@@ -0,0 +1,73 @@
1
+ # This file is part of libaditya.
2
+ #
3
+ # Copyright (c) 2025 Josh Harper <humanhaven@substack.com>
4
+ #
5
+ # libaditya is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU Affero General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # libaditya is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU Affero General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU Affero General Public License
16
+ # along with libaditya. If not, see <https://www.gnu.org/licenses/>.
17
+
18
+ import swisseph as swe
19
+
20
+ from libaditya.objects import JulianDay, Sun, EphContext
21
+ from libaditya.calc import Panchanga
22
+
23
+ def lunar_new_year(year) -> Panchanga:
24
+ """
25
+ find chinese lunar new year in calendar year "year"
26
+ chinese lunar new year is the second new moon after the winter solstice
27
+ so the lunar new year in 2026 is after the solstice in 2025
28
+ year can be an integer or a JulianDay class
29
+ """
30
+ if isinstance(year,JulianDay):
31
+ year = year.year()
32
+ # get january 1st of the previous year so we can find the winter solstice of that year
33
+ solstice_yearJD = JulianDay(swe.julday(year-1,1,1))
34
+ solsticeSun = Sun(EphContext(timeJD=solstice_yearJD)).ingress(270)
35
+
36
+ # now get a Panchanga for this time
37
+ panch = Panchanga(solsticeSun.context)
38
+
39
+ new_year = panch.next_new_moon().next_new_moon()
40
+
41
+ return new_year
42
+
43
+
44
+
45
+ def cardinal_points(year) -> [JulianDay]:
46
+ """
47
+ return the cardinal points of the year in order
48
+ i.e., ascending equinox, northern solstice, descedending equinox, southern solstice
49
+ """
50
+ year_jd = swe.julday(year,1,1,0)
51
+ s=Sun(EphContext(timeJD=JulianDay(year_jd)))
52
+ return [s.ingress(0), s.ingress(90), s.ingress(180), s.ingress(270)]
53
+
54
+ def print_cardinal_points(year: int | JulianDay) -> None:
55
+ """
56
+ print four cardinal points of year
57
+ year can be specified as either integer or JulianDay
58
+ """
59
+ # get january 1 of year
60
+ if isinstance(year,JulianDay):
61
+ year = year.year()
62
+ points = cardinal_points(year)
63
+
64
+ print(f"Cardinal points for {year}\n")
65
+
66
+ print("Ascending equinox:")
67
+ print(points[0])
68
+ print("Northern solstice:")
69
+ print(points[1])
70
+ print("Descending equinox:")
71
+ print(points[2])
72
+ print("Southern solstice:")
73
+ print(points[3])