Ejemplo n.º 1
0
def single_cc(filename,
              obsds,
              synts,
              windows,
              calc_type,
              conf,
              rec_weights,
              write_now=False):
    """Compute CC Adjoint for one trace
    """
    config = ConfigCrossCorrelation(conf.adjoint.freq_min,
                                    conf.adjoint.freq_max,
                                    conf.adjoint.taper_type,
                                    conf.adjoint.taper_percentage)
    obsd = obsds[filename]
    synt = synts[filename]
    adj_windows = windows[filename]

    try:
        info = conf.get_info_from_filename(filename)
        sta = info["station"]
        cat = (info["event"], sta[-1])
    except AttributeError:
        net, name, comp, ev = filename.split(".")
        sta = ".".join([net, name, comp])
        cat = (ev, comp[-1])
    rec_weight = rec_weights.get(cat, sta)

    p = pyadjoint.calculate_adjoint_source(
        "cc_traveltime_misfit",
        obsd,
        synt,
        config,
        adj_windows,
        adjoint_src=bool(calc_type & CALC_ADJOINT),  # NOQA
        plot=False)
    try:
        _, event, _, basefilename = filename.split("/")
        net, sta, _, _ = basefilename.split(".")
    except ValueError:
        net, sta, _, event = filename.split(".")

    res = {filename: {}}
    weight = conf.adjoint.weight_cc * rec_weight
    if calc_type & CALC_MISFIT:
        res[filename]["misfit"] = weight * p.misfit

    if calc_type & CALC_ADJOINT:
        times = obsd.times()
        adj_data = np.zeros((len(times), 2))
        adj_data[:, 0] = times
        adj_data[:, 1] = weight * p.adjoint_source[::-1]
        if write_now:
            np.savetxt(
                filename.replace("DATA_obs", "SEM").replace("semd", "adj"),
                adj_data)
        else:
            res[filename]["src"] = adj_data[:, 1]

    return res
def test_no_adjoint_src_calculation_is_honored(adj_src):
    """
    If no adjoint source is requested, it should not be returned/calculated.
    """
    config = pyadjoint.Config(min_period=30.0,
                              max_period=75.0,
                              lnpt=15,
                              transfunc_waterlevel=1.0E-10,
                              water_threshold=0.02,
                              ipower_costaper=10,
                              min_cycle_in_window=3,
                              taper_percentage=0.3,
                              taper_type='hann',
                              mt_nw=4,
                              phase_step=1.5,
                              use_cc_error=False,
                              use_mt_error=False)

    obs, syn = pyadjoint.utils.get_example_data()
    obs = obs.select(component="Z")[0]
    syn = syn.select(component="Z")[0]
    window = [[2076., 2418.0]]

    a_src = pyadjoint.calculate_adjoint_source(adj_src_type=adj_src,
                                               observed=obs,
                                               synthetic=syn,
                                               config=config,
                                               window=window,
                                               adjoint_src=False,
                                               plot=False)
    # start, end = pyadjoint.utils.EXAMPLE_DATA_PDIFF
    # a_src = pyadjoint.calculate_adjoint_source(
    #    adj_src, obs, syn, 20, 100, start, end, adjoint_src=False)

    assert a_src.adjoint_source is None
Ejemplo n.º 3
0
def test_no_adjoint_src_calculation_is_honored(adj_src):
    """
    If no adjoint source is requested, it should not be returned/calculated.
    """
    config = pyadjoint.Config(min_period=30.0, max_period=75.0,
                              lnpt=15,
                              transfunc_waterlevel=1.0E-10,
                              water_threshold=0.02,
                              ipower_costaper=10,
                              min_cycle_in_window=3,
                              taper_percentage=0.3,
                              taper_type='hann',
                              mt_nw=4,
                              phase_step=1.5,
                              use_cc_error=False,
                              use_mt_error=False)

    obs, syn = pyadjoint.utils.get_example_data()
    obs = obs.select(component="Z")[0]
    syn = syn.select(component="Z")[0]
    window = [[2076., 2418.0]]

    a_src = pyadjoint.calculate_adjoint_source(
        adj_src_type=adj_src, observed=obs,
        synthetic=syn, config=config, window=window,
        adjoint_src=False, plot=False)
    # start, end = pyadjoint.utils.EXAMPLE_DATA_PDIFF
    # a_src = pyadjoint.calculate_adjoint_source(
    #    adj_src, obs, syn, 20, 100, start, end, adjoint_src=False)

    assert a_src.adjoint_source is None
Ejemplo n.º 4
0
def single_misfit(filename,
                  obsds, synts, windows,
                  adjoint_misfit_type,
                  adjoint_config,
                  calc_type, conf,
                  rec_weights):
    """Compute an adjoint source/misfit for one trace"""

    obsd = obsds[filename]
    synt = synts[filename]
    adj_windows = windows[(filename, "single")]

    rec_weight = rec_weights.get_from_stationname(filename, "single")

    p = pyadjoint.calculate_adjoint_source(adjoint_misfit_type,
                                           obsd, synt,
                                           adjoint_config,
                                           adj_windows,
                                           adjoint_src=bool(calc_type & CALC_ADJOINT),  # NOQA
                                           plot=False)

    res = {filename: {}}
    weight = conf.adjoint.weight_single*rec_weight
    if calc_type & CALC_MISFIT:
        res[filename]["misfit"] = weight*p.misfit

    if calc_type & CALC_ADJOINT:
        res[filename]["src"] = weight*p.adjoint_source[::-1]

    return res
def test_no_adjoint_src_calculation_is_honored(adj_src):
    """
    If no adjoint source is requested, it should not be returned/calculated.
    """
    obs, syn = pyadjoint.utils.get_example_data()
    obs = obs.select(component="Z")[0]
    syn = syn.select(component="Z")[0]
    start, end = pyadjoint.utils.EXAMPLE_DATA_PDIFF
    a_src = pyadjoint.calculate_adjoint_source(
        adj_src, obs, syn, 20, 100, start, end, adjoint_src=False)

    assert a_src.adjoint_source is None
    assert a_src.misfit >= 0.0

    # But the misfit should nonetheless be identical as if the adjoint
    # source would have been calculated.
    assert a_src.misfit == pyadjoint.calculate_adjoint_source(
        adj_src, obs, syn, 20, 100, start, end, adjoint_src=True).misfit
Ejemplo n.º 6
0
def calculate_adjsrc_on_trace(obs, syn, windows, config, adj_src_type,
                              figure_mode=False, figure_dir=None,
                              adjoint_src_flag=True):
    """
    Calculate adjoint source on a pair of trace and windows selected

    :param obs: observed trace
    :type obs: obspy.Trace
    :param syn: synthetic trace
    :type syn: obspy.Trace
    :param window_time: window time information, 2-dimension array, like
        [[win_1_left, win_1_right], [win_2_left, win_2_right], ...]
    :type windows: 2-d list or numpy.array
    :param config: config of pyadjoint
    :type config: pyadjoint.Config
    :param adj_src_type: adjoint source type, options include:
        1) "cc_traveltime_misfit"
        2) "multitaper_misfit"
        3) "waveform_misfit"
    :type adj_src_type: str
    :param adjoint_src_flag: whether calcualte adjoint source or not.
        If False, only make measurements
    :type adjoint_src_flag: bool
    :param plot_flag: whether make plots or not. If True, it will lot
        a adjoint source figure right after calculation
    :type plot_flag:  bool
    :return: adjoint source(pyadjoit.AdjointSource)
    """
    if not isinstance(obs, Trace):
        raise ValueError("Input obs should be obspy.Trace")
    if not isinstance(syn, Trace):
        raise ValueError("Input syn should be obspy.Trace")
    # if not isinstance(config, pyadjoint.Config):
    #    raise ValueError("Input config should be pyadjoint.Config")

    window_time = _extract_window_time(windows)
    if len(window_time.shape) != 2 or window_time.shape[1] != 2:
        raise ValueError("Input windows dimension incorrect, dimension"
                         "(*, 2) expected")

    adjsrc = pyadjoint.calculate_adjoint_source(
        adj_src_type=adj_src_type, observed=obs, synthetic=syn,
        config=config, window=window_time, adjoint_src=adjoint_src_flag,
        plot=figure_mode)

    if figure_mode:
        if figure_dir is None:
            figname = None
        else:
            figname = os.path.join(figure_dir, "%s.pdf" % obs.id)
        plot_adjoint_source(adjsrc, win_times=window_time, obs_tr=obs,
                            syn_tr=syn, figname=figname)

    return adjsrc
def test_normal_adjoint_source_calculation(adj_src):
    """
    Make sure everything at least runs. Executed for every adjoint source type.
    """
    obs, syn = pyadjoint.utils.get_example_data()
    obs = obs.select(component="Z")[0]
    syn = syn.select(component="Z")[0]
    start, end = pyadjoint.utils.EXAMPLE_DATA_PDIFF
    a_src = pyadjoint.calculate_adjoint_source(
        adj_src, obs, syn, 20, 100, start, end)

    assert a_src.adjoint_source.any()
    assert a_src.misfit >= 0.0

    assert isinstance(a_src.adjoint_source, np.ndarray)
Ejemplo n.º 8
0
def test_normal_adjoint_source_calculation(adj_src):
    """
    Make sure everything at least runs. Executed for every adjoint source type.
    """
    obs, syn = pyadjoint.utils.get_example_data()
    obs = obs.select(component="Z")[0]
    syn = syn.select(component="Z")[0]
    start, end = pyadjoint.utils.EXAMPLE_DATA_PDIFF
    a_src = pyadjoint.calculate_adjoint_source(adj_src, obs, syn, 20, 100,
                                               start, end)

    assert a_src.adjoint_source.any()
    assert a_src.misfit >= 0.0

    assert isinstance(a_src.adjoint_source, np.ndarray)
Ejemplo n.º 9
0
def test_no_adjoint_src_calculation_is_honored(adj_src):
    """
    If no adjoint source is requested, it should not be returned/calculated.
    """
    obs, syn = pyadjoint.utils.get_example_data()
    obs = obs.select(component="Z")[0]
    syn = syn.select(component="Z")[0]
    start, end = pyadjoint.utils.EXAMPLE_DATA_PDIFF
    a_src = pyadjoint.calculate_adjoint_source(adj_src,
                                               obs,
                                               syn,
                                               20,
                                               100,
                                               start,
                                               end,
                                               adjoint_src=False)

    assert a_src.adjoint_source is None
    assert a_src.misfit >= 0.0

    # But the misfit should nonetheless be identical as if the adjoint
    # source would have been calculated.
    assert a_src.misfit == pyadjoint.calculate_adjoint_source(
        adj_src, obs, syn, 20, 100, start, end, adjoint_src=True).misfit
Ejemplo n.º 10
0
def calculate_adjsrc_on_trace(obs, syn, windows, config, adj_src_type,
                              adjoint_src_flag=True, plot_flag=False):
    """
    Calculate adjoint source on a pair of trace and windows selected

    :param obs: observed trace
    :type obs: obspy.Trace
    :param syn: synthetic trace
    :type syn: obspy.Trace
    :param windows: windows information, 2-dimension array, like
        [[win_1_left, win_1_right], [win_2_left, win_2_right], ...]
    :type windows: list or numpy.array
    :param config: config of pyadjoint
    :type config: pyadjoint.Config
    :param adj_src_type: adjoint source type, like "multitaper"
    :type adj_src_type: str
    :param adjoint_src_flag: whether calcualte adjoint source or not.
        If False, only make measurements
    :type adjoint_src_flag: bool
    :param plot_flag: whether make plots or not. If True, it will lot
        a adjoint source figure right after calculation
    :type plot_flag:  bool
    :return: adjoint source(pyadjoit.AdjointSource)
    """
    if not isinstance(obs, Trace):
        raise ValueError("Input obs should be obspy.Trace")
    if not isinstance(syn, Trace):
        raise ValueError("Input syn should be obspy.Trace")
    if not isinstance(config, pyadjoint.Config):
        raise ValueError("Input config should be pyadjoint.Config")
    windows = np.array(windows)
    if len(windows.shape) != 2 or windows.shape[1] != 2:
        raise ValueError("Input windows dimension incorrect, dimention"
                         "(*, 2) expected")

    try:
        adjsrc = pyadjoint.calculate_adjoint_source(
            adj_src_type=adj_src_type, observed=obs, synthetic=syn,
            config=config, window=windows, adjoint_src=adjoint_src_flag,
            plot=plot_flag)
    except:
        adjsrc = None

    return adjsrc
def test_normal_adjoint_source_calculation(adj_src):
    """
    Make sure everything at least runs. Executed for every adjoint source type.
    """
    config = pyadjoint.Config(min_period=30.0,
                              max_period=75.0,
                              lnpt=15,
                              transfunc_waterlevel=1.0E-10,
                              water_threshold=0.02,
                              ipower_costaper=10,
                              min_cycle_in_window=3,
                              taper_percentage=0.3,
                              taper_type='hann',
                              mt_nw=4,
                              phase_step=1.5,
                              use_cc_error=False,
                              use_mt_error=False)

    obs, syn = pyadjoint.utils.get_example_data()
    obs = obs.select(component="Z")[0]
    syn = syn.select(component="Z")[0]
    # start, end = pyadjoint.utils.EXAMPLE_DATA_PDIFF

    window = [[2076., 2418.0]]

    a_src = pyadjoint.calculate_adjoint_source(adj_src_type=adj_src,
                                               observed=obs,
                                               synthetic=syn,
                                               config=config,
                                               window=window,
                                               adjoint_src=True,
                                               plot=False)

    # a_src = pyadjoint.calculate_adjoint_source(
    #    adj_src, obs, syn, 20, 100, start, end)

    assert a_src.adjoint_source.any()
    assert a_src.misfit >= 0.0

    assert isinstance(a_src.adjoint_source, np.ndarray)
Ejemplo n.º 12
0
def test_normal_adjoint_source_calculation(adj_src):
    """
    Make sure everything at least runs. Executed for every adjoint source type.
    """
    config = pyadjoint.Config(min_period=30.0, max_period=75.0,
                              lnpt=15,
                              transfunc_waterlevel=1.0E-10,
                              water_threshold=0.02,
                              ipower_costaper=10,
                              min_cycle_in_window=3,
                              taper_percentage=0.3,
                              taper_type='hann',
                              mt_nw=4,
                              phase_step=1.5,
                              use_cc_error=False,
                              use_mt_error=False)

    obs, syn = pyadjoint.utils.get_example_data()
    obs = obs.select(component="Z")[0]
    syn = syn.select(component="Z")[0]
    # start, end = pyadjoint.utils.EXAMPLE_DATA_PDIFF

    window = [[2076., 2418.0]]

    a_src = pyadjoint.calculate_adjoint_source(
        adj_src_type=adj_src, observed=obs,
        synthetic=syn, config=config, window=window,
        adjoint_src=True, plot=False)

    # a_src = pyadjoint.calculate_adjoint_source(
    #    adj_src, obs, syn, 20, 100, start, end)

    assert a_src.adjoint_source.any()
    assert a_src.misfit >= 0.0

    assert isinstance(a_src.adjoint_source, np.ndarray)
Ejemplo n.º 13
0
    def measure(self, force=False, save=True):
        """
        Measure misfit and calculate adjoint sources using PyAdjoint.

        Method for caluculating misfit set in Config, Pyadjoint expects
        standardized traces with the same spectral content, so this function
        will not run unless these flags are passed.

        Returns a dictionary of adjoint sources based on component.
        Saves resultant dictionary to a pyasdf dataset if given.

        .. note::
            Pyadjoint returns an unscaled misfit value for an entire set of
            windows. To return a "total misfit" value as defined by 
            Tape (2010) Eq. 6, the total summed misfit will need to be scaled by 
            the number of misfit windows chosen in Manager.window().

        :type force: bool
        :param force: ignore flag checks and run function, useful if e.g.
            external preprocessing is used that doesn't meet flag criteria
        :type save: bool
        :param save: save adjoint sources to ASDFDataSet
        """
        self.check()

        if self.config.adj_src_type is None:
            logger.info("adjoint source type is 'None', will not measure")
            return

        # Check that data has been filtered and standardized
        if not self.stats.standardized and not force:
            raise ManagerError("cannot measure misfit, not standardized")
        elif not (self.stats.obs_processed and self.stats.syn_processed) \
                and not force:
            raise ManagerError("cannot measure misfit, not filtered")
        elif self.stats.nwin == 0 and not force:
            raise ManagerError("cannot measure misfit, no windows recovered")
        logger.debug(f"running Pyadjoint w/ type: {self.config.adj_src_type}")

        # Create list of windows needed for Pyadjoint
        adjoint_windows = self._format_windows()

        # Run Pyadjoint to retrieve adjoint source objects
        total_misfit, adjoint_sources = 0, {}
        for comp, adj_win in adjoint_windows.items():
            try:
                adj_src = pyadjoint.calculate_adjoint_source(
                    adj_src_type=self.config.adj_src_type,
                    config=self.config.pyadjoint_config,
                    observed=self.st_obs.select(component=comp)[0],
                    synthetic=self.st_syn.select(component=comp)[0],
                    window=adj_win,
                    plot=False)

                # Re-format component name to reflect SPECFEM convention
                adj_src.component = f"{channel_code(adj_src.dt)}X{comp}"

                # Save adjoint sources in dictionary object. Sum total misfit
                adjoint_sources[comp] = adj_src
                logger.info(f"{adj_src.misfit:.3f} misfit for comp {comp}")
                total_misfit += adj_src.misfit
            except IndexError:
                continue

        # Save adjoint source internally and to dataset
        self.adjsrcs = adjoint_sources
        if save:
            self.save_adjsrcs()

        # Run check to get total misfit
        self.check()
        logger.info(f"total misfit {self.stats.misfit:.3f}")

        return self
Ejemplo n.º 14
0
def calculate_adjsrc_on_trace(obs,
                              syn,
                              window_time,
                              config,
                              adj_src_type,
                              figure_mode=False,
                              figure_dir=None,
                              adjoint_src_flag=True):
    """
    Calculate adjoint source on a pair of trace and windows selected

    :param obs: observed trace
    :type obs: obspy.Trace
    :param syn: synthetic trace
    :type syn: obspy.Trace
    :param window_time: window time information, 2-dimension array, like
        [[win_1_left, win_1_right], [win_2_left, win_2_right], ...]
    :type windows: 2-d list or numpy.array
    :param config: config of pyadjoint
    :type config: pyadjoint.Config
    :param adj_src_type: adjoint source type, options include:
        1) "cc_traveltime_misfit"
        2) "multitaper_misfit"
        3) "waveform_misfit"
    :type adj_src_type: str
    :param adjoint_src_flag: whether calcualte adjoint source or not.
        If False, only make measurements
    :type adjoint_src_flag: bool
    :param plot_flag: whether make plots or not. If True, it will lot
        a adjoint source figure right after calculation
    :type plot_flag:  bool
    :return: adjoint source(pyadjoit.AdjointSource)
    """
    if not isinstance(obs, Trace):
        raise ValueError("Input obs should be obspy.Trace")
    if not isinstance(syn, Trace):
        raise ValueError("Input syn should be obspy.Trace")
    if not isinstance(config, pyadjoint.Config):
        raise ValueError("Input config should be pyadjoint.Config")
    windows = np.array(window_time)
    if len(windows.shape) != 2 or windows.shape[1] != 2:
        raise ValueError("Input windows dimension incorrect, dimention"
                         "(*, 2) expected")

    adjsrc = pyadjoint.calculate_adjoint_source(adj_src_type=adj_src_type,
                                                observed=obs,
                                                synthetic=syn,
                                                config=config,
                                                window=window_time,
                                                adjoint_src=adjoint_src_flag,
                                                plot=figure_mode)

    if figure_mode:
        if figure_dir is None:
            figname = None
        else:
            figname = os.path.join(figure_dir, "%s.pdf" % obs.id)
        plot_adjoint_source(adjsrc,
                            win_times=windows,
                            obs_tr=obs,
                            syn_tr=syn,
                            figname=figname)

    return adjsrc
Ejemplo n.º 15
0
# ### Elastic Adjoint Source
# Cross-correlation elastic adjoint source is computed using following equation:
# $$
#     f^{\dagger}(t) = - \left[ T^{obs} - T(\mathbf{m}) \right] ~ \frac{1}{N} ~
#     \partial_t \mathbf{s}(T - t, \mathbf{m})
# $$

# In[7]:


# Define the adjoint source config
cc_config = ConfigCrossCorrelation(40.0, 250.0, taper_type="cos_p10", taper_percentage=1.0, measure_type="dt")

# Calculate the adjoint source
elastic_adj = pyadjoint.calculate_adjoint_source("cc_traveltime_misfit",
                                                 obsd, synt, cc_config,
                                                 windows)

times = synt.times() + offset
win_offset = windows + offset

fig, axes = plt.subplots(figsize=(16, 8), nrows=2, sharex=True)
axes[0].plot(times, obsd.data, "k", label="obsd")
axes[0].plot(times, synt.data, "r", label="synt")
axes[0].set_xlim((win_offset[0][0]*0.8, win_offset[0][1]*1.2))
axes[0].axvline(win_offset[0][0], color="gray", linestyle="--")
axes[0].axvline(win_offset[0][1], color="gray", linestyle="--")
axes[0].set_ylim(get_ylim(axes[0], obsd))
axes[0].legend()

axes[1].plot(times, elastic_adj.adjoint_source[::-1], "b", label="Elastic Adjoint Source")
Ejemplo n.º 16
0
         horizontalalignment="right",
         verticalalignment="top",
         size="small",
         multialignment="right")

pretty_colors = ["#5B76A1", "#619C6F", "#867CA8", "#BFB281", "#74ACBD"]

_i = 0
for key, value in srcs:
    _i += 1
    plt.subplot(len(srcs) + 1, 1, _i + 1)

    adj_src = pyadjoint.calculate_adjoint_source(key,
                                                 obs,
                                                 syn,
                                                 20,
                                                 100,
                                                 start,
                                                 end,
                                                 adjoint_src=True)

    plt.plot(obs.times(),
             adj_src.adjoint_source,
             color=pretty_colors[(_i - 1) % len(pretty_colors)],
             lw=2,
             label=value[1])
    plt.xlim(x_range - right_window_border, x_range - left_window_border)
    plt.legend(fancybox=True, framealpha=0.5)
    plt.grid()

plt.tight_layout()
plt.show()
Ejemplo n.º 17
0
    fwindow_chi = "%s_%s_window_chi.txt" % (tag_type, catlog)
    fchi = open(fwindow_chi, 'w')

    adjsrc_all = []
    # R
    fobsd = os.path.join(obsd_dir, "IU.KBL..BHR.proc_obsd_60_100.sac")
    fsynt = os.path.join(synt_dir, "IU.KBL.S3.MXR.proc_synt_60_100.sac")

    obs = read(fobsd)
    syn = read(fsynt)

    fadjsrc_r = "IU.KBL..BHR.%s.%s.adj" % (tag_type, config.measure_type)
    window_r = [[4032., 4304.6]]
    adjsrc_r =\
        pyadjoint.calculate_adjoint_source(adj_src_type=src_type,
                                           observed=obs[0], synthetic=syn[0],
                                           config=config, window=window_r,
                                           adjoint_src=True, plot=False)

    adjsrc_r.write(filename=fadjsrc_r, format="SPECFEM", time_offset=0)
    fchi.write("%s %f\n" % (fadjsrc_r, adjsrc_r.misfit))
    print("%s %s tr_chi %f am_chi %f" %
          (fadjsrc_r, adjsrc_r.measurement[0]["type"],
           adjsrc_r.measurement[0]["misfit_dt"],
           adjsrc_r.measurement[0]["misfit_dlna"]))
    adjsrc_all.append(adjsrc_r)

    # T
    fobsd = os.path.join(obsd_dir, "IU.KBL..BHT.proc_obsd_60_100.sac")
    fsynt = os.path.join(synt_dir, "IU.KBL.S3.MXT.proc_synt_60_100.sac")

    obs = read(fobsd)
plt.gca().add_patch(re)
plt.text(x=end - 0.02 * (end - start),
         y=plt.ylim()[1] - 0.01 * (plt.ylim()[1] - plt.ylim()[0]),
         s="Chosen window",
         color="0.2",
         fontweight=900,
         horizontalalignment="right",
         verticalalignment="top",
         size="small", multialignment="right")


pretty_colors = ["#5B76A1", "#619C6F", "#867CA8", "#BFB281", "#74ACBD"]

_i = 0
for key, value in srcs:
    _i += 1
    plt.subplot(len(srcs) + 1, 1, _i + 1)

    adj_src = pyadjoint.calculate_adjoint_source(key, obs, syn, 20, 100,
                                                 start, end, adjoint_src=True)

    plt.plot(obs.times(), adj_src.adjoint_source,
             color=pretty_colors[(_i - 1) % len(pretty_colors)], lw=2,
             label=value[1])
    plt.xlim(x_range - right_window_border, x_range - left_window_border)
    plt.legend(fancybox=True, framealpha=0.5)
    plt.grid()

plt.tight_layout()
plt.show()