File: plotter.py

package info (click to toggle)
python-mcstasscript 0.0.46%2Bgit20250402111921.bfa5a26-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 11,440 kB
  • sloc: python: 13,421; makefile: 14
file content (207 lines) | stat: -rw-r--r-- 5,804 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
import math

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation

from mcstasscript.helper.plot_helper import _fmt
from mcstasscript.helper.plot_helper import _find_min_max_I
from mcstasscript.helper.plot_helper import _plot_fig_ax
from mcstasscript.helper.plot_helper import _handle_kwargs


def make_plot(data_list, **kwargs):
    """
    make_plot plots contents of McStasData objects given in list

    Here a new figure is used for each dataset

    Plotting is controlled through options assosciated with the
    McStasData objects.  If a list is given, the plots appear in one
    subplot.
    """

    data_list = _handle_kwargs(data_list, **kwargs)

    if "figsize" in kwargs:
        figsize = kwargs["figsize"]
        if isinstance(figsize, list):
            figsize = (figsize[0], figsize[1])
    else:
        figsize = (13, 7)

    for data in data_list:
        fig, ax0 = plt.subplots(figsize=figsize, tight_layout=True)
        _plot_fig_ax(data, fig, ax0, **kwargs)

    if "filename" in kwargs:
        fig.tight_layout()
        fig.savefig(kwargs["filename"])
    else:
        plt.show()


def make_sub_plot(data_list, **kwargs):
    """
    make_sub_plot plots contents of McStasData objects given in list

    It is fit into one big figure, each data set as a subplot.

    Plotting is controlled through options assosciated with the
    McStasData objects.  If a list is given, the plots appear in one
    subplot.
    """

    data_list = _handle_kwargs(data_list, **kwargs)

    number_of_plots = len(data_list)
    if number_of_plots == 0:
        print("No data to plot")
        return

    # Find reasonable grid size for the number of plots
    special_cases = {
        1: (1, 1),
        4: (2, 2),
    }

    if number_of_plots in special_cases:
        dim1 = special_cases[number_of_plots][0]
        dim2 = special_cases[number_of_plots][0]
    else:
        if number_of_plots < 3:
            dim2 = number_of_plots
            dim1 = 1
        else:
            dim2 = 3
            dim1 = math.ceil(number_of_plots / dim2)

    if "figsize" in kwargs:
        figsize = kwargs["figsize"]
        if isinstance(figsize, list):
            figsize = (figsize[0], figsize[1])
    else:
        # Adjust figure size after number of plots
        figsize = (1 + dim2*4, 0.5 + 3.0*dim1)
        if dim1 == 1 and dim2 == 1:
            # Single plots can be a bit larger
            figsize = (7, 5)

    fig, axs = plt.subplots(dim1, dim2, figsize=figsize, tight_layout=True)
    axs = np.array(axs)
    ax = axs.reshape(-1)

    for data, ax0 in zip(data_list, ax):
        _plot_fig_ax(data, fig, ax0, **kwargs)

    fig.tight_layout()

    if "filename" in kwargs:
        fig.tight_layout()
        fig.savefig(kwargs["filename"])
        plt.close(fig)
    else:
        plt.show()


def make_animation(data_list, **kwargs):
    """
    Creates an animation from list of McStasData objects

    Parameters
    ----------
    data_list : list of McStasData
        List of McStasData objects for animation

    Keyword arguments
    -----------------
        filename : str
            Filename for saving the gif

        fps : float
            Number of frames per second

    """

    data_list = _handle_kwargs(data_list, **kwargs)

    if "figsize" in kwargs:
        figsize = kwargs["figsize"]
        if isinstance(figsize, list):
            figsize = (figsize[0], figsize[1])
    else:
        figsize = (13, 7)

    if "fps" in kwargs:
        period_in_ms = 1000 / kwargs["fps"]
    else:
        period_in_ms = 200

    # find limits for entire dataset
    maximum_values = []
    minimum_values = []

    is_1D = False
    is_2D = False

    for data in data_list:
        if isinstance(data.metadata.dimension, int):
            is_1D = True

        elif len(data.metadata.dimension) == 2:
            is_2D = True

        min_value, max_value = _find_min_max_I(data)

        # When data empty, min and max value is 0, skip
        if not (min_value == 0 and max_value == 0):
            minimum_values.append(min_value)
            maximum_values.append(max_value)

    if is_1D and is_2D:
        raise ValueError(
            "Both 1D and 2D data in animation, only one allowed.")

    if len(minimum_values) == 0:
        raise ValueError(
            "No data found for animation!")

    maximum_value = np.array(maximum_values).max()
    minimum_value = np.array(minimum_values).min()

    if "orders_of_mag" in kwargs:
        orders_of_mag = kwargs["orders_of_mag"]
        mag_diff = np.log10(maximum_value) - np.log10(minimum_value)
        if mag_diff > orders_of_mag:
            minimum_value_log10 = np.log10(maximum_value) - orders_of_mag
            minimum_value = 10**(minimum_value_log10)

    kwargs["fixed_minimum_value"] = minimum_value
    kwargs["fixed_maximum_value"] = maximum_value

    fig, ax0 = plt.subplots(figsize=figsize, tight_layout=True)
    im = _plot_fig_ax(data_list[0], fig, ax0, **kwargs)

    def animate_2D(index):
        data = data_list[index]
        intensity = data.Intensity

        im.set_array(intensity.ravel())
        return im,

    anim = animation.FuncAnimation(fig, animate_2D,
                                   frames=len(data_list),
                                   interval=period_in_ms,
                                   blit=False, repeat=True)

    plt.show()

    # The animation doesn't play unless it is saved. Bug.
    if "filename" in kwargs:
        filename = kwargs["filename"]
        if not filename.endswith(".gif"):
            filename = filename + ".gif"

        # check if imagemagick available?
        print("Saving animation with filename : \"" + filename + "\"")
        anim.save(filename, writer="imagemagick")