Skip to content

Commit ea1e7d1

Browse files
authored
Merge pull request #156 from pysat/rc_v0.2.1
Release Candidate v0.2.1
2 parents 9976fa8 + feea232 commit ea1e7d1

11 files changed

Lines changed: 131 additions & 40 deletions

File tree

.github/workflows/main.yml

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ jobs:
2626
os: ubuntu-latest
2727
test_config: "NEP29"
2828
# Operational compliance settings
29-
- python-version: "3.6.8"
30-
numpy_ver: "1.19.5"
29+
- python-version: "3.9"
30+
numpy_ver: "1.23.5"
3131
os: "ubuntu-20.04"
3232
test_config: "Ops"
3333

@@ -44,9 +44,7 @@ jobs:
4444
if: ${{ matrix.test_config == 'Ops'}}
4545
run: |
4646
pip install numpy==${{ matrix.numpy_ver }}
47-
pip install -r requirements.txt
48-
pip install -r test_requirements.txt
49-
pip install .
47+
pip install --upgrade-strategy only-if-needed .[test]
5048
5149
- name: Install NEP29 dependencies
5250
if: ${{ matrix.test_config == 'NEP29'}}

CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,17 @@ Change Log
33
All notable changes to this project will be documented in this file.
44
This project adheres to [Semantic Versioning](https://semver.org/).
55

6+
[0.2.1] - 2024-11-18
7+
--------------------
8+
* Enhancements
9+
* Added a utility function for evaluating fill values of different types
10+
* Maintenance
11+
* Updated Ops tests to new lower limit of Python 3.9 and removed 3.6 support
12+
* Bugs
13+
* Fixed error in mock downloading F10.7 prelim files
14+
* Fixed combine_kp to consider desired time limits and fill values when
15+
loading the standard dataset
16+
617
[0.2.0] - 2024-08-30
718
--------------------
819
* Enhancements

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ give some examples on how to use the routines.
2222

2323
pysatSpaceWeather uses common Python modules, as well as modules developed by
2424
and for the Space Physics community. This module officially supports
25-
Python 3.7+.
25+
Python 3.9+.
2626

2727
| Common modules | Community modules |
2828
| -------------- | ----------------- |

docs/installation.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ Prerequisites
1919

2020
pysatSpaceWeather uses common Python modules, as well as modules developed by
2121
and for the Space Physics community. This module officially supports
22-
Python 3.6 and 3.9+.
22+
Python 3.9+.
2323

2424
============== =================
2525
Common modules Community modules

pyproject.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "pysatSpaceWeather"
7-
version = "0.2.0"
7+
version = "0.2.1"
88
description = 'pysat support for Space Weather Indices'
99
readme = "README.md"
10-
requires-python = ">=3.6"
10+
requires-python = ">=3.9"
1111
license = {file = "LICENSE"}
1212
authors = [
1313
{name = "Angeline Burrell", email = "pysat.developers@gmail.com"}
@@ -21,7 +21,7 @@ classifiers = [
2121
"License :: OSI Approved :: BSD License",
2222
"Natural Language :: English",
2323
"Programming Language :: Python :: 3",
24-
"Programming Language :: Python :: 3.6",
24+
"Programming Language :: Python :: 3.9",
2525
"Programming Language :: Python :: 3.10",
2626
"Programming Language :: Python :: 3.11",
2727
"Programming Language :: Python :: 3.12",

pysatSpaceWeather/instruments/methods/f107.py

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import pysat
1818

1919
import pysatSpaceWeather as pysat_sw
20+
from pysatSpaceWeather.instruments.methods.general import is_fill_val
2021

2122

2223
def acknowledgements(tag):
@@ -162,7 +163,6 @@ def combine_f107(standard_inst, forecast_inst, start=None, stop=None):
162163

163164
# Cycle through the desired time range
164165
itime = dt.datetime(start.year, start.month, start.day)
165-
166166
while itime < stop and inst_flag is not None:
167167
# Load and save the standard data for as many times as possible
168168
if inst_flag == 'standard':
@@ -193,8 +193,13 @@ def combine_f107(standard_inst, forecast_inst, start=None, stop=None):
193193
fill_val = f107_inst.meta['f107'][
194194
f107_inst.meta.labels.fill_val]
195195

196-
good_vals = standard_inst['f107'][good_times] != fill_val
196+
good_vals = np.array([not is_fill_val(val, fill_val) for val
197+
in standard_inst['f107'][good_times]])
197198
new_times = list(standard_inst.index[good_times][good_vals])
199+
else:
200+
new_times = []
201+
202+
if len(new_times) > 0:
198203
f107_times.extend(new_times)
199204
new_vals = list(standard_inst['f107'][good_times][good_vals])
200205
f107_values.extend(new_vals)
@@ -237,12 +242,14 @@ def combine_f107(standard_inst, forecast_inst, start=None, stop=None):
237242
# Get the good times and values
238243
good_times = ((forecast_inst.index >= itime)
239244
& (forecast_inst.index < stop))
240-
good_vals = forecast_inst['f107'][good_times] != fill_val
245+
good_vals = np.array([
246+
not is_fill_val(val, fill_val) for val
247+
in forecast_inst['f107'][good_times]])
241248

242249
# Save desired data and cycle time
243250
if len(good_vals) > 0:
244-
new_times = list(
245-
forecast_inst.index[good_times][good_vals])
251+
new_times = list(forecast_inst.index[good_times][
252+
good_vals])
246253
f107_times.extend(new_times)
247254
new_vals = list(
248255
forecast_inst['f107'][good_times][good_vals])
@@ -267,8 +274,6 @@ def combine_f107(standard_inst, forecast_inst, start=None, stop=None):
267274
if len(f107_times) == 0:
268275
f107_times = date_range
269276

270-
date_range = pds.date_range(start=start, end=end_date, freq=freq)
271-
272277
if date_range[0] < f107_times[0]:
273278
# Extend the time and value arrays from their beginning with fill
274279
# values

pysatSpaceWeather/instruments/methods/general.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,38 @@
1717
import pysat
1818

1919

20+
def is_fill_val(data, fill_val):
21+
"""Evaluate whether or not a value is a fill value.
22+
23+
Parameters
24+
----------
25+
data : int, float, or str
26+
Data value
27+
fill_val : int, float, or str
28+
Fill value
29+
30+
Returns
31+
-------
32+
is_fill : bool
33+
True if the data is equal to the fill value, False if it is not.
34+
35+
"""
36+
37+
try:
38+
# NaN and finite evaluation will fail for non-numeric types
39+
if np.isnan(fill_val):
40+
is_fill = np.isnan(data)
41+
elif np.isfinite(fill_val):
42+
is_fill = data == fill_val
43+
else:
44+
is_fill = ~np.isfinite(data)
45+
except TypeError:
46+
# Use equality for string and similar types
47+
is_fill = data == fill_val
48+
49+
return is_fill
50+
51+
2052
def preprocess(inst):
2153
"""Preprocess the meta data by replacing the file fill values with NaN.
2254

pysatSpaceWeather/instruments/methods/kp_ap.py

Lines changed: 39 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import pysat
1717

1818
import pysatSpaceWeather as pysat_sw
19+
from pysatSpaceWeather.instruments.methods import general
1920
from pysatSpaceWeather.instruments.methods import gfz
2021
from pysatSpaceWeather.instruments.methods import swpc
2122

@@ -600,7 +601,9 @@ def combine_kp(standard_inst=None, recent_inst=None, forecast_inst=None,
600601
while itime < stop and inst_flag is not None:
601602
# Load and save the standard data for as many times as possible
602603
if inst_flag == 'standard':
603-
standard_inst.load(date=itime)
604+
# Test to see if data loading is needed
605+
if not np.any(standard_inst.index == itime):
606+
standard_inst.load(date=itime)
604607

605608
if notes.find("standard") < 0:
606609
notes += " the {:} source ({:} to ".format(inst_flag,
@@ -610,9 +613,23 @@ def combine_kp(standard_inst=None, recent_inst=None, forecast_inst=None,
610613
inst_flag = 'forecast' if recent_inst is None else 'recent'
611614
notes += "{:})".format(itime.date())
612615
else:
613-
kp_times.extend(list(standard_inst.index))
614-
kp_values.extend(list(standard_inst['Kp']))
615-
itime = kp_times[-1] + pds.DateOffset(hours=3)
616+
local_fill_val = standard_inst.meta[
617+
'Kp', standard_inst.meta.labels.fill_val]
618+
good_times = ((standard_inst.index >= itime)
619+
& (standard_inst.index < stop))
620+
good_vals = np.array([
621+
not general.is_fill_val(val, local_fill_val)
622+
for val in standard_inst['Kp'][good_times]])
623+
new_times = list(standard_inst.index[good_times][good_vals])
624+
625+
if len(new_times) > 0:
626+
kp_times.extend(new_times)
627+
kp_values.extend(list(
628+
standard_inst['Kp'][good_times][good_vals]))
629+
itime = kp_times[-1] + pds.DateOffset(hours=3)
630+
else:
631+
inst_flag = 'forecast' if recent_inst is None else 'recent'
632+
notes += "{:})".format(itime.date())
616633

617634
# Load and save the recent data for as many times as possible
618635
if inst_flag == 'recent':
@@ -637,18 +654,20 @@ def combine_kp(standard_inst=None, recent_inst=None, forecast_inst=None,
637654

638655
# Determine which times to save
639656
if recent_inst.empty:
640-
good_vals = []
657+
new_times = []
641658
else:
642659
local_fill_val = recent_inst.meta[
643660
'Kp', recent_inst.meta.labels.fill_val]
644661
good_times = ((recent_inst.index >= itime)
645662
& (recent_inst.index < stop))
646-
good_vals = recent_inst['Kp'][good_times] != local_fill_val
663+
good_vals = np.array([
664+
not general.is_fill_val(val, local_fill_val)
665+
for val in recent_inst['Kp'][good_times]])
666+
new_times = list(recent_inst.index[good_times][good_vals])
647667

648668
# Save output data and cycle time
649-
if len(good_vals):
650-
kp_times.extend(list(
651-
recent_inst.index[good_times][good_vals]))
669+
if len(new_times) > 0:
670+
kp_times.extend(new_times)
652671
kp_values.extend(list(
653672
recent_inst['Kp'][good_times][good_vals]))
654673
itime = kp_times[-1] + pds.DateOffset(hours=3)
@@ -683,17 +702,21 @@ def combine_kp(standard_inst=None, recent_inst=None, forecast_inst=None,
683702
'Kp', forecast_inst.meta.labels.fill_val]
684703
good_times = ((forecast_inst.index >= itime)
685704
& (forecast_inst.index < stop))
686-
good_vals = forecast_inst['Kp'][
687-
good_times] != local_fill_val
705+
good_vals = np.array([
706+
not general.is_fill_val(val, local_fill_val)
707+
for val in forecast_inst['Kp'][good_times]])
688708

689709
# Save desired data
690710
new_times = list(forecast_inst.index[good_times][good_vals])
691-
kp_times.extend(new_times)
692-
new_vals = list(forecast_inst['Kp'][good_times][good_vals])
693-
kp_values.extend(new_vals)
694711

695-
# Cycle time
696-
itime = kp_times[-1] + pds.DateOffset(hours=3)
712+
if len(new_times) > 0:
713+
kp_times.extend(new_times)
714+
new_vals = list(forecast_inst['Kp'][good_times][
715+
good_vals])
716+
kp_values.extend(new_vals)
717+
718+
# Cycle time
719+
itime = kp_times[-1] + pds.DateOffset(hours=3)
697720
notes += "{:})".format(itime.date())
698721

699722
inst_flag = None

pysatSpaceWeather/instruments/methods/swpc.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,9 +240,9 @@ def old_indices_dsd_download(name, date_array, data_path, local_files, today,
240240
else:
241241
# Set the saved filename
242242
saved_fname = os.path.join(mock_download_dir, local_fname)
243-
downloaded = True
244243

245244
if os.path.isfile(saved_fname):
245+
downloaded = True
246246
rewritten = True
247247
else:
248248
pysat.logger.info("".join([saved_fname, "is missing, ",
@@ -273,6 +273,7 @@ def old_indices_dsd_download(name, date_array, data_path, local_files, today,
273273
# Close connection after downloading all dates
274274
if mock_download_dir is None:
275275
ftp.close()
276+
276277
return
277278

278279

pysatSpaceWeather/tests/test_methods_general.py

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
"""Integration and unit test suite for ACE methods."""
1010

1111
import numpy as np
12+
import pytest
1213

1314
import pysat
1415

@@ -22,24 +23,45 @@ def setup_method(self):
2223
"""Create a clean testing setup."""
2324
self.testInst = pysat.Instrument('pysat', 'testing')
2425
self.testInst.load(date=self.testInst.inst_module._test_dates[''][''])
26+
self.var = self.testInst.variables[0]
2527
return
2628

2729
def teardown_method(self):
2830
"""Clean up previous testing setup."""
29-
del self.testInst
31+
del self.testInst, self.var
3032
return
3133

3234
def test_preprocess(self):
3335
"""Test the preprocessing routine updates all fill values to be NaN."""
3436

3537
# Make sure at least one fill value is not already NaN
36-
var = self.testInst.variables[0]
37-
self.testInst.meta[var] = {self.testInst.meta.labels.fill_val: 0.0}
38+
self.testInst.meta[self.var] = {self.testInst.meta.labels.fill_val: 0.0}
3839

3940
# Update the meta data using the general preprocess routine
4041
general.preprocess(self.testInst)
4142

4243
# Test the output
4344
assert np.isnan(
44-
self.testInst.meta[var, self.testInst.meta.labels.fill_val])
45+
self.testInst.meta[self.var, self.testInst.meta.labels.fill_val])
46+
return
47+
48+
@pytest.mark.parametrize("fill_val", [-1.0, -1, np.nan, np.inf, ''])
49+
def test_is_fill(self, fill_val):
50+
"""Test the successful evaluation of fill values.
51+
52+
Parameters
53+
----------
54+
fill_val : float, int, or str
55+
Fill value to use as a comparison
56+
57+
"""
58+
# Set the data value to not be a fill value
59+
if fill_val != '':
60+
self.var = -47
61+
62+
# Evaluate the variable is False
63+
assert not general.is_fill_val(self.var, fill_val)
64+
65+
# Evaluate the fill value is a fill value
66+
assert general.is_fill_val(fill_val, fill_val)
4567
return

0 commit comments

Comments
 (0)