Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • RD/spinifex
1 result
Show changes
Commits on Source (4)
Showing
with 284 additions and 131 deletions
......@@ -38,13 +38,13 @@ run_ruff:
stage: lint
script:
- tox -e ruff
allow_failure: true
allow_failure: false
run_pylint:
stage: lint
script:
- tox -e pylint
allow_failure: true
allow_failure: false
# build_extensions:
# stage: build_extensions
......
......@@ -55,6 +55,7 @@ docs = [
"sphinx-autoapi",
"sphinx_autodoc_typehints",
]
casa = ["python-casacore"]
[project.urls]
Homepage = "https://git.astron.nl/RD/spinifex"
......@@ -146,7 +147,7 @@ isort.required-imports = ["from __future__ import annotations"]
[tool.pylint]
py-version = "3.8"
py-version = "3.9"
ignore-paths = [".*/_version.py"]
reports.output-format = "colorized"
similarities.ignore-imports = "yes"
......
# Copyright (C) 2024 ASTRON (Netherlands Institute for Radio Astronomy)
# SPDX-License-Identifier: Apache-2.0
"""Module for getting ionospheric Faraday rotation from external geomagnetic and ionospheric models"""
"""Module for getting ionospheric Faraday rotation from external geomagnetic and ionospheric ionospheric_models"""
from __future__ import annotations
from importlib import metadata
from typing import Callable
__version__ = metadata.version("spinifex")
from spinifex.ionospheric import models
from spinifex.ionospheric import ModelDesnsityFunction, ionospheric_models
def get_rm(iono_model: Callable = models.IONEX):
"""Prints a nice message"""
# IPP = geometry.getIPP()
# iono = get_density_profile(IPP, time, iono_model)
return iono_model
get_rm(models.IONEX)
__all__ = ["ModelDesnsityFunction", "ionospheric_models"]
from __future__ import annotations
class IonexError(Exception):
"""Error in IONEX files."""
......@@ -11,9 +11,11 @@ import astropy.units as u
import numpy as np
from astropy.coordinates import ITRS, AltAz, EarthLocation, SkyCoord
from astropy.time import Time
from numpy.typing import ArrayLike
class IPP(NamedTuple):
"""Ionospheric Piercepoints"""
loc: EarthLocation
"""location of the piercepoints, dimension: times x altitudes. All altitudes are assumed to be equal"""
times: Time
......@@ -23,7 +25,7 @@ class IPP(NamedTuple):
def get_ipp_from_skycoord(
loc: EarthLocation, times: Time, source: SkyCoord, height_array: np.ndarray[float]
loc: EarthLocation, times: Time, source: SkyCoord, height_array: ArrayLike
) -> IPP:
"""Get the ionospheric piercepoints as EarhtLocations for a given EarthLocation, time, SkyCoord"""
# height_array = np.arange(100, 1000, 10) * u.km
......@@ -33,7 +35,7 @@ def get_ipp_from_skycoord(
def get_ipp_from_altaz(
loc: EarthLocation, altaz: AltAz, height_array: np.ndarray[float], times: Time
loc: EarthLocation, altaz: AltAz, height_array: ArrayLike, times: Time
) -> IPP:
altaz_dir = altaz.transform_to(ITRS)
ipp_vector = altaz_dir.cartesian[np.newaxis] * height_array[:, np.newaxis]
......
......@@ -11,11 +11,13 @@ import astropy.units as u
import numpy as np
from astropy.coordinates import EarthLocation, SkyCoord
from astropy.time import Time
from numpy.typing import ArrayLike
from spinifex.geometry.get_ipp import IPP, get_ipp_from_skycoord
class MsMetaData(NamedTuple):
"""Metadata from a Measurement Set"""
times: Time
location: EarthLocation
name: str
......@@ -23,7 +25,7 @@ class MsMetaData(NamedTuple):
def get_ipp_from_ms(
ms: str, height_array: np.ndarray[float], timestep: int = 1
ms: str, height_array: ArrayLike, timestep: int = 1
) -> IPP: # depends on casacore,
# an ms has different stations, you optionally want to return an ipp object for every station
msmetadata = get_metadata_from_ms(ms, timestep)
......
# Copyright (C) 2024 ASTRON (Netherlands Institute for Radio Astronomy)
# SPDX-License-Identifier: Apache-2.0
"""Module for getting ionospheric models"""
"""Module for getting ionospheric ionospheric_models"""
from __future__ import annotations
from dataclasses import dataclass
from typing import Callable
import astropy.units as u
from astropy.coordinates import EarthLocation
from astropy.time import Time
from spinifex.ionospheric.iono_models import (
from spinifex.ionospheric.models import (
ModelDesnsityFunction,
get_density_ionex_iri,
get_density_ionex_single_layer,
get_ionosphere,
ionospheric_models,
)
__all__ = ["get_ionosphere"]
@dataclass
class IonosphericModels:
"""Names space for different ionospheric models. An ionospheric model should be
a callable get_density"""
IONEX: Callable
IONEX_IRI: Callable
models = IonosphericModels(
IONEX=get_density_ionex_single_layer,
IONEX_IRI=get_density_ionex_iri,
)
def get_ionosphere(
loc: EarthLocation,
time: Time,
iono_model: Callable = models.IONEX,
) -> u.Quantity[u.m**-3]:
"""Prints a nice message"""
return iono_model(loc=loc, times=time)
__all__ = [
"ModelDesnsityFunction",
"get_density_ionex_iri",
"get_density_ionex_single_layer",
"get_ionosphere",
"ionospheric_models",
]
......@@ -5,41 +5,42 @@ from __future__ import annotations
from typing import NamedTuple
import numpy as np
from numpy.typing import ArrayLike
class Indices(NamedTuple):
"""Indices of the closest two points in a possibly wrapping selection and the inverse distance weights"""
idx1: np.ndarray[int]
idx1: ArrayLike
"""Index of the first closest point"""
idx2: np.ndarray[int]
idx2: ArrayLike
"""Index of the second closest point"""
w1: np.ndarray[float]
w1: ArrayLike
"""Weight of the first closest point"""
w2: np.ndarray[float]
w2: ArrayLike
"""Weight of the second closest point"""
class SortedIndices(NamedTuple):
"""Indices of the closest two points in a possibly wrapping selection"""
indices: np.ndarray
indices: ArrayLike
"""Index of the first closest point"""
distance: np.ndarray
distance: ArrayLike
"""Index of the second closest point"""
class Weights(NamedTuple):
"""Weights of the closest two points in a possibly wrapping selection"""
w1: np.ndarray
w1: ArrayLike
"""Weight of the first closest point"""
w2: np.ndarray
w2: ArrayLike
"""Weight of the second closest point"""
def get_indices_axis(
goal: np.ndarray, selection: np.ndarray, wrap_unit: float = 0
goal: ArrayLike, selection: ArrayLike, wrap_unit: float = 0
) -> Indices:
"""get indices of the closest two points in a possibly wrapping selection for an array
of goals"""
......@@ -83,23 +84,23 @@ def get_indices_axis(
def _get_weights(
goal: np.ndarray,
index1: np.ndarray,
index2: np.ndarray,
selection: np.ndarray,
goal: ArrayLike,
index1: ArrayLike,
index2: ArrayLike,
selection: ArrayLike,
wrap_unit: float = 0,
) -> Weights:
"""Calculate weights based on distance of goal to selection
Parameters
----------
goal : np.ndarray
goal : ArrayLike
array of points to get weights for
index1 : np.ndarray
index1 : ArrayLike
indices in selection for goals (index1, index2) per goal
index2 : np.ndarray
index2 : ArrayLike
indices in selection for goals (index1, index2) per goal
selection : np.ndarray
selection : ArrayLike
array to select from
wrap_unit : float, optional
if goal/selection is a wrapable (e.g. angle) set this unit (e.g. 360), by default 0
......@@ -115,14 +116,14 @@ def _get_weights(
return Weights(w1=1 - distance1 / sumdist, w2=1 - distance2 / sumdist)
def get_indices(goal: float, selection: np.ndarray, wrap_unit: float = 0) -> Indices:
def get_indices(goal: float, selection: ArrayLike, wrap_unit: float = 0) -> Indices:
"""find the indices of the closest two points in a possibly wrapping array selection
Parameters
----------
goal : float
location of point
selection : np.ndarray
selection : ArrayLike
array of points
wrap_unit : float, optional
if goal/selection is a wrapping entity (e.g. angles) set this to the wrap value (e.g. 360), by default 0
......@@ -154,8 +155,8 @@ def get_indices(goal: float, selection: np.ndarray, wrap_unit: float = 0) -> Ind
def get_sorted_indices(
lon: float,
lat: float,
avail_lon: np.ndarray,
avail_lat: np.ndarray,
avail_lon: ArrayLike,
avail_lat: ArrayLike,
wrap_unit: float = 360.0,
) -> SortedIndices:
"""find distances of a lon/lat grid to a point and return sorted list of indices"""
......@@ -167,24 +168,24 @@ def get_sorted_indices(
return SortedIndices(indices=sorted_idx, distance=distance[sorted_idx])
def get_interpol(data: np.ndarray, dist: np.ndarray):
def get_interpol(data: ArrayLike, dist: ArrayLike) -> float:
"""get distance weighted sum of data"""
w = 1.0 / dist
w /= np.sum(w)
return np.sum(data * w)
return float(np.sum(data * w))
def wrap_around_zero(data: np.ndarray, wrap_unit: float = 2 * np.pi):
def wrap_around_zero(data: ArrayLike, wrap_unit: float = 2 * np.pi) -> ArrayLike:
"""Function to calculate the remainder of data such that this is centered around zero"""
return np.remainder(data + 0.5 * wrap_unit, wrap_unit) - 0.5 * wrap_unit
def _compute_index_and_weights(maparray: np.ndarray, mapvalues: np.ndarray) -> Indices:
def _compute_index_and_weights(maparray: ArrayLike, mapvalues: ArrayLike) -> Indices:
"""helper function to get indices and weights for interpolating tecmaps
Args:
maparray (np.ndarray) : array to get indices in
maparray (ArrayLike) : array to get indices in
mapvalues (Union[float,np.array]) : values to get indices for
Returns:
Tuple[np.array, np.array, np.array]: idx1,idx2 and weights for idx2,
......
......@@ -4,9 +4,9 @@ from __future__ import annotations
from pathlib import Path
import numpy as np
from astropy.coordinates import EarthLocation
from astropy.time import Time
from numpy.typing import ArrayLike
from spinifex.ionospheric.index_tools import (
_compute_index_and_weights,
......@@ -17,11 +17,11 @@ from spinifex.ionospheric.ionex_parser import IonexData, read_ionex
def interpolate_ionex(
ionex: IonexData,
lons: np.ndarray,
lats: np.ndarray,
lons: ArrayLike,
lats: ArrayLike,
times: Time,
apply_earth_rotation: float = 1,
) -> np.ndarray:
) -> ArrayLike:
"""Interpolate ionex data to a given lon/lat/height grid.
lons, lats, times all should have the same length
apply_earth_rotation:
......@@ -34,9 +34,9 @@ def interpolate_ionex(
----------
ionex : IonexData
ionex object containing the information of the ionex file
lons : np.ndarray
lons : ArrayLike
longitudes (deg) of all points to interpolate to
lats : np.ndarray
lats : ArrayLike
lattitudes (deg) of all points to interpolate to
times : Time
times of all points to interpolate to
......@@ -46,7 +46,7 @@ def interpolate_ionex(
Returns
-------
np.ndarray
ArrayLike
array with interpolated tec values
"""
timeindex = _compute_index_and_weights(ionex.times.mjd, times.mjd)
......@@ -118,7 +118,9 @@ def interpolate_ionex(
return tecdata
def get_ionex_file(time: Time, server: str | None = None, prefix: str = "CODG") -> str:
def get_ionex_file(
time: Time, server: str | None = None, prefix: str = "CODG"
) -> Path | None:
"""Find ionex file locally or online.
Parameters
......@@ -144,8 +146,12 @@ def get_ionex_file(time: Time, server: str | None = None, prefix: str = "CODG")
return None
def _read_ionex_stuff(loc: EarthLocation, times: Time, **kwargs) -> np.ndarray:
server = kwargs.get("server")
def _read_ionex_stuff(
loc: EarthLocation, times: Time, server: str | None = None
) -> ArrayLike:
ionex_file = get_ionex_file(times[0], server=server)
if ionex_file is None:
msg = "No ionex file found!"
raise FileNotFoundError(msg)
ionex = read_ionex(ionex_file)
return interpolate_ionex(ionex, loc.lon.deg, loc.lat.deg, times)
......@@ -7,44 +7,47 @@ as described in Schaer and Gurtner (1998)"""
from __future__ import annotations
from pathlib import Path
from typing import NamedTuple, TextIO
from typing import Any, NamedTuple, TextIO
import astropy.units as u
import numpy as np
from astropy.time import Time
from numpy.typing import ArrayLike
from spinifex.exceptions import IonexError
class IonexData(NamedTuple):
"""Object containing all necessary information from Ionex data"""
lons: np.ndarray[float]
lons: ArrayLike
"""array with available longitude values (degrees)"""
lats: np.ndarray[float]
lats: ArrayLike
"""array with available latitude values (degrees)"""
times: Time
"""available times"""
dims: int
"""dimension of the heights (usually 1)"""
h: np.ndarray[float]
h: ArrayLike
"""available heights (km)"""
tec: np.ndarray[float]
tec: ArrayLike
"""array with tecvalues times x lons x lats (TECU)"""
rms: np.ndarray[float]
rms: ArrayLike
"""array with rms of tecvalues times x lons x lats (TECU, if available, zeros otherwise)"""
class IonexHeader(NamedTuple):
"""Object containing header information from ionex file"""
lons: np.ndarray[float]
lons: ArrayLike
"""array with available longitude values (degrees)"""
lats: np.ndarray[float]
lats: ArrayLike
"""array with available latitude values (degrees)"""
times: Time
"""available times"""
dims: int
"""dimension of the heights (usually 1)"""
h: np.ndarray[float]
h: ArrayLike
"""available heights (km)"""
mfactor: float
"""multiplication factor for tec values"""
......@@ -70,11 +73,23 @@ def read_ionex(ionex_filename: Path) -> IonexData:
def _read_ionex_header(filep: TextIO) -> IonexHeader:
"""Read header from ionex file. Put filepointer to the end of the header."""
# Declare variables with types
h1: float | None = None
h2: float | None = None
hstep: float | None = None
start_lon: float | None = None
end_lon: float | None = None
step_lon: float | None = None
start_lat: float | None = None
end_lat: float | None = None
step_lat: float | None = None
start_time: Time | None = None
ntimes: int | None = None
step_time: u.Quantity | None = None
mfactor: float | None = None
dimension: int | None = None
filep.seek(0)
h1, h2, hstep = (None,) * 3
start_lon, end_lon, step_lon, start_lat, end_lat, step_lat = (None,) * 6
start_time, ntimes, step_time = (None,) * 3
mfactor, dimension = (None,) * 2
for line in filep:
if "END OF HEADER" in line:
break
......@@ -98,9 +113,39 @@ def _read_ionex_header(filep: TextIO) -> IonexHeader:
start_lat, end_lat, step_lat = (float(i) for i in record.split())
if "# OF MAPS IN FILE" in label:
ntimes = int(record)
if h1 is None or start_lon is None or start_lat is None or start_time is None:
msg = f"Not a valid IONex file: {filep.name}"
raise OSError(msg)
# Check that all optional values are not None
# Need to do this one by one to get MyPy to understand that they are not None
# Should probably replace with higher level checks of the header values
bad_msg = f"Not a valid IONex file: {filep.name}"
if h1 is None:
raise IonexError(bad_msg)
if h2 is None:
raise IonexError(bad_msg)
if hstep is None:
raise IonexError(bad_msg)
if start_lon is None:
raise IonexError(bad_msg)
if end_lon is None:
raise IonexError(bad_msg)
if step_lon is None:
raise IonexError(bad_msg)
if start_lat is None:
raise IonexError(bad_msg)
if end_lat is None:
raise IonexError(bad_msg)
if step_lat is None:
raise IonexError(bad_msg)
if start_time is None:
raise IonexError(bad_msg)
if ntimes is None:
raise IonexError(bad_msg)
if step_time is None:
raise IonexError(bad_msg)
if mfactor is None:
raise IonexError(bad_msg)
if dimension is None:
raise IonexError(bad_msg)
harray = np.arange(h1, h2 + 0.5 * hstep, hstep) if hstep > 0 else np.array([h1])
......@@ -119,18 +164,18 @@ def _read_ionex_header(filep: TextIO) -> IonexHeader:
def _fill_data_record(
data: np.ndarray,
data: ArrayLike,
filep: TextIO,
stop_label: str,
timeidx: int,
ionex_header: IonexHeader,
):
) -> None:
"""Helper function to parse a data block of a single map in ionex.
Puts filepointer to the end of the map
Parameters
----------
data : np.ndarray
data : ArrayLike
pre allocated array to store the datablock
filep : TextIO
_description_
......@@ -142,9 +187,11 @@ def _fill_data_record(
header information
"""
line = filep.readline() # read EPOCH (not needed since we have the index)
tec = []
# TODO: Properly type tec
tec: list[Any] = []
lonidx = 0
latidx = 0
# TODO: Replace magic numbers with named constants
for line in filep:
label = line[60:-1]
if stop_label in label:
......@@ -159,8 +206,8 @@ def _fill_data_record(
tec = []
record = line[:60]
lat, lon1, _, _, _ = (float(record[i : i + 6]) for i in range(2, 32, 6))
latidx = np.argmin(np.abs(ionex_header.lats - lat))
lonidx = np.argmin(np.abs(ionex_header.lons - lon1))
latidx = int(np.argmin(np.abs(ionex_header.lats - lat)))
lonidx = int(np.argmin(np.abs(ionex_header.lons - lon1)))
else:
record = line[:-1]
tec += [float(record[i : i + 5]) for i in range(0, len(record), 5)]
......
......@@ -6,11 +6,12 @@ import astropy.units as u
import numpy as np
from astropy.coordinates import EarthLocation
from astropy.time import Time
from numpy.typing import ArrayLike
from PyIRI import coeff_dir
from PyIRI.main_library import IRI_density_1day
def get_profile(loc: EarthLocation, times: Time) -> np.ndarray[float]:
def get_profile(loc: EarthLocation, times: Time) -> ArrayLike:
aalt = loc[:, 0].height.to(u.km).value
hidx = np.argmin(
np.abs(aalt - 350)
......
......@@ -2,41 +2,74 @@
from __future__ import annotations
from dataclasses import dataclass
from typing import Protocol
import astropy.units as u
import numpy as np
from astropy.coordinates import EarthLocation
from astropy.time import Time
from numpy.typing import ArrayLike
import spinifex.ionospheric.iri_density as iri
from spinifex.ionospheric.ionex_manipulation import _read_ionex_stuff
def _read_tomion_stuff() -> np.ndarray[float]:
class ModelDesnsityFunction(Protocol):
"""Model density callable"""
def __call__(self, loc: EarthLocation, times: Time) -> ArrayLike: ...
@dataclass
class IonosphericModels:
"""Names space for different ionospheric ionospheric_models. An ionospheric model should be
a callable get_density"""
ionex: ModelDesnsityFunction
ionex_iri: ModelDesnsityFunction
def _read_tomion_stuff() -> ArrayLike:
return None # ionex_data
def get_density_ionex(loc: EarthLocation, times: Time, **kwargs) -> np.ndarray[float]:
return _read_ionex_stuff(loc=loc, times=times, **kwargs)
def get_density_ionex(loc: EarthLocation, times: Time) -> ArrayLike:
return _read_ionex_stuff(loc=loc, times=times)
def get_density_ionex_single_layer(
loc: EarthLocation, times: Time, height=350 * u.km, **kwargs
) -> np.ndarray[float]:
nT = loc.shape[1] # we assume time is second axis
loc: EarthLocation,
times: Time,
height: u.Quantity = 350 * u.km,
) -> ArrayLike:
n_times = loc.shape[1] # we assume time is second axis
index = np.argmin(np.abs(loc.height.to(u.km).value - height.to(u.km).value), axis=0)
single_layer_loc = loc[index, np.arange(nT)]
return _read_ionex_stuff(loc=single_layer_loc, times=times, **kwargs)
single_layer_loc = loc[index, np.arange(n_times)]
return _read_ionex_stuff(loc=single_layer_loc, times=times)
def get_density_ionex_iri(
loc: EarthLocation, times: Time, **kwargs
) -> np.ndarray[float]:
def get_density_ionex_iri(loc: EarthLocation, times: Time) -> ArrayLike:
profile = iri.get_profile(loc, times)
tec = get_density_ionex_single_layer(
loc=loc, times=times, **kwargs
loc=loc, times=times
) # get tec at single altitude
return tec[np.newaxis] * profile
# def get_density_tomion(loc: EarthLocation, times: Time, **kwargs) -> np.ndarray[float]:
# def get_density_tomion(loc: EarthLocation, times: Time) -> ArrayLike:
# return _read_tomion_stuff()
ionospheric_models = IonosphericModels(
ionex=get_density_ionex_single_layer,
ionex_iri=get_density_ionex_iri,
)
def get_ionosphere(
loc: EarthLocation,
time: Time,
iono_model: ModelDesnsityFunction = ionospheric_models.ionex,
) -> u.Quantity[u.m**-3]:
"""Prints a nice message"""
return iono_model(loc=loc, times=time)
......@@ -5,15 +5,6 @@
from __future__ import annotations
from dataclasses import dataclass
from typing import Callable
from spinifex.magnetic.models import magnetic_models
from spinifex.magnetic.magnetic_models import get_ppigrf_magnetic_field
@dataclass
class MagneticModels:
ppigrf: Callable
models = MagneticModels(ppigrf=get_ppigrf_magnetic_field)
__all__ = ["magnetic_models"]
......@@ -5,16 +5,35 @@
from __future__ import annotations
from dataclasses import dataclass
from typing import Protocol
import astropy.units as u
from astropy.coordinates import EarthLocation
from astropy.time import Time
from ppigrf import igrf
class MagneticFieldFunction(Protocol):
"""Magnetic field callable"""
def __call__(self, loc: EarthLocation, date: Time) -> u.Quantity: ...
@dataclass
class MagneticModels:
"""Supported magnetic field models"""
ppigrf: MagneticFieldFunction
def get_ppigrf_magnetic_field(loc: EarthLocation, date: Time) -> u.Quantity:
"""Get the magnetic field at a given EarthLocation"""
lon_deg = loc.lon.to(u.deg).value
lat_deg = loc.lat.to(u.deg).value
height_km = loc.height.to(u.km).value
be, bn, bu = igrf(lon=lon_deg, lat=lat_deg, h=height_km, date=date.to_datetime())
return u.Quantity((be, bn, bu) * u.nanotesla)
b_field_east, b_field_north, b_field_up = igrf(
lon=lon_deg, lat=lat_deg, h=height_km, date=date.to_datetime()
)
return u.Quantity((b_field_east, b_field_north, b_field_up) * u.nanotesla)
magnetic_models = MagneticModels(ppigrf=get_ppigrf_magnetic_field)
from __future__ import annotations
try:
from casacore.tables import table
except ImportError as e:
MSG = "casacore is not installed! To operate on MeasurementSets, install spinifex[casa]."
raise ImportError(MSG) from e
from pathlib import Path
def get_columns_from_ms(ms_path: Path) -> list[str]:
"""Get the columns from a MeasurementSet"""
with table(ms_path.as_posix()) as tab:
return tab.colnames()
......@@ -11,7 +11,7 @@ import astropy.units as u
import numpy as np
from astropy.coordinates import EarthLocation
from astropy.time import Time
from spinifex.ionospheric import models
from spinifex.ionospheric import ionospheric_models
from spinifex.ionospheric.ionex_parser import read_ionex
......@@ -28,9 +28,9 @@ def test_get_ionosphere():
lon = np.array([[6.367, 6.5], [3.367, 7.5]]) * u.deg
lat = np.array([[52.833, 60.0], [52.833, 60.0]]) * u.deg # h x times
above_dwingeloo = EarthLocation(lon=lon, lat=lat, height=height[:, np.newaxis])
tec = models.IONEX(loc=above_dwingeloo, times=times)
tec = ionospheric_models.ionex(loc=above_dwingeloo, times=times)
assert tec.shape == (2,)
tec = models.IONEX(loc=above_dwingeloo[:, :1], times=times[:1])
tec = ionospheric_models.ionex(loc=above_dwingeloo[:, :1], times=times[:1])
assert tec.shape == (1,)
tec = models.IONEX_IRI(loc=above_dwingeloo[:, :], times=times[:])
tec = ionospheric_models.ionex_iri(loc=above_dwingeloo[:, :], times=times[:])
assert tec.shape == (2, 2)
......@@ -8,7 +8,7 @@ from __future__ import annotations
import astropy.units as u
from astropy.coordinates import EarthLocation
from astropy.time import Time
from spinifex.magnetic import models
from spinifex.magnetic import magnetic_models
def is_convertible_to_unit(quantity: u.Quantity, unit: u.Unit) -> bool:
......@@ -26,5 +26,5 @@ def test_get_magnetic_field():
lon=6.367 * u.deg, lat=52.833 * u.deg, height=100 * u.km
)
time = Time("2024-01-01T13:42")
field = models.ppigrf(above_dwingeloo, time)
field = magnetic_models.ppigrf(above_dwingeloo, time)
assert is_convertible_to_unit(field, u.tesla)
......@@ -35,7 +35,8 @@ deps =
pylint
pre-commit
commands =
ruff: "{envpython}" -m ruff check spinifex tests --diff
ruff: "{envpython}" -m ruff check spinifex --diff
ruff: "{envpython}" -m ruff check tests --diff
pylint: "{envpython}" -m pylint --version
pylint: "{envpython}" -m pylint --ignored-modules=astropy.units spinifex tests
format: "{envpython}" -m ruff format spinifex tests
......