ararpy 0.1.13__tar.gz → 0.1.14__tar.gz
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.
- {ararpy-0.1.13 → ararpy-0.1.14}/PKG-INFO +10 -2
- ararpy-0.1.14/ararpy/argon_diffusion_simulator/__init__.py +12 -0
- ararpy-0.1.14/ararpy/argon_diffusion_simulator/main.py +542 -0
- {ararpy-0.1.13 → ararpy-0.1.14}/ararpy/calc/corr.py +4 -4
- {ararpy-0.1.13 → ararpy-0.1.14}/ararpy/calc/plot.py +3 -3
- {ararpy-0.1.13 → ararpy-0.1.14}/ararpy/calc/raw_funcs.py +2 -0
- {ararpy-0.1.13 → ararpy-0.1.14}/ararpy/calc/regression.py +0 -5
- {ararpy-0.1.13 → ararpy-0.1.14}/ararpy/calc/spectra.py +3 -1
- {ararpy-0.1.13 → ararpy-0.1.14}/ararpy/smp/corr.py +9 -1
- {ararpy-0.1.13 → ararpy-0.1.14}/ararpy/smp/diffusion_funcs.py +34 -12
- {ararpy-0.1.13 → ararpy-0.1.14}/ararpy/smp/export.py +620 -374
- {ararpy-0.1.13 → ararpy-0.1.14}/ararpy/smp/json.py +7 -0
- {ararpy-0.1.13 → ararpy-0.1.14}/ararpy/smp/plots.py +10 -8
- {ararpy-0.1.13 → ararpy-0.1.14}/ararpy/smp/sample.py +9 -6
- {ararpy-0.1.13 → ararpy-0.1.14}/ararpy.egg-info/PKG-INFO +10 -2
- {ararpy-0.1.13 → ararpy-0.1.14}/ararpy.egg-info/SOURCES.txt +2 -0
- {ararpy-0.1.13 → ararpy-0.1.14}/setup.py +1 -1
- {ararpy-0.1.13 → ararpy-0.1.14}/LICENSE +0 -0
- {ararpy-0.1.13 → ararpy-0.1.14}/README.md +0 -0
- {ararpy-0.1.13 → ararpy-0.1.14}/ararpy/__init__.py +0 -0
- {ararpy-0.1.13 → ararpy-0.1.14}/ararpy/calc/__init__.py +0 -0
- {ararpy-0.1.13 → ararpy-0.1.14}/ararpy/calc/age.py +0 -0
- {ararpy-0.1.13 → ararpy-0.1.14}/ararpy/calc/arr.py +0 -0
- {ararpy-0.1.13 → ararpy-0.1.14}/ararpy/calc/basic.py +0 -0
- {ararpy-0.1.13 → ararpy-0.1.14}/ararpy/calc/err.py +0 -0
- {ararpy-0.1.13 → ararpy-0.1.14}/ararpy/calc/histogram.py +0 -0
- {ararpy-0.1.13 → ararpy-0.1.14}/ararpy/calc/isochron.py +0 -0
- {ararpy-0.1.13 → ararpy-0.1.14}/ararpy/calc/jvalue.py +0 -0
- {ararpy-0.1.13 → ararpy-0.1.14}/ararpy/examples/022_VU124-M11a.ahd +0 -0
- {ararpy-0.1.13 → ararpy-0.1.14}/ararpy/examples/20WHA0103.age +0 -0
- {ararpy-0.1.13 → ararpy-0.1.14}/ararpy/examples/22WHA0078.xls +0 -0
- {ararpy-0.1.13 → ararpy-0.1.14}/ararpy/examples/22WHA0433.age +0 -0
- {ararpy-0.1.13 → ararpy-0.1.14}/ararpy/examples/22WHA0433.arr +0 -0
- {ararpy-0.1.13 → ararpy-0.1.14}/ararpy/examples/22WHA0433.full.xls +0 -0
- {ararpy-0.1.13 → ararpy-0.1.14}/ararpy/examples/24WHN0001-51-592.XLS +0 -0
- {ararpy-0.1.13 → ararpy-0.1.14}/ararpy/examples/AHD.input-filter +0 -0
- {ararpy-0.1.13 → ararpy-0.1.14}/ararpy/examples/ArAr.calc +0 -0
- {ararpy-0.1.13 → ararpy-0.1.14}/ararpy/examples/ArArCALC.age +0 -0
- {ararpy-0.1.13 → ararpy-0.1.14}/ararpy/examples/NGX-600 - Copy.TXT +0 -0
- {ararpy-0.1.13 → ararpy-0.1.14}/ararpy/examples/NGX-600.TXT +0 -0
- {ararpy-0.1.13 → ararpy-0.1.14}/ararpy/examples/NGX-XLS.input-filter +0 -0
- {ararpy-0.1.13 → ararpy-0.1.14}/ararpy/examples/Qtegra-exported-xls.input-filter +0 -0
- {ararpy-0.1.13 → ararpy-0.1.14}/ararpy/examples/S01-239.csv +0 -0
- {ararpy-0.1.13 → ararpy-0.1.14}/ararpy/examples/WH01.irra +0 -0
- {ararpy-0.1.13 → ararpy-0.1.14}/ararpy/examples/WHA.pdf +0 -0
- {ararpy-0.1.13 → ararpy-0.1.14}/ararpy/examples/raw_example.xls +0 -0
- {ararpy-0.1.13 → ararpy-0.1.14}/ararpy/examples/sample-default.smp +0 -0
- {ararpy-0.1.13 → ararpy-0.1.14}/ararpy/files/__init__.py +0 -0
- {ararpy-0.1.13 → ararpy-0.1.14}/ararpy/files/arr_file.py +0 -0
- {ararpy-0.1.13 → ararpy-0.1.14}/ararpy/files/basic.py +0 -0
- {ararpy-0.1.13 → ararpy-0.1.14}/ararpy/files/calc_file.py +0 -0
- {ararpy-0.1.13 → ararpy-0.1.14}/ararpy/files/new_file.py +0 -0
- {ararpy-0.1.13 → ararpy-0.1.14}/ararpy/files/raw_file.py +0 -0
- {ararpy-0.1.13 → ararpy-0.1.14}/ararpy/files/xls.py +0 -0
- {ararpy-0.1.13 → ararpy-0.1.14}/ararpy/smp/EXPORT_TO_PDF_DATA_PROPERTIES.py +0 -0
- {ararpy-0.1.13 → ararpy-0.1.14}/ararpy/smp/__init__.py +0 -0
- {ararpy-0.1.13 → ararpy-0.1.14}/ararpy/smp/basic.py +0 -0
- {ararpy-0.1.13 → ararpy-0.1.14}/ararpy/smp/calculation.py +0 -0
- {ararpy-0.1.13 → ararpy-0.1.14}/ararpy/smp/consts.py +0 -0
- {ararpy-0.1.13 → ararpy-0.1.14}/ararpy/smp/info.py +0 -0
- {ararpy-0.1.13 → ararpy-0.1.14}/ararpy/smp/initial.py +0 -0
- {ararpy-0.1.13 → ararpy-0.1.14}/ararpy/smp/raw.py +0 -0
- {ararpy-0.1.13 → ararpy-0.1.14}/ararpy/smp/style.py +0 -0
- {ararpy-0.1.13 → ararpy-0.1.14}/ararpy/smp/table.py +0 -0
- {ararpy-0.1.13 → ararpy-0.1.14}/ararpy/test.py +0 -0
- {ararpy-0.1.13 → ararpy-0.1.14}/ararpy.egg-info/dependency_links.txt +0 -0
- {ararpy-0.1.13 → ararpy-0.1.14}/ararpy.egg-info/top_level.txt +0 -0
- {ararpy-0.1.13 → ararpy-0.1.14}/setup.cfg +0 -0
- {ararpy-0.1.13 → ararpy-0.1.14}/tests/test.py +0 -0
- {ararpy-0.1.13 → ararpy-0.1.14}/tests/test2.py +0 -0
- {ararpy-0.1.13 → ararpy-0.1.14}/tests/test_error_correlation.py +0 -0
- {ararpy-0.1.13 → ararpy-0.1.14}/tests/test_regression_methods.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
2
|
Name: ararpy
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.14
|
|
4
4
|
Summary: A project for Ar-Ar geochronology
|
|
5
5
|
Home-page: https://github.com/wuyangchn/ararpy.git
|
|
6
6
|
Author: Yang Wu
|
|
@@ -12,6 +12,14 @@ Classifier: Operating System :: OS Independent
|
|
|
12
12
|
Requires-Python: >=3.5
|
|
13
13
|
Description-Content-Type: text/markdown
|
|
14
14
|
License-File: LICENSE
|
|
15
|
+
Dynamic: author
|
|
16
|
+
Dynamic: author-email
|
|
17
|
+
Dynamic: classifier
|
|
18
|
+
Dynamic: description
|
|
19
|
+
Dynamic: description-content-type
|
|
20
|
+
Dynamic: home-page
|
|
21
|
+
Dynamic: requires-python
|
|
22
|
+
Dynamic: summary
|
|
15
23
|
|
|
16
24
|
# ArArPy
|
|
17
25
|
|
|
@@ -0,0 +1,542 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# -*- coding: UTF-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
# ==========================================
|
|
5
|
+
# Copyright 2024 Yang
|
|
6
|
+
# ArgonDiffusionRandomWalk - main
|
|
7
|
+
# ==========================================
|
|
8
|
+
#
|
|
9
|
+
#
|
|
10
|
+
#
|
|
11
|
+
"""
|
|
12
|
+
import os
|
|
13
|
+
import pickle
|
|
14
|
+
import numpy as np
|
|
15
|
+
import math
|
|
16
|
+
import copy
|
|
17
|
+
import time
|
|
18
|
+
from typing import List
|
|
19
|
+
|
|
20
|
+
np.set_printoptions(precision=18, threshold=10000, linewidth=np.inf)
|
|
21
|
+
|
|
22
|
+
SETUP_PRINT = "natoms = {0}, nsteps = {1}, grain_size = {2}, time_scale = {3}, size_scale = {4}, number_scale = {5}"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _uniform_sphere_step(step_length, shape):
|
|
26
|
+
"""
|
|
27
|
+
在三维空间中生成均匀分布的球面样本,且步长严格为指定长度。
|
|
28
|
+
|
|
29
|
+
Parameters:
|
|
30
|
+
step_length (array, float): 步长的固定长度, (n, m)
|
|
31
|
+
num_samples (tuple): 步长shape, (n, m)
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
np.ndarray: 形状为 (num_samples, 3) 的数组,每行是一个随机步长向量
|
|
35
|
+
"""
|
|
36
|
+
# 随机生成方位角 phi 和极角的 cos(theta)
|
|
37
|
+
num_samples, dimension = shape
|
|
38
|
+
phi = np.random.uniform(0, 2 * np.pi, num_samples)
|
|
39
|
+
cos_theta = np.random.uniform(-1, 1, num_samples)
|
|
40
|
+
sin_theta = np.sqrt(1 - cos_theta ** 2) # sin(theta) = sqrt(1 - cos^2(theta))
|
|
41
|
+
|
|
42
|
+
# 将球面坐标转换为笛卡尔坐标
|
|
43
|
+
x = sin_theta * np.cos(phi)
|
|
44
|
+
y = sin_theta * np.sin(phi)
|
|
45
|
+
z = cos_theta
|
|
46
|
+
|
|
47
|
+
# 构建单位向量
|
|
48
|
+
unit_vectors = np.vstack((x, y, z)).T
|
|
49
|
+
|
|
50
|
+
# 乘以步长,得到最终的步长向量
|
|
51
|
+
steps = step_length * unit_vectors
|
|
52
|
+
return steps
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def walker(pos, step_length, total_nsteps, min_bound, max_bound, scale=0., compensation=0, remove=True,
|
|
56
|
+
conditions=None, boundary_factor=1):
|
|
57
|
+
|
|
58
|
+
if len(pos) == 0:
|
|
59
|
+
return pos
|
|
60
|
+
|
|
61
|
+
dimension = pos.shape[1] if len(pos.shape) > 1 else 1
|
|
62
|
+
|
|
63
|
+
sigma = step_length * math.sqrt(scale)
|
|
64
|
+
|
|
65
|
+
if conditions is None:
|
|
66
|
+
if dimension == 3:
|
|
67
|
+
conditions = np.array([[-50, -50, -50, 50, 50, 50, 1]])
|
|
68
|
+
else:
|
|
69
|
+
conditions = np.array([[-50, -50, 50, 50, 1]])
|
|
70
|
+
|
|
71
|
+
num_big_steps = int(total_nsteps // scale) if int(scale) > 1 else 0
|
|
72
|
+
num_small_steps = int(total_nsteps % scale) if int(scale) > 1 else int(total_nsteps)
|
|
73
|
+
|
|
74
|
+
for _ in range(num_big_steps):
|
|
75
|
+
# print(f"new walker {_ = } {len(pos) = }")
|
|
76
|
+
# 边界判断(布尔掩码)
|
|
77
|
+
in_boundary_mask = np.all(
|
|
78
|
+
(pos >= (min_bound + sigma * compensation)) & (pos <= (max_bound - sigma * compensation)), axis=1)
|
|
79
|
+
in_core = pos[in_boundary_mask]
|
|
80
|
+
in_boundary = pos[~in_boundary_mask]
|
|
81
|
+
|
|
82
|
+
# 核心粒子随机行走
|
|
83
|
+
coefficients = np.ones(len(in_core))
|
|
84
|
+
for each in conditions:
|
|
85
|
+
# each = x1, y1, z1, x2, y2, z2, coeff for dimension=3
|
|
86
|
+
cond = np.all(
|
|
87
|
+
(in_core >= np.array(each[:dimension])) & (in_core <= np.array(each[dimension:int(2*dimension)])),
|
|
88
|
+
axis=1)
|
|
89
|
+
coefficients = np.where(cond, each[-1] ** 0.5, coefficients)
|
|
90
|
+
steps = np.random.normal(0, sigma, size=in_core.shape)
|
|
91
|
+
in_core += steps * coefficients[:, None]
|
|
92
|
+
|
|
93
|
+
# 将边界粒子以更小 scale 的步长模拟
|
|
94
|
+
if scale > 1:
|
|
95
|
+
in_boundary = walker(in_boundary, step_length, scale, min_bound, max_bound, scale // 2,
|
|
96
|
+
compensation, remove, conditions, boundary_factor)
|
|
97
|
+
|
|
98
|
+
# 拼接核心和边界粒子
|
|
99
|
+
pos = np.concatenate((in_boundary, in_core))
|
|
100
|
+
|
|
101
|
+
# 移除超出范围的粒子
|
|
102
|
+
if remove:
|
|
103
|
+
pos = pos[np.all((pos >= min_bound) & (pos <= max_bound), axis=1)]
|
|
104
|
+
|
|
105
|
+
# 最后处理 scale < 1 的小步模拟, boundary_factor用来缩放这里的处理步数,1 < boundary_factor <= 1如果 boundary_factor=1,将完全吻合实际
|
|
106
|
+
# boundary_factor < 1 将加快一些速度, boundary_factor=0 将忽略小步数
|
|
107
|
+
k = boundary_factor * max(conditions[:, -1])
|
|
108
|
+
for _ in range(int(num_small_steps * k)):
|
|
109
|
+
coefficients = np.ones(len(pos))
|
|
110
|
+
for each in conditions:
|
|
111
|
+
cond = np.all((pos >= np.array(each[:dimension])) & (pos <= np.array(each[dimension:int(2*dimension)])),
|
|
112
|
+
axis=1)
|
|
113
|
+
coefficients = np.where(cond, (each[-1] / k) ** 0.5, coefficients)
|
|
114
|
+
# coefficients = np.where(cond, coeff ** 0.5, coefficients)
|
|
115
|
+
steps = np.random.normal(0, step_length, size=np.shape(pos))
|
|
116
|
+
pos += steps * coefficients[:, None]
|
|
117
|
+
|
|
118
|
+
if remove:
|
|
119
|
+
pos = pos[np.all((pos >= min_bound) & (pos <= max_bound), axis=1)]
|
|
120
|
+
|
|
121
|
+
return pos
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def walker2(pos: np.ndarray, duration, step_length, min_bound, max_bound, time_scale, frequency,
|
|
125
|
+
conditions=None, remove: bool = True):
|
|
126
|
+
"""
|
|
127
|
+
:return:
|
|
128
|
+
"""
|
|
129
|
+
|
|
130
|
+
if len(pos) == 0:
|
|
131
|
+
return pos
|
|
132
|
+
|
|
133
|
+
dimension = pos.shape[-1] if len(pos.shape) > 1 else 1
|
|
134
|
+
|
|
135
|
+
dt = time_scale
|
|
136
|
+
if conditions is None:
|
|
137
|
+
if dimension == 3:
|
|
138
|
+
conditions = np.array([[-50, -50, -50, 50, 50, 50, 1]])
|
|
139
|
+
else:
|
|
140
|
+
conditions = np.array([[-50, -50, 50, 50, 1]])
|
|
141
|
+
|
|
142
|
+
for step in range(int(1e16)):
|
|
143
|
+
|
|
144
|
+
res = np.zeros(0).reshape(0, dimension)
|
|
145
|
+
for index, each in enumerate(conditions):
|
|
146
|
+
# each = (x1, y1, z1, x2, y2, z2, gamma)
|
|
147
|
+
d = step_length * math.sqrt(each[-1] * frequency * time_scale)
|
|
148
|
+
|
|
149
|
+
locs = np.ones(len(pos)) * 999
|
|
150
|
+
for _index, _each in enumerate(conditions):
|
|
151
|
+
cond = np.all((pos >= np.array(_each[:dimension])) & (pos <= np.array(_each[dimension:int(2*dimension)])), axis=1)
|
|
152
|
+
locs = np.where(cond, _index, locs)
|
|
153
|
+
|
|
154
|
+
partial_pos = pos[locs == index]
|
|
155
|
+
|
|
156
|
+
# c = len(partial_pos)
|
|
157
|
+
# # 初始化步长
|
|
158
|
+
# steps = np.zeros(partial_pos.shape, dtype=float)
|
|
159
|
+
# # 移动方向, x or y or z
|
|
160
|
+
# directions = np.random.choice(list(range(dimension)), size=c)
|
|
161
|
+
# # 方向, positive or negative, 步长
|
|
162
|
+
# steps[np.arange(c), directions] = np.random.choice([-d, d], size=c, p=[0.5, 0.5])
|
|
163
|
+
steps = np.random.normal(0, d, size=partial_pos.shape)
|
|
164
|
+
|
|
165
|
+
partial_pos += steps
|
|
166
|
+
|
|
167
|
+
if remove:
|
|
168
|
+
partial_pos = partial_pos[np.all((partial_pos >= min_bound) & (partial_pos <= max_bound), axis=1)]
|
|
169
|
+
|
|
170
|
+
res = np.concatenate((res, partial_pos))
|
|
171
|
+
|
|
172
|
+
pos = copy.deepcopy(res)
|
|
173
|
+
|
|
174
|
+
_t = (step + 1) * dt
|
|
175
|
+
|
|
176
|
+
if abs(_t - duration) < dt:
|
|
177
|
+
break
|
|
178
|
+
|
|
179
|
+
return pos
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
class OverEpsilonError(Exception):
|
|
183
|
+
def __init__(self, i, d, e):
|
|
184
|
+
self.i = i
|
|
185
|
+
self.d = d
|
|
186
|
+
self.e = e
|
|
187
|
+
|
|
188
|
+
def __str__(self):
|
|
189
|
+
return f"OverEpsilonError: Index = {self.i}, d = {self.d} > e = {self.e}"
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
class Domain:
|
|
193
|
+
def __init__(self, dimension: int = 3, atom_density: float = 1e14, fraction: float = 1.0,
|
|
194
|
+
min_bound=None, max_bound=None, **kwargs):
|
|
195
|
+
|
|
196
|
+
self.dimension = dimension
|
|
197
|
+
self.size = ...
|
|
198
|
+
self.atom_density = atom_density
|
|
199
|
+
self.fraction = fraction
|
|
200
|
+
self.time_scale = 1
|
|
201
|
+
self.size_scale = 0.05
|
|
202
|
+
|
|
203
|
+
self.energy = 120 * 1000 # J/mol
|
|
204
|
+
self.density = 0
|
|
205
|
+
self.fracture = 0
|
|
206
|
+
self.boundary = 0
|
|
207
|
+
self.defect = 0
|
|
208
|
+
self.pho = 1
|
|
209
|
+
self.step_length = 0.0001 # 1 A = 0.1 nm = 0.0001 μm
|
|
210
|
+
|
|
211
|
+
self.current_temp = 273.15
|
|
212
|
+
|
|
213
|
+
self.inclusions: List[Domain] = []
|
|
214
|
+
self.natoms: int = 0
|
|
215
|
+
self.positions = []
|
|
216
|
+
self.min_bound = min_bound
|
|
217
|
+
self.max_bound = max_bound
|
|
218
|
+
self.remained_per_step = []
|
|
219
|
+
self.released_per_step = []
|
|
220
|
+
|
|
221
|
+
self.attr = {}
|
|
222
|
+
|
|
223
|
+
for key, val in kwargs.items():
|
|
224
|
+
if hasattr(self, key):
|
|
225
|
+
setattr(self, key, val)
|
|
226
|
+
|
|
227
|
+
self.setup()
|
|
228
|
+
|
|
229
|
+
def setup(self):
|
|
230
|
+
"""
|
|
231
|
+
Used to set or reset positions of atoms
|
|
232
|
+
:return:
|
|
233
|
+
"""
|
|
234
|
+
|
|
235
|
+
# 初始化粒子位置,随机分布在网格内
|
|
236
|
+
# 初始原子个数 1e14 原子密度 个/立方厘米 1e-4是单位换算 cm/µm
|
|
237
|
+
natoms = int(self.atom_density ** (self.dimension / 3) * (abs(max(self.max_bound) - min(self.min_bound)) * 1e-4) ** self.dimension)
|
|
238
|
+
self.positions = np.random.uniform(max(self.max_bound), min(self.min_bound), size=(int(natoms), self.dimension))
|
|
239
|
+
self.positions = self.positions[np.all((self.positions >= self.min_bound) & (self.positions <= self.max_bound), axis=1)]
|
|
240
|
+
for dom in self.inclusions:
|
|
241
|
+
self.positions = self.positions[~np.all((self.positions >= dom.min_bound) & (self.positions <= dom.max_bound), axis=1)]
|
|
242
|
+
self.natoms = len(self.positions)
|
|
243
|
+
|
|
244
|
+
self.remained_per_step = []
|
|
245
|
+
self.released_per_step = []
|
|
246
|
+
|
|
247
|
+
def get_gamma(self, energy: float = None, temperature: float = None):
|
|
248
|
+
r = 8.31446261815324 # J / (K * mol)
|
|
249
|
+
T = self.current_temp if temperature is None else temperature
|
|
250
|
+
e = self.energy if energy is None else energy
|
|
251
|
+
alpha = 1 - self.density
|
|
252
|
+
p1 = math.exp(- e / (r * T))
|
|
253
|
+
p2 = math.exp(- e / 2 / (r * T)) # p2是给定活化能的一半,用于假定晶面扩散的活化能
|
|
254
|
+
gamma = p1 * alpha * (1 - self.fracture) + self.fracture * p2 + self.boundary + self.defect
|
|
255
|
+
return gamma
|
|
256
|
+
|
|
257
|
+
def get_natoms(self, pos=None):
|
|
258
|
+
if pos is None:
|
|
259
|
+
return len(self.positions)
|
|
260
|
+
pos = pos[np.all((pos >= self.min_bound) & (pos <= self.max_bound), axis=1)]
|
|
261
|
+
for dom in self.inclusions:
|
|
262
|
+
pos = pos[~np.all((pos >= dom.min_bound) & (pos <= dom.max_bound), axis=1)]
|
|
263
|
+
return len(pos)
|
|
264
|
+
|
|
265
|
+
def set_attr(self, name, value, index=None):
|
|
266
|
+
if index is None:
|
|
267
|
+
self.attr.update({name: value})
|
|
268
|
+
else:
|
|
269
|
+
if name not in self.attr.keys():
|
|
270
|
+
self.attr.update({name: {}})
|
|
271
|
+
self.attr[name].update({index: value})
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
class DiffSimulation:
|
|
275
|
+
def __init__(self):
|
|
276
|
+
self.name = "example"
|
|
277
|
+
self.loc = "examples"
|
|
278
|
+
self.score = 0
|
|
279
|
+
self.end_at = 0
|
|
280
|
+
self.dimension = 3
|
|
281
|
+
|
|
282
|
+
self.size_scale = 1
|
|
283
|
+
self.length_scale = 1
|
|
284
|
+
|
|
285
|
+
self.atom_density = 1e14 # 原子密度 个/立方厘米
|
|
286
|
+
self.frequency = 1e13 # 每秒振动的次数, Debye frequency
|
|
287
|
+
|
|
288
|
+
self.grain_size = 300 # 0.3 mm = 300 μm = 300000 nm
|
|
289
|
+
self.grid_size = self.grain_size * self.size_scale # 网格大小
|
|
290
|
+
self.nsteps: int = ...
|
|
291
|
+
self.natoms: int = ...
|
|
292
|
+
self.epsilon = 1e-5
|
|
293
|
+
self.step_length = 0.0001 * 2.5 # 1 A = 0.1 nm = 0.0001 μm
|
|
294
|
+
|
|
295
|
+
self.seq: [List, np.ndarray] = []
|
|
296
|
+
self.domains: List[Domain] = []
|
|
297
|
+
self.positions: [List, np.ndarray] = []
|
|
298
|
+
|
|
299
|
+
self.remained_per_step = []
|
|
300
|
+
self.released_per_step = []
|
|
301
|
+
|
|
302
|
+
def setup(self):
|
|
303
|
+
"""
|
|
304
|
+
Used to set or reset positions of atoms
|
|
305
|
+
:return:
|
|
306
|
+
"""
|
|
307
|
+
# 网格大小和颗粒边界
|
|
308
|
+
self.grid_size = self.grain_size * self.size_scale # 网格大小
|
|
309
|
+
center = np.array([0 for i in range(self.dimension)])
|
|
310
|
+
self.min_bound = center - self.grid_size / 2
|
|
311
|
+
self.max_bound = center + self.grid_size / 2
|
|
312
|
+
|
|
313
|
+
# 粒子位置
|
|
314
|
+
self.positions = np.concatenate([dom.positions for dom in self.domains])
|
|
315
|
+
# 粒子数目
|
|
316
|
+
self.natoms = len(self.positions)
|
|
317
|
+
|
|
318
|
+
self.remained_per_step = []
|
|
319
|
+
self.released_per_step = []
|
|
320
|
+
|
|
321
|
+
def run_sequence(self, times, temperatures, statuses, targets, domains: List[Domain], nsteps_factor=None,
|
|
322
|
+
simulating: bool = False, epsilon=0.001, start_time=0, k=1.2, use_walker1=True,
|
|
323
|
+
scoring: bool = False):
|
|
324
|
+
pos = copy.deepcopy(self.positions)
|
|
325
|
+
self.seq = [] # 用于记录参数
|
|
326
|
+
|
|
327
|
+
for index, (duration, temperature, target, status) in enumerate(zip(times, temperatures, targets, statuses)):
|
|
328
|
+
|
|
329
|
+
self.seq.append({})
|
|
330
|
+
|
|
331
|
+
loop = 0
|
|
332
|
+
e_low = 100 * 1000
|
|
333
|
+
e_up = 300 * 1000
|
|
334
|
+
|
|
335
|
+
_start = time.time()
|
|
336
|
+
heating_duration = duration - start_time # in seconds
|
|
337
|
+
start_time = duration
|
|
338
|
+
temperature = temperature + 273.15 # in Kelvin
|
|
339
|
+
|
|
340
|
+
while loop < 50:
|
|
341
|
+
|
|
342
|
+
e = (e_low + e_up) / 2
|
|
343
|
+
|
|
344
|
+
total_steps = self.frequency * heating_duration # 振动次数
|
|
345
|
+
conditions = np.array(
|
|
346
|
+
[[*dom.min_bound, *dom.max_bound,
|
|
347
|
+
dom.get_gamma(temperature=temperature, energy=e if simulating else None)] for dom in domains if dom.energy != 0 and not simulating])
|
|
348
|
+
|
|
349
|
+
if use_walker1:
|
|
350
|
+
|
|
351
|
+
# print(f"调整前: {nsteps_factor = }, gamma = {conditions[0][-1]}, {total_steps = }")
|
|
352
|
+
nsteps_factor = 10 ** math.floor(
|
|
353
|
+
-math.log10(max(conditions[:, -1]))) * 1000 # 用来调节次数和步长的巨大差异,次数太大,但步程太小
|
|
354
|
+
while int(total_steps / nsteps_factor) < 1:
|
|
355
|
+
nsteps_factor /= 10
|
|
356
|
+
while int(total_steps / nsteps_factor) > 10000:
|
|
357
|
+
nsteps_factor *= 10
|
|
358
|
+
total_steps = int(total_steps / nsteps_factor) # 振动次数
|
|
359
|
+
conditions[:, -1] *= nsteps_factor
|
|
360
|
+
compensation = max(3, *(np.ceil(np.sqrt(conditions[:, -1])) * 3))
|
|
361
|
+
boundary_factor = 1
|
|
362
|
+
if max(conditions[:, -1]) > 1000:
|
|
363
|
+
# k = 0时,bf = 1, 完全无缩放 # k = 1时,bf <= 0.5 小步行走将至少缩放一半 # 推荐值 k = 1.2
|
|
364
|
+
boundary_factor = 0.1 ** (k * math.log10(1 + (max(conditions[:, -1]) // 1000)))
|
|
365
|
+
step_length = self.step_length / np.sqrt(pos.shape[1] if len(pos.shape) > 1 else 1)
|
|
366
|
+
scale = int(total_steps)
|
|
367
|
+
# print(f"调整后: {nsteps_factor = }, gamma = {conditions[0][-1]}, {total_steps = }, {compensation = }, {boundary_factor = }")
|
|
368
|
+
|
|
369
|
+
_pos = walker(
|
|
370
|
+
copy.deepcopy(pos), step_length=step_length, total_nsteps=total_steps,
|
|
371
|
+
min_bound=self.min_bound, max_bound=self.max_bound, scale=scale,
|
|
372
|
+
compensation=compensation, remove=True, conditions=conditions, boundary_factor=boundary_factor
|
|
373
|
+
)
|
|
374
|
+
|
|
375
|
+
# self.seq[index].update({
|
|
376
|
+
# "duration": duration, "temperature": temperature, "target": target, "step_length": step_length,
|
|
377
|
+
# "scale": scale, "compensation": compensation, "boundary_factor": boundary_factor, "e": e,
|
|
378
|
+
# "simulation": simulating, "nsteps_factor": nsteps_factor, "conditions": conditions
|
|
379
|
+
# })
|
|
380
|
+
|
|
381
|
+
else:
|
|
382
|
+
step_length = self.step_length / np.sqrt(pos.shape[1] if len(pos.shape) > 1 else 1)
|
|
383
|
+
scale = k
|
|
384
|
+
_pos = walker2(copy.deepcopy(pos), duration=heating_duration, step_length=step_length,
|
|
385
|
+
min_bound=self.min_bound, max_bound=self.max_bound, time_scale=scale,
|
|
386
|
+
frequency=self.frequency, conditions=conditions, remove=True)
|
|
387
|
+
boundary_factor = 1
|
|
388
|
+
compensation = 0
|
|
389
|
+
|
|
390
|
+
self.seq[index].update({
|
|
391
|
+
"duration": duration, "temperature": temperature, "target": target, "step_length": step_length,
|
|
392
|
+
"scale": scale, "compensation": compensation, "boundary_factor": boundary_factor, "e": e,
|
|
393
|
+
"simulation": simulating, "nsteps_factor": nsteps_factor, "conditions": conditions
|
|
394
|
+
})
|
|
395
|
+
|
|
396
|
+
released = 1 - len(_pos) / self.natoms
|
|
397
|
+
d = released - target
|
|
398
|
+
if simulating:
|
|
399
|
+
loop += 1
|
|
400
|
+
print(f"{e = }, {d = }, {abs(d) <= epsilon}")
|
|
401
|
+
if abs(d) <= epsilon:
|
|
402
|
+
break
|
|
403
|
+
elif d > 0:
|
|
404
|
+
e_low = e
|
|
405
|
+
else:
|
|
406
|
+
e_up = e
|
|
407
|
+
elif scoring:
|
|
408
|
+
self.score += (d * 100) ** 2
|
|
409
|
+
print(f"{self.score = }, {d = }")
|
|
410
|
+
if self.score > 4 * (index + 1):
|
|
411
|
+
raise OverEpsilonError(index, self.score, 4 * (index + 1))
|
|
412
|
+
else:
|
|
413
|
+
break
|
|
414
|
+
else:
|
|
415
|
+
break
|
|
416
|
+
|
|
417
|
+
pos = _pos
|
|
418
|
+
|
|
419
|
+
if status:
|
|
420
|
+
# status == True: argon collection, False: pumping
|
|
421
|
+
for dom in self.domains:
|
|
422
|
+
_c = dom.get_natoms(pos)
|
|
423
|
+
dom.remained_per_step.append(_c)
|
|
424
|
+
dom.released_per_step.append(dom.natoms - _c)
|
|
425
|
+
dom.set_attr("energy", float(e), index=index)
|
|
426
|
+
|
|
427
|
+
self.remained_per_step.append(len(pos))
|
|
428
|
+
self.released_per_step.append(self.natoms - len(pos))
|
|
429
|
+
|
|
430
|
+
print(f"{index = } {duration} - {heating_duration = } - {temperature = } - {total_steps = } - conc = {len(pos) / self.natoms * 100:.2f}% - {time.time() - _start:.5f}s")
|
|
431
|
+
|
|
432
|
+
def run_persecond(self, times, temperatures, statuses, targets, domains: List[Domain], scale=None,
|
|
433
|
+
simulation: bool = False, epsilon=0.001, start_time=0):
|
|
434
|
+
seq = []
|
|
435
|
+
for i in range(1, int(times[-1] + 1), 300):
|
|
436
|
+
for index, time in enumerate(times):
|
|
437
|
+
if (times[index-1] if index > 0 else 0) < i <= times[index]:
|
|
438
|
+
seq.append([i, temperatures[index], statuses[index], targets[index]])
|
|
439
|
+
seq = np.transpose(seq)
|
|
440
|
+
return self.run_sequence(*seq, domains=domains, nsteps_factor=scale, simulating=simulation,
|
|
441
|
+
epsilon=epsilon, start_time=start_time)
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
def run(times, temps, statuses, energies, fractions, ndoms: int = 1, grain_szie=275, atom_density=1e10, frequency=1e13,
|
|
445
|
+
dimension: int = 3, targets: list = None, epsilon: float = 0.001, simulation: bool = False,
|
|
446
|
+
file_name: str = "Y70", ignore_error: bool = False, **kwargs):
|
|
447
|
+
"""
|
|
448
|
+
:param times:
|
|
449
|
+
:param temps:
|
|
450
|
+
:param statuses:
|
|
451
|
+
:param energies:
|
|
452
|
+
:param fractions:
|
|
453
|
+
:param dimension:
|
|
454
|
+
:param ndoms:
|
|
455
|
+
:param targets:
|
|
456
|
+
:param epsilon:
|
|
457
|
+
:param simulation:
|
|
458
|
+
:param file_name:
|
|
459
|
+
:param ignore_error:
|
|
460
|
+
:return:
|
|
461
|
+
"""
|
|
462
|
+
|
|
463
|
+
demo = DiffSimulation()
|
|
464
|
+
|
|
465
|
+
def _(n, es, fs):
|
|
466
|
+
# fs 应从大到小,父空间在前,子空间在后
|
|
467
|
+
|
|
468
|
+
# demo.grain_size = 300
|
|
469
|
+
# demo.size_scale = 0.05
|
|
470
|
+
# demo.atom_density = 1e14 # 原子密度 个/立方厘米
|
|
471
|
+
demo.dimension = dimension
|
|
472
|
+
|
|
473
|
+
demo.size_scale = 1
|
|
474
|
+
demo.grain_size = grain_szie
|
|
475
|
+
demo.atom_density = atom_density # 原子密度 个/立方厘米
|
|
476
|
+
demo.frequency = frequency
|
|
477
|
+
|
|
478
|
+
# domains应该从外到内
|
|
479
|
+
domains = []
|
|
480
|
+
for i in range(n-1, 0-1, -1):
|
|
481
|
+
size = int(demo.grain_size * fs[i]) * demo.size_scale
|
|
482
|
+
center = np.zeros(demo.dimension)
|
|
483
|
+
dom = Domain(
|
|
484
|
+
dimension=demo.dimension, atom_density=demo.atom_density, min_bound=center - size / 2, max_bound=center + size / 2,
|
|
485
|
+
energy=es[i], fraction=fs[i], inclusions=[domains[-1]] if len(domains) >= 1 else []
|
|
486
|
+
)
|
|
487
|
+
domains.append(dom)
|
|
488
|
+
# domains应该从外到内, 上面为了inclusion以及方便不同扩散域设置不同的密度,要按照从小到大的顺序生成,但是后面行走的时候要根据不同条件设置系数,要从外到内
|
|
489
|
+
demo.domains = sorted(domains, key=lambda dom: dom.fraction, reverse=True)
|
|
490
|
+
|
|
491
|
+
demo.setup()
|
|
492
|
+
|
|
493
|
+
demo.name = f"{file_name}"
|
|
494
|
+
|
|
495
|
+
print(f"Total Atoms: {demo.natoms}, atoms in each dom: {[dom.natoms for dom in demo.domains]} filename: {demo.name}")
|
|
496
|
+
|
|
497
|
+
demo.run_sequence(times=times, temperatures=temps, statuses=statuses, targets=targets, domains=demo.domains,
|
|
498
|
+
epsilon=epsilon, simulating=simulation, **kwargs)
|
|
499
|
+
# demo.run_persecond(times=times, temperatures=temps, domains=demo.domains, targets=target,
|
|
500
|
+
# epsilon=epsilon, simulation=simulation)
|
|
501
|
+
|
|
502
|
+
return demo
|
|
503
|
+
|
|
504
|
+
try:
|
|
505
|
+
return _(ndoms, energies, fractions), True
|
|
506
|
+
except OverEpsilonError as e:
|
|
507
|
+
if ignore_error:
|
|
508
|
+
return demo, False
|
|
509
|
+
else:
|
|
510
|
+
raise
|
|
511
|
+
|
|
512
|
+
|
|
513
|
+
def save_ads(ads: DiffSimulation, dir_path: str = None, name: str = None):
|
|
514
|
+
if dir_path is None:
|
|
515
|
+
dir_path = ""
|
|
516
|
+
if name is None:
|
|
517
|
+
name = ads.name
|
|
518
|
+
if not name.endswith(".ads"):
|
|
519
|
+
name = f"{name}.ads"
|
|
520
|
+
dir_path = os.path.join(dir_path, name)
|
|
521
|
+
with open(dir_path, 'wb') as f:
|
|
522
|
+
f.write(pickle.dumps(ads))
|
|
523
|
+
return name
|
|
524
|
+
|
|
525
|
+
|
|
526
|
+
def read_ads(file_path: str) -> DiffSimulation:
|
|
527
|
+
|
|
528
|
+
class RenameUnpickler(pickle.Unpickler):
|
|
529
|
+
def find_class(self, module: str, name: str):
|
|
530
|
+
if "argon_diffusion_simulator" in module:
|
|
531
|
+
return super(RenameUnpickler, self).find_class(DiffSimulation().__module__, name)
|
|
532
|
+
else:
|
|
533
|
+
return super(RenameUnpickler, self).find_class(module, name)
|
|
534
|
+
|
|
535
|
+
def renamed_load(file_obj) -> DiffSimulation:
|
|
536
|
+
return RenameUnpickler(file_obj).load()
|
|
537
|
+
|
|
538
|
+
try:
|
|
539
|
+
with open(file_path, 'rb') as f:
|
|
540
|
+
return renamed_load(f)
|
|
541
|
+
except (Exception, BaseException):
|
|
542
|
+
raise ValueError(f"ModuleNotFoundError")
|
|
@@ -75,7 +75,7 @@ def mdf(rm: float, srm: float, m1: float, m2: float, ra: float = 298.56,
|
|
|
75
75
|
k3, k4 = "Null", "Null"
|
|
76
76
|
# pow
|
|
77
77
|
try:
|
|
78
|
-
k5 = pow((ra / rm), (1 / delta_m)) # A.A.P.Koppers
|
|
78
|
+
k5 = pow((ra / rm), (1 / delta_m)) # A.A.P.Koppers, also Renne2009, B.D. Turrin2010
|
|
79
79
|
k6 = err.pow((ra / rm, err.div((ra, sra), (rm, srm))),
|
|
80
80
|
(1 / delta_m, err.div((1, 0), (delta_m, sdelta_m))))
|
|
81
81
|
except Exception:
|
|
@@ -107,14 +107,14 @@ def discr(a0: list, e0: list, mdf: list, smdf: list, m: list, m40: list,
|
|
|
107
107
|
for i in range(min([len(arg) for arg in [a0, e0, mdf, smdf]])):
|
|
108
108
|
delta_mass = abs(m40[i] - m[i])
|
|
109
109
|
ratio_mass = abs(m40[i] / m[i]) if m[i] != 0 else 1
|
|
110
|
-
if method.lower()
|
|
110
|
+
if method.lower().startswith("l"):
|
|
111
111
|
k0 = 1 / (delta_mass * mdf[i] - delta_mass + 1) if (delta_mass * mdf[i] - delta_mass + 1) != 0 else 0
|
|
112
112
|
k1 = err.div((1, 0), (delta_mass * mdf[i] - delta_mass + 1, smdf[i] * delta_mass))
|
|
113
|
-
elif method.lower()
|
|
113
|
+
elif method.lower().startswith("e"):
|
|
114
114
|
k0 = 1 / (ratio_mass ** (mdf[i] * m40[i] - m[i]))
|
|
115
115
|
k1 = err.div((1, 0), (ratio_mass ** (mdf[i] * m40[i] - m[i]), err.pow((ratio_mass, 0), (
|
|
116
116
|
mdf[i] * m40[i] - m[i], err.mul((mdf[i], smdf[i]), (m40[i], 0))))))
|
|
117
|
-
elif method.lower()
|
|
117
|
+
elif method.lower().startswith("p"):
|
|
118
118
|
k0 = 1 / (mdf[i] ** delta_mass)
|
|
119
119
|
k1 = err.div((1, 0), (mdf[i] ** delta_mass, err.pow((mdf[i], smdf[i]), (delta_mass, 0))))
|
|
120
120
|
else:
|
|
@@ -13,9 +13,9 @@ import traceback
|
|
|
13
13
|
|
|
14
14
|
import decimal
|
|
15
15
|
from math import exp, log, cos, acos, ceil, sqrt, atan, sin, gamma
|
|
16
|
-
from typing import List, Any
|
|
17
|
-
from scipy.optimize import fsolve
|
|
18
|
-
from scipy.stats import distributions
|
|
16
|
+
# from typing import List, Any
|
|
17
|
+
# from scipy.optimize import fsolve
|
|
18
|
+
# from scipy.stats import distributions
|
|
19
19
|
import numpy as np
|
|
20
20
|
|
|
21
21
|
math_e = 2.718281828459045
|
|
@@ -46,6 +46,8 @@ def get_raw_data_regression_results(points_data, unselected: list = None):
|
|
|
46
46
|
# line_data = transpose([lines_x, res[7](lines_x)])
|
|
47
47
|
line_results = res[0:4]
|
|
48
48
|
reg_coeffs = res[5]
|
|
49
|
+
if any(np.isnan(line_results)):
|
|
50
|
+
raise ValueError
|
|
49
51
|
# if np.isin(np.inf, line_data) or np.isin(np.nan, line_data):
|
|
50
52
|
# raise ZeroDivisionError(f"Infinite value or nan value.")
|
|
51
53
|
if abs(res[0] - min(y)) > 5 * (max(y) - min(y)):
|
|
@@ -966,11 +966,6 @@ def exponential(a0: list, a1: list):
|
|
|
966
966
|
se_intercept = sec / errfz * errfx
|
|
967
967
|
rse_intercept = se_intercept / intercept * 100
|
|
968
968
|
|
|
969
|
-
if abs(intercept) > 10 * max(a0):
|
|
970
|
-
raise ValueError
|
|
971
|
-
if intercept < 0:
|
|
972
|
-
raise ValueError
|
|
973
|
-
|
|
974
969
|
return intercept, se_intercept, rse_intercept, r2, 'mswd', [a, b, c], 'se', \
|
|
975
970
|
lambda x: [_exp_func(i, a, b, c) for i in x], m_ssresid
|
|
976
971
|
|
|
@@ -13,7 +13,7 @@ import numpy as np
|
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
def get_data(y: list, sy: list, x: list, f: int = 1, indices: list = None,
|
|
16
|
-
cumulative: bool = False, successive: bool = True):
|
|
16
|
+
cumulative: bool = False, successive: bool = True, sigma: int = 1):
|
|
17
17
|
"""
|
|
18
18
|
Get spectra data based on passed x, y, and sy.
|
|
19
19
|
|
|
@@ -28,6 +28,7 @@ def get_data(y: list, sy: list, x: list, f: int = 1, indices: list = None,
|
|
|
28
28
|
cumulative : bool, default False.
|
|
29
29
|
This parameter should be True if x is already cumulative.
|
|
30
30
|
successive : If setting indices successive
|
|
31
|
+
sigma:
|
|
31
32
|
|
|
32
33
|
Returns
|
|
33
34
|
-------
|
|
@@ -35,6 +36,7 @@ def get_data(y: list, sy: list, x: list, f: int = 1, indices: list = None,
|
|
|
35
36
|
"""
|
|
36
37
|
if np.issubdtype(type(f), np.integer) and f > 1:
|
|
37
38
|
sy = np.divide(sy, f)
|
|
39
|
+
sy = np.array(sy) * sigma
|
|
38
40
|
dp = np.shape([y, sy, x])[-1]
|
|
39
41
|
if indices is None:
|
|
40
42
|
indices = list(range(dp))
|
|
@@ -49,8 +49,16 @@ def corr_blank(sample: Sample):
|
|
|
49
49
|
blank_corrected = np.zeros([10, len(sample.SequenceName)])
|
|
50
50
|
try:
|
|
51
51
|
for i in range(5):
|
|
52
|
+
b, sb = copy.deepcopy(sample.BlankIntercept[i * 2: i * 2 + 2])
|
|
53
|
+
f, sf = np.array(sample.TotalParam[126 + i * 2:128 + i * 2])
|
|
54
|
+
sf = f * sf / 100
|
|
55
|
+
_ = calc.corr.gain(*sample.BlankIntercept[i * 2:2 + i * 2], f, sf)
|
|
56
|
+
for index in range(len(sample.BlankIntercept[i * 2])):
|
|
57
|
+
if sample.TotalParam[111][index]: # use same parameters to correct blank intercepts
|
|
58
|
+
b[index] = _[0][index]
|
|
59
|
+
sb[index] = _[1][index]
|
|
52
60
|
blank_corrected[i * 2:2 + i * 2] = calc.corr.blank(
|
|
53
|
-
*sample.CorrectedValues[i * 2:2 + i * 2],
|
|
61
|
+
*sample.CorrectedValues[i * 2:2 + i * 2], b, sb)
|
|
54
62
|
except Exception as e:
|
|
55
63
|
print(traceback.format_exc())
|
|
56
64
|
raise ValueError('Blank correction error')
|