majoplot 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,62 @@
1
+ from __future__ import annotations
2
+ import numpy as np
3
+
4
+ from ..base import *
5
+
6
+ class XRD:
7
+ instrument = "XRD"
8
+
9
+ @classmethod
10
+ def fetch_raw_data(cls, raw_data_file:TextIO, raw_data_name:str)->Data|FAIL:
11
+ labels = LabelDict(
12
+ initital={"instrument":cls.instrument ,"raw_data": LabelValue(raw_data_name)},
13
+ )
14
+
15
+ # read
16
+ initial_pos = raw_data_file.tell() # remember current position
17
+ first_line = raw_data_file.readline().strip()
18
+ match first_line:
19
+ case "<?xml version=\"1.0\" encoding=\"UTF-8\"?>":
20
+ # xrdml from XRD
21
+ import xml.etree.ElementTree as ET
22
+ from datetime import datetime
23
+ raw_data_file.seek(initial_pos) # rewind to original position
24
+ _headers = ("2θ","I")
25
+ tree = ET.parse(raw_data_file)
26
+ root = tree.getroot()
27
+ # Get the default namespace URI from the root tag: "{uri}xrdMeasurements"
28
+ ns_uri = root.tag.split("}")[0].lstrip("{")
29
+ ns = {"x": ns_uri}
30
+ start = float(root.find(".//x:positions[@axis='2Theta']/x:startPosition", ns).text)
31
+ end = float(root.find(".//x:positions[@axis='2Theta']/x:endPosition", ns).text)
32
+ ts_elem = root.find(".//x:startTimeStamp", ns)
33
+ ts = ts_elem.text.strip()
34
+ # Parse ISO 8601 with timezone offset
35
+ dt = datetime.fromisoformat(ts)
36
+
37
+ # Format date as YYYYMMDD
38
+ labels["date"] = dt.strftime("%Y%m%d")
39
+
40
+ intensities_text = root.find(".//x:intensities", ns).text
41
+ I = np.fromstring(intensities_text, sep=" ")
42
+ base = np.max(I) / 100
43
+ I = I / base
44
+ two_theta = np.linspace(start, end, I.size)
45
+ points = np.column_stack([two_theta, I])
46
+ labels["source"] = "experiment"
47
+ return Data(labels=labels, _headers=_headers, points=points)
48
+
49
+
50
+ case "h k l d (Å) F(real) F(imag) |F| 2θ I M ID(λ) Phase":
51
+ # Refraction table from CIF by VESTA
52
+ _headers = ("2θ","I","h","k","l")
53
+ indexs = {"2θ":7,"I":8,"h":0,"k":1,"l":2}
54
+ points = []
55
+ for line in raw_data_file:
56
+ cells = line.split()
57
+ points.append([float(cells[indexs[head]]) for head in _headers])
58
+ labels["source"] = "cif"
59
+ return Data(labels=labels,_headers=_headers,points=np.array(points))
60
+
61
+ case _:
62
+ return fail_signal
@@ -0,0 +1,172 @@
1
+ from __future__ import annotations
2
+ from typing import NamedTuple
3
+ from dataclasses import dataclass, field
4
+ import numpy as np
5
+ from itertools import chain
6
+
7
+ from .base import *
8
+
9
+ class InsertAxesSpec(NamedTuple):
10
+ x:float
11
+ y:float
12
+ width:float
13
+ height:float
14
+ legend_holder:str="last axes"
15
+
16
+ @classmethod
17
+ def default(cls, figure_size:tuple[float], axes_pool:list[Axes])->InsertAxesSpec:
18
+ POINT_BOX = 0.01
19
+ MARGIN_SCALE_OFFSET = 0.05
20
+ PIXEL_NUM = 20
21
+ INSERT_MARGIN_SCALE = 0.8
22
+ MARGIN_CENTER_OFFSET = 0.85
23
+ INSERT_TOP_PIXEL_MARGEN = 0
24
+ INSERT_RIGHT_PIXEL_MARGEN = 0
25
+ FAIL_AREA_THRESHOLD = 0.1
26
+ ASPECT_RATIO = 1
27
+
28
+ if len(axes_pool) > 2:
29
+ return fail_signal
30
+ main = axes_pool[0]
31
+
32
+ # collect scatters in main
33
+ main_scatters = chain.from_iterable(data.points_for_plot for data in main.data_pool)
34
+
35
+ # get the xlim, ylim of main
36
+ xlim = main.xlim
37
+ ylim = main.ylim
38
+ x_span_min = main.spec.x_log_scale_min_span
39
+ y_span_min = main.spec.y_log_scale_min_span
40
+ if xlim[0] > 0 and xlim[1] > 0 and (xlim[1] / xlim[0]) >= x_span_min:
41
+ xlim = (np.log10(xlim[0]), np.log10(xlim[1]))
42
+ if ylim[0] > 0 and ylim[0] > 0 and (ylim[1] / ylim[0]) >= y_span_min:
43
+ ylim = (np.log10(ylim[0]), np.log10(ylim[1]))
44
+
45
+ width = xlim[1] - xlim[0]
46
+ height = ylim[1] - ylim[0]
47
+ x_offset = width * MARGIN_SCALE_OFFSET
48
+ y_offset = height * MARGIN_SCALE_OFFSET
49
+ width += x_offset * 2
50
+ height += y_offset *2
51
+
52
+
53
+ # Examine the occupation situation
54
+ occ = np.zeros((PIXEL_NUM, PIXEL_NUM))
55
+ box_t = lambda n: min(n,PIXEL_NUM-1)
56
+ box_b = lambda n: max(n,0)
57
+ for scatter in main_scatters:
58
+ x,y = scatter[:2]
59
+ x_pos = (x - xlim[0] + x_offset)/width
60
+ y_pos = (y - ylim[0] + y_offset)/height
61
+ xbox = (x_pos - POINT_BOX, x_pos + POINT_BOX)
62
+ ybox = (y_pos - POINT_BOX, y_pos + POINT_BOX)
63
+ x_pixel_box = range(box_b(np.int_(xbox[0]*PIXEL_NUM)), box_t(np.int_(xbox[1]*PIXEL_NUM)+1))
64
+ y_pixel_box = range(box_b(np.int_(ybox[0]*PIXEL_NUM)), box_t(np.int_(ybox[1]*PIXEL_NUM)+1))
65
+ for i in x_pixel_box:
66
+ for j in y_pixel_box:
67
+ occ[j,i] = 1
68
+
69
+ # matrix to histograms
70
+ histograms = np.zeros((PIXEL_NUM, PIXEL_NUM))
71
+ for x in range(PIXEL_NUM):
72
+ for y in range(PIXEL_NUM):
73
+ h = 0
74
+ for i in reversed(range(y+1)):
75
+ if occ[i,x] == 0:
76
+ h+=1
77
+ else:
78
+ break
79
+ histograms[y, x] = h
80
+ import matplotlib.pyplot as plt
81
+ @dataclass(slots=True)
82
+ class Rectangle:
83
+ x0:int|np.int_
84
+ x1:int|np.int_
85
+ y1:int|np.int_
86
+ h:int|np.int_
87
+ w:int|np.int_ = field(init=False,default=0)
88
+ taller:bool = field(init=False,default=False)
89
+ S:float|np.float64 = field(init=False,default=0)
90
+
91
+ def __post_init__(self):
92
+ self.w = self.x1 - self.x0 + 1
93
+ self_aspect_ratio = self.h / self.w
94
+ if self_aspect_ratio < ASPECT_RATIO:
95
+ self.taller = False
96
+ self.S = self.h ** 2 / ASPECT_RATIO
97
+ else:
98
+ self.taller = True
99
+ self.S = self.w ** 2 * ASPECT_RATIO
100
+
101
+
102
+ largest_rec = Rectangle(PIXEL_NUM-1,PIXEL_NUM-1,PIXEL_NUM-1,histograms[PIXEL_NUM-1,PIXEL_NUM-1])
103
+
104
+ for y in reversed(range(PIXEL_NUM)):
105
+ x_stack = []
106
+ for x in reversed(range(-1,PIXEL_NUM)):
107
+ cur_h = histograms[y, x] if x != -1 else 0
108
+ while x_stack:
109
+ top_x = x_stack[-1]
110
+ h = histograms[y, top_x]
111
+
112
+ if cur_h >= h:
113
+ break
114
+
115
+ x_stack.pop()
116
+
117
+ right = x_stack[-1]-1 if x_stack else PIXEL_NUM-1
118
+ cur_rec = Rectangle(
119
+ x0=x+1,
120
+ x1=right,
121
+ y1=y,
122
+ h=h
123
+ )
124
+ if cur_rec.S > largest_rec.S:
125
+ largest_rec = cur_rec
126
+ x_stack.append(x)
127
+
128
+
129
+ # calculate the final rec para (anchor on left bottom)
130
+ p_x = (largest_rec.x0 - INSERT_RIGHT_PIXEL_MARGEN) / PIXEL_NUM
131
+ p_y = (largest_rec.y1-largest_rec.h - INSERT_TOP_PIXEL_MARGEN) / PIXEL_NUM
132
+ p_w = largest_rec.w / PIXEL_NUM
133
+ p_h = largest_rec.h / PIXEL_NUM
134
+
135
+ margined_S = largest_rec.S / ( PIXEL_NUM ** 2 ) * ( INSERT_MARGIN_SCALE ** 2)
136
+ if margined_S < FAIL_AREA_THRESHOLD:
137
+ return fail_signal
138
+
139
+ if largest_rec.taller:
140
+ adjusted_x = p_x
141
+ adjusted_y = p_y + p_h - p_w * ASPECT_RATIO
142
+ margin_c_x = adjusted_x + p_w * MARGIN_CENTER_OFFSET
143
+ margin_c_y = adjusted_y + p_w * ASPECT_RATIO * MARGIN_CENTER_OFFSET
144
+ margined_w = p_w * INSERT_MARGIN_SCALE
145
+ margined_h = margined_w * ASPECT_RATIO
146
+ else:
147
+ adjusted_x = p_x + p_w - p_h / ASPECT_RATIO
148
+ adjusted_y = p_y
149
+ margin_c_x = adjusted_x + p_h / ASPECT_RATIO * MARGIN_CENTER_OFFSET
150
+ margin_c_y = adjusted_y + p_h * MARGIN_CENTER_OFFSET
151
+ margined_h = p_h * INSERT_MARGIN_SCALE
152
+ margined_w = margined_h / ASPECT_RATIO
153
+ margined_x = (adjusted_x - margin_c_x) * INSERT_MARGIN_SCALE + margin_c_x
154
+ margined_y = (adjusted_y - margin_c_y) * INSERT_MARGIN_SCALE + margin_c_y
155
+
156
+ return InsertAxesSpec(
157
+ legend_holder="last axes",
158
+ x=margined_x,
159
+ y=margined_y,
160
+ width=margined_w,
161
+ height=margined_h,
162
+ )
163
+
164
+
165
+
166
+ class DualYAxesSpec(NamedTuple):
167
+ legend_holder = "figure"
168
+
169
+ class StackAxesSpec(NamedTuple):
170
+ legend_holder = "axes_self"
171
+ nrows:int
172
+ ncols:int
@@ -0,0 +1,119 @@
1
+ from __future__ import annotations
2
+
3
+ from ...base import *
4
+
5
+
6
+ FIGSIZE = (8, 6)
7
+
8
+ T = "Temperature (K)"
9
+ H = "Magnetic Field (Oe)"
10
+ R = "Resistivity (Ohm-m)"
11
+ RI ={
12
+ 1: {"R":"Bridge 1 Resistivity (Ohm-m)", "I":"Bridge 1 Excitation (uA)"},
13
+ 2: {"R":"Bridge 2 Resistivity (Ohm-m)", "I":"Bridge 2 Excitation (uA)"},
14
+ 3: {"R":"Bridge 3 Resistivity (Ohm-m)", "I":"Bridge 3 Excitation (uA)"},
15
+ }
16
+
17
+ class RT:
18
+ data_summary_label_names = ["H"]
19
+ axes_label_names = ("date", "raw_data", "bridge", "sample_name")
20
+ figure_label_names = ("date", "raw_data", "bridge", "sample_name")
21
+ figure_summary_label_names = ("raw_data","bridge","sample_name")
22
+ max_axes_in_one_figure = 1
23
+ project_to_child_folder_label_names = {"date":"sample_name"}
24
+ parent_folder_name = "RT"
25
+
26
+ @classmethod
27
+ def preprocess(cls, raw_datas:list[Data])->list[Data]:
28
+ datas = []
29
+ for raw_data in raw_datas:
30
+ raw_labels = raw_data.labels
31
+ headers = raw_data.headers
32
+ raw_points = raw_data.points
33
+
34
+ # Split by H
35
+ split_datas = []
36
+
37
+ last_H_stage = round(raw_points[0][headers[H]])
38
+ current_points = [ last_H_stage, [ raw_points[0] ] ]
39
+
40
+ for point in raw_points[1:]:
41
+ cur_H_stage = round(point[headers[H]])
42
+ if cur_H_stage != last_H_stage:
43
+ split_datas.append(current_points)
44
+ last_H_stage = cur_H_stage
45
+ current_points = [ last_H_stage, [ point ] ]
46
+ else:
47
+ current_points[1].append(point)
48
+ else:
49
+ split_datas.append(current_points)
50
+
51
+ # 3 bridges
52
+ for H_stage, points in split_datas:
53
+ for i in range(1,4):
54
+ _headerTRI = (T,RI[i]["R"],RI[i]["I"])
55
+ _headers = (T,RI[i]["R"])
56
+ s_points = [ [point[headers[x]] for x in _headerTRI] for point in points]
57
+ # clear null R points
58
+ s_points = np.array([point for point in s_points if point[1]])
59
+ # record
60
+ Imin = np.min(s_points[:,1])
61
+ Imax = np.max(s_points[:,1])
62
+ if (Imax - Imin) / Imax < 0.03:
63
+ Irange = f"{np.mean(s_points[:,1]):.1e}"
64
+ else:
65
+ Irange = f"{Imin:.1e}~{Imax:.1e}"
66
+ labels = LabelDict()
67
+ labels["instrument"] = raw_labels["instrument"]
68
+ labels["raw_data"] = raw_labels["raw_data"]
69
+ labels["date"] = raw_labels["date"]
70
+ labels["bridge"] = LabelValue(i,unit="Bridge",unit_as_postfix=False)
71
+ labels["sample_name"] = raw_labels[f"sample{i}_name"]
72
+ labels["sample_units"] = raw_labels[f"sample{i}_units"]
73
+ labels["H"] = LabelValue(H_stage, unit="Oe")
74
+ labels["I_range"] = LabelValue(Irange,unit="μA")
75
+ labels.summary_names = cls.data_summary_label_names
76
+ datas.append(Data(
77
+ labels=labels,
78
+ _headers=_headers,
79
+ points=s_points[:,0:2],
80
+ ignore_outliers=IgnoreOutlierSpec(min_gap_base=1e-6,min_gap_multiple=10),
81
+ ))
82
+
83
+ return datas
84
+
85
+
86
+
87
+ @classmethod
88
+ def make_axes_spec(cls, axes_labels)->AxesSpec:
89
+ return AxesSpec(
90
+ x_axis_title=T,
91
+ y_axis_title=R,
92
+ major_grid=None,
93
+ major_tick=TickSpec(),
94
+ legend=LegendSpec(fontsize=5),
95
+ )
96
+
97
+
98
+ @classmethod
99
+ def make_figure_spec(cls,figure_labels, axes_pool:Iterable[Axes])->FigureSpec:
100
+ figure_name = figure_labels.brief_summary
101
+
102
+ return FigureSpec(
103
+ name=figure_name,
104
+ title=None,
105
+ figsize=FIGSIZE,
106
+ linestyle_cycle= ("-",),
107
+ linecolor_cycle = (
108
+ "#2d0b59", "#3b0f6f", "#4a136e", "#5a176e", "#6a1c6e",
109
+ "#7a216f", "#8b2770", "#9b2d71", "#ac3372", "#bd3973",
110
+ "#ce4074", "#df4775", "#f04f76", "#f86a5a", "#fb8c3c",
111
+ "#fdbb2d", "#fcfdbf",
112
+ ),
113
+ linemarker_cycle = ("o","s","^","v","d","*","x","+"),
114
+ alpa_cycle = (1.0,),
115
+ )
116
+
117
+ @classmethod
118
+ def make_muti_axes_spec(cls, axes_pool:list[Axes])->MutiAxesSpec|FAIL|None:
119
+ return None
@@ -0,0 +1,131 @@
1
+ from __future__ import annotations
2
+ from copy import copy
3
+
4
+ from ...base import *
5
+
6
+
7
+ FIGSIZE = (8, 6)
8
+
9
+ T = "Temperature (K)"
10
+ M = "DC Moment Free Ctr (emu)"
11
+ H = "Magnetic Field (Oe)"
12
+
13
+
14
+ class MT:
15
+ data_summary_label_names = ["H","cooling_type"]
16
+ axes_label_names = ("material","date","raw_data", "H")
17
+ figure_label_names = ("material","date", "raw_data","H")
18
+ figure_summary_label_names = ("raw_data","date")
19
+ max_axes_in_one_figure = 1
20
+ project_to_child_folder_label_names = {"material":"date","date":"material"}
21
+ parent_folder_name = "MT"
22
+
23
+ @classmethod
24
+ def preprocess(cls, raw_datas:list[Data])->list[Data]:
25
+ datas = []
26
+ for raw_data in raw_datas:
27
+ raw_labels = raw_data.labels
28
+ headers = raw_data.headers
29
+ raw_points = raw_data.points
30
+ iT = headers[T]
31
+ iH = headers[H]
32
+ iM = headers[M]
33
+
34
+ check_deque = []
35
+
36
+ current_points = []
37
+ current_cool_type = "ZFC"
38
+ try:
39
+ current_points.append(raw_points[0])
40
+ H_stage = round(raw_points[0, iH])
41
+ except KeyError:
42
+ return []
43
+
44
+ def append_data():
45
+ nonlocal current_points, check_deque, datas
46
+ nonlocal current_cool_type
47
+ current_points.append(check_deque.pop(0))
48
+ labels = copy(raw_labels)
49
+ labels["H"] = LabelValue(H_stage, "Oe")
50
+ if current_points[-1][iT] < current_points[0][iT]:
51
+ unused = True
52
+ labels["cooling_type"] = "cooling"
53
+ else:
54
+ unused = False
55
+ if current_cool_type == "ZFC":
56
+ labels["cooling_type"] = "ZFC"
57
+ current_cool_type = "FC"
58
+ else:
59
+ labels["cooling_type"] = "FC"
60
+ current_cool_type = "ZFC"
61
+ labels["scenario"] = "MT"
62
+ labels.summary_names = cls.data_summary_label_names
63
+ datas.append(
64
+ Data(
65
+ labels=labels,
66
+ _headers=(T,M),
67
+ points=np.array(current_points)[:,[iT,iM]],
68
+ ignore_outliers=IgnoreOutlierSpec(
69
+ min_gap_base=5e-7,
70
+ min_gap_multiple=10
71
+ ),
72
+ unused=unused,
73
+ ))
74
+
75
+ for point in raw_points[1:]:
76
+ check_deque.append(point)
77
+ if len(check_deque) == 2:
78
+ # not the same H?
79
+ if abs(check_deque[1][iH] - H_stage) > 2:
80
+ append_data()
81
+ current_points = [check_deque.pop()]
82
+ H_stage = round(current_points[0][iH])
83
+ current_cool_type = "ZFC"
84
+ # not the same heating curve?
85
+ elif (check_deque[0][iT] - current_points[0][iT])>2 and (check_deque[0][iT] - check_deque[1][iT])>2:
86
+ append_data()
87
+ current_points = [check_deque.pop()]
88
+ else:
89
+ # the same curve
90
+ current_points.append(check_deque.pop(0))
91
+
92
+
93
+ else:
94
+ while check_deque:
95
+ append_data()
96
+
97
+ return datas
98
+
99
+
100
+ @classmethod
101
+ def make_axes_spec(cls,axes_labels)->AxesSpec:
102
+ return AxesSpec(
103
+ x_axis_title=T,
104
+ y_axis_title=M,
105
+ major_grid=None,
106
+ major_tick=TickSpec(),
107
+ legend=LegendSpec(),
108
+ )
109
+
110
+
111
+ @classmethod
112
+ def make_figure_spec(cls,figure_labels, axes_pool:Iterable[Axes])->FigureSpec:
113
+ H_stages = {}
114
+ for axes in axes_pool:
115
+ H_stages[axes.labels["H"]] = None
116
+
117
+ figure_name = f"{figure_labels.brief_summary}-{",".join(str(H_stage) for H_stage in H_stages)}"
118
+
119
+ return FigureSpec(
120
+ name=figure_name,
121
+ title=None,
122
+ figsize=FIGSIZE,
123
+ linestyle_cycle= ("-", "--"),
124
+ linecolor_cycle = ("black", "red"),
125
+ linemarker_cycle = ("o","o","s","s","^","^","v","v","d","d","*","*","x","x","+","+"),
126
+ alpa_cycle = (1.0,),
127
+ )
128
+
129
+ @classmethod
130
+ def make_muti_axes_spec(cls, axes_pool:list[Axes])->MutiAxesSpec|FAIL|None:
131
+ return None
@@ -0,0 +1,135 @@
1
+ from __future__ import annotations
2
+ from copy import copy
3
+
4
+ from ...base import *
5
+ from ...muti_axes_spec import InsertAxesSpec
6
+
7
+
8
+ FIGSIZE = (8, 6)
9
+
10
+ T = "Temperature (K)"
11
+ M = "DC Moment Free Ctr (emu)"
12
+ H = "Magnetic Field (Oe)"
13
+
14
+
15
+ class MT_insert:
16
+ data_summary_label_names = ["H","cooling_type"]
17
+ axes_label_names = ("material","date","raw_data", "H")
18
+ figure_label_names = ("material","date", "raw_data")
19
+ figure_summary_label_names = ("raw_data","date")
20
+ max_axes_in_one_figure = 2
21
+ project_to_child_folder_label_names = {"material":"date","date":"material"}
22
+ parent_folder_name = "MT"
23
+
24
+ @classmethod
25
+ def preprocess(cls, raw_datas:list[Data])->list[Data]:
26
+ datas = []
27
+ for raw_data in raw_datas:
28
+ raw_labels = raw_data.labels
29
+ headers = raw_data.headers
30
+ raw_points = raw_data.points
31
+ iT = headers[T]
32
+ iH = headers[H]
33
+ iM = headers[M]
34
+
35
+ check_deque = []
36
+
37
+ current_points = []
38
+ current_cool_type = "ZFC"
39
+ try:
40
+ current_points.append(raw_points[0])
41
+ H_stage = round(raw_points[0, iH])
42
+ except KeyError:
43
+ return []
44
+
45
+ def append_data():
46
+ nonlocal current_points, check_deque, datas
47
+ nonlocal current_cool_type
48
+ current_points.append(check_deque.pop(0))
49
+ labels = copy(raw_labels)
50
+ labels["H"] = LabelValue(H_stage, "Oe")
51
+ if current_points[-1][iT] < current_points[0][iT]:
52
+ unused = True
53
+ labels["cooling_type"] = "cooling"
54
+ else:
55
+ unused = False
56
+ if current_cool_type == "ZFC":
57
+ labels["cooling_type"] = "ZFC"
58
+ current_cool_type = "FC"
59
+ else:
60
+ labels["cooling_type"] = "FC"
61
+ current_cool_type = "ZFC"
62
+ labels["scenario"] = "MT"
63
+ labels.summary_names = cls.data_summary_label_names
64
+ datas.append(
65
+ Data(
66
+ labels=labels,
67
+ _headers=(T,M),
68
+ points=np.array(current_points)[:,[iT,iM]],
69
+ ignore_outliers=IgnoreOutlierSpec(
70
+ min_gap_base=5e-7,
71
+ min_gap_multiple=10
72
+ ),
73
+ unused=unused,
74
+ ))
75
+
76
+ for point in raw_points[1:]:
77
+ check_deque.append(point)
78
+ if len(check_deque) == 2:
79
+ # not the same H?
80
+ if abs(check_deque[1][iH] - H_stage) > 2:
81
+ append_data()
82
+ current_points = [check_deque.pop()]
83
+ H_stage = round(current_points[0][iH])
84
+ current_cool_type = "ZFC"
85
+ # not the same heating curve?
86
+ elif (check_deque[0][iT] - current_points[0][iT])>2 and (check_deque[0][iT] - check_deque[1][iT])>2:
87
+ append_data()
88
+ current_points = [check_deque.pop()]
89
+ else:
90
+ # the same curve
91
+ current_points.append(check_deque.pop(0))
92
+
93
+
94
+ else:
95
+ while check_deque:
96
+ append_data()
97
+
98
+ return datas
99
+
100
+
101
+ @classmethod
102
+ def make_axes_spec(cls, axes_labels)->AxesSpec:
103
+ return AxesSpec(
104
+ x_axis_title=T,
105
+ y_axis_title=M,
106
+ major_grid=None,
107
+ major_tick=TickSpec(),
108
+ legend=LegendSpec(),
109
+ )
110
+
111
+
112
+ @classmethod
113
+ def make_figure_spec(cls,figure_labels, axes_pool:Iterable[Axes])->FigureSpec:
114
+ H_stages = {}
115
+ for axes in axes_pool:
116
+ H_stages[axes.labels["H"]] = None
117
+
118
+ figure_name = f"{figure_labels.brief_summary}-{",".join(str(H_stage) for H_stage in H_stages)}"
119
+
120
+ return FigureSpec(
121
+ name=figure_name,
122
+ title=None,
123
+ figsize=FIGSIZE,
124
+ linestyle_cycle= ("-", "--"),
125
+ linecolor_cycle = ("black", "red"),
126
+ linemarker_cycle = ("o","o","s","s","^","^","v","v","d","d","*","*","x","x","+","+"),
127
+ alpa_cycle = (1.0,),
128
+ )
129
+
130
+ @classmethod
131
+ def make_muti_axes_spec(cls, axes_pool:list[Axes])->MutiAxesSpec|FAIL|None:
132
+ if len(axes_pool) == 2:
133
+ return InsertAxesSpec.default(FIGSIZE, axes_pool)
134
+ else:
135
+ return None