Beispiel #1
0
  def __init__(self, city, start_date, end_date, computation_system: ComputationSystem = None):
    """Constructor for the panchaanga.
        """
    super(Panchaanga, self).__init__()
    self.version = Panchaanga.LATEST_VERSION
    self.city = city
    self.start_date = Date(*([int(x) for x in start_date.split('-')])) if isinstance(start_date, str) else start_date
    self.start_date.set_time_to_day_start()
    self.end_date = Date(*([int(x) for x in end_date.split('-')])) if isinstance(end_date, str) else end_date
    self.end_date.set_time_to_day_start()

    self.computation_system = default_if_none(computation_system, ComputationSystem.DEFAULT)

    self.jd_start = time.utc_gregorian_to_jd(self.start_date)
    self.jd_end = time.utc_gregorian_to_jd(self.end_date)

    self.duration = int(self.jd_end - self.jd_start) + 1

    # For accurate festival assignment, we sometimes need panchaanga information about succeeding or preceeding days. 
    # For example, consider a festival to be selebrated during naxatra 27 in solar sideral month 9. If naxatra 27 occurs twice in sidereal_solar_month 9 (gap of 27+ daus), the latter occurence is to be selected - the former day will not get a festival. 
    self.duration_posterior_padding = int(self.duration + 30)
    self.duration_prior_padding = 2

    self.weekday_start = time.get_weekday(self.jd_start)

    self.festival_id_to_days = defaultdict(set, {})
    self.compute_angas(compute_lagnas=self.computation_system.options.lagnas)
    if not self.computation_system.options.no_fests:
      self.update_festival_details()
Beispiel #2
0
def test_get_anga():

  nd = NakshatraDivision(jd=time.ist_timezone.local_time_to_julian_day(Date(2018, 7, 14)), ayanaamsha_id=Ayanamsha.CHITRA_AT_180)
  assert nd.get_anga(
    anga_type=AngaType.TITHI).index == 1

  nd = NakshatraDivision(jd=time.ist_timezone.local_time_to_julian_day(Date(2018, 7, 14, 6, 1)), ayanaamsha_id=Ayanamsha.CHITRA_AT_180)
  assert nd.get_anga(
    anga_type=AngaType.TITHI).index == 2

  nd = NakshatraDivision(jd=time.ist_timezone.local_time_to_julian_day(Date(2018, 7, 13)), ayanaamsha_id=Ayanamsha.CHITRA_AT_180)
  assert nd.get_anga(
    anga_type=AngaType.TITHI).index == 30
  assert nd.get_anga(
    anga_type=AngaType.SIDEREAL_MONTH).index == 3

  # Just before meSha sankrAnti
  assert NakshatraDivision(jd=time.ist_timezone.local_time_to_julian_day(Date(2018, 4, 13)), ayanaamsha_id=Ayanamsha.CHITRA_AT_180).get_anga(
    anga_type=AngaType.SIDEREAL_MONTH).index == 12


  # 5:6:0.00 UT on December 23, 1981
  nd = NakshatraDivision(2444961.7125, ayanaamsha_id=Ayanamsha.CHITRA_AT_180)
  assert nd.get_anga(AngaType.NAKSHATRA).index == 16
  assert nd.get_anga(AngaType.TITHI).index == 28
  assert nd.get_anga(AngaType.YOGA).index == 8
  assert nd.get_anga(AngaType.KARANA).index == 55
  assert nd.get_solar_raashi().index == 9
Beispiel #3
0
def test_daily_solar_viSukkaNi():

  computation_system = ComputationSystem.DEFAULT
  panchaanga = periodical.Panchaanga(city=chennai, start_date=Date(2018, 4, 14), end_date=Date(2018, 4, 15), computation_system=computation_system)
 
  assert "viSukkan2i" not in panchaanga.date_str_to_panchaanga[Date(2018, 4, 14).get_date_str()].festival_id_to_instance
  assert "viSukkan2i" in panchaanga.date_str_to_panchaanga[Date(2018, 4, 15).get_date_str()].festival_id_to_instance.keys()
Beispiel #4
0
def get_panchaanga_for_shaka_year(city,
                                  year,
                                  precomputed_json_dir="~/Documents/jyotisha",
                                  computation_system: ComputationSystem = None,
                                  allow_precomputed=True):
    fname = os.path.expanduser(
        '%s/%s__shaka_%s__%s.json' %
        (precomputed_json_dir, city.name, year, computation_system))
    if os.path.isfile(fname) and allow_precomputed:
        fn = lambda: get_panchaanga_for_shaka_year(
            city=city,
            year=year,
            precomputed_json_dir=precomputed_json_dir,
            computation_system=computation_system,
            allow_precomputed=False)
        panchaanga = load_panchaanga(fname=fname, fallback_fn=fn)
        # Fest repos to be used might have changed in this call.
        panchaanga.computation_system = computation_system
        panchaanga.update_festival_details()
        return panchaanga
    else:
        logging.info(
            'No precomputed data available. Computing panchaanga...\n')
        SHAKA_CIVIL_ERA_DIFF = 78
        start_year_civil = year + era.get_year_0_offset(era_id=era.ERA_SHAKA)
        anga_span_finder = AngaSpanFinder.get_cached(
            ayanaamsha_id=Ayanamsha.ASHVINI_STARTING_0,
            anga_type=AngaType.SIDEREAL_MONTH)
        start_equinox = anga_span_finder.find(jd1=time.utc_gregorian_to_jd(
            Date(year=start_year_civil, month=3, day=1)),
                                              jd2=time.utc_gregorian_to_jd(
                                                  Date(year=start_year_civil,
                                                       month=5,
                                                       day=1)),
                                              target_anga_id=1)
        end_equinox = anga_span_finder.find(jd1=time.utc_gregorian_to_jd(
            Date(year=start_year_civil + 1, month=3, day=1)),
                                            jd2=time.utc_gregorian_to_jd(
                                                Date(year=start_year_civil + 1,
                                                     month=5,
                                                     day=1)),
                                            target_anga_id=1)
        tz = Timezone(city.timezone)
        panchaanga = periodical.Panchaanga(
            city=city,
            start_date=tz.julian_day_to_local_time(
                julian_day=start_equinox.jd_start),
            end_date=tz.julian_day_to_local_time(
                julian_day=end_equinox.jd_start),
            computation_system=computation_system)
        panchaanga.year = year
        # Festival data may be updated more frequently and a precomputed panchaanga may go out of sync. Hence we keep this method separate.
        logging.info('Writing computed panchaanga to %s...\n' % fname)

        try:
            panchaanga.dump_to_file(filename=fname)
        except EnvironmentError:
            logging.warning("Not able to save.")
            logging.error(traceback.format_exc())
        return panchaanga
Beispiel #5
0
def test_SolsticePostDark10AdhikaAssigner():
    panchaanga = daily.DailyPanchaanga(
        city=chennai,
        date=Date(2019, 12, 1),
        computation_system=ComputationSystem.
        SOLSTICE_POST_DARK_10_ADHIKA__CHITRA_180)
    assert panchaanga.lunar_month_sunrise.index == 10.5

    # Though this month contained a solstice on amAvAsyA, it is not intercalary since the preceeding solstice was intercalary.
    panchaanga = daily.DailyPanchaanga(
        city=chennai,
        date=Date(2020, 6, 1),
        computation_system=ComputationSystem.
        SOLSTICE_POST_DARK_10_ADHIKA__CHITRA_180)
    assert panchaanga.lunar_month_sunrise.index == 4

    # Though this month contained a solstice on amAvAsyA, it is not intercalary since the preceeding solstice was intercalary.
    panchaanga = daily.DailyPanchaanga(
        city=chennai,
        date=Date(2020, 6, 21),
        computation_system=ComputationSystem.
        SOLSTICE_POST_DARK_10_ADHIKA__CHITRA_180)
    assert panchaanga.lunar_month_sunrise.index == 4

    # Though this month is after a post-dark10 solstice, it does not succeed an adhikamAsa in the ayanANta.
    panchaanga = daily.DailyPanchaanga(
        city=chennai,
        date=Date(2020, 10, 3),
        computation_system=ComputationSystem.
        SOLSTICE_POST_DARK_10_ADHIKA__CHITRA_180)
    assert panchaanga.lunar_month_sunrise.index == 8
Beispiel #6
0
def test_MultiNewMoonAssigner():
    # Online - https://www.drikpanchang.com/panchang/month-panchang.html?date=13/07/2018
    # karka-sankrAnti was on 16th.
    panchaanga = daily.DailyPanchaanga(
        city=chennai,
        date=Date(2018, 7, 13),
        computation_system=ComputationSystem.
        MULTI_NEW_MOON_SIDEREAL_MONTH_ADHIKA__CHITRA_180)
    assert panchaanga.lunar_month_sunrise.index == 3
    panchaanga = daily.DailyPanchaanga(
        # dvitIyA following amAvAsyA - can trip up previous day panchAnga utilization logic.
        city=chennai,
        date=Date(2018, 7, 14),
        computation_system=ComputationSystem.
        MULTI_NEW_MOON_SIDEREAL_MONTH_ADHIKA__CHITRA_180,
        previous_day_panchaanga=panchaanga)
    assert panchaanga.lunar_month_sunrise.index == 4

    # Online : https://www.drikpanchang.com/panchang/month-panchang.html?date=21/07/2018
    panchaanga = daily.DailyPanchaanga(
        city=chennai,
        date=Date(2018, 7, 21),
        computation_system=ComputationSystem.
        MULTI_NEW_MOON_SIDEREAL_MONTH_ADHIKA__CHITRA_180)
    assert panchaanga.lunar_month_sunrise.index == 4
Beispiel #7
0
    def __init__(self,
                 city: City,
                 date: Date,
                 computation_system=None,
                 previous_day_panchaanga=None) -> None:
        """Constructor for the panchaanga.
    """
        super(DailyPanchaanga, self).__init__()
        self.city = city
        self.date = date
        date.set_time_to_day_start()
        self.julian_day_start = Timezone(
            self.city.timezone).local_time_to_julian_day(date=self.date)
        self.computation_system = default_if_none(computation_system,
                                                  ComputationSystem.DEFAULT)

        self.jd_sunrise = None
        self.jd_sunset = None
        self.jd_previous_sunset = None
        self.jd_next_sunrise = None
        self.jd_moonrise = None
        self.jd_moonset = None

        self.lagna_data = None
        self.sunrise_day_angas = None

        self.solar_sidereal_date_sunset = None

        self.tropical_date_sunset = None

        self.lunar_month_sunrise = None

        self.shraaddha_tithi = []
        self.festival_id_to_instance = {}
        self.mauDhyas = None
        self.amauDhyas = None

        self.compute_sun_moon_transitions(
            previous_day_panchaanga=previous_day_panchaanga)
        self.compute_solar_day_sunset(
            previous_day_panchaanga=previous_day_panchaanga)
        self.set_tropical_date_sunset(
            previous_day_panchaanga=previous_day_panchaanga)
        self.day_length_based_periods = DayLengthBasedPeriods(
            jd_previous_sunset=self.jd_previous_sunset,
            jd_sunrise=self.jd_sunrise,
            jd_sunset=self.jd_sunset,
            jd_next_sunrise=self.jd_next_sunrise,
            weekday=self.date.get_weekday())

        if self.computation_system.lunar_month_assigner_type is not None:
            lunar_month_assigner = LunarMonthAssigner.get_assigner(
                computation_system=self.computation_system)
            self.set_lunar_month_sunrise(
                month_assigner=lunar_month_assigner,
                previous_day_panchaanga=previous_day_panchaanga)
            self.set_mauDhyas()
Beispiel #8
0
def test_periodic_solar_mUDavaN_muLukku():
    computation_system = ComputationSystem.DEFAULT
    panchaanga = periodical.Panchaanga(city=chennai,
                                       start_date=Date(2018, 11, 14),
                                       end_date=Date(2018, 11, 20),
                                       computation_system=computation_system)
    assert "muDavan2 muzhukku" in panchaanga.date_str_to_panchaanga[Date(
        2018, 11, 17).get_date_str()].festival_id_to_instance
    assert "muDavan2 muzhukku" not in panchaanga.date_str_to_panchaanga[Date(
        2018, 11, 18).get_date_str()].festival_id_to_instance
Beispiel #9
0
def test_get_previous_solstice():
  solstice = zodiac.get_previous_solstice(jd=time.ist_timezone.local_time_to_julian_day(Date(2018, 1, 14)))
  expected_jd_start = time.ist_timezone.local_time_to_julian_day(date=Date(year=2017, month=12, day=21, hour=16, minute=28))
  numpy.testing.assert_approx_equal(solstice.jd_start, expected_jd_start, significant=4)

  solstice = zodiac.get_previous_solstice(jd=time.ist_timezone.local_time_to_julian_day(Date(2018, 3, 14)))
  expected_jd_start = time.ist_timezone.local_time_to_julian_day(date=Date(year=2017, month=12, day=21, hour=16, minute=28))
  numpy.testing.assert_approx_equal(solstice.jd_start, expected_jd_start, significant=4)

  solstice = zodiac.get_previous_solstice(jd=time.ist_timezone.local_time_to_julian_day(Date(2018, 7, 14)))
  expected_jd_start = time.ist_timezone.local_time_to_julian_day(date=Date(year=2018, month=6, day=20, hour=21, minute=44))
  numpy.testing.assert_approx_equal(solstice.jd_start, expected_jd_start, significant=4)
Beispiel #10
0
def test_orinda_ca_dst_2019():
    city = City('Orinda', '37:51:38', '-122:10:59', 'America/Los_Angeles')
    panchaanga = periodical.Panchaanga(city=city,
                                       start_date=Date(2019, 1, 1),
                                       end_date=Date(2019, 5, 1))
    # March 10 is the 69th day of the year (70th in leap years) in the Gregorian calendar.
    # Sunrise on that day is around 7:27 AM according to Google, which is JD 2458553.14375 according to https://ssd.jpl.nasa.gov/tc.cgi#top .
    # We use the index 70 below as the annual panchaanga object seems to use the index d + 1.
    assert round(
        panchaanga.daily_panchaangas_sorted()[panchaanga.duration_prior_padding
                                              + 69].jd_sunrise,
        ndigits=4) == round(2458554.104348237,
                            ndigits=4)  # 2019-Mar-10 07:30:15.68
Beispiel #11
0
def test_get_tithis_in_period():
    span_finder = AngaSpanFinder.get_cached(
        anga_type=AngaType.TITHI, ayanaamsha_id=Ayanamsha.ASHVINI_STARTING_0)
    spans = span_finder.get_spans_in_period(
        jd_start=time.ist_timezone.local_time_to_julian_day(
            Date(year=2020, month=1, day=1)),
        jd_end=time.ist_timezone.local_time_to_julian_day(
            Date(year=2020, month=6, day=30)),
        target_anga_id=30)
    jds = [x.jd_start for x in spans]
    numpy.testing.assert_array_almost_equal(jds, [
        2458872.36655025, 2458902.0647052005, 2458931.792117506,
        2458961.5055956016, 2458991.1712410315, 2459020.765607745
    ],
                                            decimal=3)
Beispiel #12
0
def test_get_karanas_in_period():
    span_finder = AngaSpanFinder.get_cached(
        anga_type=AngaType.KARANA, ayanaamsha_id=Ayanamsha.ASHVINI_STARTING_0)
    spans = span_finder.get_spans_in_period(
        jd_start=time.ist_timezone.local_time_to_julian_day(
            Date(year=2020, month=1, day=1)),
        jd_end=time.ist_timezone.local_time_to_julian_day(
            Date(year=2020, month=6, day=30)),
        target_anga_id=30)
    jds = [x.jd_start for x in spans]
    numpy.testing.assert_array_almost_equal(jds, [
        2458858.845, 2458888.379, 2458917.821, 2458947.19, 2458976.52,
        2459005.852
    ],
                                            decimal=3)
Beispiel #13
0
def test_get_yogas_in_period():
    span_finder = AngaSpanFinder.get_cached(
        anga_type=AngaType.KARANA, ayanaamsha_id=Ayanamsha.ASHVINI_STARTING_0)
    spans = span_finder.get_spans_in_period(
        jd_start=time.ist_timezone.local_time_to_julian_day(
            Date(year=2020, month=1, day=1)),
        jd_end=time.ist_timezone.local_time_to_julian_day(
            Date(year=2020, month=6, day=30)),
        target_anga_id=15)
    jds = [x.jd_start for x in spans]
    numpy.testing.assert_array_almost_equal(jds, [
        2458851.146, 2458881.029, 2458910.808, 2458940.431, 2458969.882,
        2458999.185, 2459028.392
    ],
                                            decimal=3)
Beispiel #14
0
def import_to_xaatra_later():
  import toml
  input_path = ""
  events_in = toml.load(input_path)
  repo = RulesRepo(name="mahApuruSha/xatra-later")
  for event in events_in["data"]:
    logging.debug(event)
    from jyotisha.panchaanga.temporal.time import Date
    timing = HinduCalendarEventTiming()
    if "Gregorian date" in event:
      date_str = event["Gregorian date"]
      timing.month_type = RulesRepo.GREGORIAN_MONTH_DIR
    else:
      date_str = event["Julian date"]
      timing.month_type = RulesRepo.JULIAN_MONTH_DIR
    dt = Date.from_string(date_str)
    timing.anga_type = RulesRepo.DAY_DIR
    timing.month_number = dt.month
    timing.anga_number = dt.day
    timing.year_start = dt.year
    timing.year_start_era = era.ERA_GREGORIAN
    rule = HinduCalendarEvent()
    rule.timing = timing
    rule.id = event["name_sa"].replace(" ", "_")
    en_description = " ".join([event["tithi"], event["Incident"], event["Other notes"]])
    rule.description = {"en": en_description.strip()}
    rule.names = {"sa": [sanscript.transliterate(data=event["name_sa"], _from=sanscript.OPTITRANS, _to=sanscript.DEVANAGARI)]}
    rule.dump_to_file(filename=rule.get_storage_file_name(base_dir=repo.get_path()))
Beispiel #15
0
def test_jd_from_local_time():
    jd = Timezone("America/Los_Angeles").local_time_to_julian_day(
        date=Date(year=2018, month=11, day=11, hour=6, minute=0, second=0))
    import numpy.testing
    numpy.testing.assert_approx_equal(actual=jd,
                                      desired=2458433.750000,
                                      significant=7)
Beispiel #16
0
def test_MultiFullMoonAssigner():
    # Online - https://www.drikpanchang.com/panchang/month-panchang.html?date=13/07/2018 https://www.prokerala.com/astrology/panchang/2018-july-13.html
    # karka-sankrAnti was on 16th.
    panchaanga = daily.DailyPanchaanga(
        city=chennai,
        date=Date(2018, 7, 13),
        computation_system=ComputationSystem.
        MULTI_FULL_MOON_SIDEREAL_MONTH_ADHIKA__CHITRA_180)
    assert panchaanga.lunar_month_sunrise.index == 4
    panchaanga = daily.DailyPanchaanga(
        city=chennai,
        date=Date(2018, 7, 14),
        computation_system=ComputationSystem.
        MULTI_FULL_MOON_SIDEREAL_MONTH_ADHIKA__CHITRA_180,
        previous_day_panchaanga=panchaanga)
    assert panchaanga.lunar_month_sunrise.index == 4
Beispiel #17
0
def test_get_anga_data_1981_12_23():
    panchaanga = panchaanga_json_comparer(chennai, date=Date(1981, 12, 23))
    angas = [
        s.anga.index
        for s in panchaanga.sunrise_day_angas.get_anga_spans_in_interval(
            interval=panchaanga.day_length_based_periods.puurvaahna,
            anga_type=AngaType.NAKSHATRA)
    ]
    assert angas == [16, 17]

    angas = [
        s.anga.index
        for s in panchaanga.sunrise_day_angas.get_anga_spans_in_interval(
            interval=Interval(jd_start=panchaanga.jd_sunrise,
                              jd_end=panchaanga.jd_sunrise),
            anga_type=AngaType.NAKSHATRA)
    ]
    assert angas == [16]

    angas = [
        s.anga.index
        for s in panchaanga.sunrise_day_angas.get_anga_spans_in_interval(
            interval=Interval(jd_start=panchaanga.jd_sunrise,
                              jd_end=panchaanga.jd_next_sunrise),
            anga_type=AngaType.NAKSHATRA)
    ]
    assert angas == [16, 17]
Beispiel #18
0
 def get(self, latitude, longitude, year, month, day):
     args = self.get_parser.parse_args()
     city = City("", latitude, longitude, args['timezone'])
     panchaanga = daily.DailyPanchaanga(city=city,
                                        date=Date(year=int(year),
                                                  month=int(month),
                                                  day=int(day)))
     return panchaanga.day_length_based_periods.to_json_map()
Beispiel #19
0
 def get(self, timezone, year, month, day, hour, minute, second):
     jd = Timezone(timezone).local_time_to_julian_day(
         Date(year, month, day, hour, minute, second))
     from jyotisha.panchaanga import temporal
     raashi = NakshatraDivision(
         jd, ayanaamsha_id=Ayanamsha.CHITRA_AT_180).get_solar_raashi()
     logging.info(raashi)
     return str(raashi)
Beispiel #20
0
def test_tb_muhuurta_blr():
    city = City.get_city_from_db('Bangalore')
    panchaanga = daily.DailyPanchaanga(city=city,
                                       date=Date(year=2019, month=9, day=10))
    assert len(panchaanga.day_length_based_periods.fifteen_fold_division.
               tb_muhuurtas) == 15
    assert panchaanga.day_length_based_periods.fifteen_fold_division.tb_muhuurtas[
        0].jd_start == panchaanga.jd_sunrise
    for muhurta in panchaanga.day_length_based_periods.fifteen_fold_division.tb_muhuurtas:
        logging.info(muhurta.to_localized_string(city=city))
Beispiel #21
0
def test_get_samvatsara():
    city = City.get_city_from_db('Bangalore')
    panchaanga = daily.DailyPanchaanga(city=city,
                                       date=Date(year=2019, month=9, day=10))
    assert panchaanga.get_samvatsara(
        month_type=RulesRepo.LUNAR_MONTH_DIR).get_name(
            script=sanscript.DEVANAGARI) == "विकारी"
    panchaanga = daily.DailyPanchaanga(city=city,
                                       date=Date(year=2020, month=2, day=10))
    assert panchaanga.get_samvatsara(
        month_type=RulesRepo.LUNAR_MONTH_DIR).get_name(
            script=sanscript.DEVANAGARI) == "विकारी"
    panchaanga = daily.DailyPanchaanga(city=city,
                                       date=Date(year=2020, month=4, day=10))
    assert panchaanga.get_samvatsara(
        month_type=RulesRepo.LUNAR_MONTH_DIR).get_name(
            script=sanscript.DEVANAGARI) == "शार्वरी"
    panchaanga = daily.DailyPanchaanga(city=city,
                                       date=Date(year=2020, month=4, day=10))
    assert panchaanga.get_samvatsara(
        month_type=RulesRepo.TROPICAL_MONTH_DIR).get_name(
            script=sanscript.DEVANAGARI) == "शार्वरी"
    panchaanga = daily.DailyPanchaanga(city=city,
                                       date=Date(year=2020, month=4, day=10))
    assert panchaanga.get_samvatsara(
        month_type=RulesRepo.SIDEREAL_SOLAR_MONTH_DIR).get_name(
            script=sanscript.DEVANAGARI) == "विकारी"
    panchaanga = daily.DailyPanchaanga(city=city,
                                       date=Date(year=2020, month=4, day=20))
    assert panchaanga.get_samvatsara(
        month_type=RulesRepo.SIDEREAL_SOLAR_MONTH_DIR).get_name(
            script=sanscript.DEVANAGARI) == "शार्वरी"
Beispiel #22
0
 def get(self, body_name, anga_type_str, timezone, year, month, day, hour,
         minute, second):
     jd = Timezone(timezone).local_time_to_julian_day(
         Date(year, month, day, hour, minute, second))
     nd = NakshatraDivision(jd=jd, ayanaamsha_id=Ayanamsha.CHITRA_AT_180)
     body = Graha(body_name=body_name)
     anga_type = AngaType.NAKSHATRA
     if anga_type_str == AngaType.RASHI.name:
         anga_type = AngaType.RASHI
     division = nd.get_fractional_division_for_body(body=body,
                                                    anga_type=anga_type)
     logging.info(division)
     return str(division)
Beispiel #23
0
 def get(self, timezone, year, month, day, hour, minute, second, body):
     from jyotisha import zodiac
     jd = Timezone(timezone).local_time_to_julian_day(
         Date(year, month, day, hour, minute, second))
     from jyotisha.panchaanga import temporal
     transits = Graha.singleton(body).get_transits(
         jd_start=jd,
         jd_end=jd + 100,
         anga_type=AngaType.RASHI,
         ayanaamsha_id=Ayanamsha.CHITRA_AT_180)
     # logging.debug(transits)
     transits_local = [
         (Timezone(timezone).julian_day_to_local_time(transit.jd),
          transit.value_1, transit.value_2) for transit in transits
     ]
     return str(transits_local)
Beispiel #24
0
def test_solar_day():

    panchaanga = daily.DailyPanchaanga.from_city_and_julian_day(
        city=chennai, julian_day=time.utc_gregorian_to_jd(Date(2018, 12, 31)))
    assert panchaanga.solar_sidereal_date_sunset.month_transition is None

    panchaanga = daily.DailyPanchaanga.from_city_and_julian_day(
        city=chennai, julian_day=time.utc_gregorian_to_jd(Date(2018, 1, 14)))
    assert panchaanga.solar_sidereal_date_sunset.day == 1
    assert panchaanga.solar_sidereal_date_sunset.month == 10
    assert panchaanga.solar_sidereal_date_sunset.month_transition == 2458132.8291680976

    panchaanga = daily.DailyPanchaanga.from_city_and_julian_day(
        city=chennai, julian_day=time.utc_gregorian_to_jd(Date(2018, 2, 12)))
    numpy.testing.assert_approx_equal(
        panchaanga.solar_sidereal_date_sunset.month_transition, 2458162.3747)

    panchaanga = daily.DailyPanchaanga.from_city_and_julian_day(
        city=chennai, julian_day=time.utc_gregorian_to_jd(Date(2018, 4, 13)))
    assert panchaanga.solar_sidereal_date_sunset.month_transition is None

    panchaanga = daily.DailyPanchaanga.from_city_and_julian_day(
        city=chennai, julian_day=time.utc_gregorian_to_jd(Date(2017, 12, 16)))
    assert panchaanga.solar_sidereal_date_sunset.day == 1
    assert panchaanga.solar_sidereal_date_sunset.month == 9

    panchaanga = daily.DailyPanchaanga.from_city_and_julian_day(
        city=chennai, julian_day=2457023.27)
    logging.debug(str(panchaanga))
    assert panchaanga.solar_sidereal_date_sunset.day == 16
    assert panchaanga.solar_sidereal_date_sunset.month == 9

    panchaanga = daily.DailyPanchaanga.from_city_and_julian_day(
        city=chennai, julian_day=time.utc_gregorian_to_jd(Date(2017, 12, 31)))
    assert panchaanga.solar_sidereal_date_sunset.day == 16
    assert panchaanga.solar_sidereal_date_sunset.month == 9
Beispiel #25
0
def get_panchaanga_for_kali_year(city,
                                 year,
                                 precomputed_json_dir="~/Documents/jyotisha",
                                 computation_system: ComputationSystem = None,
                                 allow_precomputed=True,
                                 recompute_festivals=True):
    year = int(year)
    fname = os.path.expanduser(
        '%s/%s__kali_%s__%s.json' %
        (precomputed_json_dir, city.name, year, computation_system))
    if os.path.isfile(fname) and allow_precomputed:
        fn = lambda: get_panchaanga_for_kali_year(
            city=city,
            year=year,
            precomputed_json_dir=precomputed_json_dir,
            computation_system=computation_system,
            allow_precomputed=False)
        panchaanga = load_panchaanga(fname=fname, fallback_fn=fn)
        # Fest repos to be used might have changed in this call.
        panchaanga.computation_system = computation_system
        if recompute_festivals:
            panchaanga.update_festival_details()
        return panchaanga
    else:
        logging.info(
            'No precomputed data available or allowed. Computing panchaanga...\n'
        )
        start_year_civil = year - era.get_year_0_offset(era_id=era.ERA_KALI)
        anga_span_finder = AngaSpanFinder.get_cached(
            ayanaamsha_id=Ayanamsha.CHITRA_AT_180,
            anga_type=AngaType.SIDEREAL_MONTH)
        start_mesha = anga_span_finder.find(jd1=time.utc_gregorian_to_jd(
            Date(year=start_year_civil, month=3, day=1)),
                                            jd2=time.utc_gregorian_to_jd(
                                                Date(year=start_year_civil,
                                                     month=5,
                                                     day=1)),
                                            target_anga_id=1)
        jd_next_sunset_start_mesha = city.get_setting_time(
            julian_day_start=start_mesha.jd_start, body=Graha.SUN)
        end_mina = anga_span_finder.find(jd1=time.utc_gregorian_to_jd(
            Date(year=start_year_civil + 1, month=3, day=1)),
                                         jd2=time.utc_gregorian_to_jd(
                                             Date(year=start_year_civil + 1,
                                                  month=5,
                                                  day=1)),
                                         target_anga_id=1)
        jd_preceding_sunset_end_mina = city.get_setting_time(
            julian_day_start=end_mina.jd_start - 1, body=Graha.SUN)
        tz = Timezone(city.timezone)
        panchaanga = periodical.Panchaanga(
            city=city,
            start_date=tz.julian_day_to_local_time(
                julian_day=jd_next_sunset_start_mesha),
            end_date=tz.julian_day_to_local_time(
                julian_day=jd_preceding_sunset_end_mina),
            computation_system=computation_system)
        panchaanga.year = year
        # Festival data may be updated more frequently and a precomputed panchaanga may go out of sync. Hence we keep this method separate.
        logging.info('Writing computed panchaanga to %s...\n' % fname)

        try:
            panchaanga.dump_to_file(filename=fname)
        except EnvironmentError:
            logging.warning("Not able to save.")
            logging.error(traceback.format_exc())
        return panchaanga
Beispiel #26
0
def test_sanitize_time():
    assert Date(2018, 11, 11, 10, 8,
                60).sanitize() == (2018, 11, 11, 10, 9, 00)
    assert Date(2018, 12, 31, 23, 60,
                00).sanitize() == (2019, 1, 1, 00, 00, 00)
Beispiel #27
0
def test_utc_to_jd():
    assert abs(
        time.utc_gregorian_to_jd(Date(2018, 11, 11, 14, 0, 0)) -
        2458434.083333251) < .001
Beispiel #28
0
class Panchaanga(common.JsonObject):
    """This class enables the construction of a panchaanga for arbitrary periods, with festival_id_to_instance.
  
    Generally, which days is a given festival associated with (esp pre-sunrise events)? We follow the same conventions as the adyatithi repo.
    """
    LATEST_VERSION = "0.0.4"

    def __init__(self,
                 city,
                 start_date,
                 end_date,
                 computation_system: ComputationSystem = None):
        """Constructor for the panchaanga.
        """
        super(Panchaanga, self).__init__()
        self.version = Panchaanga.LATEST_VERSION
        self.city = city
        self.start_date = Date(
            *([int(x) for x in start_date.split('-')])) if isinstance(
                start_date, str) else start_date
        self.start_date.set_time_to_day_start()
        self.end_date = Date(*([int(x)
                                for x in end_date.split('-')])) if isinstance(
                                    end_date, str) else end_date
        self.end_date.set_time_to_day_start()

        self.computation_system = default_if_none(computation_system,
                                                  ComputationSystem.DEFAULT)

        self.jd_start = time.utc_gregorian_to_jd(self.start_date)
        self.jd_end = time.utc_gregorian_to_jd(self.end_date)

        self.duration = int(self.jd_end - self.jd_start) + 1

        # For accurate festival assignment, we sometimes need panchaanga information about succeeding or preceding days.
        # For example, consider a festival to be selebrated during naxatra 27 in solar sideral month 9. If naxatra 27 occurs twice in sidereal_solar_month 9 (gap of 27+ daus), the latter occurence is to be selected - the former day will not get a festival.
        self.duration_posterior_padding = int(self.duration + 30)
        self.duration_prior_padding = 2

        self.weekday_start = time.get_weekday(self.jd_start)

        self.festival_id_to_days = defaultdict(set, {})
        self.compute_angas(
            compute_lagnas=self.computation_system.festival_options.lagnas)
        if not self.computation_system.festival_options.no_fests:
            self.update_festival_details()

    @timebudget
    def compute_angas(self, compute_lagnas=True):
        """Compute the entire panchaanga
    """

        # INITIALISE VARIABLES
        self.date_str_to_panchaanga: Dict[str, daily.DailyPanchaanga] = {}

        #############################################################
        # Compute all parameters -- sun/moon latitude/longitude etc #
        #############################################################

        for d in range(-self.duration_prior_padding,
                       self.duration_posterior_padding - 1):
            # The below block is temporary code to make the transition seamless.
            date_d = time.jd_to_utc_gregorian(self.jd_start + d)
            date_d.set_time_to_day_start()
            previous_daily_panchaanga = self.date_str_to_panchaanga.get(
                date_d.offset_date(days=-1).get_date_str(), None)
            daily_panchaanga = daily.DailyPanchaanga(
                city=self.city,
                date=date_d,
                computation_system=self.computation_system,
                previous_day_panchaanga=previous_daily_panchaanga)
            if compute_lagnas:
                daily_panchaanga.get_lagna_data()
            self.date_str_to_panchaanga[
                date_d.get_date_str()] = daily_panchaanga

    @methodtools.lru_cache(maxsize=10)
    def daily_panchaangas_sorted(self, skip_padding_days=False):
        if not skip_padding_days:
            return sorted(self.date_str_to_panchaanga.values())
        else:
            full_list = sorted(self.date_str_to_panchaanga.values())
            return [
                x for x in full_list
                if self.start_date <= x.date and x.date <= self.end_date
            ]

    def daily_panchaanga_for_jd(self, jd):
        date = self.city.get_timezone_obj().julian_day_to_local_time(
            julian_day=jd)
        return self.daily_panchaanga_for_date(date=date)

    def daily_panchaanga_for_date(self, date):
        return self.date_str_to_panchaanga.get(date.get_date_str(), None)

    def pre_sunset_daily_panchaanga_for_jd(self, jd):
        panchaanga = self.daily_panchaanga_for_jd(jd=jd)
        if panchaanga is None:
            return None
        elif panchaanga.jd_sunset >= jd:
            return panchaanga
        else:
            return self.daily_panchaanga_for_date(date=panchaanga.date + 1)

    def post_sunrise_daily_panchaanga_for_jd(self, jd):
        panchaanga = self.daily_panchaanga_for_jd(jd=jd)
        if panchaanga is None:
            return None
        elif panchaanga.jd_sunrise <= jd:
            return panchaanga
        else:
            return self.daily_panchaanga_for_date(date=panchaanga.date - 1)

    def get_interval_anga_spans(self, date, interval_id, anga_type):
        dp = self.daily_panchaanga_for_date(date)
        (anga_spans, _) = dp.get_interval_anga_spans(interval_id=interval_id,
                                                     anga_type=anga_type)
        anga_spans = copy.deepcopy(anga_spans)

        if anga_type == AngaType.TITHI:
            for span in anga_spans:
                if span.anga.index in (1, 2):
                    # The below is necessary because tithi 1 or 2 may start after sunrise.
                    dp_next = self.daily_panchaanga_for_date(date + 1)
                    # Lunar month below may be incorrect (adhika mAsa complication) if dp_next is not available (eg when the next day is beyond this panchaanga duration). Downstream code should be aware of that case.
                    month = dp_next.lunar_month_sunrise if dp_next is not None else dp.lunar_month_sunrise + 1
                    span.anga = Tithi.from_anga(anga=span.anga, month=month)
                else:
                    span.anga = Tithi.from_anga(anga=span.anga,
                                                month=dp.lunar_month_sunrise)

        return anga_spans

    def clear_padding_day_festivals(self):
        """Festival assignments for padding days are not trustworthy - since one would need to look-ahead or before into further days for accurate festival assignment. They were computed only to ensure accurate computation of the core days in this panchaanga. To avoid misleading, we ought to clear festivals provisionally assigned to the padding days."""
        daily_panchaangas = self.daily_panchaangas_sorted()
        for dp in daily_panchaangas[:self.duration_prior_padding]:
            self.delete_festivals_on_date(date=dp.date)
        for dp in daily_panchaangas[self.duration_prior_padding +
                                    self.duration:]:
            self.delete_festivals_on_date(date=dp.date)

    @timebudget
    def update_festival_details(self):
        """

    Festival data may be updated more frequently and a precomputed panchaanga may go out of sync. Hence we keep this method separate.
    :return:
    """
        self._reset_festivals()
        rule_lookup_assigner = rule_repo_based.RuleLookupAssigner(
            panchaanga=self)
        rule_lookup_assigner.apply_festival_from_rules_repos()
        ShraddhaTithiAssigner(panchaanga=self).assign_shraaddha_tithi()
        ecliptic.EclipticFestivalAssigner(panchaanga=self).assign_all()
        tithi_festival.TithiFestivalAssigner(panchaanga=self).assign_all()
        solar.SolarFestivalAssigner(panchaanga=self).assign_all()
        vaara.VaraFestivalAssigner(panchaanga=self).assign_all()
        generic_assigner = FestivalAssigner(panchaanga=self)
        generic_assigner.cleanup_festivals()
        rule_lookup_assigner.assign_relative_festivals()
        # self._sync_festivals_dict_and_daily_festivals(here_to_daily=True, daily_to_here=True)
        generic_assigner.assign_festival_numbers()
        self.clear_padding_day_festivals()

    def _sync_festivals_dict_and_daily_festivals(self,
                                                 here_to_daily=False,
                                                 daily_to_here=True):
        if here_to_daily:
            for festival_id, days in self.festival_id_to_days.items():
                for fest_day in days:
                    if not isinstance(fest_day, Date):
                        logging.fatal(festival_id + " " + str(days))
                    fest_day_str = fest_day.get_date_str()
                    if fest_day_str in self.date_str_to_panchaanga and festival_id not in self.date_str_to_panchaanga[
                            fest_day_str].festival_id_to_instance:
                        self.date_str_to_panchaanga[
                            fest_day_str].festival_id_to_instance[
                                festival_id] = FestivalInstance(
                                    name=festival_id)

        if daily_to_here:
            for dp in self.date_str_to_panchaanga.values():
                for fest in dp.festival_id_to_instance.values():
                    days = self.festival_id_to_days.get(fest.name, set())
                    if dp.date not in days:
                        days.add(dp.date)
                    self.festival_id_to_days[fest.name] = days

    def _reset_festivals(self):
        self.festival_id_to_days = defaultdict(set, {})
        for daily_panchaanga in self.date_str_to_panchaanga.values():
            daily_panchaanga.festival_id_to_instance = {}

    def delete_festival(self, fest_id):
        for date in self.festival_id_to_days.pop(fest_id, []):
            self.date_str_to_panchaanga[
                date.get_date_str()].festival_id_to_instance.pop(
                    fest_id, None)

    def add_festival(self, fest_id, date, interval_id="full_day"):
        if date.get_date_str() not in self.date_str_to_panchaanga:
            return
        interval = self.date_str_to_panchaanga[
            date.get_date_str()].get_interval(interval_id=interval_id)
        self.add_festival_instance(date=date,
                                   festival_instance=FestivalInstance(
                                       name=fest_id, interval=interval))

    def add_festival_instance(self, festival_instance, date):
        from jyotisha.panchaanga.temporal.festival import rules
        festival_instance.name = rules.clean_id(id=festival_instance.name)
        p_fday = self.date_str_to_panchaanga.get(date.get_date_str(), None)
        if p_fday is not None:
            p_fday.festival_id_to_instance[
                festival_instance.name] = festival_instance
        self.festival_id_to_days[festival_instance.name].add(date)

    def delete_festival_date(self, fest_id, date):
        self.festival_id_to_days[fest_id].discard(date)
        if len(self.festival_id_to_days[fest_id]) == 0:
            # Avoid empty items (when serializing).
            self.delete_festival(fest_id=fest_id)
        self.date_str_to_panchaanga[
            date.get_date_str()].festival_id_to_instance.pop(fest_id, None)

    def delete_festivals_on_date(self, date):
        # Reason for casting to list below: Avoid RuntimeError: dictionary changed size during iteration
        dp = self.date_str_to_panchaanga[date.get_date_str()]
        for fest_id in list(dp.festival_id_to_instance.keys()):
            self.delete_festival_date(fest_id=fest_id, date=dp.date)

    def _refill_daily_panchaangas(self):
        """Avoids duplication for memory efficiency.
    
    Inverse of _force_non_redundancy_in_daily_panchaangas
    """
        for daily_panchaanga in self.date_str_to_panchaanga.values():
            daily_panchaanga.city = self.city
            daily_panchaanga.computation_system = self.computation_system

    def _force_non_redundancy_in_daily_panchaangas(self):
        """Avoids duplication for memory efficiency."""
        for daily_panchaanga in self.date_str_to_panchaanga.values():
            daily_panchaanga.city = None
            daily_panchaanga.computation_system = None

    def post_load_ops(self):
        self._refill_daily_panchaangas()
        self.festival_id_to_days = collection_helper.lists_to_sets(
            self.festival_id_to_days)

    @timebudget
    def dump_to_file(self,
                     filename,
                     floating_point_precision=None,
                     sort_keys=True):
        self._force_non_redundancy_in_daily_panchaangas()
        self.festival_id_to_days = collection_helper.sets_to_lists(
            self.festival_id_to_days)
        super(Panchaanga, self).dump_to_file(
            filename=filename,
            floating_point_precision=floating_point_precision,
            sort_keys=sort_keys)
        self.festival_id_to_days = collection_helper.lists_to_sets(
            self.festival_id_to_days)
        self._refill_daily_panchaangas()
Beispiel #29
0
def test_sunrise_mtv():
    city = City.get_city_from_db('Cupertino')
    panchaanga = daily.DailyPanchaanga(city=city,
                                       date=Date(year=2018, month=11, day=11))
    panchaanga.compute_sun_moon_transitions()
    numpy.testing.assert_approx_equal(panchaanga.jd_sunrise, 2458434.11)
Beispiel #30
0
class Panchaanga(common.JsonObject):
  """This class enables the construction of a panchaanga for arbitrary periods, with festival_id_to_instance.
  
    Generally, which days is a given festival associated with (esp pre-sunrise events)? We follow the same conventions as the adyatithi repo.
    """
  LATEST_VERSION = "0.0.4"

  def __init__(self, city, start_date, end_date, computation_system: ComputationSystem = None):
    """Constructor for the panchaanga.
        """
    super(Panchaanga, self).__init__()
    self.version = Panchaanga.LATEST_VERSION
    self.city = city
    self.start_date = Date(*([int(x) for x in start_date.split('-')])) if isinstance(start_date, str) else start_date
    self.start_date.set_time_to_day_start()
    self.end_date = Date(*([int(x) for x in end_date.split('-')])) if isinstance(end_date, str) else end_date
    self.end_date.set_time_to_day_start()

    self.computation_system = default_if_none(computation_system, ComputationSystem.DEFAULT)

    self.jd_start = time.utc_gregorian_to_jd(self.start_date)
    self.jd_end = time.utc_gregorian_to_jd(self.end_date)

    self.duration = int(self.jd_end - self.jd_start) + 1

    # For accurate festival assignment, we sometimes need panchaanga information about succeeding or preceeding days. 
    # For example, consider a festival to be selebrated during naxatra 27 in solar sideral month 9. If naxatra 27 occurs twice in sidereal_solar_month 9 (gap of 27+ daus), the latter occurence is to be selected - the former day will not get a festival. 
    self.duration_posterior_padding = int(self.duration + 30)
    self.duration_prior_padding = 2

    self.weekday_start = time.get_weekday(self.jd_start)

    self.festival_id_to_days = defaultdict(set, {})
    self.compute_angas(compute_lagnas=self.computation_system.options.lagnas)
    if not self.computation_system.options.no_fests:
      self.update_festival_details()

  @timebudget
  def compute_angas(self, compute_lagnas=True):
    """Compute the entire panchaanga
    """

    nDays = self.duration_posterior_padding

    # INITIALISE VARIABLES
    self.date_str_to_panchaanga: Dict[str, daily.DailyPanchaanga] = {}


    #############################################################
    # Compute all parameters -- sun/moon latitude/longitude etc #
    #############################################################

    for d in range(-self.duration_prior_padding, nDays - 1):
      # TODO: Eventually, we are shifting to an array of daily panchangas. Reason: Better modularity.
      # The below block is temporary code to make the transition seamless.
      date_d = time.jd_to_utc_gregorian(self.jd_start + d)
      date_d.set_time_to_day_start()
      previous_daily_panchaanga = self.date_str_to_panchaanga.get(date_d.offset_date(days=-1).get_date_str(), None)
      daily_panchaanga = daily.DailyPanchaanga(city=self.city, date=date_d,
                                               computation_system=self.computation_system,
                                               previous_day_panchaanga=previous_daily_panchaanga)
      if compute_lagnas:
        daily_panchaanga.get_lagna_data()
      self.date_str_to_panchaanga[date_d.get_date_str()] = daily_panchaanga

  @methodtools.lru_cache(maxsize=10)
  def daily_panchaangas_sorted(self):
    return sorted(self.date_str_to_panchaanga.values())

  def daily_panchaanga_for_jd(self, jd):
    date = self.city.timezone.julian_day_to_local_time(julian_day=jd)
    return self.daily_panchaanga_for_date(date=date)

  def daily_panchaanga_for_date(self, date):
    from copy import deepcopy
    date_alt = deepcopy(date)
    date_alt.set_time_to_day_start()
    return self.date_str_to_panchaanga.get(date_alt.get_date_str(), None)

  def pre_sunset_daily_panchaanga_for_jd(self, jd):
    panchaanga = self.daily_panchaanga_for_jd(jd=jd)
    if panchaanga.jd_sunset >= jd:
      return panchaanga
    else:
      return self.daily_panchaanga_for_date(date=panchaanga.date + 1)

  def post_sunrise_daily_panchaanga_for_jd(self, jd):
    panchaanga = self.daily_panchaanga_for_jd(jd=jd)
    if panchaanga.jd_sunrise <= jd:
      return panchaanga
    else:
      return self.daily_panchaanga_for_date(date=panchaanga.date - 1)

  def clear_padding_day_festivals(self):
    """Festival assignments for padding days are not trustworthy - since one would need to look-ahead or before into further days for accurate festival assignment. They were computed only to ensure accurate computation of the core days in this panchaanga. To avoid misleading, we ought to clear festivals provisionally assigned to the padding days."""
    daily_panchaangas = self.daily_panchaangas_sorted()
    for dp in daily_panchaangas[:self.duration_prior_padding]:
      for fest_id in dp.festival_id_to_instance.keys():
        self.festival_id_to_days[fest_id].discard(dp.date)
      dp.festival_id_to_instance = {}
    for dp in daily_panchaangas[self.duration_prior_padding + self.duration:]:
      for fest_id in dp.festival_id_to_instance.keys():
        self.festival_id_to_days[fest_id].discard(dp.date)
      dp.festival_id_to_instance = {}

  @timebudget
  def update_festival_details(self, debug=False):
    """

    Festival data may be updated more frequently and a precomputed panchaanga may go out of sync. Hence we keep this method separate.
    :return:
    """
    self._reset_festivals()
    rule_lookup_assigner = rule_repo_based.RuleLookupAssigner(panchaanga=self)
    rule_lookup_assigner.apply_festival_from_rules_repos()
    TithiAssigner(panchaanga=self).assign_shraaddha_tithi()
    inefficient.FestivalsTimesDaysAssigner(panchaanga=self).assign_festivals_from_rules()
    ecliptic.EclipticFestivalAssigner(panchaanga=self).assign_all()
    tithi_festival.TithiFestivalAssigner(panchaanga=self).assign_all()
    solar.SolarFestivalAssigner(panchaanga=self).assign_all()
    vaara.VaraFestivalAssigner(panchaanga=self).assign_all()
    generic_assigner = FestivalAssigner(panchaanga=self)
    generic_assigner.cleanup_festivals()
    rule_lookup_assigner.assign_relative_festivals()
    self._sync_festivals_dict_and_daily_festivals(here_to_daily=True, daily_to_here=True)
    generic_assigner.assign_festival_numbers()
    self.clear_padding_day_festivals()


  def _sync_festivals_dict_and_daily_festivals(self, here_to_daily=False, daily_to_here=True):
    if here_to_daily:
      for festival_id, days in self.festival_id_to_days.items():
        for fest_day in days:
          if not isinstance(fest_day, Date):
            logging.fatal(festival_id + " " + str(days))
          fest_day_str = fest_day.get_date_str()
          if fest_day_str in self.date_str_to_panchaanga:
            self.date_str_to_panchaanga[fest_day_str].festival_id_to_instance[festival_id] =  FestivalInstance(name=festival_id)

    if daily_to_here:
      for dp in self.date_str_to_panchaanga.values():
        for fest in dp.festival_id_to_instance.values():
          days = self.festival_id_to_days.get(fest.name, set())
          if dp.date not in days:
            days.add(dp.date)
          self.festival_id_to_days[fest.name] = days

  def _reset_festivals(self):
    self.festival_id_to_days = defaultdict(set, {})
    for daily_panchaanga in self.date_str_to_panchaanga.values():
      daily_panchaanga.festival_id_to_instance = {}

  def delete_festival(self, fest_id):
    for date in self.festival_id_to_days.pop(fest_id, []):
      self.date_str_to_panchaanga[date.get_date_str()].festival_id_to_instance.pop(fest_id, None)
    

  def _refill_daily_panchaangas(self):
    """Avoids duplication for memory efficiency.
    
    Inverse of _force_non_redundancy_in_daily_panchaangas
    """
    for daily_panchaanga in self.date_str_to_panchaanga.values():
      daily_panchaanga.city = self.city
      daily_panchaanga.computation_system = self.computation_system

  def _force_non_redundancy_in_daily_panchaangas(self):
    """Avoids duplication for memory efficiency."""
    for daily_panchaanga in self.date_str_to_panchaanga.values():
      daily_panchaanga.city = None
      daily_panchaanga.computation_system = None

  def post_load_ops(self):
    self._refill_daily_panchaangas()
    self.festival_id_to_days = collection_helper.lists_to_sets(self.festival_id_to_days)

  @timebudget
  def dump_to_file(self, filename, floating_point_precision=None, sort_keys=True):
    self._force_non_redundancy_in_daily_panchaangas()
    self.festival_id_to_days = collection_helper.sets_to_lists(self.festival_id_to_days)
    super(Panchaanga, self).dump_to_file(filename=filename, floating_point_precision=floating_point_precision,
                                         sort_keys=sort_keys)
    self.festival_id_to_days = collection_helper.lists_to_sets(self.festival_id_to_days)
    self._refill_daily_panchaangas()