kim-tools 0.2.0b0__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.
- kim_tools/__init__.py +14 -0
- kim_tools/aflow_util/__init__.py +4 -0
- kim_tools/aflow_util/core.py +1782 -0
- kim_tools/aflow_util/data/README_PROTO.TXT +3241 -0
- kim_tools/ase/__init__.py +4 -0
- kim_tools/ase/core.py +749 -0
- kim_tools/kimunits.py +162 -0
- kim_tools/symmetry_util/__init__.py +4 -0
- kim_tools/symmetry_util/core.py +552 -0
- kim_tools/symmetry_util/data/possible_primitive_shifts.json +1 -0
- kim_tools/symmetry_util/data/primitive_GENPOS_ops.json +1 -0
- kim_tools/symmetry_util/data/space_groups_for_each_bravais_lattice.json +179 -0
- kim_tools/symmetry_util/data/wyck_pos_xform_under_normalizer.json +1344 -0
- kim_tools/symmetry_util/data/wyckoff_multiplicities.json +2193 -0
- kim_tools/symmetry_util/data/wyckoff_sets.json +232 -0
- kim_tools/test_driver/__init__.py +4 -0
- kim_tools/test_driver/core.py +1932 -0
- kim_tools/vc/__init__.py +4 -0
- kim_tools/vc/core.py +397 -0
- kim_tools-0.2.0b0.dist-info/METADATA +32 -0
- kim_tools-0.2.0b0.dist-info/RECORD +24 -0
- kim_tools-0.2.0b0.dist-info/WHEEL +5 -0
- kim_tools-0.2.0b0.dist-info/licenses/LICENSE.CDDL +380 -0
- kim_tools-0.2.0b0.dist-info/top_level.txt +1 -0
kim_tools/kimunits.py
ADDED
@@ -0,0 +1,162 @@
|
|
1
|
+
"""
|
2
|
+
Simple wrapper for executable for converting arbitrary units to SI units
|
3
|
+
|
4
|
+
Copyright (c) 2014-2022, Regents of the University of Minnesota. All rights
|
5
|
+
reserved.
|
6
|
+
|
7
|
+
This software may be distributed as-is, without modification.
|
8
|
+
"""
|
9
|
+
|
10
|
+
import math
|
11
|
+
import re
|
12
|
+
import subprocess
|
13
|
+
import warnings
|
14
|
+
|
15
|
+
warnings.simplefilter("ignore")
|
16
|
+
|
17
|
+
|
18
|
+
class UnitConversion(Exception):
|
19
|
+
"""Class for unit conversion errors"""
|
20
|
+
|
21
|
+
|
22
|
+
_units_output_expression = re.compile(
|
23
|
+
r"(?P<value>(?:[-+]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][-+]?\d+)?))(?: (?P<unit>.+))?"
|
24
|
+
)
|
25
|
+
|
26
|
+
|
27
|
+
def linear_fit(x, y):
|
28
|
+
"""
|
29
|
+
Perform a linear fit between x,y, returning the average error for each data
|
30
|
+
point as well. This is written this way so as to not add a numpy dependency
|
31
|
+
"""
|
32
|
+
n = len(x)
|
33
|
+
xx = sum([x**2 for x in x]) - sum(x) ** 2 / n
|
34
|
+
xy = sum(map(lambda x, y: x * y, x, y)) - sum(x) * sum(y) / n
|
35
|
+
a, b = sum(y) / n - xy / xx * sum(x) / n, xy / xx
|
36
|
+
yhat = [a + b * x for x in x]
|
37
|
+
yerr = math.sqrt(sum(map(lambda y, yh: (y - yh) ** 2 / y**2, y, yhat)) / n)
|
38
|
+
return a, b, yerr
|
39
|
+
|
40
|
+
|
41
|
+
def islinear(unit, to_unit=None):
|
42
|
+
"""
|
43
|
+
Detect if the conversion from `unit` to `to_unit` is a linear map. Apparently
|
44
|
+
the units utility is float precision, so if error is less than 1e-7 we know
|
45
|
+
it is linear.
|
46
|
+
"""
|
47
|
+
x = [100 ** (1e-2 * (i - 50)) for i in range(20)]
|
48
|
+
y = convert_list(x, unit, to_unit=to_unit, dofit=False)[0]
|
49
|
+
a, b, err = linear_fit(x, y)
|
50
|
+
|
51
|
+
a = convert_list(0, unit, to_unit=to_unit, dofit=False)[0]
|
52
|
+
b = convert_list(1, unit, to_unit=to_unit, dofit=False)[0] - a
|
53
|
+
return a, b, err < 1e-7
|
54
|
+
|
55
|
+
|
56
|
+
def convert_units(from_value, from_unit, wanted_unit=None, suppress_unit=False):
|
57
|
+
"""Works with 'units' utility"""
|
58
|
+
from_sign = from_value < 0
|
59
|
+
from_value = str(abs(from_value))
|
60
|
+
from_unit = str(from_unit)
|
61
|
+
|
62
|
+
TEMPERATURE_FUNCTION_UNITS = ["degC", "tempC", "degF", "tempF"]
|
63
|
+
|
64
|
+
if from_unit in TEMPERATURE_FUNCTION_UNITS:
|
65
|
+
args = [
|
66
|
+
"units",
|
67
|
+
"-o",
|
68
|
+
"%1.15e",
|
69
|
+
"-qt1",
|
70
|
+
"".join((from_unit, "(", from_value, ")")),
|
71
|
+
]
|
72
|
+
|
73
|
+
else:
|
74
|
+
args = ["units", "-o", "%1.15e", "-qt1", " ".join((from_value, from_unit))]
|
75
|
+
|
76
|
+
if wanted_unit:
|
77
|
+
args.append(wanted_unit)
|
78
|
+
|
79
|
+
try:
|
80
|
+
output = subprocess.check_output(args).decode("utf-8")
|
81
|
+
except subprocess.CalledProcessError:
|
82
|
+
tag = wanted_unit if wanted_unit else "SI"
|
83
|
+
raise UnitConversion(
|
84
|
+
"Error in unit conversion of {} {} to {}".format(from_value, from_unit, tag)
|
85
|
+
)
|
86
|
+
|
87
|
+
matches = _units_output_expression.match(output).groupdict(None)
|
88
|
+
out = ((-1) ** from_sign * float(matches["value"]), matches["unit"] or wanted_unit)
|
89
|
+
|
90
|
+
if suppress_unit:
|
91
|
+
return out[0]
|
92
|
+
return out
|
93
|
+
|
94
|
+
|
95
|
+
# Set default behavior
|
96
|
+
convert = convert_units
|
97
|
+
|
98
|
+
|
99
|
+
def convert_list(x, from_unit, to_unit=None, convert=convert, dofit=True):
|
100
|
+
"""Thread conversion over a list, or list of lists"""
|
101
|
+
# Need a list for scoping reasons
|
102
|
+
|
103
|
+
# Constant shortcut
|
104
|
+
if from_unit in (1, 1.0, "1"):
|
105
|
+
to_unit = "1"
|
106
|
+
|
107
|
+
# get the SI unit if none provided
|
108
|
+
if to_unit is None:
|
109
|
+
_, to_unit = convert(1.0, from_unit)
|
110
|
+
|
111
|
+
def convert_inner(x, fit=None):
|
112
|
+
if isinstance(x, (list, tuple)):
|
113
|
+
return type(x)(convert_inner(i, fit=fit) for i in x)
|
114
|
+
else:
|
115
|
+
if to_unit == "1":
|
116
|
+
return float(x)
|
117
|
+
else:
|
118
|
+
if fit is not None:
|
119
|
+
return fit[0] + fit[1] * x
|
120
|
+
return float(convert(x, from_unit, to_unit, suppress_unit=True))
|
121
|
+
|
122
|
+
# setup the linear fit if we are requested to simplify
|
123
|
+
fit = None
|
124
|
+
if dofit and isinstance(x, (list, tuple)) and len(x) > 20:
|
125
|
+
a, b, linear = islinear(from_unit, to_unit)
|
126
|
+
fit = (a, b) if linear else None
|
127
|
+
|
128
|
+
output = convert_inner(x, fit=fit)
|
129
|
+
return output, to_unit
|
130
|
+
|
131
|
+
|
132
|
+
def add_si_units(doc, convert=convert):
|
133
|
+
"""Given a document, add all of the appropriate si-units fields"""
|
134
|
+
if isinstance(doc, dict):
|
135
|
+
# check for a source-unit to defined a value with units
|
136
|
+
if "source-unit" in doc:
|
137
|
+
# we've found a place to add
|
138
|
+
assert "source-value" in doc, "Badly formed doc"
|
139
|
+
o_value = doc.get("source-value", None)
|
140
|
+
o_unit = doc.get("source-unit", None)
|
141
|
+
|
142
|
+
if o_value is None:
|
143
|
+
raise UnitConversion("No source-value provided")
|
144
|
+
if o_unit is None:
|
145
|
+
raise UnitConversion("No source-unit provided")
|
146
|
+
|
147
|
+
# convert the units and insert
|
148
|
+
value, unit = convert_list(o_value, o_unit, convert=convert)
|
149
|
+
si_dict = {"si-unit": unit, "si-value": value}
|
150
|
+
doc = doc.copy()
|
151
|
+
doc.update(si_dict)
|
152
|
+
return doc
|
153
|
+
else:
|
154
|
+
# recurse
|
155
|
+
return type(doc)(
|
156
|
+
(key, add_si_units(value)) for key, value in list(doc.items())
|
157
|
+
)
|
158
|
+
|
159
|
+
elif isinstance(doc, (list, tuple)):
|
160
|
+
return type(doc)(add_si_units(x) for x in doc)
|
161
|
+
|
162
|
+
return doc
|