#!/usr/bin/env python
# -*- coding: utf-8 -*-
# DOI: 10.5281/zenodo.15102100
# Full license can be found in License.md
#
# DISTRIBUTION STATEMENT A: Approved for public release. Distribution is
# unlimited.
# -----------------------------------------------------------------------------
"""Tests for functions in `utils.coords`."""
import datetime as dt
import numpy as np
import unittest
from pyIntensityFeatures.utils import coords
try:
import apexpy
except ImportError:
[docs]
class TestTimeFuncs(unittest.TestCase):
"""Tests for time-handling functions."""
[docs]
def setUp(self):
"""Set up the test runs."""
self.dtime = dt.datetime(1999, 2, 11)
self.out = None
return
[docs]
def tearDown(self):
"""Tear down the test environment."""
del self.dtime, self.out
return
[docs]
def test_as_datetime(self):
"""Test success for datetime casting."""
# Cycle through potential time formats
for in_time in [dt.date(self.dtime.year, self.dtime.month,
self.dtime.day), self.dtime,
np.datetime64(self.dtime.strftime('%Y-%m-%d')),
dt.datetime(self.dtime.year, self.dtime.month,
self.dtime.day, tzinfo=dt.timezone.utc)]:
with self.subTest(in_time=in_time):
# Convert the time
self.out = coords.as_datetime(in_time)
self.assertEqual(self.out, self.dtime)
return
[docs]
class TestSliceMinMax(unittest.TestCase):
"""Tests for `get_slice_mlat_max_min`."""
[docs]
def setUp(self):
"""Set up the test runs."""
self.mlat_inc = 0.5
self.mlat_bins = np.arange(50, 90, self.mlat_inc)
self.mlt_bins = np.arange(0.0, 24.0, self.mlat_inc)
self.num_samples = np.full(shape=(self.mlat_bins.shape[0],
self.mlt_bins.shape[0]),
fill_value=10)
self.mlat_min = self.mlat_bins.min()
self.mlat_max = self.mlat_bins.max()
self.out_min = None
self.out_max = None
return
[docs]
def tearDown(self):
"""Tear down the test environment."""
del self.mlat_inc, self.mlat_bins, self.mlt_bins, self.num_samples
del self.mlat_min, self.mlat_max, self.out_min, self.out_max
return
[docs]
def test_get_slice_mlat_max_min_all_good(self):
"""Test with all bins valid."""
for hemi in [1.0, -1.0]:
with self.subTest(hemisphere=hemi):
self.out_max, self.out_min = coords.get_slice_mlat_max_min(
self.num_samples, hemi * self.mlat_bins, self.mlt_bins,
mlat_inc=self.mlat_inc)
# Ensure difference in output max/min is less than the bin
# increment away from the input min/max
self.assertLess(abs(self.out_max - hemi * self.mlat_max),
self.mlat_inc)
self.assertLess(abs(self.out_min - hemi * self.mlat_min),
self.mlat_inc)
return
[docs]
def test_get_slice_mlat_max_min_with_cutout(self):
"""Test with a cutout in samples."""
# Change the number of samples at the first time
self.num_samples[:int(len(self.mlat_bins) / 2), 0] = 0
for hemi in [1.0, -1.0]:
with self.subTest(hemisphere=hemi):
self.out_max, self.out_min = coords.get_slice_mlat_max_min(
self.num_samples, hemi * self.mlat_bins, self.mlt_bins,
mlat_inc=self.mlat_inc)
# Ensure difference in output max/min is less than the bin
# increment away from the input min/max
self.assertLess(abs(self.out_max - hemi * self.mlat_max),
self.mlat_inc)
# Ensure the returned minimum value is above the bin minimum
self.assertGreater(hemi * self.out_min, self.mlat_min)
return
[docs]
def test_get_slice_mlat_max_min_all_bad(self):
"""Test with no valid samples."""
# Change the number of samples at the first time
self.num_samples[:, :] = 0
for hemi in [1.0, -1.0]:
with self.subTest(hemisphere=hemi):
self.out_max, self.out_min = coords.get_slice_mlat_max_min(
self.num_samples, hemi * self.mlat_bins, self.mlt_bins,
mlat_inc=self.mlat_inc)
# Ensure the defaults are returned
self.assertEqual(self.out_max, 0.0)
self.assertEqual(self.out_min, 0.0)
return
[docs]
class TestConvertGeoToMag(unittest.TestCase):
"""Tests for `convert_geo_to_mag`."""
[docs]
def setUp(self):
"""Set up the test runs."""
self.ctime = dt.datetime(1999, 2, 11)
self.glat = np.linspace(45.0, 90.0, 25).reshape(5, 5)
self.glon = np.full(shape=(5, 5), fill_value=180.0)
self.alt = 110.0
self.mlat = None
self.mlon = None
self.mlt = None
self.aacgmv2_method = ['TRACE', 'ALLOWTRACE', 'BADIDEA', 'GEOCENTRIC']
self.apexpy_method = ['apex', 'qd']
return
[docs]
def tearDown(self):
"""Tear down the test environment."""
del self.ctime, self.glat, self.glon, self.alt, self.mlat, self.mlon
del self.mlt, self.aacgmv2_method, self.apexpy_method
return
[docs]
def test_aacgmv2_conversion(self):
"""Test that the conversion with the AACGMV2 methods."""
# Cycle through all the AACGMV2 methods
for method in self.aacgmv2_method:
with self.subTest(method=method):
# Run the conversion
self.mlat, self.mlon, self.mlt = coords.convert_geo_to_mag(
self.ctime, self.glat, self.glon, self.alt, method=method)
# Test the output shapes
self.assertEqual(self.mlat.shape, self.glat.shape)
self.assertEqual(self.mlon.shape, self.glat.shape)
self.assertEqual(self.mlt.shape, self.glat.shape)
# Test the max and min latitude indices
self.assertEqual(self.mlat.argmin(), self.glat.argmin())
self.assertEqual(self.mlat.argmax(), self.glat.argmax())
@unittest.skipIf(apexpy is None, "cannot test apexpy without module")
[docs]
def test_apexpy_conversion(self):
"""Test that the conversion with the apexpy methods."""
# Cycle through all the apexpy methods
for method in self.apexpy_method:
with self.subTest(method=method):
# Run the conversion
self.mlat, self.mlon, self.mlt = coords.convert_geo_to_mag(
self.ctime, self.glat, self.glon, self.alt, method=method)
# Test the output shapes
self.assertEqual(self.mlat.shape, self.glat.shape)
self.assertEqual(self.mlon.shape, self.glat.shape)
self.assertEqual(self.mlt.shape, self.glat.shape)
# Test the max and min latitude indices
self.assertEqual(self.mlat.argmin(), self.glat.argmin())
self.assertEqual(self.mlat.argmax(), self.glat.argmax())
@unittest.skipIf(apexpy is not None,
"cannot test apexpy failure with module")
[docs]
def test_apexpy_conversion_no_apexpy(self):
"""Test the conversion raises ValueError with the apexpy methods."""
# Cycle through all the apexpy methods
for method in self.apexpy_method:
with self.subTest(method=method):
# Run the conversion and catch the error
args = [self.ctime, self.glat, self.glon, self.alt, method]
self.assertRaisesRegex(ValueError, "apexpy is not available.",
coords.convert_geo_to_mag, *args)
return