Project

General

Profile

Statistics
| Branch: | Tag: | Revision:

pycama / src / pycama / EventPlot.py @ 834:d989df597b80

History | View | Annotate | Download (7.64 KB)

1
#!/usr/bin/env python3
2
# -*- coding: utf-8 -*-
3

    
4
# Copyright 2016-2017 Maarten Sneep, KNMI
5
#
6
# Redistribution and use in source and binary forms, with or without
7
# modification, are permitted provided that the following conditions are met:
8
#
9
# 1. Redistributions of source code must retain the above copyright notice,
10
#    this list of conditions and the following disclaimer.
11
#
12
# 2. Redistributions in binary form must reproduce the above copyright notice,
13
#    this list of conditions and the following disclaimer in the documentation
14
#    and/or other materials provided with the distribution.
15
#
16
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
20
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
23
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26

    
27
## @file EventPlot.py
28
#
29
# This file defines a subclass of the pycama.AnalysisAndPlot.AnalysisAndPlot class.
30
# This subclass gathers the 'events', the counts of ground pixels that have an
31
# error or warning.
32
#
33
# @author Maarten Sneep
34

    
35
import math
36
import logging
37
import warnings
38
warnings.filterwarnings("ignore", category=FutureWarning)
39

    
40
import numpy as np
41
import netCDF4
42
import h5py
43

    
44
from .AnalysisAndPlot import AnalysisAndPlot
45

    
46
from .utilities import *
47

    
48
## This class handles event counts.
49
#
50
#  This is a subclass of the pycama.AnalysisAndPlot.AnalysisAndPlot class.
51
#  The event counting is maintained per granule.
52
#
53
#  This class relies on the pycama.Reader.Reader.read_events() method to extract
54
#  the desired data from the level 2 input files.
55
class EventPlot(AnalysisAndPlot):
56
    ## Class specific setup
57
    def setup(self, **kwargs):
58
        self.products = []
59
        self.event_counting = {}
60

    
61
    ## Extract the required information from the input data.
62
    #
63
    # The event counts per granule are already read in the pycama.Reader.Reader.read_events() method.
64
    # This method copies those results into the `event_counting` instance variable.
65
    def process(self):
66
        self.products = sorted(self.input_variables.event_counting.keys())
67
        for product in self.products:
68
            self.event_counting[product] = self.input_variables.event_counting[product]
69

    
70
    ## Write processed data to output netcdf file.
71
    #
72
    #  @param fname File to write to
73
    #  @param mode  Writing mode, defaults to append.
74
    #
75
    #  Write data (including extraction specific dimensions) to the group with
76
    #  the name given in the `storage_group_name` property ("`eventplot_data`" for this class).
77
    #
78
    #  For each product in the input a subgroup is generated, and in each of
79
    #  these groups, a subgroup with the name `orbit_{orbit:05d}_{validity_start:%Y%m%dT%H%M%S}` is created.
80
    #  To each of these subgroups the event counts are attached as attributes, using the names defined by the
81
    #  pycama.Reader.Reader.read_events() method.
82
    def dump(self, fname, mode='a'):
83
        time_step = self.time_index_in_output
84
        with h5py.File(fname, 'a') as ref:
85
            # group
86
            try:
87
                grp = ref.create_group(self.storage_group_name)
88
                grp.attrs['comment'] = 'Event counting'
89
            except:
90
                grp = ref[self.storage_group_name]
91

    
92
            time_step = self.time_index_in_output
93

    
94
            for product in self.products:
95
                try:
96
                    pgrp = grp.create_group(product)
97
                    pgrp.attrs['comment'] = 'Event counting for {0}'.format(product)
98
                except:
99
                    pgrp = grp[product]
100

    
101
                for event_grp in [k for k in self.event_counting[product].keys() if k.endswith('_events')]:
102
                    try:
103
                        egrp = pgrp.create_group(event_grp)
104
                        egrp.attrs['comment'] = 'Event counting for {0}'.format(event_grp)
105
                    except:
106
                        egrp = pgrp[event_grp]
107

    
108
                    for event_name in self.event_counting[product][event_grp].keys():
109
                        event_count = self.event_counting[product][event_grp][event_name]
110

    
111
                        egrp.attrs[event_name]  = event_count
112

    
113
    ## Read processed data from the input file, for specified time index.
114
    #
115
    #  @param fname      The pycama data file (netCDF4).
116
    #  @param time_index The time-index to read.
117
    #
118
    #  This method reads the date from file to restore itself for plotting.
119
    #  The exected format is as written by the pycama.EventPlot.EventPlot.dump() method.
120
    def ingest(self, fname, time_index, exclude=None):
121
        self.time_index_in_output = time_index
122
        with h5py.File(fname, 'r') as ref:
123
            try:
124
                grp = ref[self.storage_group_name]
125
            except:
126
                self.logger.error("event counting data not in '%s'.", os.path.basename(fname))
127
                return False
128

    
129
            self.products = list(grp.keys())
130
            for product in self.products:
131
                pgrp = grp[product]
132
                if product not in self.event_counting:
133
                    self.event_counting[product] = {}
134
                for event_grp in list(pgrp.keys()):
135
                    egrp = pgrp[event_grp]
136
                    if event_grp not in self.event_counting[product]:
137
                        self.event_counting[product][event_grp] = {}
138
                    for event_name in [k for k in egrp.attrs.keys() if k != "comment"]:
139
                        self.event_counting[product][event_grp][event_name] = egrp.attrs[event_name]
140
        return True
141

    
142
    ## Merge data into a combined dataset.
143
    #
144
    #  @param other The object to be added to self,
145
    #               also an instance of the pycama.EventPlot.EventPlot class.
146
    def __iadd__(self, other):
147
        for product in list(other.event_counting.keys()):
148
            for event_grp in list(other.event_counting[product].keys()):
149
                if event_grp not in self.event_counting[product]:
150
                    self.event_counting[product][event_grp] = {}
151
                for event_name in [k for k in list(other.event_counting[product][event_grp].keys()) if k != "comment"]:
152
                    if event_name in self.event_counting[product][event_grp]:
153
                        self.event_counting[product][event_grp][event_name] += other.event_counting[product][event_grp][event_name]
154
                    else:
155
                        self.event_counting[product][event_grp][event_name] = other.event_counting[product][event_grp][event_name]
156

    
157

    
158
    ## Make a plot of a specified variable
159
    #
160
    #  @param varname The name of the variable to plot.
161
    #  @param figure  The matplotlib.figure.Figure instance to plot to.
162
    #  @param ax      The matplotlib.axes.Axes object to use. Default is None, in that case `ax = matplotlib.pyplot.gca()` shall be used.
163
    #  @param kwargs  Other keyword parameters (sub-class dependent)
164
    #
165
    #  No plotting is done by this class. We typically have a long list of
166
    #  numbers, not very interesing to plot.
167
    #
168
    #  @return `False` to indicate that no (meaningful) plot was created.
169
    def plot(self, figure, ax=None, **kwargs):
170
        self.logger.warning("Plotting for '{0}' is not implemented.".format(self.__class__.__name__))
171
        return False