Source code for irfpy.mexpvat.domain

""" Domain of the spacecraft

Domains (solar wind, induced magnetosphere, magnetosheath) are handled.
"""
import datetime as _datetime
import logging as _logging

from irfpy.mars import bowshock as _bs
from irfpy.mars import mpb as _mpb
from irfpy.mars import radius as _rm

from irfpy.mexpvat import mexspice as _ms
from irfpy.mexpvat import orbnum as _onr

from irfpy.asperacommon.domain_common import bisect_boundary_crossing as _boundary_crossing


[docs]def inside_bowshock(t, model=None, model_param=None): """ Return if the spacecraft is inside the bow shock. :param t: :keyword model: The model used. As default, use the Vignes model https://irfpy.irf.se/projects/planets/api/api_irfpy.mars.bowshock.html is used. As a default, Vignes original model is used, and only implemented :keyword model_param: Parameter to pass to the model. Dictionary. For example, {"scale": 1.3} will give the scale parameter to Vignes model. :return: *True* if the MEX is inside the bow shock. It include the magnetosheath, induced magnetosphere, magnetotail, or innosphere >>> import datetime >>> t = datetime.datetime(2009, 2, 13, 8) >>> print(inside_bowshock(t)) True >>> t = datetime.datetime(2009, 2, 13, 12) >>> print(inside_bowshock(t)) False >>> inside_bowshock(t, model_param={"scale": 4.5}) True """ _ms.init() # For MEX, it is ok if multiple load was done. pos = _ms.get_position(t, target='MEX', origin='MARS') pos /= _rm # Now in Rm if model_param is None: model_param = {} model = _bs.BowshockVignesScaled(**model_param) return model.inside(pos[0], pos[1], pos[2])
[docs]def inside_mpb(t, model=None): """ Return if the spacecraft is inside MPB :param t: :keyword model: The model used. As default, use the Vignes model https://irfpy.irf.se/projects/planets/api/api_irfpy.mars.mpb.html is used. **Currently, only Vignes model is implemented** :return: *True* if the MEX is inside the MPB. It include the induced magnetosphere, magnetotail, or ionosphere. >>> import datetime >>> t = datetime.datetime(2009, 2, 13, 9) >>> print(inside_mpb(t)) True >>> t = datetime.datetime(2009, 2, 13, 12) >>> print(inside_mpb(t)) False """ _ms.init() pos = _ms.get_position(t, target='MEX', origin='MARS') pos /= _rm # Now in Rm return _mpb.MpbVignes.inside(pos[0], pos[1], pos[2])
[docs]def inside_eclipse(t): r""" Return if the spacecraft is inside the geometric eclipse. :param t: Time :returns: Boolean value, *True* if the spacecraft is in the eclipse. The definition of the eclipse is just geometric: :math:`R(=\sqrt{x^2+y^2+z^2})\ge 1 [Rm]` and :math:`x<0` and :math:`r(=\sqrt{y^2+z^2})\le 1 [Rm]` where ``R`` is the radius and ``r`` is the distance from x-axis. >>> import datetime >>> t = datetime.datetime(2009, 2, 13, 10) >>> print(inside_eclipse(t)) True >>> t = datetime.datetime(2009, 2, 13, 11) >>> print(inside_eclipse(t)) False """ _ms.init() pos = _ms.get_position(t, target='MEX', origin='MARS') x, y, z = pos / _rm # Now in Rm if x >= 0: return False r3_2 = x ** 2 + y ** 2 + z ** 2 if r3_2 < 1: return False r2_2 = y ** 2 + z ** 2 if r2_2 > 1: return False return True
[docs]def bowshock_crossings(orbit_number, dt_survey=600, tolerance=0.1, model=None, model_param=None): """ Return the list of bowshock crossing time for the specific orbit :param orbit_number: Orbit nubmer. Int. :param dt_survey: Time difference for surveying the position. Every ``dt_survey`` seconds, the function calculates the position and evaluate if the spacecraft cross the boundary. If crossed, the detailed calculation is done when it is crossed, down to ``tolerance`` seconds accuracy. :param tolerance: The tolerance in seconds. :param model: Model name, while only Vignes model is implemented. :param model_param: Model parameter. {"scale": 1.3} gives larger bowshock by 30%. :return: List of bow shock crossing times This function returns the bowshock crossing times as a list. >>> bowshock_crossings(900) [datetime.datetime(2004, 10, 1, 17, 45, 20, 301269), datetime.datetime(2004, 10, 1, 20, 29, 27, 859863)] >>> bowshock_crossings(900, model_param={"scale": 0.8}) [datetime.datetime(2004, 10, 1, 17, 56, 3, 807129), datetime.datetime(2004, 10, 1, 20, 6, 19, 554199)] >>> bowshock_crossings(900, model_param={"scale": 1.2}) [datetime.datetime(2004, 10, 1, 17, 32, 27, 962402), datetime.datetime(2004, 10, 1, 20, 54, 43, 460449)] >>> bowshock_crossings(900, model_param={"scale": 4}) # Unreasonably wide bow shock [] """ # Due to the difference of orbit from VEX, MEX cannot determine the # inbound and outbound crossings. So the number of crossing depends on # the orbit. logger = _logging.getLogger(__name__) insidefunc = lambda t: inside_bowshock(t, model=model, model_param=model_param) t_now = t0 = _onr.get_start_time(orbit_number) t1 = _onr.get_stop_time(orbit_number) # Survey the position, and specify the time of crossing roughly. dt_survey = _datetime.timedelta(seconds=dt_survey) t_prev = t0 c_prev = insidefunc(t_prev) crossing_time = [] while t_now <= t1: t_now += dt_survey c_now = insidefunc(t_now) logger.debug('Survey: t_prev={} c_prev={}'.format(t_prev, c_prev)) logger.debug('Survey: t_now={} c_now={}'.format(t_prev, c_prev)) if c_now is not c_prev: logger.debug('Survey: CROSSED!') crossing_time.append(t_prev) t_prev = t_now c_prev = c_now # Calculate the time of crossing more precisely. exact_crossing_time = [] for t_cross in crossing_time: tc = _boundary_crossing(t_cross, t_cross + dt_survey, insidefunc, tolerance=tolerance) exact_crossing_time.append(tc) return exact_crossing_time
[docs]def mpb_crossings(orbit_number, dt_survey=600, tolerance=0.1): """ Return the list of MPB crossing time for the specific orbit :param orbit_number: Orbit nubmer. Int. :param dt_survey: Time difference for surveying the position. Every ``dt_survey`` seconds, the function calculates the position and evaluate if the spacecraft cross the boundary. If crossed, the detailed calculation is done when it is crossed, down to ``tolerance`` seconds accuracy. :param tolerance: The tolerance in seconds. :return: List of MPB crossing times This function returns the MPB crossing times as a list. >>> mpb_crossings(900) [datetime.datetime(2004, 10, 1, 18, 9, 57, 449707), datetime.datetime(2004, 10, 1, 19, 16, 18, 235840)] """ # Due to the difference of orbit from VEX, MEX cannot determine the # inbound and outbound crossings. So the number of crossing depends on # the orbit. logger = _logging.getLogger(__name__) t_now = t0 = _onr.get_start_time(orbit_number) t1 = _onr.get_stop_time(orbit_number) # Survey the position, and specify the time of crossing roughly. dt_survey = _datetime.timedelta(seconds=dt_survey) t_prev = t0 c_prev = inside_mpb(t_prev) crossing_time = [] while t_now <= t1: t_now += dt_survey c_now = inside_mpb(t_now) logger.debug('Survey: t_prev={} c_prev={}'.format(t_prev, c_prev)) logger.debug('Survey: t_now={} c_now={}'.format(t_prev, c_prev)) if c_now is not c_prev: logger.debug('Survey: CROSSED!') crossing_time.append(t_prev) t_prev = t_now c_prev = c_now # Calculate the time of crossing more precisely. exact_crossing_time = [] for t_cross in crossing_time: tc = _boundary_crossing(t_cross, t_cross + dt_survey, inside_mpb, tolerance=tolerance) exact_crossing_time.append(tc) return exact_crossing_time