1# -*- coding: utf-8 -*-
2
3# Copyright 2017-2018 PaGMO development team
4#
5# This file is part of the PaGMO library.
6#
7# The PaGMO library is free software; you can redistribute it and/or modify
8# it under the terms of either:
9#
10#   * the GNU Lesser General Public License as published by the Free
11#     Software Foundation; either version 3 of the License, or (at your
12#     option) any later version.
13#
14# or
15#
16#   * the GNU General Public License as published by the Free Software
17#     Foundation; either version 3 of the License, or (at your option) any
18#     later version.
19#
20# or both in parallel, as here.
21#
22# The PaGMO library is distributed in the hope that it will be useful, but
23# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
24# or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
25# for more details.
26#
27# You should have received copies of the GNU General Public License and the
28# GNU Lesser General Public License along with the PaGMO library.  If not,
29# see https://www.gnu.org/licenses/.
30
31from __future__ import absolute_import as _ai
32
33import unittest as _ut
34import numpy as np
35import pathlib
36
37from pykep import __extensions__
38
39
40class core_functions_test_case(_ut.TestCase):
41    """Test case for the core functions
42
43    """
44
45    def runTest(self):
46        self.run_epoch_test()
47
48    def run_epoch_test(self):
49        from .core import epoch
50        epoch(julian_date=0, julian_date_type='mjd2000')
51
52
53class lambert_test_case(_ut.TestCase):
54    """Test case for the lambert problem class
55
56    """
57
58    def runTest(self):
59        from .core import lambert_problem
60        lambert_problem(r1=[1, 1, 0], r2=[0, 1, 0],
61                        mu=1., cw=False, max_revs=0, tof=0.3)
62
63
64class mga_1dsm_test_case(_ut.TestCase):
65    """Test case for the mga1_dsm class
66
67    """
68
69    def runTest(self):
70        self.run_construction_test()
71        self.run_decode_times_and_vinf_test()
72
73    def run_construction_test(self):
74        from .trajopt import mga_1dsm
75        # Correct use (nothrow)
76        udp = mga_1dsm()
77        self.assertEqual(udp.get_nobj(), 1)
78        udp = mga_1dsm(tof_encoding='direct', tof=[[20, 400], [20, 400]])
79        self.assertEqual(udp.get_nobj(), 1)
80        udp = mga_1dsm(tof_encoding='eta', tof=500)
81        self.assertEqual(udp.get_nobj(), 1)
82        udp = mga_1dsm(tof_encoding='alpha', tof=[20, 500])
83        self.assertEqual(udp.get_nobj(), 1)
84        udp = mga_1dsm(tof_encoding='direct', tof=[
85                       [20, 400], [20, 400]], multi_objective=True)
86        self.assertEqual(udp.get_nobj(), 2)
87
88        # Incorrect use (raise)
89        with self.assertRaises(TypeError):
90            mga_1dsm(tof_encoding='direct', tof=34)
91        with self.assertRaises(TypeError):
92            mga_1dsm(tof_encoding='direct', tof=[[400], [20, 400]])
93        with self.assertRaises(TypeError):
94            mga_1dsm(tof_encoding='direct', tof=[20, 400])
95        with self.assertRaises(TypeError):
96            mga_1dsm(tof_encoding='eta', tof=[20, 400])
97        with self.assertRaises(TypeError):
98            mga_1dsm(tof_encoding='eta', tof=[[20, 400], [20, 400]])
99        with self.assertRaises(TypeError):
100            mga_1dsm(tof_encoding='alpha', tof=4)
101        with self.assertRaises(TypeError):
102            mga_1dsm(tof_encoding='alpha', tof=[[20, 400], [20, 400]])
103
104    def run_decode_times_and_vinf_test(self):
105        from .trajopt import mga_1dsm
106        # Alpha
107        udp = mga_1dsm(tof_encoding='alpha', tof=[20, 500])
108        x = [10] + [0., 0., 1., 1., 0.5] + [0.4, 1.3, 0.5, 0.5] + [50]
109        retval = udp._decode_times_and_vinf(x)
110        self.assertAlmostEqual(retval[0][0], 25.)
111        self.assertAlmostEqual(retval[0][1], 25.)
112        self.assertAlmostEqual(retval[1], 0.)
113        self.assertAlmostEqual(retval[2], 0.)
114        self.assertAlmostEqual(retval[3], 1.)
115        # Eta
116        udp = mga_1dsm(tof_encoding='eta', tof=50)
117        x = [10] + [0., 0., 1., 1., 0.5] + [0.4, 1.3, 0.5, 0.5]
118        retval = udp._decode_times_and_vinf(x)
119        self.assertAlmostEqual(retval[0][0], 25.)
120        self.assertAlmostEqual(retval[0][1], 12.5)
121        self.assertAlmostEqual(retval[1], 0.)
122        self.assertAlmostEqual(retval[2], 0.)
123        self.assertAlmostEqual(retval[3], 1.)
124        # Direct
125        udp = mga_1dsm(tof_encoding='direct', tof=[[10, 400], [10, 400]])
126        x = [10] + [0., 0., 1., 1., 123] + [0.4, 1.3, 0.5, 321]
127        retval = udp._decode_times_and_vinf(x)
128        self.assertAlmostEqual(retval[0][0], 123)
129        self.assertAlmostEqual(retval[0][1], 321)
130        self.assertAlmostEqual(retval[1], 0.)
131        self.assertAlmostEqual(retval[2], 0.)
132        self.assertAlmostEqual(retval[3], 1.)
133
134
135class gym_test_case(_ut.TestCase):
136    """Test case for the gym
137
138    """
139
140    def runTest(self):
141        self.run_rosetta_test()
142        self.run_cassini2_test()
143        self.run_tandem_test()
144        self.run_juice_test()
145        self.run_messenger_test()
146
147    def run_rosetta_test(self):
148        from .trajopt import gym
149        udp = gym.rosetta
150        x = [1.53488329e+03, 4.56388378e-01, 9.51717655e-01, 4.18212047e+03,
151             4.32159299e-01, 3.65256539e+02, 5.03363275e+00, 2.38949977e+00,
152             4.55746823e-01, 7.09999954e+02, 1.79894273e+00, 1.05000003e+00,
153             6.09083347e-01, 2.60816142e+02, 4.95158968e+00, 3.16049580e+00,
154             6.89049263e-01, 7.29775762e+02, 4.30823655e+00, 1.10842692e+00,
155             4.16075410e-01, 1.84999995e+03]
156        self.assertAlmostEqual(udp.fitness(x)[0], 1371.4992595125018)
157
158    def run_cassini2_test(self):
159        from .trajopt import gym
160        udp = gym.cassini2
161        x = [-7.75699976e+02,  9.15777367e-01,  4.06442043e-01,  3.21309562e+03,
162        6.81118341e-01,  1.62660490e+02, -1.58051063e+00,  1.28479507e+00,
163        4.72699902e-01,  4.24319550e+02,  4.30475919e+00,  1.15739933e+00,
164        2.55718252e-01,  5.44489098e+01, -1.54332794e+00,  1.27160729e+00,
165        9.00000000e-01,  5.88481599e+02,  4.76774269e+00,  7.00000000e+01,
166        1.00000000e-02,  2.20000000e+03]
167        self.assertAlmostEqual(udp.fitness(x)[0], 1511.731793212)
168
169    def run_tandem_test(self):
170        from .trajopt import gym
171        udp = gym.tandem(prob_id = 6, constrained=False)
172        x = [ 7.98593791e+03,  9.02569236e-01,  3.59510662e-01,  3.31011777e+03,
173        1.00000000e-02,  1.68854894e+02, -1.15321363e+00,  1.05006538e+00,
174        4.21911761e-01,  3.38579352e+02, -1.68143357e+00,  1.10121434e+00,
175        6.39679220e-01,  1.17925136e+03, -2.02838607e+00,  1.05000000e+00,
176        2.31554532e-01,  1.80000707e+03]
177        self.assertAlmostEqual(udp.fitness(x)[0], -7.302117213470749)
178
179    def run_juice_test(self):
180        from .trajopt import gym
181        udp = gym.juice
182        x = [ 8.16283083e+03,  6.41922787e-01,  6.51202691e-01,  2.51009414e+03,
183        2.97841478e-01,  3.81541370e+02,  9.58572190e-01,  1.53007674e+00,
184        3.06125365e-01,  1.49264351e+02,  4.10103546e+00,  2.39297670e+00,
185        4.34424957e-01,  3.16066418e+02,  4.33225338e+00,  1.30946367e+00,
186        4.52048883e-01,  1.63208108e+02,  5.93850330e-01,  1.34871269e+00,
187        2.03288502e-01,  6.52494606e+02, -1.37902374e+00,  1.55482534e+00,
188        1.96917559e-01,  1.08471126e+03]
189        self.assertAlmostEqual(udp.fitness(x)[0], -7.987614956531155)
190
191    def run_messenger_test(self):
192        from .trajopt import gym
193        udp = gym.messenger
194        x = [ 2.03241398e+03,  6.40762059e-01,  6.63357785e-01,  4.04989271e+03,
195        6.63732323e-01,  4.50068524e+02, -3.86553343e+00,  3.52631372e+00,
196        5.57888828e-01,  2.24619580e+02, -4.45910441e+00,  1.22736521e+00,
197        7.08063036e-01,  2.17965497e+02, -2.47894274e+00,  1.43586128e+00,
198        5.88391838e-01,  2.62423586e+02, -2.40594385e-02,  2.45470457e+00,
199        7.25370468e-01,  3.58067954e+02,  1.47192632e+00,  1.05000000e+00,
200        9.02984391e-01,  5.38436770e+02]
201        self.assertAlmostEqual(udp.fitness(x)[0], 5855.8143406005165)
202
203
204class spherical_harmonics_loader_test_case(_ut.TestCase):
205    """Test case for the spherical harmonic gravity file loader function.
206
207    """
208
209    def runTest(self):
210        self.run_egm96_test()
211        self.run_ggm02c_test()
212        self.run_ggm02s_test()
213        self.run_jgmro120d_test()
214        self.run_glgm3150_test()
215        self.run_jgl150q1_test()
216        self.run_lpe200_test()
217
218    def run_egm96_test(self):
219        from .util import load_gravity_model
220
221        req_r = 6378137
222        req_mu = 3.986004418e+14
223
224        c20_20 = 4.01448327968e-09
225        c100_100 = 1.10930637955e-09
226
227        s100_50 = -1.06362863541e-09
228        s250_125 = -8.34356616626e-11
229
230        r, mu, c, s, n, m = load_gravity_model(pathlib.Path(__file__).parent / "util/gravity_models/Earth/egm96.txt")
231
232        self.assertEqual(r, req_r)
233        self.assertEqual(mu, req_mu)
234
235        self.assertEqual(c[20, 20], c20_20)
236        self.assertEqual(c[100, 100], c100_100)
237
238        self.assertEqual(s[100, 50], s100_50)
239        self.assertEqual(s[250, 125], s250_125)
240
241    def run_ggm02c_test(self):
242        from .util import load_gravity_model
243
244        req_r = 6378136.3
245        req_mu = 3.986004415e+14
246
247        c20_20 = 3.734698472368e-09
248        c100_100 = 8.8043569591782e-10
249
250        s100_50 = -8.2691933615552e-10
251        s200_100 = -1.3266576400742e-12
252
253        r, mu, c, s, n, m = load_gravity_model(pathlib.Path(__file__).parent / "util/gravity_models/Earth/ggm02c.txt")
254
255        self.assertEqual(r, req_r)
256        self.assertEqual(mu, req_mu)
257
258        self.assertEqual(c[20, 20], c20_20)
259        self.assertEqual(c[100, 100], c100_100)
260
261        self.assertEqual(s[100, 50], s100_50)
262        self.assertEqual(s[200, 100], s200_100)
263
264    def run_ggm02s_test(self):
265        from .util import load_gravity_model
266
267        req_r = 6378136.3
268        req_mu = 3.986004415e+14
269
270        c20_20 = 3.7323168217059e-09
271        c100_100 = 1.0356187365035e-09
272
273        s100_50 = -8.3581312522994e-10
274        s160_80 = -2.327147027496e-09
275
276        r, mu, c, s, n, m = load_gravity_model(pathlib.Path(__file__).parent / "util/gravity_models/Earth/ggm02s.txt")
277
278        self.assertEqual(r, req_r)
279        self.assertEqual(mu, req_mu)
280
281        self.assertEqual(c[20, 20], c20_20)
282        self.assertEqual(c[100, 100], c100_100)
283
284        self.assertEqual(s[100, 50], s100_50)
285        self.assertEqual(s[160, 80], s160_80)
286
287    def run_jgmro120d_test(self):
288        from .util import load_gravity_model
289
290        req_r = 3396000.0
291        req_mu = 4.28283758157561e+13
292
293        c20_20 = -4.767064931643e-07
294        c100_100 = -6.238892373683e-09
295
296        s100_50 = -1.191278972881e-08
297        s120_60 = 5.598027946471e-10
298
299        r, mu, c, s, n, m = load_gravity_model(pathlib.Path(__file__).parent / "util/gravity_models/Mars/jgmro_120d.txt")
300
301        self.assertEqual(r, req_r)
302        self.assertEqual(mu, req_mu)
303
304        self.assertEqual(c[20, 20], c20_20)
305        self.assertEqual(c[100, 100], c100_100)
306
307        self.assertEqual(s[100, 50], s100_50)
308        self.assertEqual(s[120, 60], s120_60)
309
310    def run_glgm3150_test(self):
311        from .util import load_gravity_model
312
313        req_r = 1738000.0
314        req_mu = 4.9002800238e+12
315
316        c20_20 = -1.4913227206107e-08
317        c100_100 = -3.0367607155944e-08
318
319        s100_50 = 1.7233711523911e-10
320        s120_60 = -5.3052488170131e-09
321
322        r, mu, c, s, n, m = load_gravity_model(pathlib.Path(__file__).parent / "util/gravity_models/Moon/glgm3_150.txt")
323
324        self.assertEqual(r, req_r)
325        self.assertEqual(mu, req_mu)
326
327        self.assertEqual(c[20, 20], c20_20)
328        self.assertEqual(c[100, 100], c100_100)
329
330        self.assertEqual(s[100, 50], s100_50)
331        self.assertEqual(s[120, 60], s120_60)
332
333    def run_jgl150q1_test(self):
334        from .util import load_gravity_model
335
336        req_r = 1738000.0
337        req_mu = 4.902801076e+12
338
339        c20_20 = 1.17705963027e-07
340        c100_100 = -1.5618538762e-08
341
342        s100_50 = 5.1145625569e-09
343        s120_60 = -1.05725130139e-09
344
345        r, mu, c, s, n, m = load_gravity_model(pathlib.Path(__file__).parent / "util/gravity_models/Moon/jgl_150q1.txt")
346
347        self.assertEqual(r, req_r)
348        self.assertEqual(mu, req_mu)
349
350        self.assertEqual(c[20, 20], c20_20)
351        self.assertEqual(c[100, 100], c100_100)
352
353        self.assertEqual(s[100, 50], s100_50)
354        self.assertEqual(s[120, 60], s120_60)
355
356    def run_lpe200_test(self):
357        from .util import load_gravity_model
358
359        req_r = 1738000.0
360        req_mu = 4.902800238e+12
361
362        c20_20 = -1.5489032082686e-08
363        c100_100 = -3.0841142975112e-08
364
365        s100_50 = -1.53194061215089e-09
366        s200_100 = 4.25825545837e-09
367
368        r, mu, c, s, n, m = load_gravity_model(pathlib.Path(__file__).parent / "util/gravity_models/Moon/lpe_200.txt")
369
370        self.assertEqual(r, req_r)
371        self.assertEqual(mu, req_mu)
372
373        self.assertEqual(c[20, 20], c20_20)
374        self.assertEqual(c[100, 100], c100_100)
375
376        self.assertEqual(s[100, 50], s100_50)
377        self.assertEqual(s[200, 100], s200_100)
378
379class gravity_spherical_harmonic_test_case(_ut.TestCase):
380    """Test case for the spherical harmonic gravity calculator function.
381
382    """
383
384    def runTest(self):
385        from .util import load_gravity_model, gravity_spherical_harmonic
386
387        r, mu, c, s, n_max, m_max = load_gravity_model(pathlib.Path(__file__).parent / "util/gravity_models/Earth/egm96.txt")
388
389        x = np.array([[6.07303362e+06, -1.63535914e-9, -3.22908926e+06],
390                      [-5874145.34596, 1745831.60905, 3123338.4834],
391                      [5290507.45841, -3377313.30177, -2813012.71391],
392                      [-4360347.55688, 4787584.94679, 2318437.92131],
393                      [3144590.02052, -5884275.41062, -1672008.17262]])
394
395        acc = gravity_spherical_harmonic(x, r, mu, c, s, n_max, m_max)
396
397        req_acc = np.array([[-7.438268885207450, 4.174587578722027e-05, 3.966055730251360],
398                            [7.195259383674340, -2.138389507954604, -3.836583575161607],
399                            [-6.482132055071062, 4.138055191303929, 3.456233485081509],
400                            [5.344543324379746, -5.868302782861193, -2.849822685569829],
401                            [-3.855999728216701, 7.215225587047862, 2.055872629061049]])
402
403        for a, req_a in zip(acc, req_acc):
404            for ax, req_ax in zip(a, req_a):
405                self.assertAlmostEqual(ax, req_ax, 13)
406
407
408def run_test_suite(level=0):
409    """Run the full test suite.
410
411    This function will raise an exception if at least one test fails.
412
413    Args:
414        level(``int``): the test level (higher values run longer tests)
415
416    """
417
418    retval = 0
419    suite = _ut.TestLoader().loadTestsFromTestCase(core_functions_test_case)
420    suite.addTest(lambert_test_case())
421    suite.addTest(mga_1dsm_test_case())
422    suite.addTest(gym_test_case())
423
424    if __extensions__['numba']:
425        suite.addTest(spherical_harmonics_loader_test_case())
426        suite.addTest(gravity_spherical_harmonic_test_case())
427
428
429    test_result = _ut.TextTestRunner(verbosity=2).run(suite)
430
431    if len(test_result.failures) > 0 or len(test_result.errors) > 0:
432        retval = 1
433    if retval != 0:
434        raise RuntimeError('One or more tests failed.')
435