Source code for gdt.core.data_primitives.ranges
# CONTAINS TECHNICAL DATA/COMPUTER SOFTWARE DELIVERED TO THE U.S. GOVERNMENT WITH UNLIMITED RIGHTS
#
# Contract Nos.: CA 80MSFC17M0022 / 80NSSC24M0035
# Contractor Name: Universities Space Research Association
# Contractor Address: 7178 Columbia Gateway Drive, Columbia, MD 21046
#
# Copyright 2017-2022 by Universities Space Research Association (USRA). All rights reserved.
#
# Developed by: William Cleveland and Adam Goldstein
# Universities Space Research Association
# Science and Technology Institute
# https://sti.usra.edu
#
# Developed by: Daniel Kocevski
# National Aeronautics and Space Administration (NASA)
# Marshall Space Flight Center
# Astrophysics Branch (ST-12)
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
# in compliance with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed under the License
# is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied. See the License for the specific language governing permissions and limitations under the
# License.
#
import numpy as np
__all__ = ['Range', 'TimeRange', 'EnergyRange']
[docs]class Range():
"""A primitive class defining a range
Parameters:
low (float): The low end of the range
high (float): The high end of the range
"""
def __init__(self, low, high):
if high >= low:
self._low = low
self._high = high
else:
self._low = high
self._high = low
@property
def center(self):
"""(float): The center of the range"""
return (self._high + self._low) / 2.0
@property
def width(self):
"""(float): The width of the range"""
return self._high - self._low
[docs] def as_tuple(self):
"""Return the range as a tuple.
Returns:
(float, float)
"""
return (self._low, self._high)
[docs] def contains(self, value, inclusive=True):
"""Determine if the range contains a value.
Args:
value (float): The input value to check
inclusive (bool, optional):
If True, then includes the edges of the range for the check,
otherwise it is edge-exclusive. Default is True.
Returns:
bool: True if the value is in the range, False otherwise
"""
if inclusive:
test = (value <= self._high) and (value >= self._low)
else:
test = (value < self._high) and (value > self._low)
if test:
return True
else:
return False
[docs] @classmethod
def intersection(cls, range1, range2):
"""Return a new Range that is the intersection of two input Ranges.
If the input Ranges do not intersect, then None is returned.
Args:
range1 (:class:`Range`): A range used to calculate the intersection
range2 (:class:`Range`): Another range used to calculate the
intersection
Returns:
:class:`Range`: The intersected range
"""
# test if one low is inside the other range
if range1.contains(range2._low):
lower = range1
upper = range2
elif range2.contains(range1._low):
lower = range2
upper = range1
else:
return None
# do the merge
low = upper._low
high = np.min((lower._high, upper._high))
obj = cls(low, high)
return obj
[docs] @classmethod
def union(cls, range1, range2):
"""Return a new Range that is the union of two input Ranges
Args:
range1 (:class:`Range`): A range used to calculate the union
range2 (:class:`Range`): Another range used to calculate the union
Returns:
:class:`Range`: The unionized range
"""
low = np.min((range1._low, range2._low))
high = np.max((range1._high, range2._high))
obj = cls(low, high)
return obj
[docs] def translate(self, value):
"""Returns a new Range that is the translated (shifted) by the given value
Args:
value (float): The value to shift the range by
Returns:
:class:`Range`: The shifted range
"""
return self.__class__(self._low + value, self._high + value)
def __eq__(self, other):
return (self._low == other._low) and (self._high == other._high)
def __repr__(self):
return '<{0}: ({1}, {2})>'.format(self.__class__.__name__, self._low,
self._high)
def _assert_float(self, value):
try:
value = float(value)
except:
raise TypeError('value must be a float')
return value
# mark: TODO: add units to TimeRange/EnergyRange
[docs]class TimeRange(Range):
"""A primitive class defining a time range
Parameters:
tstart (float): The start time of the range
tstop (float): The end time of the range
"""
def __init__(self, tstart, tstop):
tstart = self._assert_float(tstart)
tstop = self._assert_float(tstop)
super().__init__(tstart, tstop)
@property
def duration(self):
"""(float): The duration of the time range.
Alias for ``width``."""
return self.width
@property
def tstart(self):
"""(float): The start time of the range"""
return self._low
@property
def tstop(self):
"""(float): The end time of the range"""
return self._high
[docs]class EnergyRange(Range):
"""A primitive class defining an energy range
Parameters:
emin (float): The low end of the energy range
emax (float): The high end of the energy range
"""
def __init__(self, emin, emax):
emin = self._assert_float(emin)
emax = self._assert_float(emax)
super().__init__(emin, emax)
@property
def emax(self):
"""(float): The high end of the energy range"""
return self._high
@property
def emin(self):
"""(float): The low end of the energy range"""
return self._low
@property
def log_center(self):
"""log_center (float): The logarithmic center of the energy range"""
return np.sqrt(self.emin * self.emax)