GrisRun.py 18.7 KB
Newer Older
Carl Schaffer's avatar
Carl Schaffer committed
1
2
3
4
5
6
7
8
9
10
11
12
13
"""
Created on 2018-04-09
@author Carl Schaffer
@mail   carl.schaffer@leibniz-kis.de

class for retrieving run information for a set of GrisFitsFiles

If this file is called directly, it will attempt to create a GrisRun
instance for target passed as the first commandline argument. If no
argument is passed, it will attempt to process a dummy folder.
"""

import datetime
14
import re
15
import sys
16
17
from glob import glob  # get filenames in unix style
from os.path import join, dirname, abspath, isdir, basename
18
from pathlib import Path
Carl Schaffer's avatar
Carl Schaffer committed
19

20
21
22
import astropy.io.fits as fitsio
import matplotlib.pyplot as plt
import numpy as np
23
from pandas import to_datetime
24
25
from tqdm import tqdm

26
27
from kis_tools.gris.GrisFitsFile import GrisFitsFile
from kis_tools.gris.util import get_observers
28
from kis_tools.util.locplot import make_loc_plot
29
from kis_tools.util.util import reglob, gris_run_number, groupby_function
Carl Schaffer's avatar
Carl Schaffer committed
30

Carl Schaffer's avatar
Carl Schaffer committed
31

Carl Schaffer's avatar
Carl Schaffer committed
32
33
class GrisRun(object):
    """Class to handle run-wise gris data
Carl Schaffer's avatar
Carl Schaffer committed
34

35
36
37
38
    Args:
      gris_fits_files: list of gris files to form one run

    Returns:
Carl Schaffer's avatar
Linting    
Carl Schaffer committed
39
      ris_run: GrisRun instance
Carl Schaffer's avatar
Carl Schaffer committed
40
    """
Carl Schaffer's avatar
Carl Schaffer committed
41

42
    # Patterns for files:
43
44
    _l0pattern = r"(-\d\d){0,1}"
    _l1pattern = r"(?:-\d\d){0,1}c[cm]"
45

Carl Schaffer's avatar
Carl Schaffer committed
46
47
    # List of keywords to extract from header, values are passed
    # through self.query
48
49
50
51
52
53
54
    pars_native = {
        "BITPIX": "BITPIX",
        "NAXIS": "NAXIS",
        "BTYPE": "BTYPE",
        "DATE-OBS": "date",
        "TELESCOP": "TELESCOP",
        "CAMERA": "CAMERA",
55
        "WAVELENG": ["WAVELENG", "AWAVLNTH"],
56
57
58
59
        "STATES": "states",
        "AOSYSTEM": "AOSYSTEM",
        "AO_LOCK": "AO_LOCK",
        "NSUMEXP": "NSUMEXP",
60
        "SERIES": ["NSERIES", "NMAPS"],
61
        "IOBS": "IOBS",
62
        "RSUN_ARC": "RSUN_ARC",
63
64
        "STEPS": "NSTEPS",
        "STEPSIZE": "STEPSIZE",
Carl Schaffer's avatar
Carl Schaffer committed
65
66
        "EXPTIME": "XPOSURE",
        "TARGET": "OBS_TRGT",
67
        "OBS_MODE": "obs_mode",
68
    }
Carl Schaffer's avatar
Carl Schaffer committed
69

Carl Schaffer's avatar
Carl Schaffer committed
70
    # list of keywords where a range is deduced from the input files, values are passed through GrisFitsFile.query
71
72
73
74
75
76
77
78
    pars_minmax = {
        "UT": ["DATE-BEG"],
        "FRIEDR0": "ATMOS_R0",
        "AZIMUT": "AZIMUT",
        "ELEVATIO": "ELEV_ANG",
        "SLITPOSX": "SLITPOSX",
        "SLITPOSY": "SLITPOSY",
    }
Carl Schaffer's avatar
Carl Schaffer committed
79

Carl Schaffer's avatar
Carl Schaffer committed
80
    # Format string for storing revision dates in DB
Carl Schaffer's avatar
Carl Schaffer committed
81
82
    date_formatstring = "%Y-%m-%d %H:%M:%S"

Carl Schaffer's avatar
Carl Schaffer committed
83
    def __init__(self, gris_fits_files):
84
        # check inputs
Carl Schaffer's avatar
Carl Schaffer committed
85
        if not gris_fits_files:
86
            raise ValueError("Empty list of files passed to GrisRun")
Carl Schaffer's avatar
Carl Schaffer committed
87

88

Carl Schaffer's avatar
Carl Schaffer committed
89
        # Setup data containers
Carl Schaffer's avatar
Carl Schaffer committed
90
91
92
93
94
        self.properties = {}
        self.files_broken = []
        self.errors = []
        self.is_parsed = False

95
96
97
        # check inputs:
        if not isinstance(gris_fits_files, list):
            print("Invalid input to GrisRun!")
Carl Schaffer's avatar
Linting    
Carl Schaffer committed
98
99
100
101
            raise TypeError

        # check if inputs are already instances of GrisFitsFile, if
        # not, generate instances
102
        elif not isinstance(gris_fits_files[0], GrisFitsFile):
Carl Schaffer's avatar
Linting    
Carl Schaffer committed
103
            gris_fits_files = [
104
                GrisFitsFile(gff) for gff in gris_fits_files
Carl Schaffer's avatar
Linting    
Carl Schaffer committed
105
106
107
108
            ]

        # store files sorted by filename
        self.files = sorted(gris_fits_files, key=lambda x: x.filename)
109

Carl Schaffer's avatar
Carl Schaffer committed
110
        # Extract run-wise parameters from first file of the run
Carl Schaffer's avatar
Carl Schaffer committed
111
112
113
        self.runnumber = self.files[0].runnumber
        self.properties["RUNNUMBER"] = self.runnumber
        self.date = self.files[0].date
114
        self.path = abspath(join(dirname(self.files[0].path), ".."))
Carl Schaffer's avatar
Carl Schaffer committed
115
        self.properties["DATE-OBS"] = self.date
Carl Schaffer's avatar
Carl Schaffer committed
116
        self.maps = list(set([gris_fitsfile.mapnumber for gris_fitsfile in self.files]))
Carl Schaffer's avatar
Carl Schaffer committed
117
        self.map_lengths = {}
118
        self.get_map_lengths()
Carl Schaffer's avatar
minor    
Carl Schaffer committed
119

Carl Schaffer's avatar
Carl Schaffer committed
120
        # Check data levels:
Carl Schaffer's avatar
Carl Schaffer committed
121
        self.check_data_levels()
Carl Schaffer's avatar
Carl Schaffer committed
122
        # Look for preview images and log files
Carl Schaffer's avatar
Carl Schaffer committed
123
124
        self.get_previews()

125
126
127
128
    @classmethod
    def from_date_run(cls, date, run_number):

        # Check date for validity, try to cast to date
Carl Schaffer's avatar
fix    
Carl Schaffer committed
129
        if not isinstance(date, datetime.datetime):
130
131
132
133
134
135
136
137
138
139
140
141
            date = to_datetime(date)

        # Check operating system and set path accordingly
        # Assumes mars mounted at Y: on windows
        if sys.platform == "win32":
            archive_root = Path('Y:\dat')
        else:
            archive_root = Path('/dat')
        archive = archive_root / 'sdc' / 'gris' / date.strftime("%Y%m%d") / 'level1_split'

        # Glob for files matching the run
        pattern = f'*l1?_{run_number:03d}*'
Carl Schaffer's avatar
Carl Schaffer committed
142
        return GrisRun(list(archive.glob(pattern)))
143

Carl Schaffer's avatar
Carl Schaffer committed
144
145
146
    def parse(self):
        """Performs all information gathering that requires opening
        individual fits files instead of only using info from their
147
148
149
150
151
152
153
        filenames

        Args:

        Returns:

        """
Carl Schaffer's avatar
Carl Schaffer committed
154
        if self.is_parsed:
Carl Schaffer's avatar
Carl Schaffer committed
155
            return self.is_parsed
156

Carl Schaffer's avatar
Carl Schaffer committed
157
        progress_bar = tqdm(sorted(self.files, key=lambda x: x.filename, reverse=True))
158
        for fits_file in progress_bar:
Carl Schaffer's avatar
Carl Schaffer committed
159
            progress_bar.set_description(f"Parsing {basename(fits_file.filename)}")
160
            fits_file.parse()
Carl Schaffer's avatar
Carl Schaffer committed
161
            if fits_file.errors:
Carl Schaffer's avatar
Carl Schaffer committed
162
                # Get errors from file,move file to broken list
163
                for error in fits_file.errors:
Carl Schaffer's avatar
Linting    
Carl Schaffer committed
164
                    self.errors.append(error)
Carl Schaffer's avatar
Carl Schaffer committed
165
                self.files_broken.append(self.files.pop(self.files.index(fits_file)))
Carl Schaffer's avatar
Carl Schaffer committed
166
        if not self.files:
Carl Schaffer's avatar
Carl Schaffer committed
167
            self.errors.append("ERROR in %s: No fits files for run!" % (str(self)))
Carl Schaffer's avatar
Linting    
Carl Schaffer committed
168
            return False
Carl Schaffer's avatar
Carl Schaffer committed
169

170
171
172
        # Extract observation properites from files
        pass

173
        # Set is parsed flag
Carl Schaffer's avatar
Carl Schaffer committed
174
175
        self.is_parsed = True

176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
    def query(self, attribute):
        """
        Get query attribute either from self or search constituents for that attribute
        Args:
            attribute: name of attribute to query

        Returns:
            res: value of given attribute if the current instance has the attribute, list of attributes for all
            constituents if there is no attribute in "self"
        """
        if hasattr(self, attribute):
            res = getattr(self, attribute)
        else:
            res = [f.query(attribute) for f in self.files]
        return res

192
    @property
193
194
195
196
197
198
    def verbose_name(self, date=None):
        """Make verbose name for GRIS runs, turns the date of the run,
        or a date passed as an argument into a string like 26aug14.001
        where the sectionafter the point is the runnumber of the
        corresponding run. If a specific date is passed, only the
        first section of the string is generated.
199

200
201
202
203
204
        Args:
            date (datetime): specific date defaults to None
        Returns:
            outstring (string): verbose date
        """
205
206
207
        if date is None:
            outstring = self.date.strftime("%d%b%y").lower()
            outstring += f".{int(self.runnumber):03d}"
208
        else:
209
            outstring = date.strftime("%d%b%y").lower()
210
211
212
213
214
        return outstring

    def matching_files(self, subfolder, pattern=None):
        query = self.verbose_name
        if not pattern:
215
            files = glob(join(self.path, subfolder, "*"))
216
217
218
219
220
        else:
            query += pattern
            files = reglob(join(self.path, subfolder), query)
        return files

221
222
223
224
225
    @property
    def obs_name(self):
        """Observation name unique within all data for the instrument"""
        return f"{self.date.strftime('%Y%m%d')}_{self.runnumber:03d}"

226
227
    @property
    def files_l0(self):
228
        return self.matching_files("level0", self._l0pattern)
229
230
231

    @property
    def files_l1(self):
232
        return self.matching_files("level1", self._l1pattern)
233
234
235

    @property
    def files_l1_split(self):
236
        files = glob(join(self.path, "level1_split", "*.fits"))
Carl Schaffer's avatar
Carl Schaffer committed
237
        res = list(filter(lambda x: gris_run_number(x) == self.runnumber, files))
238
239
240
241
        return res

    @property
    def files_l1_split_mod(self):
242
        files = glob(join(self.path, "level1_split_modified", "*.fits"))
Carl Schaffer's avatar
Carl Schaffer committed
243
        res = list(filter(lambda x: gris_run_number(x) == self.runnumber, files))
244
245
246
247
        return res

    @property
    def files_l2(self):
248
        files = glob(join(self.path, "level2", "*"))
249
250
251
252
253
        res = list(filter(lambda x: self.verbose_name in basename(x), files))
        return res

    @property
    def files_l3(self):
254
        files = glob(join(self.path, "level3", "*"))
255
256
257
258
259
        res = list(filter(lambda x: self.verbose_name in basename(x), files))
        return res

    @property
    def context_files(self):
260
        return self.matching_files("context_data")
Carl Schaffer's avatar
Carl Schaffer committed
261

Carl Schaffer's avatar
Carl Schaffer committed
262
263
    @property
    def context_folder(self):
Carl Schaffer's avatar
Carl Schaffer committed
264
        return join(self.path, "context_data")
Carl Schaffer's avatar
Carl Schaffer committed
265

266
267
    @property
    def map_saves(self):
268
        res = list(filter(lambda x: x[-1] == "m", self.files_l1))
269
        return res
Carl Schaffer's avatar
Carl Schaffer committed
270

271
272
    @property
    def coord_saves(self):
273
274
275
        res = list(
            filter(lambda x: f"{self.runnumber:03d}_coord_mu" in x, self.context_files)
        )
276
277
278
279
        return res

    @property
    def mask_files(self):
Carl Schaffer's avatar
Carl Schaffer committed
280
        res = list(filter(lambda x: "pen" in x and ".sav" in x, self.files_l2))
281
282
        return res

Carl Schaffer's avatar
Carl Schaffer committed
283
284
    @property
    def map_files(self):
285
        res = list(filter(lambda x: "maps" in x, self.files_l2))
Carl Schaffer's avatar
Carl Schaffer committed
286
287
288
289
        return res

    @property
    def spec_files(self):
290
        res = list(filter(lambda x: "cor_spec" in x, self.files_l2))
Carl Schaffer's avatar
Carl Schaffer committed
291
292
293
294
        return res

    @property
    def stokes_files(self):
295
        res = list(filter(lambda x: "stok" in x, self.files_l2))
Carl Schaffer's avatar
Carl Schaffer committed
296
297
        return res

298
299
    @property
    def loc_previews(self):
300
        def filterfunc(x):
Carl Schaffer's avatar
Carl Schaffer committed
301
302
303
            return re.search(
                f"HMI_{self.verbose_name}\\.png|{self.runnumber}_location.png", x
            )
304

305
306
307
        res = list(filter(filterfunc, self.context_files))
        return res

308
309
310
    @property
    def spatial_uncertainties(self):
        """Average Uncertainties as described by WCS"""
311
        return self.files[0].coord_uncertainty
312

313
314
    @property
    def map_previews(self):
315
316
317
        def filterfunc(x):
            return re.match(f"{self.verbose_name}\\.png", basename(x))

318
319
320
321
322
        res = list(filter(filterfunc, self.context_files))
        return res

    @property
    def logfiles(self):
Carl Schaffer's avatar
Carl Schaffer committed
323
        files = glob(join(self.path, "????????.txt"))
324
325
        return files

326
327
328
329
330
331
    @property
    def observers(self):
        if self.logfiles:
            observers = get_observers(self.logfiles[0])
        return observers

332
333
    @property
    def was_aborted(self):
334
        # determine number of maps found
335
336
        map_lengths = self.map_lengths
        n_maps = len(map_lengths)
337
338
339
340
341
342
343
344

        if n_maps == 0:
            return None

        # check header for planned number of maps
        planned_maps = 0
        planned_steps = 0
        header = fitsio.getheader(self.files[0].path, ignore_missing_end=True)
345
346
        planned_maps = int(header["NMAPS"])
        planned_steps = int(header["NSTEPS"])
347
348
349
350
351
352
353

        if n_maps != planned_maps:
            return True

        len_first_map = map_lengths[sorted(map_lengths.keys())[0]]
        if len_first_map != planned_steps:
            return True
Carl Schaffer's avatar
Carl Schaffer committed
354
        was_aborted = any([v != len_first_map for v in map_lengths.values()])
355
356
        return was_aborted

357
    def get_map_lengths(self):
358
        """Check number of slit positions and check if this number is as
359
        specified by the header
360
361
362
363
364

        Args:

        Returns:

365
        """
Carl Schaffer's avatar
Linting    
Carl Schaffer committed
366
        for mapnumber in self.maps:
367
            slit_numbers = [
368
369
                f.slitposnumber for f in self.files if f.mapnumber == mapnumber
            ]
Carl Schaffer's avatar
Linting    
Carl Schaffer committed
370
            self.map_lengths[mapnumber] = max(slit_numbers)
371

Carl Schaffer's avatar
Carl Schaffer committed
372
373
374
375
    def get_previews(self):
        """look for preview files and store their paths in
        self.previews as 'key':path

376
377
378
379
        Args:

        Returns:

Carl Schaffer's avatar
Carl Schaffer committed
380
381
        """
        run = self.runnumber
Carl Schaffer's avatar
Carl Schaffer committed
382

Carl Schaffer's avatar
Carl Schaffer committed
383
        # retrieve splits_level1_folder from first fits file
Carl Schaffer's avatar
Linting    
Carl Schaffer committed
384
        folder = dirname(self.files[0].filename)
Carl Schaffer's avatar
Carl Schaffer committed
385
386

        # navigate to root folder:
Carl Schaffer's avatar
Linting    
Carl Schaffer committed
387
        folder = abspath(join(folder, "../"))
388
        mapfolder = folder.replace("/gris/", "/gris_maps/")
Carl Schaffer's avatar
Carl Schaffer committed
389

Carl Schaffer's avatar
minor    
Carl Schaffer committed
390
        preview_map = []
391
392
        preview_loc = self.loc_previews
        preview_log = self.logfiles
Carl Schaffer's avatar
Linting    
Carl Schaffer committed
393

Carl Schaffer's avatar
Carl Schaffer committed
394
        preview_map = glob(join(self.context_folder, f"???????.{run:03}.png"))
Carl Schaffer's avatar
Carl Schaffer committed
395
        if not preview_map:
Carl Schaffer's avatar
Carl Schaffer committed
396
            preview_map = glob(join(mapfolder, "context_data", f"*{run:03}_*.gif"))
Carl Schaffer's avatar
Carl Schaffer committed
397
        if not preview_map:
Carl Schaffer's avatar
Carl Schaffer committed
398
            preview_map = glob(join(mapfolder, "context_data", f"*{run:03}_???.png"))
Carl Schaffer's avatar
Carl Schaffer committed
399

400
        self.previews = {}
Carl Schaffer's avatar
Carl Schaffer committed
401
        if preview_loc:
402
            self.previews["PREVIEWLOC"] = preview_loc[0]
Carl Schaffer's avatar
Carl Schaffer committed
403
        if preview_log:
404
            self.previews["PREVIEWLOG"] = preview_log[0]
Carl Schaffer's avatar
Carl Schaffer committed
405
        if preview_map:
406
            self.previews["PREVIEWMAP"] = preview_map[0]
Carl Schaffer's avatar
Carl Schaffer committed
407

Carl Schaffer's avatar
Carl Schaffer committed
408
    def check_data_levels(self):
409
        """Check which data levels are present in parent directory
Carl Schaffer's avatar
Linting    
Carl Schaffer committed
410

Carl Schaffer's avatar
Carl Schaffer committed
411
        todo : so far there is no checking whether a specific run is
Carl Schaffer's avatar
Carl Schaffer committed
412
413
414
        contained in all folders, we assume that if the folder is
        present in the day, the corresponding run is contained in that
        day. this could be improved!
415
416
417
418
419

        Args:

        Returns:

Carl Schaffer's avatar
Carl Schaffer committed
420
        """
Carl Schaffer's avatar
Carl Schaffer committed
421
        # Keys under which to store the result
422
        keys = ["DATALVL0", "DATALVL1", "DATALVL2", "DATALV1S"]
Carl Schaffer's avatar
Carl Schaffer committed
423
        # folders to check for, order MUST match 'keys' above!
424
        folders = ["level0", "level1", "level2", "level1_split"]
Carl Schaffer's avatar
Linting    
Carl Schaffer committed
425
426
427
        path = dirname(self.files[0].filename)
        path = abspath(path)
        directory = join(path, "..")
Carl Schaffer's avatar
Carl Schaffer committed
428
429

        # check if given folder is present and set run flags
Carl Schaffer's avatar
Linting    
Carl Schaffer committed
430
431
432
        for k, folder in zip(keys, folders):
            path = join(directory, folder)
            folder_present = isdir(path)
Carl Schaffer's avatar
Carl Schaffer committed
433
            self.properties[k] = folder_present
Carl Schaffer's avatar
Carl Schaffer committed
434

435
436
        self.properties["CROSS_CORRELATION"] = bool(self.coord_saves)

Carl Schaffer's avatar
Carl Schaffer committed
437
    def __str__(self):
Carl Schaffer's avatar
Carl Schaffer committed
438
439
440
        outstring = (
            f"GrisRun#{self.runnumber:3d}  Date: {self.date.strftime('%Y-%m-%d')},"
        )
441
        outstring += f" {len(self.maps):3d} maps, {len(self.files):3d} split files"
Carl Schaffer's avatar
Linting    
Carl Schaffer committed
442
        return outstring
Carl Schaffer's avatar
Carl Schaffer committed
443

Carl Schaffer's avatar
Carl Schaffer committed
444
445
446
447
    def __repr__(self):
        return str(self)

    def get_cube(self):
448
        """Retrieve data cube for run from slit files"""
Carl Schaffer's avatar
Carl Schaffer committed
449
        # Determine data shape and initialize numpy cube
450
451
        with fitsio.open(self.files[0].filename, memmap=False, ignore_missing_end=True) as gris_fitsfile:
            shape = gris_fitsfile[0].data.shape
Carl Schaffer's avatar
Linting    
Carl Schaffer committed
452
453
        n_x = len(self.files)
        n_y = shape[1]
Carl Schaffer's avatar
Carl Schaffer committed
454
455
        nlambda = shape[-1]
        npol = shape[0]
Carl Schaffer's avatar
Linting    
Carl Schaffer committed
456
        cube = np.ndarray([npol, n_x, n_y, nlambda])
Carl Schaffer's avatar
Carl Schaffer committed
457
458

        # get data into cube:
Carl Schaffer's avatar
Linting    
Carl Schaffer committed
459
        for i, fitsfile in enumerate(self.files):
Carl Schaffer's avatar
Carl Schaffer committed
460
            data = fitsio.open(fitsfile.filename, ignore_missing_end=True)[0].data
Carl Schaffer's avatar
Linting    
Carl Schaffer committed
461
            cube[:, i, :, :] = data
462
            data.close()
Carl Schaffer's avatar
Carl Schaffer committed
463
464
465
466
        self.cube = cube

    def get_mean_specs(self):
        """Calculate mean spectra for Stokes I,Q,U and V"""
Carl Schaffer's avatar
Linting    
Carl Schaffer committed
467
        # check if cube has been made
468
        if not hasattr(self, "cube"):
Carl Schaffer's avatar
Carl Schaffer committed
469
470
471
            print("Missing cube, can't plot!")
            return

Carl Schaffer's avatar
Linting    
Carl Schaffer committed
472
        # average over spatial dimensions and slit positions
Carl Schaffer's avatar
Carl Schaffer committed
473
474
        specs = np.ndarray([self.cube.shape[0], self.cube.shape[-1]])
        for i in range(specs.shape[0]):
Carl Schaffer's avatar
Linting    
Carl Schaffer committed
475
            specs[i, :] = self.cube[i, :, :, :].mean(axis=0).mean(axis=0)
Carl Schaffer's avatar
Carl Schaffer committed
476
477
478
        self.specs = specs

    def plot_toti(self):
Carl Schaffer's avatar
Carl Schaffer committed
479
        """plot self.cube totI channel"""
480
481
        if not hasattr(self, "cube"):
            print("Missing cube, not plotting!")
Carl Schaffer's avatar
Carl Schaffer committed
482
            return
Carl Schaffer's avatar
Linting    
Carl Schaffer committed
483
        toplot = self.cube[0, :, :, :].mean(axis=2)
Carl Schaffer's avatar
Carl Schaffer committed
484
485
        plt.imshow(
            np.rot90(toplot),
Carl Schaffer's avatar
Linting    
Carl Schaffer committed
486
487
            vmax=np.percentile(toplot, 95),
            vmin=np.percentile(toplot, 10),
488
            cmap="gray",
Carl Schaffer's avatar
Carl Schaffer committed
489
        )
Carl Schaffer's avatar
Carl Schaffer committed
490
491
492
        plt.ion()
        plt.show()

493
    def plot_location(self):
494
        coords = self.get_fov_corners()
495
496
497
498
499

        # get median date to better represent the observation
        dates = sorted(self.query("obs_time"))
        date = dates[len(dates) // 2]

Carl Schaffer's avatar
Carl Schaffer committed
500
        fig, ax = make_loc_plot(
501
            coords, date, uncertainties=self.spatial_uncertainties
Carl Schaffer's avatar
Carl Schaffer committed
502
        )
Carl Schaffer's avatar
Carl Schaffer committed
503
        return fig, ax
504

Carl Schaffer's avatar
Carl Schaffer committed
505
    def plot_specs(self):
506
        """plot average spectra for given run. requires average spectra and cube"""
Carl Schaffer's avatar
Carl Schaffer committed
507
        # check if mean spectra have been calculated
508
509
        if not hasattr(self, "specs"):
            print("Missing specs, not plotting!")
Carl Schaffer's avatar
Carl Schaffer committed
510
            return
Carl Schaffer's avatar
Linting    
Carl Schaffer committed
511

Carl Schaffer's avatar
Carl Schaffer committed
512
        for i in range(4):
513
            plt.plot(self.specs[i, :] / np.linalg.norm(self.specs[i, :]))
Carl Schaffer's avatar
Carl Schaffer committed
514
515
        plt.ion()
        plt.show()
516

517
518
519
520
521
522
523
524
525
    def calculate_bounding_box(self):
        """
        Determine the bounding box for the run. The box is calculated as the area covered by the first map in the
        observation. If there are subsequent maps, they are ignored.

        Returns:
            xmin,xmax,ymin,ymax : Helioprojective coordinates in arcsec
        """
        first_map_id = self.maps[0]
Carl Schaffer's avatar
Carl Schaffer committed
526
527
528
529
        map_files = sorted(
            list(filter(lambda x: x.mapnumber == first_map_id, self.files)),
            key=lambda x: x.filename,
        )
530
531
        first_coords = map_files[0]._coords_from_wcs
        last_coords = map_files[-1]._coords_from_wcs
532
533
534
535
536
537
        x_vals = np.concatenate([first_coords[:, 0], last_coords[:, 0]]).value
        y_vals = np.concatenate([first_coords[:, 1], last_coords[:, 1]]).value
        xmin, xmax = min(x_vals), max(x_vals)
        ymin, ymax = min(y_vals), max(y_vals)
        return xmin, xmax, ymin, ymax

538
539
540
541
542
543
544
545
546
547
548
549
550
    def get_fov_corners(self):
        """
        Determine the corners of the run's fov. Result is calculated from the first first map in the
        observation. If there are subsequent maps, they are ignored.

        Returns:
            x1,x2,x3,x4 : Coordinate tuples in arcseconds in Helioprojective coordnates
        """
        first_map_id = self.maps[0]
        map_files = sorted(
            list(filter(lambda x: x.mapnumber == first_map_id, self.files)),
            key=lambda x: x.filename,
        )
551
552
        first_coords = map_files[0].coords
        last_coords = map_files[-1].coords
553
554
555
556
557
        x1 = first_coords[0, :].value
        x2 = first_coords[-1, :].value
        x3 = last_coords[-1, :].value
        x4 = last_coords[0, :].value
        return x1, x2, x3, x4
558

559
560
561
562
563
    @property
    def maps_dict(self):
        grouped = groupby_function(self.files,lambda x: x.mapnumber)
        return grouped

Carl Schaffer's avatar
Carl Schaffer committed
564
# noinspection PyMethodOverriding
565
566
567
568
569
class EmptyGrisRun(GrisRun):
    def __init__(self, path):
        """run constructed from a path to a single l1 or l0 filename"""
        runnumber = gris_run_number(path)
        fn = basename(path)
570
        self.date = datetime.datetime.strptime(fn.split(".")[0], "%d%b%y")
571
572
573
        self.runnumber = runnumber
        self.map_lengths = {}
        self.files = []
574
        self.path = abspath(join(dirname(path), ".."))
575
576
        self.maps = []

Carl Schaffer's avatar
Carl Schaffer committed
577
    # noinspection PyMethodOverriding
578
579
    @property
    def verbose_name(self):
580
581
        outstring = self.date.strftime("%d%b%y").lower()
        outstring += f".{int(self.runnumber):03d}"
582
        return outstring
583
584
585


if __name__ == "__main__":
586
    # files = glob("/dat/sdc/gris/20150426/level1_split/*_001_???_*.fits")
587
    files = glob(r"Y:\dat\sdc\gris\20140426\level1_split\*_002_???_*.fits")
588
    gr = GrisRun(files)
589
    fig = gr.plot_location()
590
    props = gr.parse()