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.
- majoplot/__init__.py +0 -0
- majoplot/__main__.py +25 -0
- majoplot/app/__init__.py +0 -0
- majoplot/app/cli.py +259 -0
- majoplot/app/gui.py +6 -0
- majoplot/config.json +11 -0
- majoplot/domain/base.py +433 -0
- majoplot/domain/importers/PPMS_Resistivity.py +128 -0
- majoplot/domain/importers/VSM.py +109 -0
- majoplot/domain/importers/XRD.py +62 -0
- majoplot/domain/muti_axes_spec.py +172 -0
- majoplot/domain/scenarios/PPMS_Resistivity/RT.py +119 -0
- majoplot/domain/scenarios/VSM/MT.py +131 -0
- majoplot/domain/scenarios/VSM/MT_insert.py +135 -0
- majoplot/domain/scenarios/VSM/MT_reliability_analysis.py +145 -0
- majoplot/domain/scenarios/XRD/Compare.py +104 -0
- majoplot/domain/utils.py +87 -0
- majoplot/gui/__init__.py +0 -0
- majoplot/gui/main.py +529 -0
- majoplot/infra/plotters/matplot.py +337 -0
- majoplot/infra/plotters/origin.py +1006 -0
- majoplot/infra/plotters/origin_utils/originlab_type_library.py +403 -0
- majoplot-0.1.0.dist-info/METADATA +81 -0
- majoplot-0.1.0.dist-info/RECORD +27 -0
- majoplot-0.1.0.dist-info/WHEEL +4 -0
- majoplot-0.1.0.dist-info/entry_points.txt +2 -0
- majoplot-0.1.0.dist-info/licenses/LICENSE +21 -0
majoplot/domain/base.py
ADDED
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from typing import Protocol, runtime_checkable, NamedTuple, TextIO, Iterable, Optional, Callable, Iterator, Any
|
|
3
|
+
from collections.abc import MutableMapping
|
|
4
|
+
from dataclasses import dataclass, field
|
|
5
|
+
from numpy.typing import NDArray
|
|
6
|
+
import numpy as np
|
|
7
|
+
from numbers import Real
|
|
8
|
+
from typing import Generic, TypeVar
|
|
9
|
+
|
|
10
|
+
K = TypeVar("K")
|
|
11
|
+
V = TypeVar("V")
|
|
12
|
+
|
|
13
|
+
class FAIL:
|
|
14
|
+
__slots__ = ()
|
|
15
|
+
fail_signal = FAIL()
|
|
16
|
+
# ========= Data ========
|
|
17
|
+
|
|
18
|
+
class LabelValue(NamedTuple):
|
|
19
|
+
"""
|
|
20
|
+
value: int | float | str
|
|
21
|
+
Note:
|
|
22
|
+
bool is not a supported value type.
|
|
23
|
+
Passing bool leads to undefined ordering behavior.
|
|
24
|
+
"""
|
|
25
|
+
value: int|float|str|tuple[int|float|str]
|
|
26
|
+
unit: Optional[str] = None
|
|
27
|
+
unit_as_postfix: bool = True
|
|
28
|
+
def __lt__(self, other:LabelValue):
|
|
29
|
+
if not isinstance(other, LabelValue):
|
|
30
|
+
return NotImplemented
|
|
31
|
+
|
|
32
|
+
if isinstance(self.value, Real):
|
|
33
|
+
if isinstance(other.value, Real):
|
|
34
|
+
if self.unit == other.unit:
|
|
35
|
+
return self.value < other.value
|
|
36
|
+
elif self.unit is None:
|
|
37
|
+
return True
|
|
38
|
+
elif other.unit is None:
|
|
39
|
+
return False
|
|
40
|
+
else:
|
|
41
|
+
return self.unit.name < other.unit.name
|
|
42
|
+
else:
|
|
43
|
+
return True
|
|
44
|
+
elif isinstance(other.value, Real):
|
|
45
|
+
return False
|
|
46
|
+
else:
|
|
47
|
+
return str(self.value) < str(other.value)
|
|
48
|
+
def __str__(self):
|
|
49
|
+
if self.unit:
|
|
50
|
+
if self.unit_as_postfix:
|
|
51
|
+
return f"{self.value}{self.unit}"
|
|
52
|
+
else:
|
|
53
|
+
return f"{self.unit}{self.value}"
|
|
54
|
+
else:
|
|
55
|
+
return str(self.value)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class LabelDict(MutableMapping):
|
|
59
|
+
__slots__ = ("_items","summary_names","subgroup")
|
|
60
|
+
def __init__(self,initital:Optional[dict[str,LabelValue]]=None, summary_names:Optional[Iterable[str]]=None, subgroup:int=0):
|
|
61
|
+
self._items = dict(initital) if initital else {}
|
|
62
|
+
self.summary_names = list(summary_names) if summary_names else []
|
|
63
|
+
self.subgroup = subgroup
|
|
64
|
+
def __getitem__(self, key):
|
|
65
|
+
return self._items[key]
|
|
66
|
+
def __setitem__(self, key, value):
|
|
67
|
+
self._items[key] = value
|
|
68
|
+
def __delitem__(self, key):
|
|
69
|
+
del self._items[key]
|
|
70
|
+
def __iter__(self):
|
|
71
|
+
return iter(self._items)
|
|
72
|
+
def __len__(self):
|
|
73
|
+
return len(self._items)
|
|
74
|
+
def __str__(self):
|
|
75
|
+
return ", ".join(f"{name}:{value}" for (name, value) in self._items.items()).strip().strip(",")
|
|
76
|
+
def __copy__(self):
|
|
77
|
+
return LabelDict(
|
|
78
|
+
initital=dict(self._items),
|
|
79
|
+
summary_names=list(self.summary_names),
|
|
80
|
+
subgroup=self.subgroup
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
@property
|
|
84
|
+
def full_summary(self):
|
|
85
|
+
if self.summary_names:
|
|
86
|
+
return ", ".join(f"{name}:{self._items[name]}" for name in self.summary_names)
|
|
87
|
+
else:
|
|
88
|
+
return ", ".join(f"{name}:{value}" for (name, value) in self._items.items()).strip().strip(",")
|
|
89
|
+
@property
|
|
90
|
+
def brief_summary(self):
|
|
91
|
+
if self.summary_names:
|
|
92
|
+
return ",".join(f"{self._items[name]}" for name in self.summary_names if self._items[name]).strip().strip(",")
|
|
93
|
+
else:
|
|
94
|
+
return ",".join(f"{value}" for value in self._items.values() if value).strip().strip(",")
|
|
95
|
+
|
|
96
|
+
@classmethod
|
|
97
|
+
def group(cls,label_dict_element_pairs:Iterable[tuple[LabelDict,Any]],
|
|
98
|
+
/,
|
|
99
|
+
group_label_names:Iterable[str],
|
|
100
|
+
group_member_limit:int=0,
|
|
101
|
+
summary_label_names:Optional[Iterable[str]]=None,
|
|
102
|
+
sort_label_names:Optional[tuple[str]]=None,
|
|
103
|
+
)->tuple[list[tuple[LabelDict,list[Any]]], list[Any]]:
|
|
104
|
+
groups = {}
|
|
105
|
+
remains = []
|
|
106
|
+
group_label_names = list(group_label_names)
|
|
107
|
+
summary_label_names = list(summary_label_names) if summary_label_names else []
|
|
108
|
+
for label_dict, element in label_dict_element_pairs:
|
|
109
|
+
try:
|
|
110
|
+
group_label_values=tuple(label_dict[name] for name in group_label_names)
|
|
111
|
+
except KeyError:
|
|
112
|
+
remains.append(element)
|
|
113
|
+
continue
|
|
114
|
+
groups.setdefault(group_label_values,[]).append((label_dict, element))
|
|
115
|
+
|
|
116
|
+
if sort_label_names:
|
|
117
|
+
for label_dict_member_pairs in groups.values():
|
|
118
|
+
for sort_label_name in sort_label_names:
|
|
119
|
+
label_dict_member_pairs.sort(key=lambda x: x[0][sort_label_name])
|
|
120
|
+
|
|
121
|
+
if group_member_limit < 1:
|
|
122
|
+
return (list(
|
|
123
|
+
(
|
|
124
|
+
LabelDict(
|
|
125
|
+
initital = dict((name,value) for (name,value) in zip(group_label_names, group_label_values)),
|
|
126
|
+
summary_names=summary_label_names
|
|
127
|
+
),
|
|
128
|
+
[label_dict_member_pair[1] for label_dict_member_pair in label_dict_member_pairs]
|
|
129
|
+
)
|
|
130
|
+
for (group_label_values, label_dict_member_pairs) in groups.items()
|
|
131
|
+
), remains)
|
|
132
|
+
else:
|
|
133
|
+
subgroups = []
|
|
134
|
+
group_member_limit = int(group_member_limit)
|
|
135
|
+
for group_label_values, label_dict_member_pairs in groups.items():
|
|
136
|
+
group_dict = dict((name,value) for (name,value) in zip(group_label_names, group_label_values))
|
|
137
|
+
cur_subgroup = []
|
|
138
|
+
subgroup_end_i = group_member_limit-1
|
|
139
|
+
last_member_i = len(label_dict_member_pairs) - 1
|
|
140
|
+
for i,(_, member) in enumerate(label_dict_member_pairs):
|
|
141
|
+
subgroup_id = i // group_member_limit
|
|
142
|
+
subgroup_label_dict = LabelDict(initital=group_dict, summary_names=summary_label_names, subgroup=subgroup_id)
|
|
143
|
+
cur_subgroup.append(member)
|
|
144
|
+
if i == subgroup_end_i or i == last_member_i:
|
|
145
|
+
subgroups.append((subgroup_label_dict, cur_subgroup))
|
|
146
|
+
cur_subgroup = []
|
|
147
|
+
return subgroups, remains
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
@dataclass(slots=True)
|
|
151
|
+
class Data:
|
|
152
|
+
labels: LabelDict
|
|
153
|
+
_headers: tuple[str]
|
|
154
|
+
points:NDArray[np.floating]
|
|
155
|
+
ignore_outliers: Optional[IgnoreOutlierSpec] = None
|
|
156
|
+
unused: bool = False
|
|
157
|
+
|
|
158
|
+
headers: dict[str,int] = field(init=False, default=None)
|
|
159
|
+
_ignore_outliers_spec_cache:Optional[IgnoreOutlierSpec] = field(init=False, default=None)
|
|
160
|
+
_points_for_plot:Optional[NDArray[np.floating]] = field(init=False,default=None)
|
|
161
|
+
_x_for_plot:NDArray[np.floating] = field(init=False, default=None)
|
|
162
|
+
_y_for_plot:NDArray[np.floating] = field(init=False, default=None)
|
|
163
|
+
_xlim:tuple[np.floating,np.floating] = field(init=False, default=None)
|
|
164
|
+
_ylim:tuple[np.floating,np.floating] = field(init=False, default=None)
|
|
165
|
+
|
|
166
|
+
def __post_init__(self):
|
|
167
|
+
self.headers = dict((header, index) for (index, header) in enumerate(self._headers))
|
|
168
|
+
|
|
169
|
+
def __repr__(self):
|
|
170
|
+
return f"\nData({self.labels}\tShape: {self.points.shape})\n"
|
|
171
|
+
|
|
172
|
+
def _set_ignore_outliers(self):
|
|
173
|
+
min_gap_base = self.ignore_outliers.min_gap_base
|
|
174
|
+
min_gap_mutiple = self.ignore_outliers.min_gap_multiple
|
|
175
|
+
i = 1
|
|
176
|
+
|
|
177
|
+
def points_without_outliers():
|
|
178
|
+
cache_deque = []
|
|
179
|
+
for point in self.points:
|
|
180
|
+
cache_deque.append(point)
|
|
181
|
+
if len(cache_deque) == 3:
|
|
182
|
+
gap0to1 = abs(cache_deque[1][i] - cache_deque[0][i])
|
|
183
|
+
gap0to2 = abs(cache_deque[2][i] - cache_deque[0][i])
|
|
184
|
+
if gap0to2 < min_gap_base:
|
|
185
|
+
gap0to2 = min_gap_base
|
|
186
|
+
if gap0to1 > gap0to2 * min_gap_mutiple:
|
|
187
|
+
cache_deque.pop(1)
|
|
188
|
+
else:
|
|
189
|
+
yield cache_deque.pop(0)
|
|
190
|
+
yield from cache_deque
|
|
191
|
+
|
|
192
|
+
self._points_for_plot = np.array(list(points_without_outliers()))
|
|
193
|
+
|
|
194
|
+
@property
|
|
195
|
+
def points_for_plot(self)->NDArray[np.floating]:
|
|
196
|
+
if self.ignore_outliers is None:
|
|
197
|
+
return self.points
|
|
198
|
+
else:
|
|
199
|
+
if self._ignore_outliers_spec_cache != self.ignore_outliers:
|
|
200
|
+
self._set_ignore_outliers()
|
|
201
|
+
self._ignore_outliers_spec_cache = self.ignore_outliers
|
|
202
|
+
return self._points_for_plot
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
@property
|
|
206
|
+
def x_for_plot(self)->NDArray[np.floating]:
|
|
207
|
+
if self._x_for_plot is None:
|
|
208
|
+
self._x_for_plot = self.points_for_plot[:,0]
|
|
209
|
+
return self._x_for_plot
|
|
210
|
+
|
|
211
|
+
@property
|
|
212
|
+
def xlim(self)->tuple[np.float64,np.float64]:
|
|
213
|
+
if self._xlim is None:
|
|
214
|
+
self._xlim = (np.min(self.x_for_plot), np.max(self.x_for_plot))
|
|
215
|
+
return self._xlim
|
|
216
|
+
|
|
217
|
+
@property
|
|
218
|
+
def y_for_plot(self)->NDArray[np.floating]:
|
|
219
|
+
if self._ylim is None:
|
|
220
|
+
self._y_for_plot = self.points_for_plot[:,1]
|
|
221
|
+
return self._y_for_plot
|
|
222
|
+
|
|
223
|
+
@property
|
|
224
|
+
def ylim(self)->tuple[np.float64,np.float64]:
|
|
225
|
+
if self._ylim is None:
|
|
226
|
+
self._ylim = (min(self.y_for_plot), max(self.y_for_plot))
|
|
227
|
+
return self._ylim
|
|
228
|
+
|
|
229
|
+
@property
|
|
230
|
+
def label_for_plot(self)->str:
|
|
231
|
+
if self.labels:
|
|
232
|
+
return self.labels.brief_summary
|
|
233
|
+
else:
|
|
234
|
+
return ""
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
# ======== Plot Object ========
|
|
239
|
+
@dataclass(slots=True)
|
|
240
|
+
class Axes:
|
|
241
|
+
spec: AxesSpec
|
|
242
|
+
labels: LabelDict
|
|
243
|
+
data_pool: list[Data] = field(default_factory=list)
|
|
244
|
+
|
|
245
|
+
_xlim:tuple[np.float64,np.float64] = field(init=False, default=None)
|
|
246
|
+
_ylim:tuple[np.float64,np.float64] = field(init=False, default=None)
|
|
247
|
+
|
|
248
|
+
@property
|
|
249
|
+
def xlim(self)->tuple[np.float64,np.float64]:
|
|
250
|
+
if self._xlim is None:
|
|
251
|
+
xlims = (np.array([data.xlim for data in self.data_pool]))
|
|
252
|
+
self._xlim = np.min(xlims[:,0]), np.max(xlims[:,1])
|
|
253
|
+
return self._xlim
|
|
254
|
+
|
|
255
|
+
@property
|
|
256
|
+
def ylim(self)->tuple[np.float64,np.float64]:
|
|
257
|
+
if self._ylim is None:
|
|
258
|
+
ylims = np.array([data.ylim for data in self.data_pool])
|
|
259
|
+
self._ylim = (np.min(ylims[:,0]), np.max(ylims[:,1]))
|
|
260
|
+
return self._ylim
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
@dataclass(slots=True)
|
|
264
|
+
class Figure:
|
|
265
|
+
axes_pool: list[Axes]
|
|
266
|
+
spec: FigureSpec
|
|
267
|
+
muti_axes_spec: MutiAxesSpec
|
|
268
|
+
labels: LabelDict
|
|
269
|
+
proj_folder: dict[str,str] # Mapping from proj_name to folder_path_in_proj
|
|
270
|
+
|
|
271
|
+
# ======== Plot Spec ========
|
|
272
|
+
class AxesSpec(NamedTuple):
|
|
273
|
+
# Alaways has full Frame
|
|
274
|
+
x_axis_title: str
|
|
275
|
+
y_axis_title: str
|
|
276
|
+
axis_title_font_size:float = 8
|
|
277
|
+
x_left_lim: Optional[float] = None
|
|
278
|
+
x_right_lim: Optional[float] = None
|
|
279
|
+
y_left_lim: Optional[float] = None
|
|
280
|
+
y_right_lim: Optional[float] = None
|
|
281
|
+
x_log_scale_min_span:float = 1e12
|
|
282
|
+
y_log_scale_min_span:float = 1e12
|
|
283
|
+
linewidth:float = 1.0
|
|
284
|
+
marker_size:float = 4
|
|
285
|
+
major_tick: Optional[TickSpec] = None
|
|
286
|
+
minor_tick: Optional[TickSpec] = None
|
|
287
|
+
major_grid: Optional[GridSpec] = None
|
|
288
|
+
minor_grid: Optional[GridSpec] = None
|
|
289
|
+
legend: Optional[LegendSpec] = None
|
|
290
|
+
annotation: Optional[list[AnnotationSpec]] = None
|
|
291
|
+
|
|
292
|
+
class TickSpec(NamedTuple):
|
|
293
|
+
axis:str = "both"
|
|
294
|
+
direction:str = "in"
|
|
295
|
+
length:float = 6
|
|
296
|
+
width:float = 1.2
|
|
297
|
+
top:bool = True
|
|
298
|
+
left:bool = True
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
class GridSpec(NamedTuple):
|
|
302
|
+
linestyle:str = "--"
|
|
303
|
+
linewidth:float = 0.5
|
|
304
|
+
color:str = "grey"
|
|
305
|
+
|
|
306
|
+
class LegendSpec(NamedTuple):
|
|
307
|
+
loc:str = "upper right"
|
|
308
|
+
anchor_x:float = 1
|
|
309
|
+
anchor_y:float = 1
|
|
310
|
+
fontsize:float = 8
|
|
311
|
+
frameon:bool = True
|
|
312
|
+
|
|
313
|
+
class AnnotationSpec(NamedTuple):
|
|
314
|
+
text:str
|
|
315
|
+
text_x:float
|
|
316
|
+
text_y:float
|
|
317
|
+
fontsize:float = 8
|
|
318
|
+
arrow:Optional[ArrowSpec] = None
|
|
319
|
+
|
|
320
|
+
class ArrowSpec(NamedTuple):
|
|
321
|
+
point_x:float
|
|
322
|
+
point_y:float
|
|
323
|
+
arrowstyle:str = "->",
|
|
324
|
+
color:str = "black",
|
|
325
|
+
linewidth:float = 1.0
|
|
326
|
+
|
|
327
|
+
class IgnoreOutlierSpec(NamedTuple):
|
|
328
|
+
min_gap_base:float
|
|
329
|
+
min_gap_multiple:float = 20
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
class FigureSpec(NamedTuple):
|
|
333
|
+
name:str
|
|
334
|
+
title:str = None
|
|
335
|
+
figsize:tuple[float] = (3.4,2.6)
|
|
336
|
+
dpi:int = 300
|
|
337
|
+
global_fontsize:float = 8
|
|
338
|
+
facecolor:str = "white"
|
|
339
|
+
linestyle_cycle:tuple[str] = ("-", "--")
|
|
340
|
+
linecolor_cycle:tuple[str] = ("black", "red")
|
|
341
|
+
linemarker_cycle:tuple[str] = ("o","o","s","s","^","^","v","v","d","d","*","*","x","x","+","+")
|
|
342
|
+
alpa_cycle:tuple[float] = (1.0,)
|
|
343
|
+
legend:Optional[LegendSpec] = None
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
class MutiAxesSpec(Protocol):
|
|
347
|
+
legend_holder:str
|
|
348
|
+
@classmethod
|
|
349
|
+
def default(cls, figuresize:tuple[float], axes_pool:list[Axes]):
|
|
350
|
+
...
|
|
351
|
+
|
|
352
|
+
# ======== Proj & folder ========
|
|
353
|
+
class folder(dict[str:Figure]):
|
|
354
|
+
pass
|
|
355
|
+
|
|
356
|
+
class Project(dict[str:folder]):
|
|
357
|
+
pass
|
|
358
|
+
|
|
359
|
+
# ======== Scenario ========
|
|
360
|
+
class Importer(Protocol):
|
|
361
|
+
instrument: str
|
|
362
|
+
|
|
363
|
+
@classmethod
|
|
364
|
+
def fetch_raw_data(cls, raw_data_file:TextIO, raw_data_name:str)->Data|FAIL:
|
|
365
|
+
...
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
class Scenario(Protocol):
|
|
369
|
+
data_summary_label_names:list[str]
|
|
370
|
+
axes_label_names:tuple[str]
|
|
371
|
+
figure_label_names:tuple[str]
|
|
372
|
+
figure_summary_label_names:list[str]
|
|
373
|
+
max_axes_in_one_figure:int
|
|
374
|
+
project_to_child_folder_label_names: str
|
|
375
|
+
parent_folder_name: str
|
|
376
|
+
|
|
377
|
+
@classmethod
|
|
378
|
+
def preprocess(cls, raw_datas:list[Data])->list[Data]:
|
|
379
|
+
...
|
|
380
|
+
|
|
381
|
+
@classmethod
|
|
382
|
+
def make_axes_spec(cls, axes_labels:LabelDict)->AxesSpec:
|
|
383
|
+
...
|
|
384
|
+
|
|
385
|
+
@classmethod
|
|
386
|
+
def make_figure_spec(cls,figure_labels:LabelDict, axes_pool:Iterable[Axes])->FigureSpec:
|
|
387
|
+
...
|
|
388
|
+
|
|
389
|
+
@classmethod
|
|
390
|
+
def make_muti_axes_spec(cls, axes_pool:list[Axes])->MutiAxesSpec|FAIL:
|
|
391
|
+
...
|
|
392
|
+
|
|
393
|
+
#======== Fit ========
|
|
394
|
+
# @dataclass(slots=True)
|
|
395
|
+
# class FitData:
|
|
396
|
+
# labels: LabelDict
|
|
397
|
+
# x: NDArray
|
|
398
|
+
# y: NDArray
|
|
399
|
+
|
|
400
|
+
# class FitScenario(Scenario, Protocol):
|
|
401
|
+
# @classmethod
|
|
402
|
+
# def fit(cls, data:FitData, modelstrategy:ModelStrategy, optimizer:Callable)->NDArray:
|
|
403
|
+
# ...
|
|
404
|
+
|
|
405
|
+
# class ModelStrategy(Protocol):
|
|
406
|
+
# fit_figure_spec: FigureSpec
|
|
407
|
+
# @staticmethod
|
|
408
|
+
# def prepare_data_for_fit(all_datas:Iterable[Data])->list[FitData]:
|
|
409
|
+
# ...
|
|
410
|
+
# @staticmethod
|
|
411
|
+
# def model(self, x)->Callable:
|
|
412
|
+
# ...
|
|
413
|
+
# def residual(self, x):
|
|
414
|
+
# ...
|
|
415
|
+
|
|
416
|
+
# @runtime_checkable
|
|
417
|
+
# class HasJacobian(Protocol):
|
|
418
|
+
# def jacobian(self, x):
|
|
419
|
+
# ...
|
|
420
|
+
|
|
421
|
+
# @runtime_checkable
|
|
422
|
+
# class HasInitialGuess(Protocol):
|
|
423
|
+
# def initial_guess(self):
|
|
424
|
+
# ...
|
|
425
|
+
|
|
426
|
+
# @runtime_checkable
|
|
427
|
+
# class HasTransfrom(Protocol):
|
|
428
|
+
# @staticmethod
|
|
429
|
+
# def from_physics(x):
|
|
430
|
+
# ...
|
|
431
|
+
# @staticmethod
|
|
432
|
+
# def to_physics(x):
|
|
433
|
+
# ...
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
import numpy as np
|
|
3
|
+
|
|
4
|
+
from ..base import *
|
|
5
|
+
from ..utils import skip_lines_then_readline
|
|
6
|
+
|
|
7
|
+
ROW_FILEOPENTIME = 3
|
|
8
|
+
ROW_SAMPLE1_NAME = 6
|
|
9
|
+
ROW_SAMPLE1_UNITS = 9
|
|
10
|
+
ROW_SAMPLE2_NAME = 10
|
|
11
|
+
ROW_SAMPLE2_UNITS = 13
|
|
12
|
+
ROW_SAMPLE3_NAME = 14
|
|
13
|
+
ROW_SAMPLE3_UNITS = 17
|
|
14
|
+
ROW_HEADERS = 32
|
|
15
|
+
|
|
16
|
+
T = "Temperature (K)"
|
|
17
|
+
H = "Magnetic Field (Oe)"
|
|
18
|
+
R1, I1 = "Bridge 1 Resistivity (Ohm-m)", "Bridge 1 Excitation (uA)"
|
|
19
|
+
R2, I2 = "Bridge 2 Resistivity (Ohm-m)", "Bridge 2 Excitation (uA)"
|
|
20
|
+
R3, I3 = "Bridge 3 Resistivity (Ohm-m)", "Bridge 3 Excitation (uA)"
|
|
21
|
+
|
|
22
|
+
class PPMS_Resistivity:
|
|
23
|
+
instrument = "PPMS"
|
|
24
|
+
|
|
25
|
+
@classmethod
|
|
26
|
+
def fetch_raw_data(cls, raw_data_file:TextIO, raw_data_name:str)->Data|FAIL:
|
|
27
|
+
labels = LabelDict(
|
|
28
|
+
initital={"instrument":cls.instrument ,"raw_data": LabelValue(raw_data_name)},
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
# Date
|
|
32
|
+
line = skip_lines_then_readline(raw_data_file, ROW_FILEOPENTIME-1)
|
|
33
|
+
info = line.split(",")
|
|
34
|
+
try:
|
|
35
|
+
if info[0].strip() != "FILEOPENTIME":
|
|
36
|
+
return fail_signal
|
|
37
|
+
dateMM_DD_YYYY = info[2].split("/")
|
|
38
|
+
dateYYYYMMDD = f"{dateMM_DD_YYYY[2]}{dateMM_DD_YYYY[0].zfill(2)}{dateMM_DD_YYYY[1].zfill(2)}"
|
|
39
|
+
except IndexError:
|
|
40
|
+
return fail_signal
|
|
41
|
+
labels["date"] = LabelValue(dateYYYYMMDD)
|
|
42
|
+
|
|
43
|
+
# S1 Name
|
|
44
|
+
line = skip_lines_then_readline(raw_data_file, ROW_SAMPLE1_NAME - ROW_FILEOPENTIME -1)
|
|
45
|
+
info = line.split(",")
|
|
46
|
+
try:
|
|
47
|
+
if info[-1].strip() != "Sample1 Name":
|
|
48
|
+
return fail_signal
|
|
49
|
+
s1_name = info[1]
|
|
50
|
+
except IndexError:
|
|
51
|
+
return fail_signal
|
|
52
|
+
labels["sample1_name"] = LabelValue(s1_name)
|
|
53
|
+
|
|
54
|
+
# S1 Units
|
|
55
|
+
line = skip_lines_then_readline(raw_data_file, ROW_SAMPLE1_UNITS - ROW_SAMPLE1_NAME -1)
|
|
56
|
+
info = line.split(",")
|
|
57
|
+
try:
|
|
58
|
+
if info[-1].strip() != "Sample1 Units":
|
|
59
|
+
return fail_signal
|
|
60
|
+
s1_units = info[1]
|
|
61
|
+
except IndexError:
|
|
62
|
+
return fail_signal
|
|
63
|
+
labels["sample1_units"] = LabelValue(s1_units)
|
|
64
|
+
|
|
65
|
+
# S2 Name
|
|
66
|
+
line = skip_lines_then_readline(raw_data_file, ROW_SAMPLE2_NAME - ROW_SAMPLE1_UNITS -1)
|
|
67
|
+
info = line.split(",")
|
|
68
|
+
try:
|
|
69
|
+
if info[-1].strip() != "Sample2 Name":
|
|
70
|
+
return fail_signal
|
|
71
|
+
s2_name = info[1]
|
|
72
|
+
except IndexError:
|
|
73
|
+
return fail_signal
|
|
74
|
+
labels["sample2_name"] = LabelValue(s2_name)
|
|
75
|
+
|
|
76
|
+
# S2 Units
|
|
77
|
+
line = skip_lines_then_readline(raw_data_file, ROW_SAMPLE2_UNITS - ROW_SAMPLE2_NAME -1)
|
|
78
|
+
info = line.split(",")
|
|
79
|
+
try:
|
|
80
|
+
if info[-1].strip() != "Sample2 Units":
|
|
81
|
+
return fail_signal
|
|
82
|
+
s2_units = info[1]
|
|
83
|
+
except IndexError:
|
|
84
|
+
return fail_signal
|
|
85
|
+
labels["sample2_units"] = LabelValue(s2_units)
|
|
86
|
+
|
|
87
|
+
# S3 Name
|
|
88
|
+
line = skip_lines_then_readline(raw_data_file, ROW_SAMPLE3_NAME - ROW_SAMPLE2_UNITS -1)
|
|
89
|
+
info = line.split(",")
|
|
90
|
+
try:
|
|
91
|
+
if info[-1].strip() != "Sample3 Name":
|
|
92
|
+
return fail_signal
|
|
93
|
+
s3_name = info[1]
|
|
94
|
+
except IndexError:
|
|
95
|
+
return fail_signal
|
|
96
|
+
labels["sample3_name"] = LabelValue(s3_name)
|
|
97
|
+
|
|
98
|
+
# S3 Units
|
|
99
|
+
line = skip_lines_then_readline(raw_data_file, ROW_SAMPLE3_UNITS - ROW_SAMPLE3_NAME -1)
|
|
100
|
+
info = line.split(",")
|
|
101
|
+
try:
|
|
102
|
+
if info[-1].strip() != "Sample3 Units":
|
|
103
|
+
return fail_signal
|
|
104
|
+
s3_units = info[1]
|
|
105
|
+
except IndexError:
|
|
106
|
+
return fail_signal
|
|
107
|
+
labels["sample3_units"] = LabelValue(s3_units)
|
|
108
|
+
|
|
109
|
+
# Headers
|
|
110
|
+
line = skip_lines_then_readline(raw_data_file,ROW_HEADERS - ROW_SAMPLE3_UNITS -1)
|
|
111
|
+
all_headers = dict((header,i) for (i,header) in enumerate(line.split(",")))
|
|
112
|
+
headers = (T, H, R1, I1, R2, I2, R3, I3)
|
|
113
|
+
try:
|
|
114
|
+
indexs = tuple(all_headers[header] for header in headers)
|
|
115
|
+
except KeyError:
|
|
116
|
+
return fail_signal
|
|
117
|
+
|
|
118
|
+
# read data
|
|
119
|
+
points = []
|
|
120
|
+
for line in raw_data_file:
|
|
121
|
+
cells = line.split(",")
|
|
122
|
+
points.append([np.float64(cells[i]) if cells[i] else np.nan for i in indexs])
|
|
123
|
+
|
|
124
|
+
return Data(
|
|
125
|
+
labels=labels,
|
|
126
|
+
_headers=headers,
|
|
127
|
+
points=np.array(points),
|
|
128
|
+
)
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from ..base import *
|
|
4
|
+
from ..utils import skip_lines_then_readline
|
|
5
|
+
|
|
6
|
+
ROW_FILEOPENTIME = 5
|
|
7
|
+
ROW_SAMPLE_MATERIAL = 18
|
|
8
|
+
ROW_SAMPLE_COMMENT = 19
|
|
9
|
+
ROW_SAMPLE_MASS = 20
|
|
10
|
+
ROW_SAMPLE_HOLDER = 25
|
|
11
|
+
ROW_HEADERS = 36
|
|
12
|
+
|
|
13
|
+
T = "Temperature (K)"
|
|
14
|
+
H = "Magnetic Field (Oe)"
|
|
15
|
+
M = "DC Moment Free Ctr (emu)"
|
|
16
|
+
|
|
17
|
+
Mfit = "DC Free Fit"
|
|
18
|
+
Mfix = "DC Moment Fixed Ctr (emu)"
|
|
19
|
+
SD = "DC Squid Drift"
|
|
20
|
+
CP = "Center Position (mm)"
|
|
21
|
+
DCC = "DC Calculated Center (mm)"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
headers = (T, H, M, Mfix, Mfit, SD, CP, DCC)
|
|
25
|
+
|
|
26
|
+
class VSM:
|
|
27
|
+
instrument = "VSM"
|
|
28
|
+
|
|
29
|
+
@classmethod
|
|
30
|
+
def fetch_raw_data(cls, raw_data_file:TextIO, raw_data_name:str)->Data|FAIL:
|
|
31
|
+
labels = LabelDict(
|
|
32
|
+
initital={"instrument":cls.instrument ,"raw_data": LabelValue(raw_data_name)},
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
# Date
|
|
36
|
+
line = skip_lines_then_readline(raw_data_file, ROW_FILEOPENTIME-1)
|
|
37
|
+
info = line.split(",")
|
|
38
|
+
try:
|
|
39
|
+
if info[0].strip() != "FILEOPENTIME":
|
|
40
|
+
return fail_signal
|
|
41
|
+
dateMM_DD_YYYY = info[2].split("/")
|
|
42
|
+
dateYYYYMMDD = f"{dateMM_DD_YYYY[2]}{dateMM_DD_YYYY[0].zfill(2)}{dateMM_DD_YYYY[1].zfill(2)}"
|
|
43
|
+
except IndexError:
|
|
44
|
+
return fail_signal
|
|
45
|
+
labels["date"] = LabelValue(dateYYYYMMDD)
|
|
46
|
+
|
|
47
|
+
# Material
|
|
48
|
+
line = skip_lines_then_readline(raw_data_file, ROW_SAMPLE_MATERIAL - ROW_FILEOPENTIME -1)
|
|
49
|
+
info = line.split(",")
|
|
50
|
+
try:
|
|
51
|
+
if info[-1].strip() != "SAMPLE_MATERIAL":
|
|
52
|
+
return fail_signal
|
|
53
|
+
material = info[1]
|
|
54
|
+
except IndexError:
|
|
55
|
+
return fail_signal
|
|
56
|
+
labels["material"] = LabelValue(material)
|
|
57
|
+
|
|
58
|
+
# COMMENT
|
|
59
|
+
line = skip_lines_then_readline(raw_data_file, ROW_SAMPLE_COMMENT - ROW_SAMPLE_MATERIAL -1)
|
|
60
|
+
info = line.split(",")
|
|
61
|
+
try:
|
|
62
|
+
if info[-1].strip() != "SAMPLE_COMMENT":
|
|
63
|
+
return fail_signal
|
|
64
|
+
comment = info[1]
|
|
65
|
+
except IndexError:
|
|
66
|
+
return fail_signal
|
|
67
|
+
labels["comment"] = LabelValue(comment)
|
|
68
|
+
|
|
69
|
+
# Mass
|
|
70
|
+
line = skip_lines_then_readline(raw_data_file, ROW_SAMPLE_MASS - ROW_SAMPLE_COMMENT -1)
|
|
71
|
+
info = line.split(",")
|
|
72
|
+
try:
|
|
73
|
+
if info[-1].strip() != "SAMPLE_MASS":
|
|
74
|
+
return fail_signal
|
|
75
|
+
mass = info[1]
|
|
76
|
+
except IndexError:
|
|
77
|
+
return fail_signal
|
|
78
|
+
labels["mass"] = LabelValue(mass,"mg")
|
|
79
|
+
|
|
80
|
+
# Sample Holder
|
|
81
|
+
line = skip_lines_then_readline(raw_data_file,ROW_SAMPLE_HOLDER - ROW_SAMPLE_MASS -1)
|
|
82
|
+
info = line.split(",")
|
|
83
|
+
try:
|
|
84
|
+
if info[-1].strip() != "SAMPLE_HOLDER":
|
|
85
|
+
return fail_signal
|
|
86
|
+
sample_holder = info[1]
|
|
87
|
+
except IndexError:
|
|
88
|
+
return None
|
|
89
|
+
labels["sample_holder"] = LabelValue(sample_holder)
|
|
90
|
+
|
|
91
|
+
# Headers
|
|
92
|
+
line = skip_lines_then_readline(raw_data_file,ROW_HEADERS - ROW_SAMPLE_HOLDER -1)
|
|
93
|
+
all_headers = dict((header,i) for (i,header) in enumerate(line.split(",")))
|
|
94
|
+
try:
|
|
95
|
+
indexs = tuple(all_headers[header] for header in headers)
|
|
96
|
+
except KeyError:
|
|
97
|
+
return fail_signal
|
|
98
|
+
|
|
99
|
+
# read data
|
|
100
|
+
points = []
|
|
101
|
+
for line in raw_data_file:
|
|
102
|
+
cells = line.split(",")
|
|
103
|
+
points.append(tuple(float(cells[i]) for i in indexs))
|
|
104
|
+
|
|
105
|
+
return Data(
|
|
106
|
+
labels=labels,
|
|
107
|
+
_headers=headers,
|
|
108
|
+
points=np.array(points),
|
|
109
|
+
)
|