Test Failed
Pull Request — master (#9)
by Angeline
03:32
created

aacgmv2.wrapper   F

Complexity

Total Complexity 66

Size/Duplication

Total Lines 531
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 66
eloc 190
dl 0
loc 531
rs 3.1913
c 0
b 0
f 0

7 Functions

Rating   Name   Duplication   Size   Complexity  
B get_aacgm_coord() 0 60 4
F convert_latlon() 0 103 15
B convert_mlt() 0 59 6
B convert_str_to_bit() 0 36 3
B get_aacgm_coord_arr() 0 61 4
F convert_latlon_arr() 0 147 28
B convert_bool_to_bit() 0 35 6

How to fix   Complexity   

Complexity

Complex classes like aacgmv2.wrapper often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
# -*- coding: utf-8 -*-
2
"""Pythonic wrappers for AACGM-V2 C functions.
3
4
Functions
5
--------------
6
convert_latlon : Converts scalar location
7
convert_latlon_arr : Converts array location
8
get_aacgm_coord : Get scalar magnetic lat, lon, mlt from geographic location
9
get_aacgm_coord_arr : Get array magnetic lat, lon, mlt from geographic location
10
convert_str_to_bit : Convert human readible AACGM flag to bits
11
convert_bool_to_bit : Convert boolian flags to bits
12
convert_mlt : Get array mlt
13
--------------
14
"""
15
16
from __future__ import division, absolute_import, unicode_literals
17
import numpy as np
0 ignored issues
show
introduced by
Unable to import 'numpy'
Loading history...
18
import datetime as dt
0 ignored issues
show
introduced by
standard import "import datetime as dt" should be placed before "import numpy as np"
Loading history...
19
import logbook as logging
0 ignored issues
show
introduced by
Unable to import 'logbook'
Loading history...
20
import aacgmv2
21
import aacgmv2._aacgmv2 as c_aacgmv2
0 ignored issues
show
introduced by
Unable to import 'aacgmv2._aacgmv2'
Loading history...
22
23
def convert_latlon(in_lat, in_lon, height, dtime, code="G2A", igrf_file=None,
0 ignored issues
show
best-practice introduced by
Too many arguments (7/5)
Loading history...
24
                   coeff_prefix=None):
25
    """Converts between geomagnetic coordinates and AACGM coordinates
26
27
    Parameters
28
    ------------
29
    in_lat : (float)
30
        Input latitude in degrees N (code specifies type of latitude)
31
    in_lon : (float)
32
        Input longitude in degrees E (code specifies type of longitude)
33
    height : (float)
34
        Altitude above the surface of the earth in km
35
    dtime : (datetime)
36
        Datetime for magnetic field
37
    code : (str or int)
38
        Bit code or string denoting which type(s) of conversion to perform
39
        G2A        - geographic (geodetic) to AACGM-v2
40
        A2G        - AACGM-v2 to geographic (geodetic)
41
        TRACE      - use field-line tracing, not coefficients
42
        ALLOWTRACE - use trace only above 2000 km
43
        BADIDEA    - use coefficients above 2000 km
44
        GEOCENTRIC - assume inputs are geocentric w/ RE=6371.2
45
        (default is "G2A")
46
    igrf_file : (str or NoneType)
47
        Full filename of IGRF coefficient file or None to use
48
        aacgmv2.IGRF_12_COEFFS. (default=None)
49
    coeff_prefix : (str or NoneType)
50
        Location and file prefix for aacgm coefficient files or None to use
51
        aacgmv2.AACGM_v2_DAT_PREFIX. (default=None)
52
53
    Returns
54
    -------
55
    out_lat : (float)
56
        Output latitude in degrees
57
    out_lon : (float)
58
        Output longitude in degrees
59
    out_r : (float)
60
        Geocentric radial distance in R
61
    """
62
    # Define coefficient file prefix if not supplied
63
    if coeff_prefix is None:
64
        coeff_prefix = aacgmv2.AACGM_v2_DAT_PREFIX
65
66
    # Define IGRF file if not supplied
67
    if igrf_file is None:
68
        igrf_file = aacgmv2.IGRF_12_COEFFS
69
70
    # Test time
71
    if isinstance(dtime, dt.date):
72
        dtime = dt.datetime.combine(dtime, dt.time(0))
73
74
    assert isinstance(dtime, dt.datetime), \
75
        logging.error('time must be specified as datetime object')
76
77
    # Test height
78
    if height < 0:
79
        logging.warn('conversion not intended for altitudes < 0 km')
80
81
    # Initialise output
82
    lat_out = np.nan
83
    lon_out = np.nan
84
    r_out = np.nan
85
86
    # Test code
87
    try:
88
        code = code.upper()
89
90
        if(height > 2000 and code.find("TRACE") < 0 and
91
           code.find("ALLOWTRACE") < 0 and code.find("BADIDEA")):
92
            estr = 'coefficients are not valid for altitudes above 2000 km. You'
93
            estr += ' must either use field-line tracing (trace=True '
94
            estr += 'or allowtrace=True) or indicate you know this '
95
            estr += 'is a bad idea'
96
            logging.error(estr)
97
            return lat_out, lon_out, r_out
98
99
        # make flag
100
        bit_code = convert_str_to_bit(code)
101
    except:
0 ignored issues
show
Coding Style Best Practice introduced by
General except handlers without types should be used sparingly.

Typically, you would use general except handlers when you intend to specifically handle all types of errors, f.e. when logging. Otherwise, such general error handlers can mask errors in your application that you want to know of.

Loading history...
102
        bit_code = code
103
104
    assert isinstance(bit_code, int), \
105
        logging.error("unknown code {:}".format(bit_code))
106
107
    # Test latitude range
108
    if abs(in_lat) > 90.0:
109
        assert abs(in_lat) <= 90.1, logging.error('unrealistic latitude')
110
        in_lat = np.sign(in_lat) * 90.0
111
112
    # Constrain longitudes between -180 and 180
113
    in_lon = ((in_lon + 180.0) % 360.0) - 180.0
114
115
    # Set current date and time
116
    c_aacgmv2.set_datetime(dtime.year, dtime.month, dtime.day, dtime.hour,
117
                           dtime.minute, dtime.second, coeff_prefix)
118
119
    # convert location
120
    try:
121
        lat_out, lon_out, r_out = c_aacgmv2.convert(in_lat, in_lon, height,
122
                                                    bit_code, igrf_file)
123
    except: pass
0 ignored issues
show
Coding Style Best Practice introduced by
General except handlers without types should be used sparingly.

Typically, you would use general except handlers when you intend to specifically handle all types of errors, f.e. when logging. Otherwise, such general error handlers can mask errors in your application that you want to know of.

Loading history...
Coding Style introduced by
More than one statement on a single line
Loading history...
124
125
    return lat_out, lon_out, r_out
126
127
def convert_latlon_arr(in_lat, in_lon, height, dtime, code="G2A",
0 ignored issues
show
best-practice introduced by
Too many arguments (7/5)
Loading history...
Comprehensibility introduced by
This function exceeds the maximum number of variables (16/15).
Loading history...
128
                       igrf_file=None, coeff_prefix=None):
129
    """Converts between geomagnetic coordinates and AACGM coordinates.  At least
130
    one of in_lat, in_lon, and height must be a list or array
131
132
    Parameters
133
    ------------
134
    in_lat : (np.ndarray or list or float)
135
        Input latitude in degrees N (code specifies type of latitude)
136
    in_lon : (np.ndarray or list or float)
137
        Input longitude in degrees E (code specifies type of longitude)
138
    height : (np.ndarray or list or float)
139
        Altitude above the surface of the earth in km
140
    dtime : (datetime)
141
        Single datetime object for magnetic field
142
    code : (int or str)
143
        Bit code or string denoting which type(s) of conversion to perform
144
        G2A        - geographic (geodetic) to AACGM-v2
145
        A2G        - AACGM-v2 to geographic (geodetic)
146
        TRACE      - use field-line tracing, not coefficients
147
        ALLOWTRACE - use trace only above 2000 km
148
        BADIDEA    - use coefficients above 2000 km
149
        GEOCENTRIC - assume inputs are geocentric w/ RE=6371.2
150
        (default = "G2A")
151
    igrf_file : (str or NoneType)
152
        Full filename of IGRF coefficient file or None to use
153
        aacgmv2.IGRF_12_COEFFS. (default=None)
154
    coeff_prefix : (str or NoneType)
155
        Location and file prefix for aacgm coefficient files or None to use
156
        aacgmv2.AACGM_v2_DAT_PREFIX. (default=None)
157
158
    Returns
159
    -------
160
    out_lat : (np.ndarray)
161
        Output latitudes in degrees
162
    out_lon : (np.ndarray)
163
        Output longitudes in degrees
164
    out_r : (np.ndarray)
165
        Geocentric radial distances in R
166
    """
167
    # If a list was entered instead of a numpy array, recast it here
168
    if isinstance(in_lat, list):
169
        in_lat = np.array(in_lat)
170
171
    if isinstance(in_lon, list):
172
        in_lon = np.array(in_lon)
173
174
    if isinstance(height, list):
175
        height = np.array(height)
176
177
    # If one or two of these elements is a float or int, create an array
178
    test_array = np.array([hasattr(in_lat, "shape"), hasattr(in_lon, "shape"),
179
                           hasattr(height, "shape")])
180
    if not test_array.all():
181
        if test_array.any():
182
            arr_shape = in_lat.shape if test_array.argmax() == 0 else \
183
                        (in_lon.shape if test_array.argmax() == 1 else
184
                         height.shape)
185
            if not test_array[0]:
186
                in_lat = np.ones(shape=arr_shape, dtype=float) * in_lat
187
            if not test_array[1]:
188
                in_lon = np.ones(shape=arr_shape, dtype=float) * in_lon
189
            if not test_array[2]:
190
                height = np.ones(shape=arr_shape, dtype=float) * height
191
        else:
192
            logging.info("for a single location, consider using convert_latlon")
193
            in_lat = np.array([in_lat])
194
            in_lon = np.array([in_lon])
195
            height = np.array([height])
196
197
    # Ensure that lat, lon, and height are the same length or if the lengths
198
    # differ that the different ones contain only a single value
199
    if not (in_lat.shape == in_lon.shape and in_lat.shape == height.shape):
200
        ulen = np.unique([in_lat.shape, in_lon.shape, height.shape])
201
        if ulen.min() != (1,):
202
            logging.error("mismatched input arrays")
203
            sys.exit(1)
0 ignored issues
show
Comprehensibility Best Practice introduced by
Undefined variable 'sys'
Loading history...
204
            return None, None, None
205
206
    # Define coefficient file prefix if not supplied
207
    if coeff_prefix is None:
208
        coeff_prefix = aacgmv2.AACGM_v2_DAT_PREFIX
209
210
    # Define IGRF file if not supplied
211
    if igrf_file is None:
212
        igrf_file = aacgmv2.IGRF_12_COEFFS
213
214
    # Test time
215
    if isinstance(dtime, dt.date):
216
        dtime = dt.datetime.combine(dtime, dt.time(0))
217
218
    assert isinstance(dtime, dt.datetime), \
219
        logging.error('time must be specified as datetime object')
220
221
    # Test height
222
    if np.min(height) < 0:
223
        logging.warn('conversion not intended for altitudes < 0 km')
224
225
    # Initialise output
226
    lat_out = np.empty(shape=in_lat.shape, dtype=float) * np.nan
227
    lon_out = np.empty(shape=in_lon.shape, dtype=float) * np.nan
228
    r_out = np.empty(shape=height.shape, dtype=float) * np.nan
229
        
0 ignored issues
show
Coding Style introduced by
Trailing whitespace
Loading history...
230
    # Test code
231
    try:
232
        code = code.upper()
233
234
        if(np.nanmax(height) > 2000 and code.find("TRACE") < 0 and
235
           code.find("ALLOWTRACE") < 0 and code.find("BADIDEA")):
236
            estr = 'coefficients are not valid for altitudes above 2000 km. You'
237
            estr += ' must either use field-line tracing (trace=True '
238
            estr += 'or allowtrace=True) or indicate you know this '
239
            estr += 'is a bad idea'
240
            logging.error(estr)
241
            return lat_out, lon_out, r_out
242
243
        # make flag
244
        bit_code = convert_str_to_bit(code)
245
    except:
0 ignored issues
show
Coding Style Best Practice introduced by
General except handlers without types should be used sparingly.

Typically, you would use general except handlers when you intend to specifically handle all types of errors, f.e. when logging. Otherwise, such general error handlers can mask errors in your application that you want to know of.

Loading history...
246
        bit_code = code
247
248
    assert isinstance(bit_code, int), \
249
        logging.error("unknown code {:}".format(bit_code))
250
251
    # Test latitude range
252
    if np.abs(in_lat).max() > 90.0:
253
        assert np.abs(in_lat).max() <= 90.1, \
254
            logging.error('unrealistic latitude')
255
        in_lat = np.clip(in_lat, -90.0, 90.0)
256
257
    # Constrain longitudes between -180 and 180
258
    in_lon = ((in_lon + 180.0) % 360.0) - 180.0
259
260
    # Set current date and time
261
    c_aacgmv2.set_datetime(dtime.year, dtime.month, dtime.day, dtime.hour,
262
                           dtime.minute, dtime.second, coeff_prefix)
263
264
    # Vectorise the AACGM code
265
    convert_vectorised = np.vectorize(c_aacgmv2.convert)
266
267
    # convert
268
    try:
269
        lat_out, lon_out, r_out = convert_vectorised(in_lat, in_lon, height,
270
                                                     bit_code, igrf_file)
271
    except: pass
0 ignored issues
show
Coding Style Best Practice introduced by
General except handlers without types should be used sparingly.

Typically, you would use general except handlers when you intend to specifically handle all types of errors, f.e. when logging. Otherwise, such general error handlers can mask errors in your application that you want to know of.

Loading history...
Coding Style introduced by
More than one statement on a single line
Loading history...
272
273
    return lat_out, lon_out, r_out
274
275
def get_aacgm_coord(glat, glon, height, dtime, method="TRACE",
0 ignored issues
show
best-practice introduced by
Too many arguments (7/5)
Loading history...
276
                    igrf_file=None, coeff_prefix=None):
277
    """Get AACGM latitude, longitude, and magnetic local time
278
279
    Parameters
280
    ------------
281
    glat : (float)
282
        Geodetic latitude in degrees N
283
    glon : (float)
284
        Geodetic longitude in degrees E
285
    height : (float)
286
        Altitude above the surface of the earth in km
287
    dtime : (datetime)
288
        Date and time to calculate magnetic location
289
    method : (str)
290
        String denoting which type(s) of conversion to perform
291
        TRACE      - use field-line tracing, not coefficients
292
        ALLOWTRACE - use trace only above 2000 km
293
        BADIDEA    - use coefficients above 2000 km
294
        GEOCENTRIC - assume inputs are geocentric w/ RE=6371.2
295
        (default = "TRACE")
296
    igrf_file : (str or NoneType)
297
        Full filename of IGRF coefficient file or None to use
298
        aacgmv2.IGRF_12_COEFFS. (default=None)
299
    coeff_prefix : (str or NoneType)
300
        Location and file prefix for aacgm coefficient files or None to use
301
        aacgmv2.AACGM_v2_DAT_PREFIX. (default=None)
302
303
    Returns
304
    -------
305
    mlat : (float)
306
        magnetic latitude in degrees
307
    mlon : (float)
308
        magnetic longitude in degrees
309
    mlt : (float)
310
        magnetic local time in hours
311
    """
312
    # Define coefficient file prefix if not supplied
313
    if coeff_prefix is None:
314
        coeff_prefix = aacgmv2.AACGM_v2_DAT_PREFIX
315
316
    # Define IGRF file if not supplied
317
    if igrf_file is None:
318
        igrf_file = aacgmv2.IGRF_12_COEFFS
319
320
    # Initialize code
321
    code = "G2A|{:s}".format(method)
322
323
    # Get magnetic lat and lon.
324
    mlat, mlon, mr = convert_latlon(glat, glon, height, dtime, code=code,
0 ignored issues
show
Coding Style Naming introduced by
The name mr does not conform to the variable naming conventions ((([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
Unused Code introduced by
The variable mr seems to be unused.
Loading history...
325
                                    igrf_file=igrf_file,
326
                                    coeff_prefix=coeff_prefix)
327
    # Get magnetic local time
328
    if np.isnan(mlon):
329
        mlt = np.nan
330
    else:
331
        mlt = convert_mlt(mlon, dtime, m2a=False, coeff_prefix=coeff_prefix,
332
                          igrf_file=igrf_file)
333
334
    return mlat, mlon, mlt
335
336
337
def get_aacgm_coord_arr(glat, glon, height, dtime, method="TRACE",
0 ignored issues
show
best-practice introduced by
Too many arguments (7/5)
Loading history...
338
                        igrf_file=None, coeff_prefix=None):
339
    """Get AACGM latitude, longitude, and magnetic local time
340
341
    Parameters
342
    ------------
343
    glat : (np.array or list)
344
        Geodetic latitude in degrees N
345
    glon : (np.array or list)
346
        Geodetic longitude in degrees E
347
    height : (np.array or list)
348
        Altitude above the surface of the earth in km
349
    dtime : (datetime)
350
        Date and time to calculate magnetic location
351
    method : (str)
352
        String denoting which type(s) of conversion to perform
353
        TRACE      - use field-line tracing, not coefficients
354
        ALLOWTRACE - use trace only above 2000 km
355
        BADIDEA    - use coefficients above 2000 km
356
        GEOCENTRIC - assume inputs are geocentric w/ RE=6371.2
357
        (default = "TRACE")
358
    igrf_file : (str or NoneType)
359
        Full filename of IGRF coefficient file or None to use
360
        aacgmv2.IGRF_12_COEFFS. (default=None)
361
    coeff_prefix : (str or NoneType)
362
        Location and file prefix for aacgm coefficient files or None to use
363
        aacgmv2.AACGM_v2_DAT_PREFIX. (default=None)
364
365
    Returns
366
    -------
367
    mlat : (float)
368
        magnetic latitude in degrees
369
    mlon : (float)
370
        magnetic longitude in degrees
371
    mlt : (float)
372
        magnetic local time in hours
373
    """
374
    # Define coefficient file prefix if not supplied
375
    if coeff_prefix is None:
376
        coeff_prefix = aacgmv2.AACGM_v2_DAT_PREFIX
377
378
    # Define IGRF file if not supplied
379
    if igrf_file is None:
380
        igrf_file = aacgmv2.IGRF_12_COEFFS
381
382
    # Initialize code
383
    code = "G2A|{:s}".format(method)
384
385
    # Get magnetic lat and lon.
386
    mlat, mlon, mr = convert_latlon_arr(glat, glon, height, dtime, code=code,
0 ignored issues
show
Coding Style Naming introduced by
The name mr does not conform to the variable naming conventions ((([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
Unused Code introduced by
The variable mr seems to be unused.
Loading history...
387
                                        igrf_file=igrf_file,
388
                                        coeff_prefix=coeff_prefix)
389
390
    if np.all(np.isnan(mlon)):
391
        mlt = np.empty(shape=mlat.shape, dtype=float) * np.nan
392
    else:
393
        # Get magnetic local time
394
        mlt = convert_mlt(mlon, dtime, m2a=False, coeff_prefix=coeff_prefix,
395
                          igrf_file=igrf_file)
396
397
    return mlat, mlon, mlt
398
399
def convert_str_to_bit(code):
400
    """convert string code specification to bit code specification
401
402
    Parameters
403
    ------------
404
    code : (str)
405
        Bitwise code for passing options into converter (default=0)
406
        G2A        - geographic (geodetic) to AACGM-v2
407
        A2G        - AACGM-v2 to geographic (geodetic)
408
        TRACE      - use field-line tracing, not coefficients
409
        ALLOWTRACE - use trace only above 2000 km
410
        BADIDEA    - use coefficients above 2000 km
411
        GEOCENTRIC - assume inputs are geocentric w/ RE=6371.2
412
413
    Returns
414
    --------
415
    bit_code : (int)
416
        code specification in bits
417
418
    Notes
419
    --------
420
    Multiple codes should be seperated by pipes '|'.  Invalid parts of the code
421
    are ignored and no code defaults to 'G2A'.
422
    """
423
    convert_code = {"G2A": c_aacgmv2.G2A, "A2G": c_aacgmv2.A2G,
424
                    "TRACE": c_aacgmv2.TRACE, "BADIDEA": c_aacgmv2.BADIDEA,
425
                    "GEOCENTRIC": c_aacgmv2.GEOCENTRIC,
426
                    "ALLOWTRACE": c_aacgmv2.ALLOWTRACE}
427
428
    # Force upper case, remove any spaces, and split along pipes
429
    codes = code.upper().replace(" ", "").split("|")
430
431
    # Add the valid parts of the code, invalid elements are ignored
432
    bit_code = sum([convert_code[k] for k in convert_code.keys() if k in codes])
0 ignored issues
show
unused-code introduced by
Consider iterating the dictionary directly instead of calling .keys()
Loading history...
433
434
    return bit_code
435
436
def convert_bool_to_bit(a2g=False, trace=False, allowtrace=False,
437
                        badidea=False, geocentric=False):
438
    """convert boolian flags to bit code specification
439
440
    Parameters
441
    ----------
442
    a2g : (bool)
443
        True for AACGM-v2 to geographic (geodetic), False otherwise
444
        (default=False)
445
    trace : (bool)
446
        If True, use field-line tracing, not coefficients (default=False)
447
    allowtrace : (bool)
448
        If True, use trace only above 2000 km (default=False)
449
    badidea : (bool)
450
        If True, use coefficients above 2000 km (default=False)
451
    geocentric : (bool)
452
        True for geodetic, False for geocentric w/RE=6371.2 (default=False)
453
454
    Returns
455
    --------
456
    bit_code : (int)
457
        code specification in bits
458
    """
459
    bit_code = c_aacgmv2.A2G if a2g else c_aacgmv2.G2A
460
461
    if trace:
462
        bit_code += c_aacgmv2.TRACE
463
    if allowtrace:
464
        bit_code += c_aacgmv2.ALLOWTRACE
465
    if badidea:
466
        bit_code += c_aacgmv2.BADIDEA
467
    if geocentric:
468
        bit_code += c_aacgmv2.GEOCENTRIC
469
470
    return bit_code
471
472
def convert_mlt(arr, dtime, m2a=False, coeff_prefix=None, igrf_file=None):
473
    """Converts between magnetic local time (MLT) and AACGM-v2 longitude
474
475
    Parameters
476
    ------------
477
    arr : (array_line or float)
478
        Magnetic longitudes or MLTs to convert
479
    dtime : (datetime.datetime)
480
        Date and time for MLT conversion in Universal Time (UT).
481
    m2a : (bool)
482
        Convert MLT to AACGM-v2 longitude (True) or magnetic longitude to MLT 
0 ignored issues
show
Coding Style introduced by
Trailing whitespace
Loading history...
483
        (False).  (default=False)
484
    coeff_prefix : (str or NoneType)
485
        Location and file prefix for aacgm coefficient files or None to use
486
        aacgmv2.AACGM_v2_DAT_PREFIX. (default=None)
487
    igrf_file : (str or NoneType)
488
        Full filename of IGRF coefficient file or None to use
489
        aacgmv2.IGRF_12_COEFFS. (default=None)
490
491
    Returns
492
    --------
493
    out : (np.ndarray)
494
        Converted coordinates/MLT
495
496
    Notes
497
    -------
498
    This routine previously based on Laundal et al. 2016, but now uses the
499
    improved calculation available in AACGM-V2.4.
500
    """
501
    # Define coefficient file prefix if not supplied
502
    if coeff_prefix is None:
503
        coeff_prefix = aacgmv2.AACGM_v2_DAT_PREFIX
504
505
    # Define IGRF file if not supplied
506
    if igrf_file is None:
507
        igrf_file = aacgmv2.IGRF_12_COEFFS
508
509
    # Test time
510
    if isinstance(dtime, dt.date):
511
        dtime = dt.datetime.combine(dtime, dt.time(0))
512
513
    assert isinstance(dtime, dt.datetime), \
514
        logging.error('time must be specified as datetime object')
515
516
    # Calculate desired location, C routines set date and time
517
    if m2a:
518
        # Get the magnetic longitude
519
        inv_vectorised = np.vectorize(c_aacgmv2.inv_mlt_convert)
520
        out = inv_vectorised(dtime.year, dtime.month, dtime.day, dtime.hour,
521
                             dtime.minute, dtime.second, arr, coeff_prefix,
522
                             igrf_file)
523
    else:
524
        # Get magnetic local time
525
        mlt_vectorised = np.vectorize(c_aacgmv2.mlt_convert)
526
        out = mlt_vectorised(dtime.year, dtime.month, dtime.day, dtime.hour,
527
                             dtime.minute, dtime.second, arr, coeff_prefix,
528
                             igrf_file)
529
530
    return out
531