ararpy 0.0.1a1__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,683 @@
1
+
2
+ """
3
+ # ==========================================
4
+ # Copyright 2023 Yang
5
+ # ararpy - files - calc_file
6
+ # ==========================================
7
+
8
+ Open age files from ArArCALC. It currently supports version: 25.2 and 24.0.
9
+ Version 25.2 is preferred to 24.0 as containing more detailed parameters than 24.0.
10
+
11
+ ArArCalcFile(file_path, file_name: optional)
12
+ -> open()
13
+ -> get content and sample info ()
14
+
15
+ """
16
+ import traceback
17
+ import pandas as pd
18
+ import numpy as np
19
+ import re
20
+ from xlrd import open_workbook, biffh
21
+ import os
22
+ import msoffcrypto
23
+ # from programs.ararpy.calc.basic import get_datetime
24
+ # from programs.ararpy import smp, calc
25
+ # from programs.ararpy.files import xls
26
+ from ..files import xls
27
+ from ..smp.initial import create_sample_from_df, create_sample_from_dict
28
+ from ..calc.basic import get_datetime
29
+ from ..calc import arr, err, isochron
30
+
31
+
32
+ def to_sample(file_path: str, **kwargs):
33
+ """
34
+
35
+ Parameters
36
+ ----------
37
+ file_path
38
+
39
+ Returns
40
+ -------
41
+
42
+ """
43
+ file = ArArCalcFile(file_path=file_path, **kwargs).open()
44
+ sample = create_sample_from_df(file.get_content(), file.get_smp_info())
45
+ return sample
46
+
47
+
48
+ def read_calc_file(file_path: str):
49
+ """
50
+ :param file_path: file with suffix of .age or .air
51
+ :return: dict, keys are sheet name, value are list of [[col 0], [col 1], ...] of corresponding sheet
52
+ """
53
+ PASSWORD = 'aapkop' # password to open age file, the password to unhide worksheets is boris
54
+ decrypt_file_path = file_path + '_decrypt.xls'
55
+ try:
56
+ # opening excel in decrypt function would reveal the hidden sheet without protect
57
+ with open(file_path, 'rb') as age_file:
58
+ office_file = msoffcrypto.OfficeFile(age_file)
59
+ office_file.load_key(password=PASSWORD)
60
+ office_file.decrypt(open(decrypt_file_path, 'wb'))
61
+ wb = open_workbook(decrypt_file_path)
62
+ worksheets = wb.sheet_names()
63
+ book_contents = dict()
64
+ for each_sheet in worksheets:
65
+ sheet = wb.sheet_by_name(each_sheet)
66
+ sheet_contents = [
67
+ [sheet.cell(row, col).value for col in range(sheet.ncols)]
68
+ for row in range(sheet.nrows)
69
+ ]
70
+ book_contents[each_sheet] = sheet_contents
71
+ os.remove(decrypt_file_path)
72
+ except Exception as e:
73
+ print(traceback.format_exc())
74
+ return False
75
+ else:
76
+ return book_contents
77
+
78
+
79
+ def open_252(data: pd.DataFrame, logs01: pd.DataFrame, logs02: pd.DataFrame):
80
+ """
81
+ Open 25.2 version ArArCALC files
82
+ :param data:
83
+ :param logs01:
84
+ :param logs02:
85
+ :return:
86
+ """
87
+ # add default columns for 999, -999, and -1 index
88
+ data[-999] = [0] * data.index.size
89
+ data[999] = [1] * data.index.size
90
+ data[-1] = [np.nan] * data.index.size
91
+ # rows_unm --> 样品阶段数
92
+ sequence_index = [1, 2, ]
93
+ sample_values_index = [16, 17, 21, 22, 26, 27, 31, 32, 36, 37,]
94
+ blank_values_index = [3, 4, 5, 6, 7, 8, 9, 10, 11, 12,]
95
+ corrected_values_index = [
96
+ 188, 189, 190, 191, 192, 193, 194, 195, 196, 197,]
97
+ degas_values_index = [
98
+ 138, 139, 140, 141, 142, 143, 144, 145, # 36 a, c, ca, cl 0-9
99
+ 146, 147, # 37 ca 10-11
100
+ 156, 157, 148, 149, 150, 151, 152, 153, 154, 155, # 38 cl, a, c, k, ca 12-21
101
+ 158, 159, 160, 161, # 39 k, ca 22-25
102
+ 162, 163, 164, 165, 166, 167, 168, 169, # 40 r, a, c, k 26-33
103
+ ]
104
+ publish_values_index = [
105
+ 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
106
+ ]
107
+ apparent_age_values_index = [
108
+ 198, 199, 200, 201, -999, -999, 202, 203, # f, sf, ages, s, s, s, 40Arr%, 39Ar%
109
+ ]
110
+ isochron_values_index = [
111
+ 116, 117, 118, 119, 122, # normal isochron
112
+ -1, 127, 128, 129, 130, 133, # inverse
113
+ -1, -999, -999, -999, -999, -999, # 39/38 vs 40/38
114
+ -1, -999, -999, -999, -999, -999, # 39/40 vs 39/40
115
+ -1, -999, -999, -999, -999, -999, # 38/39 vs 40/39
116
+ -1, -999, -999, -999, -999, -999, -999, # 3D isochron, 36/39, 38/39, 40/39
117
+ ]
118
+ isochron_mark_index = [115,]
119
+ total_param_index = [
120
+ 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, # 0-9
121
+ 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, # 10-19
122
+ 91, 92, 93, 94, 95, 96, -999, -999, 63, -999, # 20-29
123
+ -999, -999, -1, -1, -999, -999, -999, -999, -999, -999, # 30-39
124
+ -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, # 40-49
125
+ -999, -999, -999, -999, -999, -999, -999, -999, 67, 49, # 50-59
126
+ 50, -999, -999, -999, -999, -999, -999, 51, 52, 53, # 60-69
127
+ 54, -999, -999, -999, -999, -999, -999, -999, -999, -999, # 70-79
128
+ -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, # 80-89
129
+ -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, # 90-99
130
+ -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, # 100-109
131
+ -999, -999, -999, -999, -1, # 110-114
132
+ -999, -999, -999, -999, -999, -999, -999, -999, # 115-122
133
+ ]
134
+
135
+ # double transpose to remove keys
136
+ get_data = lambda df, index: pd.concat([df[index].transpose()], ignore_index=True).transpose()
137
+
138
+ sample_values = get_data(data, sample_values_index)
139
+ blank_values = get_data(data, blank_values_index)
140
+ corrected_values = get_data(data, corrected_values_index)
141
+ degas_values = get_data(data, degas_values_index)
142
+ publish_values = get_data(data, publish_values_index)
143
+ apparent_age_values = get_data(data, apparent_age_values_index)
144
+ isochron_values = get_data(data, isochron_values_index)
145
+ total_param = get_data(data, total_param_index)
146
+ isochron_mark = get_data(data, isochron_mark_index)
147
+ sequence = get_data(data, sequence_index)
148
+
149
+ # adjustment
150
+ isochron_mark.replace({4.0: 1}, inplace=True)
151
+ total_param[83] = logs01[1][9] # No
152
+ total_param[84] = logs01[1][10] # %SD
153
+ total_param[81] = logs01[1][11] # K mass
154
+ total_param[82] = logs01[1][12] # K mass %SD
155
+
156
+ total_param[85] = logs01[1][13] # How many seconds in a year
157
+ total_param[86] = 0 # %SD
158
+
159
+ total_param[87] = logs01[1][14] # The constant of 40K/K
160
+ total_param[88] = logs01[1][15] # %SD
161
+
162
+ total_param[89] = logs01[1][16] # The constant of 35Cl/37Cl
163
+ total_param[90] = logs01[1][17] # %SD
164
+ total_param[56] = logs01[1][18] # The constant of 36/38Cl productivity
165
+ total_param[57] = logs01[1][19] # %SD
166
+ total_param[91] = logs01[1][24] # The constant of HCl/Cl
167
+ total_param[92] = logs01[1][25] # %SD
168
+
169
+ total_param[50] = logs01[1][26] # 40K(EC) activities param
170
+ total_param[51] = logs01[1][27] # %SD
171
+ total_param[52] = logs01[1][28] # 40K(\beta-) activities param
172
+ total_param[53] = logs01[1][29] # %SD
173
+ total_param[48] = logs01[1][26] + logs01[1][28] # 40K(EC) activities param
174
+ total_param[49] = 100 / total_param[48] * pow(
175
+ (logs01[1][27] / 100 * logs01[1][26]) ** 2 + (logs01[1][29] / 100 * logs01[1][28]) ** 2, 0.5) # %SD
176
+
177
+ total_param[34] = logs01[1][36] # decay constant of 40K total
178
+ total_param[35] = logs01[1][37] # %SD
179
+ total_param[36] = logs01[1][30] # decay constant of 40K EC
180
+ total_param[37] = logs01[1][31] # %SD
181
+ total_param[38] = logs01[1][32] # decay constant of 40K \beta-
182
+ total_param[39] = logs01[1][33] # %SD
183
+ total_param[40] = 0 # decay constant of 40K \beta+
184
+ total_param[41] = 0 # %SD
185
+ total_param[46] = logs01[1][34] # decay constant of 36Cl
186
+ total_param[47] = logs01[1][35] # %SD
187
+ total_param[42] = logs01[1][38] # decay constant of 39Ar
188
+ total_param[43] = logs01[1][39] # %SD
189
+ total_param[44] = logs01[1][40] # decay constant of 37Ar
190
+ total_param[45] = logs01[1][41] # %SD
191
+
192
+ # logs01[1][49]] # 1 for using weighted YORK isochron regression, 2 for unweighted
193
+ # logs01[1][50]] # True for including errors on irradiation constants, False for excluding those
194
+ # [logs01[1][67]] # MDF method, 'LIN'
195
+ total_param[93] = logs01[1][70] # 40Ar/36Ar air
196
+ total_param[94] = logs01[1][71] # %SD
197
+
198
+ total_param[97] = 'York-2' # Fitting method
199
+ total_param[98] = logs01[1][0] # Convergence
200
+ total_param[99] = logs01[1][1] # Iterations number
201
+ total_param[100] = logs01[1][67] # MDF method
202
+ total_param[101] = logs01[1][65] # force negative to zero when correct blank
203
+ total_param[102] = True # Apply 37Ar decay
204
+ total_param[103] = True # Apply 39Ar decay
205
+ total_param[104] = logs01[1][6] # Apply 37Ar decay
206
+ total_param[105] = logs01[1][7] # Apply 39Ar decay
207
+ total_param[106] = True # Apply K degas
208
+ total_param[107] = True # Apply Ca degas
209
+ total_param[108] = True # Apply Air degas
210
+ total_param[109] = logs01[1][8] # Apply Cl degas
211
+
212
+ total_param[110] = True if logs01[1][5] == 'MIN' else False # Calculating ages using Min equation [True] or conventional equation [False]
213
+ total_param[111] = logs01[1][89] # Use primary standard or not
214
+ total_param[112] = logs01[1][91] # Use standard age or not
215
+ total_param[113] = logs01[1][92] # Use primary ratio or not
216
+
217
+ # irradiation time
218
+ total_param = general_adjustment(
219
+ total_param.copy(), logs02, get_data(data, [59, 58, 57, 60, 61]), data[63][0]
220
+ )
221
+ return [
222
+ sample_values, blank_values, corrected_values, degas_values, publish_values,
223
+ apparent_age_values, isochron_values, total_param, isochron_mark,
224
+ sequence,
225
+ ]
226
+
227
+
228
+ def open_240(data: pd.DataFrame, logs01: pd.DataFrame, logs02: pd.DataFrame):
229
+ """
230
+ Open 24.0 version ArArCALC files
231
+ :param data:
232
+ :param logs01:
233
+ :param logs02:
234
+ :return:
235
+ """
236
+ # add default columns for 999, -999, and -1 index
237
+ data[-999] = [0] * data.index.size
238
+ data[999] = [1] * data.index.size
239
+ data[-1] = [np.nan] * data.index.size
240
+ # values index
241
+ sequence_index = [1, 2, ]
242
+ sample_values_index = [16, 17, 21, 22, 26, 27, 31, 32, 36, 37,]
243
+ blank_values_index = [3, 4, 5, 6, 7, 8, 9, 10, 11, 12,]
244
+ corrected_values_index = [
245
+ -999, -999, -999, -999, -999, -999, -999, -999, -999, -999,]
246
+ degas_values_index = [
247
+ # 36 a, c, ca, cl
248
+ 136, -999, 137, -999, 139, -999, 138, -999,
249
+ # 37 ca,
250
+ 140, -999,
251
+ # 38 cl, a, c, k, ca,
252
+ 145, -999, 141, -999, 142, -999, 143, -999, 144, -999,
253
+ # 39 k, ca
254
+ 146, -999, 147, -999,
255
+ # 40 r, a, c, k
256
+ 148, -999, 149, -999, 150, -999, 151, -999,
257
+ ]
258
+ publish_values_index = [
259
+ 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109,
260
+ ]
261
+ apparent_age_values_index = [
262
+ # f, sf, ages, s, s, s, 39Ar%
263
+ 156, 157, 104, 105, -999, -999, 106, 107,
264
+ ]
265
+ isochron_values_index = [
266
+ # normal isochron
267
+ 114, 115, 116, 117, 120,
268
+ # inverse
269
+ -1, 125, 126, 127, 128, 131,
270
+ # 39/38 vs 40/38
271
+ -1, -999, -999, -999, -999, -999,
272
+ # 39/40 vs 39/40
273
+ -1, -999, -999, -999, -999, -999,
274
+ # 38/39 vs 40/39
275
+ -1, -999, -999, -999, -999, -999,
276
+ # 3D isochron, 36/39, 38/39, 40/39, r1, r2, r3
277
+ -1, -999, -999, -999, -999, -999, -999, -999, -999, -999,
278
+ ]
279
+ isochron_mark_index = [113,]
280
+ total_param_index = [
281
+ # 0-9
282
+ 69, 70, 71, 72, 73, 74, 75, 76, 77, 78,
283
+ # 10-19
284
+ 79, 80, 81, 82, 83, 84, 85, 86, 87, 88,
285
+ # 20-29
286
+ 89, 90, 91, 92, 93, 94, -999, -999, 63, -999,
287
+ # 30-39
288
+ -999, -999, -1, -1, -999, -999, -999, -999, -999, -999,
289
+ # 40-49
290
+ -999, -999, -999, -999, -999, -999, -999, -999, -999, -999,
291
+ # 50-59
292
+ -999, -999, -999, -999, -999, -999, -999, -999, 65, 49,
293
+ # 60-69
294
+ 50, -999, -999, -999, -999, -999, -999, 51, 52, 53,
295
+ # 70-79
296
+ 54, -999, -999, -999, -999, -999, -999, -999, -999, -999,
297
+ # 80-89
298
+ -999, -999, -999, -999, -999, -999, -999, -999, -999, -999,
299
+ # 90-99
300
+ -999, -999, -999, -999, -999, -999, -999, -999, -999, -999,
301
+ # 100-99909
302
+ -999, -999, -999, -999, -999, -999, -999, -999, -999, -999,
303
+ # 110-114
304
+ -999, -999, -999, -999, -1,
305
+ # 115-122
306
+ -999, -999, -999, -999, -999, -999, -999, -999,
307
+ ]
308
+
309
+ # double transpose to remove keys
310
+ get_data = lambda df, index: pd.concat([df[index].transpose()], ignore_index=True).transpose()
311
+
312
+ sample_values = get_data(data, sample_values_index)
313
+ blank_values = get_data(data, blank_values_index)
314
+ corrected_values = get_data(data, corrected_values_index)
315
+ degas_values = get_data(data, degas_values_index)
316
+ publish_values = get_data(data, publish_values_index)
317
+ apparent_age_values = get_data(data, apparent_age_values_index)
318
+ isochron_values = get_data(data, isochron_values_index)
319
+ total_param = get_data(data, total_param_index)
320
+ isochron_mark = get_data(data, isochron_mark_index)
321
+ sequence = get_data(data, sequence_index)
322
+
323
+ # irradiation time
324
+ total_param = general_adjustment(
325
+ total_param.copy(), logs02, get_data(data, [59, 58, 57, 60, 61]), data[63][0]
326
+ )
327
+ # Do adjustment
328
+ isochron_mark.replace({4.0: 1}, inplace=True)
329
+ total_param[44] = logs01[1][40]
330
+ total_param[45] = logs01[1][41]
331
+ total_param[42] = logs01[1][38]
332
+ total_param[43] = logs01[1][39]
333
+ total_param[34] = logs01[1][36]
334
+ total_param[35] = logs01[1][37]
335
+
336
+ return [
337
+ sample_values, blank_values, corrected_values, degas_values, publish_values,
338
+ apparent_age_values, isochron_values, total_param, isochron_mark,
339
+ sequence,
340
+ ]
341
+
342
+
343
+ def change_error_type(data: pd.DataFrame, header: pd.Series):
344
+ """
345
+ :param data: 2D DataFrame
346
+ :param header: 1D Series, headers
347
+ :return:
348
+ """
349
+ # 2 sigma to 1 sigma
350
+ tochange = np.flatnonzero(header.where(header.str.contains('2s'), other=False))
351
+ data[tochange.tolist()] = data[tochange.tolist()] / 2
352
+
353
+ # 2 sigma to 1 sigma
354
+ tochange = np.flatnonzero(header.where(header.str.contains('%1s|%2s', regex=True), other=False))
355
+ data[tochange.tolist()] = \
356
+ data[tochange.tolist()] * abs(data[(tochange - 1).tolist()].rename(lambda x: x + 1, axis='columns')) / 100
357
+
358
+ return data
359
+
360
+
361
+ def general_adjustment(
362
+ total_param: pd.DataFrame, logs02: pd.DataFrame, experimental_time: pd.DataFrame,
363
+ irradiation_name: str
364
+ ):
365
+ """
366
+ General handle for all age files, including set irradiaition time, initial ratios, error display
367
+ :param total_param:
368
+ :param logs02:
369
+ :param experimental_time:
370
+ :param irradiation_name:
371
+ :return:
372
+ """
373
+ irra_project = pd.Series(logs02[1].copy())
374
+ irradiation_index = np.flatnonzero(irra_project.where(irra_project.str.fullmatch(irradiation_name), other=False))[0]
375
+ irradiation_info = logs02[32][irradiation_index].split('\n')
376
+ # 处理辐照时间
377
+ month_convert = {
378
+ r'^(?i)Jan$': '01', r'^(?i)Feb$': '02', r'^(?i)Mar$': '03', r'^(?i)Apr$': '04', r'^(?i)May$': '05', r'^(?i)Jun$': '06',
379
+ r'^(?i)Jul$': '07', r'^(?i)Aug$': '08', r'^(?i)Sep$': '09', r'^(?i)Oct$': '10', r'^(?i)Nov$': '11', r'^(?i)Dec$': '12'}
380
+ duration_hour = []
381
+ end_time_second = []
382
+ irradiation_end_time = []
383
+ last_time = []
384
+ for each_date in irradiation_info:
385
+ if '/' in each_date:
386
+ _year = each_date.split(' ')[1].split('/')[2]
387
+ _month = each_date.split(' ')[1].split('/')[1].capitalize()
388
+ for pat, val in month_convert.items():
389
+ if re.match(pat, _month):
390
+ _month = val
391
+ break
392
+ _day = each_date.split(' ')[1].split('/')[0]
393
+ _hour = each_date.split(' ')[2].split('.')[0]
394
+ _min = each_date.split(' ')[2].split('.')[1]
395
+ end_time_second.append(get_datetime(
396
+ t_year=int(_year), t_month=int(_month), t_day=int(_day),
397
+ t_hour=int(_hour), t_min=int(_min)))
398
+ each_duration_hour = each_date.split(' ')[0].split('.')[0]
399
+ each_duration_min = each_date.split(' ')[0].split('.')[1]
400
+ each_duration = int(each_duration_hour) + round(int(each_duration_min) / 60, 2)
401
+ duration_hour.append(each_duration)
402
+ irradiation_end_time.append(f"{_year}-{_month}-{_day}-{_hour}-{_min}D{each_duration}")
403
+ last_time = [_year, _month, _day, _hour, _min]
404
+
405
+ experimental_time: pd.DataFrame = experimental_time.apply(pd.to_numeric, errors='ignore', downcast='integer')
406
+ experimental_time: pd.DataFrame = experimental_time.replace(to_replace=month_convert, regex=True)
407
+ total_param[31] = [f"{i[0]}-{i[1]}-{i[2]}T{i[3]}:{i[4]}Z" for i in experimental_time.values.tolist()]
408
+ total_param[26] = len(irradiation_end_time)
409
+ total_param[27] = 'S'.join(irradiation_end_time)
410
+ total_param[29] = sum(duration_hour)
411
+ total_param[30] = '-'.join(last_time)
412
+
413
+ stand_time_second = [
414
+ get_datetime(*i) - get_datetime(*last_time) for i in experimental_time.values.tolist()]
415
+ total_param[32] = [i / (3600 * 24 * 365.242) for i in stand_time_second] # stand year
416
+
417
+ # initial ratios & error display settings
418
+ total_param[115] = 0
419
+ total_param[116] = 298.56
420
+ total_param[117] = 0.31
421
+ total_param[118] = 298.56
422
+ total_param[119] = 0.31
423
+ total_param[120] = 1
424
+ total_param[121] = 1
425
+ total_param[122] = 1
426
+
427
+ return total_param
428
+
429
+
430
+ def full_to_sample(file_path: str, sample_name: str = None):
431
+ """
432
+ Parameters
433
+ ----------
434
+ file_path
435
+ sample_name
436
+
437
+ Returns
438
+ -------
439
+
440
+ """
441
+ if sample_name is None:
442
+ sample_name = str(os.path.split(file_path)[-1]).split('.')[0]
443
+ content, sample_info = open_full_xls(file_path, sample_name)
444
+ sample = create_sample_from_dict(content=content, smp_info=sample_info)
445
+ return sample
446
+
447
+
448
+ def open_full_xls(file_path: str, sample_name: str = ''):
449
+ """
450
+ filepath: absolute full path of input file
451
+ return sample instance
452
+ """
453
+ try:
454
+ res = xls.open_xls(file_path)
455
+ except (Exception, BaseException) as e:
456
+ return e
457
+ start_row = 5
458
+ rows_num = len(res['Sample Parameters']) - 5
459
+ for key, val in res.items():
460
+ res[key] = arr.transpose(val[:start_row + rows_num])
461
+ # 2倍误差改回1倍
462
+ for i in range(len(res[key])):
463
+ if res[key][i][2] in ['2s', '%2s', '± 2s']:
464
+ _temp = []
465
+ for j in range(rows_num):
466
+ try:
467
+ _temp.append(res[key][i][start_row + j] / 2)
468
+ except TypeError:
469
+ _temp.append('')
470
+ res[key][i][start_row: start_row + rows_num] = _temp
471
+ if res['Incremental Heating Summary'][13][2] == 'K/Ca':
472
+ _temp, _s = [], []
473
+ for j in range(rows_num):
474
+ try:
475
+ _temp.append(1 / res['Relative Abundances'][13][start_row + j])
476
+ _s.append(err.div((1, 0), (
477
+ res['Relative Abundances'][13][start_row + j], res['Relative Abundances'][14][start_row + j])))
478
+ except TypeError:
479
+ _temp.append(None)
480
+ _s.append(None)
481
+ res['Incremental Heating Summary'][13][start_row: start_row + rows_num] = _temp
482
+ res['Incremental Heating Summary'][13][start_row: start_row + rows_num] = _s
483
+
484
+ # degas相对误差改为绝对误差
485
+ for i in range(len(res['Degassing Patterns'])):
486
+ if res['Degassing Patterns'][i][2] in ['%1s', '%2s']:
487
+ _temp = []
488
+ for j in range(rows_num):
489
+ try:
490
+ _temp.append(res['Degassing Patterns'][i][start_row + j] * res['Degassing Patterns'][i - 1][
491
+ start_row + j] / 100)
492
+ except TypeError:
493
+ _temp.append('')
494
+ res['Degassing Patterns'][i][start_row: start_row + rows_num] = _temp
495
+ rows = list(range(start_row, rows_num))
496
+ sequence_name = arr.partial(res['Procedure Blanks'], rows, 1)
497
+ sequence_value = arr.partial(res['Procedure Blanks'], rows, 2)
498
+ blank_values = arr.partial(
499
+ res['Procedure Blanks'], rows, [3, 4, 5, 6, 7, 8, 9, 10, 11, 12])
500
+ sample_values = arr.partial(
501
+ res['Intercept Values'], rows, [3, 4, 8, 9, 13, 14, 18, 19, 23, 24])
502
+ corrected_values = arr.partial(
503
+ res['Relative Abundances'], rows, [4, 5, 6, 7, 8, 9, 10, 11, 12, 13])
504
+ degas_values = arr.partial(
505
+ res['Degassing Patterns'], rows, [
506
+ 4, 5, 6, 7, 8, 9, 10, 11,
507
+ 12, 13,
508
+ 22, 23, 14, 15, 16, 17, 18, 19, 20, 21,
509
+ 24, 25, 26, 27,
510
+ 28, 29, 30, 31, 32, 33, 34, 35])
511
+ publish_values = arr.partial(
512
+ res['Incremental Heating Summary'], rows, [4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14])
513
+ apparent_age_values = arr.partial(
514
+ res['Relative Abundances'], rows, [14, 15, 16, 17, -1, -1, 18, 19])
515
+ isochron_values = [[np.nan] * len(rows)] * 39
516
+ isochron_values[0:5] = arr.partial(
517
+ res['Normal Isochron Table'], rows, [4, 5, 6, 7, 10])
518
+ isochron_values[6:11] = arr.partial(
519
+ res['Inverse Isochron Table'], rows, [4, 5, 6, 7, 10])
520
+ total_param = arr.partial(
521
+ res['Sample Parameters'], rows, [
522
+ # 1, 2, # 0-1
523
+ -1, -1, -1, -1, # 2-5
524
+ -1, -1, -1, -1, # 6-9
525
+ -1, -1, -1, -1, -1, -1, # 10-15
526
+ -1, -1, -1, -1, # 16-19
527
+ -1, -1, # 20-21
528
+ 23, 24, -1, -1, -1, -1, # 22-27
529
+ -1, -1, # 28-29
530
+ 22, -1, -1, -1, # 30-33
531
+ -1, -1, # 34-35
532
+ -1, -1, # 36-37
533
+ -1, -1, # 38-39
534
+ -1, -1, # 40-41
535
+ -1, -1, # 42-43
536
+ -1, -1, # 44-45
537
+ -1, -1, # 46-47
538
+ -1, -1, # 48-49
539
+ -1, -1, # 50-51
540
+ -1, -1, # 52-53
541
+ -1, -1, # 54-55
542
+ -1, -1, # 56-57
543
+ -1, -1, # 58-59
544
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, # 60-68
545
+ 10, 11, 12, 13, #
546
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, #
547
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, #
548
+ -1, -1, -1, -1, #
549
+ -1, -1, -1, -1, #
550
+ -1, -1, -1, -1, -1, #
551
+ -1, -1, -1, -1, #
552
+ -1, -1, -1, -1, #
553
+ -1, #
554
+ ])
555
+
556
+ month_convert = {'Jan': '01', 'Feb': '02', 'Mar': '03', 'Apr': '04', 'May': '05', 'Jun': '06',
557
+ 'Jul': '07', 'Aug': '08', 'Sep': '09', 'Oct': '10', 'Nov': '11', 'Dec': '12'}
558
+ experiment_start_time = []
559
+ for i in arr.transpose(
560
+ arr.partial(res['Sample Parameters'], rows, [18, 17, 16, 19, 20])):
561
+ _year = str(i[0]) if '.' not in str(i[0]) else str(i[0]).split('.')[0]
562
+ _month = str(month_convert[str(i[1]).capitalize()]) if str(i[1]).capitalize() in month_convert.keys() else str(
563
+ i[1]).capitalize()
564
+ _day = str(i[2]) if '.' not in str(i[2]) else str(i[2]).split('.')[0]
565
+ _hour = str(i[3]) if '.' not in str(i[3]) else str(i[3]).split('.')[0]
566
+ _min = str(i[4]) if '.' not in str(i[4]) else str(i[4]).split('.')[0]
567
+ experiment_start_time.append([_year, _month, _day, _hour, _min])
568
+
569
+ total_param[31] = ['-'.join(i) for i in experiment_start_time]
570
+ total_param[0:26] = arr.partial(
571
+ res['Irradiation Constants'], rows,
572
+ [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])
573
+
574
+ isochron_values[11] = [''] * len(isochron_values[0])
575
+ isochron_values[17] = [''] * len(isochron_values[0])
576
+ isochron_values[23] = [''] * len(isochron_values[0])
577
+ isochron_values[12:17] = isochron.get_data(
578
+ degas_values[20], degas_values[21], degas_values[24], degas_values[25], degas_values[10], degas_values[11]
579
+ ) # 39/38, 40/38
580
+ isochron_values[18:23] = isochron.get_data(
581
+ degas_values[20], degas_values[21], degas_values[10], degas_values[11], degas_values[24], degas_values[25]
582
+ ) # 39/40, 38/40
583
+ isochron_values[24:29] = isochron.get_data(
584
+ degas_values[10], degas_values[11], degas_values[24], degas_values[25], degas_values[20], degas_values[21]
585
+ ) # 38/39, 40/39
586
+ isochron_mark = [1 if i == 'P' else '' for i in arr.partial(
587
+ res['Normal Isochron Table'], rows, 3)]
588
+
589
+ sample_info = {
590
+ 'sample': {'name': sample_name, 'material': 'MATERIAL', 'location': 'LOCATION'},
591
+ 'researcher': {'name': 'RESEARCHER', 'addr': 'ADDRESS'},
592
+ 'laboratory': {'name': 'LABORATORY', 'addr': 'ADDRESS', 'analyst': 'ANALYST'}
593
+ }
594
+
595
+ res = [sample_values, blank_values, corrected_values, degas_values, publish_values,
596
+ apparent_age_values, isochron_values, total_param, [isochron_mark],
597
+ [sequence_name, sequence_value]]
598
+
599
+ return dict(zip(['smp', 'blk', 'cor', 'deg', 'pub', 'age', 'iso', 'pam', 'mak', 'seq'], res)), sample_info
600
+
601
+
602
+ class ArArCalcFile:
603
+ def __init__(self, file_path: str, sample_name: str = ""):
604
+ self.file_path = file_path
605
+ self.sample_name = sample_name
606
+ self.supported_versions = ['25.2', '24.0']
607
+ self.content = pd.DataFrame([])
608
+ self.sample_info = {
609
+ 'sample': {'name': 'undefined', 'material': 'undefined',},
610
+ 'researcher': {'name': 'undefined'},
611
+ 'laboratory': {'name': 'undefined', 'analyst': 'undefined', 'info': 'undefined',}
612
+ }
613
+
614
+ def open(self):
615
+ book_contents = read_calc_file(self.file_path)
616
+ if not book_contents:
617
+ raise ValueError('Fail to open the file')
618
+ # create data frames for book values
619
+ content = pd.DataFrame(book_contents['Data Tables'])
620
+ logs01 = pd.DataFrame(book_contents['Logs01'])
621
+ logs02 = pd.DataFrame(book_contents['Logs02'])
622
+ logs03 = pd.DataFrame(book_contents['Logs03'])
623
+
624
+ start_row = 5
625
+ sequence_num = int(logs03[2][0])
626
+ header = content.loc[2]
627
+ data = pd.concat([content.loc[list(range(start_row, start_row + sequence_num))]], ignore_index=True).apply(
628
+ pd.to_numeric, errors='ignore'
629
+ )
630
+
631
+ # check version
632
+ version = logs03[3][0]
633
+ if version == '25.2':
634
+ handler = open_252
635
+ material = data[45][0]
636
+ analyst = data[47][0]
637
+ elif version == '24.0':
638
+ handler = open_240
639
+ material = data[46][0]
640
+ analyst = data[48][0]
641
+ else:
642
+ raise ValueError(f'non-supported version: {version}')
643
+
644
+ # change error type, 2sigma to 1sigma...
645
+ data = change_error_type(data, header)
646
+
647
+ # get full data frames
648
+ # ['smp', 'blk', 'cor', 'deg', 'pub', 'age', 'iso', 'pam', 'inf', 'mak', 'seq',] are abbreviations for
649
+ # [sample_values, blank_values, corrected_values, degas_values, publish_values,
650
+ # apparent_age_values, isochron_values, total_param, sample_info, isochron_mark,
651
+ # sequence_name, sequence_value]
652
+ self.content = pd.concat(
653
+ handler(data.copy(), logs01, logs02), axis=1,
654
+ keys=['smp', 'blk', 'cor', 'deg', 'pub', 'age', 'iso', 'pam', 'mak', 'seq', ],
655
+ )
656
+
657
+ # sample info
658
+ self.sample_info = {
659
+ 'sample': {
660
+ 'name': self.sample_name or data[44][0],
661
+ 'material': material, 'location': 'LOCATION'
662
+ },
663
+ 'researcher': {'name': data[64][0]},
664
+ 'laboratory': {
665
+ 'name': logs01[1][44],
666
+ 'analyst': analyst,
667
+ 'info': '\n'.join([logs01[1][45], logs01[1][46], logs01[1][47]]),
668
+ }
669
+ }
670
+
671
+ return self
672
+
673
+ def get_content(self):
674
+ return self.content
675
+
676
+ def get_smp_info(self):
677
+ return self.sample_info
678
+
679
+ def get_df(self):
680
+ return self.content
681
+
682
+ def get_list(self):
683
+ return self.content