Skip to content

Commit

Permalink
Updates for recent heyoka changes.
Browse files Browse the repository at this point in the history
  • Loading branch information
bluescarni committed Jan 2, 2025
1 parent 0f82fb2 commit 09b95fb
Show file tree
Hide file tree
Showing 16 changed files with 96 additions and 189 deletions.
16 changes: 3 additions & 13 deletions doc/notebooks/sgp4_propagator.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -123,17 +123,7 @@
"id": "1e3ec1fa-1c09-46f7-a8fe-f6da4c33edb2",
"metadata": {},
"source": [
"{py:func}`~heyoka.model.sgp4_propagator()` takes as only mandatory input argument the list of satellites, here represented as ``Satrec`` objects from the [sgp4 Python module](https://pypi.org/project/sgp4/). The list of satellites can alternatively be passed as a NumPy 2D array containing the GPE data for all satellites (see the documentation). {py:func}`~heyoka.model.sgp4_propagator()` returns a propagator object, by default of type {py:func}`~heyoka.model.sgp4_propagator_dbl()` (i.e., a double-precision propagator). You can also request a single-precision propagator via the {ref}`fp_type keyword argument <api_common_kwargs_fp_type>`.\n",
"\n",
"(tut_sgp4_propagator_epochs)=\n",
"\n",
"## A caveat on epochs and scales of time\n",
"\n",
"When an {py:func}`~heyoka.model.sgp4_propagator()` is constructed from a list of ``Satrec`` objects, the satellite epochs are represented as UTC Julian dates. Consequently, UTC Julian dates must also be used when specifying the {ref}`propagation epochs <tut_sgp4_scalar_propagation>`. The use of UTC Julian dates comes with an important caveat: UTC days do not constitute a uniform scale of time due to the potential presence of [leap seconds](https://en.wikipedia.org/wiki/Leap_second). As a consequence, if the time interval between the satellite epoch and the propagation epoch includes a leap second, the result of the propagation will be slightly wrong.\n",
"\n",
"Considering that the last leap second was added in 2016 and that leap seconds may be phased out altogether by 2035, this quirk should not manifest itself when using contemporary GPE data. For the purpose of this tutorial we will thus ignore the leap seconds issue altogether.\n",
"\n",
"If precise propagation across leap seconds is required, one should construct an {py:func}`~heyoka.model.sgp4_propagator()` not via a list of ``Satrec`` objects, but instead using the second overload which takes as input the GPE data as a NumPy 2D array. The GPE epochs in the array should be represented as Julian dates in a continuous scale of time such as [TAI](https://en.wikipedia.org/wiki/International_Atomic_Time). Similarly, the propagation epochs should be provided as Julian dates in the same scale of time. We recommend to use of a library such as [Astropy](https://docs.astropy.org/en/stable/time/index.html) to convert between UTC epochs and TAI Julian dates."
"{py:func}`~heyoka.model.sgp4_propagator()` takes as only mandatory input argument the list of satellites, here represented as ``Satrec`` objects from the [sgp4 Python module](https://pypi.org/project/sgp4/). The list of satellites can alternatively be passed as a NumPy 2D array containing the GPE data for all satellites (see the documentation). {py:func}`~heyoka.model.sgp4_propagator()` returns a propagator object, by default of type {py:func}`~heyoka.model.sgp4_propagator_dbl()` (i.e., a double-precision propagator). You can also request a single-precision propagator via the {ref}`fp_type keyword argument <api_common_kwargs_fp_type>`."
]
},
{
Expand Down Expand Up @@ -263,7 +253,7 @@
"source": [
"Julian dates are represented via the {py:attr}`~heyoka.model.sgp4_propagator_dbl.jdtype` type, which is a {ref}`structured NumPy datatype<numpy:defining-structured-types>` consisting of\n",
"two floating-point fields, the first one called ``jd`` and representing a Julian date, the second one called ``frac`` representing a fractional correction to ``jd`` (so that the full\n",
"Julian date is ``jd + frac``). Remember that, since we constructed the propagator from a list of ``Satrec`` objects, the Julian dates are to be provided in the UTC time scale.\n",
"Julian date is ``jd + frac``). Julian dates must be provided in the UTC scale of time, following the [SOFA](http://www.iausofa.org/)/[ERFA](https://github.com/liberfa/erfa/) convention of handling leap seconds via slightly longer/shorter days. We highly recommend the use of a library such as [Astropy](https://docs.astropy.org/en/stable/time/index.html) to convert between calendar days and UTC Julian dates.\n",
"\n",
"Although in principle we could propagate each satellite to a different date, here we will be propagating *all* satellites to the same Julian date:"
]
Expand Down Expand Up @@ -1206,7 +1196,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.12.4"
"version": "3.13.1"
}
},
"nbformat": 4,
Expand Down
15 changes: 9 additions & 6 deletions heyoka/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,8 +153,9 @@ def from_sympy(ex, s_dict={}):

if not isinstance(ex, Basic):
raise TypeError(
"The 'ex' parameter must be a sympy expression but it is of type {} instead"
.format(type(ex))
"The 'ex' parameter must be a sympy expression but it is of type {} instead".format(
type(ex)
)
)

if not isinstance(s_dict, dict):
Expand Down Expand Up @@ -215,8 +216,9 @@ def set_serialization_backend(name):

if not name in _s11n_backend_map:
raise ValueError(
"The serialization backend '{}' is not valid. The valid backends are: {}"
.format(name, list(_s11n_backend_map.keys()))
"The serialization backend '{}' is not valid. The valid backends are: {}".format(
name, list(_s11n_backend_map.keys())
)
)

new_backend = _s11n_backend_map[name]
Expand Down Expand Up @@ -289,8 +291,9 @@ def is_iterable(x):
return _ensemble_propagate_process(tp, ta, arg, n_iter, gen, **kwargs)

raise ValueError(
"The parallelisation algorithm must be one of {}, but '{}' was provided instead"
.format(allowed_algos, algo)
"The parallelisation algorithm must be one of {}, but '{}' was provided instead".format(
allowed_algos, algo
)
)


Expand Down
17 changes: 6 additions & 11 deletions heyoka/_test_batch_integrator.py
Original file line number Diff line number Diff line change
Expand Up @@ -214,8 +214,7 @@ def __call__(self, ta):
ta.propagate_for(fp_t(10.0), callback=broken_cb())
self.assertTrue(
"The call operator of a step callback is expected to return a boolean,"
" but a value of type"
in str(cm.exception)
" but a value of type" in str(cm.exception)
)

# Callback with pre_hook().
Expand Down Expand Up @@ -321,8 +320,7 @@ def __call__(self, ta):
ta.propagate_until(fp_t(10.0), callback=broken_cb())
self.assertTrue(
"The call operator of a step callback is expected to return a boolean,"
" but a value of type"
in str(cm.exception)
" but a value of type" in str(cm.exception)
)

# Callback with pre_hook().
Expand Down Expand Up @@ -713,17 +711,15 @@ def test_propagate_grid(self):
self.assertTrue(
"Invalid grid passed to the propagate_grid() method of a batch"
" integrator: the expected number of dimensions is 2, but the input"
" array has a dimension of 1"
in str(cm.exception)
" array has a dimension of 1" in str(cm.exception)
)

with self.assertRaises(ValueError) as cm:
ta.propagate_grid(np.array([[1, 2], [3, 4]], dtype=fp_t))
self.assertTrue(
"Invalid grid passed to the propagate_grid() method of a batch"
" integrator: the shape must be (n, 4) but the number of columns is 2"
" instead"
in str(cm.exception)
" instead" in str(cm.exception)
)

# Run a simple scalar/batch comparison.
Expand All @@ -747,7 +743,7 @@ def test_propagate_grid(self):
self.assertTrue(bres[0] is None)

for idx, cur_ta in enumerate(tas):
cur_ta.propagate_until(grid[0, idx]),
(cur_ta.propagate_until(grid[0, idx]),)

sres = [
tas[0].propagate_grid(grid[:, 0]),
Expand Down Expand Up @@ -842,8 +838,7 @@ def __call__(self, ta):
ta.propagate_grid(grid, callback=broken_cb())
self.assertTrue(
"The call operator of a step callback is expected to return a boolean,"
" but a value of type"
in str(cm.exception)
" but a value of type" in str(cm.exception)
)

# Callback with pre_hook().
Expand Down
69 changes: 23 additions & 46 deletions heyoka/_test_cfunc.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,8 +144,7 @@ def test_multi(self):
cfunc(func, vars=[y, x], fp_type=fp_t, batch_size=2)
self.assertTrue(
"Batch sizes greater than 1 are not supported for this"
" floating-point type"
in str(cm.exception)
" floating-point type" in str(cm.exception)
)

fn = cfunc(func, vars=[y, x], fp_type=fp_t)
Expand Down Expand Up @@ -176,8 +175,7 @@ def test_multi(self):
self.assertTrue(
"The array of parameter values provided for the evaluation of a"
" compiled function has 4 column(s), but the expected number of columns"
" deduced from the outputs array is 5"
in str(cm.exception)
" deduced from the outputs array is 5" in str(cm.exception)
)

nw_arr = np.zeros((3, 5), dtype=fp_t)
Expand All @@ -191,8 +189,7 @@ def test_multi(self):
)
self.assertTrue(
"The array of outputs provided for the evaluation of a compiled"
" function is not writeable"
in str(cm.exception)
" function is not writeable" in str(cm.exception)
)

with self.assertRaises(ValueError) as cm:
Expand All @@ -202,8 +199,7 @@ def test_multi(self):
)
self.assertTrue(
"An array of time values must be provided in order to evaluate a"
" time-dependent function"
in str(cm.exception)
" time-dependent function" in str(cm.exception)
)

with self.assertRaises(TypeError) as cm:
Expand Down Expand Up @@ -239,8 +235,7 @@ def test_multi(self):
self.assertTrue(
"The array of time values provided for the evaluation of a compiled"
" function has a size of 6, but the expected size deduced from the"
" outputs array is 5"
in str(cm.exception)
" outputs array is 5" in str(cm.exception)
)

# Check with wrong dtypes.
Expand All @@ -253,8 +248,7 @@ def test_multi(self):
)
self.assertTrue(
"Invalid dtype detected for the inputs of a compiled function: the"
" expected dtype "
in str(cm.exception)
" expected dtype " in str(cm.exception)
)

with self.assertRaises(TypeError) as cm:
Expand All @@ -265,8 +259,7 @@ def test_multi(self):
)
self.assertTrue(
"Invalid dtype detected for the parameters of a compiled function:"
" the expected dtype "
in str(cm.exception)
" the expected dtype " in str(cm.exception)
)

with self.assertRaises(TypeError) as cm:
Expand All @@ -278,8 +271,7 @@ def test_multi(self):
)
self.assertTrue(
"Invalid dtype detected for the outputs of a compiled function: the"
" expected dtype "
in str(cm.exception)
" expected dtype " in str(cm.exception)
)

with self.assertRaises(TypeError) as cm:
Expand All @@ -290,8 +282,7 @@ def test_multi(self):
)
self.assertTrue(
"Invalid dtype detected for the time values of a compiled function:"
" the expected dtype "
in str(cm.exception)
" the expected dtype " in str(cm.exception)
)

# Non-contiguous time array.
Expand Down Expand Up @@ -527,16 +518,14 @@ def test_multi(self):
self.assertTrue(
"Potential memory overlaps detected when attempting to evaluate"
" a compiled function: please make sure that all input arrays"
" are distinct"
in str(cm.exception)
" are distinct" in str(cm.exception)
)
with self.assertRaises(ValueError) as cm:
fn(inputs=inputs, pars=pars, time=pars[0, :])
self.assertTrue(
"Potential memory overlaps detected when attempting to evaluate"
" a compiled function: please make sure that all input arrays"
" are distinct"
in str(cm.exception)
" are distinct" in str(cm.exception)
)

# Test with arrays which are not C style.
Expand Down Expand Up @@ -604,8 +593,7 @@ def test_multi(self):
self.assertTrue(
"Invalid inputs array detected: the array is not C-style"
" contiguous, please consider using numpy.ascontiguousarray()"
" to turn it into one"
in str(cm.exception)
" to turn it into one" in str(cm.exception)
)

pars = rng.random((4, nevals), dtype=float).astype(fp_t)
Expand All @@ -615,8 +603,7 @@ def test_multi(self):
self.assertTrue(
"Invalid inputs array detected: the array is not C-style"
" contiguous, please consider using numpy.ascontiguousarray()"
" to turn it into one"
in str(cm.exception)
" to turn it into one" in str(cm.exception)
)

# Tests with no inputs.
Expand Down Expand Up @@ -784,8 +771,7 @@ def test_single(self):
self.assertTrue(
"The array of parameter values provided for the evaluation of a"
" compiled function has 0 element(s), but the number of parameters in"
" the function is 2"
in str(cm.exception)
" the function is 2" in str(cm.exception)
)

with self.assertRaises(ValueError) as cm:
Expand All @@ -800,8 +786,7 @@ def test_single(self):
fn([fp_t(0)], pars=[fp_t(0)], time=fp_t(0))
self.assertTrue(
"Invalid inputs array passed to a cfunc: the number of function inputs"
" is 2, but the inputs array has a size of 1"
in str(cm.exception)
" is 2, but the inputs array has a size of 1" in str(cm.exception)
)

with self.assertRaises(ValueError) as cm:
Expand All @@ -826,8 +811,7 @@ def test_single(self):
self.assertTrue(
"The array of parameter values provided for the evaluation of a"
" compiled function has 0 element(s), but the number of parameters in"
" the function is 2"
in str(cm.exception)
" the function is 2" in str(cm.exception)
)

with self.assertRaises(ValueError) as cm:
Expand All @@ -837,8 +821,7 @@ def test_single(self):
)
self.assertTrue(
"A time value must be provided in order to evaluate a time-dependent"
" function"
in str(cm.exception)
" function" in str(cm.exception)
)

eval_arr = fn([fp_t(1), fp_t(2)], pars=[fp_t(-5), fp_t(1)], time=fp_t(3))
Expand Down Expand Up @@ -916,17 +899,15 @@ def test_single(self):
self.assertTrue(
"Potential memory overlaps detected when attempting to evaluate a"
" compiled function: please make sure that all input arrays are"
" distinct"
in str(cm.exception)
" distinct" in str(cm.exception)
)

with self.assertRaises(ValueError) as cm:
fn(inputs=inputs, pars=pars, time=fp_t(3), outputs=inputs)
self.assertTrue(
"Potential memory overlaps detected when attempting to evaluate a"
" compiled function: please make sure that all input arrays are"
" distinct"
in str(cm.exception)
" distinct" in str(cm.exception)
)

# Test with arrays which are not C style.
Expand All @@ -948,8 +929,7 @@ def test_single(self):
self.assertTrue(
"Invalid parameters array detected: the array is not C-style"
" contiguous, please consider using numpy.ascontiguousarray() to turn"
" it into one"
in str(cm.exception)
" it into one" in str(cm.exception)
)

inputs = np.array([fp_t(1), fp_t(2)])
Expand All @@ -973,8 +953,7 @@ def test_single(self):
)
self.assertTrue(
"Invalid dtype detected for the inputs of a compiled function: the"
" expected dtype "
in str(cm.exception)
" expected dtype " in str(cm.exception)
)

with self.assertRaises(TypeError) as cm:
Expand All @@ -985,8 +964,7 @@ def test_single(self):
)
self.assertTrue(
"Invalid dtype detected for the parameters of a compiled function:"
" the expected dtype "
in str(cm.exception)
" the expected dtype " in str(cm.exception)
)

with self.assertRaises(TypeError) as cm:
Expand All @@ -998,8 +976,7 @@ def test_single(self):
)
self.assertTrue(
"Invalid dtype detected for the outputs of a compiled function: the"
" expected dtype "
in str(cm.exception)
" expected dtype " in str(cm.exception)
)

# Time as an array instead of scalar.
Expand Down
3 changes: 1 addition & 2 deletions heyoka/_test_dtens.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,7 @@ def test_basic(self):
dt[1]
self.assertTrue(
"The derivative at index 1 was requested, but the total number of"
" derivatives is 0"
in str(cm.exception)
" derivatives is 0" in str(cm.exception)
)
self.assertFalse([1, 2, 3] in dt)
self.assertFalse((1, [(0, 2), (1, 3)]) in dt)
Expand Down
Loading

0 comments on commit 09b95fb

Please sign in to comment.