quantized-lab 0.8.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.
- quantized/__init__.py +11 -0
- quantized/__main__.py +14 -0
- quantized/api.py +220 -0
- quantized/app.py +206 -0
- quantized/calc/__init__.py +8 -0
- quantized/calc/_clipfit.py +76 -0
- quantized/calc/_natural_neighbor.py +219 -0
- quantized/calc/aggregate.py +159 -0
- quantized/calc/backgrounds.py +353 -0
- quantized/calc/baseline.py +349 -0
- quantized/calc/batch_fit.py +148 -0
- quantized/calc/constants.py +27 -0
- quantized/calc/corrections.py +192 -0
- quantized/calc/crystallography.py +400 -0
- quantized/calc/diffusion.py +120 -0
- quantized/calc/electrical.py +248 -0
- quantized/calc/electrochemistry.py +176 -0
- quantized/calc/element_data.json +1 -0
- quantized/calc/element_data.py +59 -0
- quantized/calc/errors.py +246 -0
- quantized/calc/figure.py +417 -0
- quantized/calc/figure_break.py +152 -0
- quantized/calc/figure_categorical.py +156 -0
- quantized/calc/figure_corner.py +229 -0
- quantized/calc/figure_facets.py +127 -0
- quantized/calc/figure_field.py +137 -0
- quantized/calc/figure_hitmap.py +116 -0
- quantized/calc/figure_labels.py +62 -0
- quantized/calc/figure_map.py +287 -0
- quantized/calc/figure_overrides.py +159 -0
- quantized/calc/figure_page.py +266 -0
- quantized/calc/figure_scale.py +125 -0
- quantized/calc/figure_statplots.py +167 -0
- quantized/calc/figure_styles.py +131 -0
- quantized/calc/figure_ternary.py +239 -0
- quantized/calc/figure_ticks.py +217 -0
- quantized/calc/fit_autoguess.py +156 -0
- quantized/calc/fit_bootstrap.py +163 -0
- quantized/calc/fit_bumps.py +258 -0
- quantized/calc/fit_constraints.py +114 -0
- quantized/calc/fit_equation.py +264 -0
- quantized/calc/fit_findxy.py +80 -0
- quantized/calc/fit_models.py +195 -0
- quantized/calc/fit_models_special.py +189 -0
- quantized/calc/fit_odr.py +99 -0
- quantized/calc/fit_scan.py +243 -0
- quantized/calc/fit_stats.py +215 -0
- quantized/calc/fitting.py +199 -0
- quantized/calc/formula.py +90 -0
- quantized/calc/global_curve_fit.py +305 -0
- quantized/calc/global_fit.py +181 -0
- quantized/calc/interp2d.py +260 -0
- quantized/calc/linecut.py +269 -0
- quantized/calc/magnetic.py +414 -0
- quantized/calc/magnetometry.py +464 -0
- quantized/calc/map.py +228 -0
- quantized/calc/mcmc.py +177 -0
- quantized/calc/optics.py +228 -0
- quantized/calc/pawley.py +251 -0
- quantized/calc/peak_batch.py +128 -0
- quantized/calc/peak_fit.py +259 -0
- quantized/calc/peak_integrate.py +104 -0
- quantized/calc/peak_multifit.py +260 -0
- quantized/calc/peak_track.py +134 -0
- quantized/calc/peaks.py +298 -0
- quantized/calc/peakshapes.py +85 -0
- quantized/calc/plotting.py +147 -0
- quantized/calc/processing.py +232 -0
- quantized/calc/qspace.py +48 -0
- quantized/calc/reductions.py +155 -0
- quantized/calc/reductions_fft.py +383 -0
- quantized/calc/refl_sld_presets.json +1 -0
- quantized/calc/reflectivity.py +80 -0
- quantized/calc/registry.py +303 -0
- quantized/calc/relaxation.py +119 -0
- quantized/calc/report.py +253 -0
- quantized/calc/report_emit.py +227 -0
- quantized/calc/resample.py +142 -0
- quantized/calc/rsm.py +91 -0
- quantized/calc/rsm_analyze.py +245 -0
- quantized/calc/semiconductor.py +488 -0
- quantized/calc/sld.py +131 -0
- quantized/calc/sld_formula.py +138 -0
- quantized/calc/spectral.py +357 -0
- quantized/calc/statplots.py +214 -0
- quantized/calc/stats.py +399 -0
- quantized/calc/stats_anova2.py +202 -0
- quantized/calc/stats_anova_ext.py +338 -0
- quantized/calc/stats_dist.py +196 -0
- quantized/calc/stats_glm.py +245 -0
- quantized/calc/stats_multivar.py +289 -0
- quantized/calc/stats_roc.py +157 -0
- quantized/calc/stats_survival.py +261 -0
- quantized/calc/stats_tests.py +380 -0
- quantized/calc/substrates.py +181 -0
- quantized/calc/superconductor.py +359 -0
- quantized/calc/surface_fit.py +290 -0
- quantized/calc/surface_models.py +156 -0
- quantized/calc/thermal.py +119 -0
- quantized/calc/thin_film.py +425 -0
- quantized/calc/unit_convert.py +259 -0
- quantized/calc/units.py +80 -0
- quantized/calc/vacuum.py +290 -0
- quantized/calc/xray.py +169 -0
- quantized/cli.py +214 -0
- quantized/datastruct.py +153 -0
- quantized/io/__init__.py +11 -0
- quantized/io/_hdf5_layout.py +308 -0
- quantized/io/_jcamp_asdf.py +135 -0
- quantized/io/_xrdml_scan.py +291 -0
- quantized/io/base.py +82 -0
- quantized/io/bruker_brml.py +177 -0
- quantized/io/bruker_raw.py +158 -0
- quantized/io/cif.py +266 -0
- quantized/io/consolidated.py +122 -0
- quantized/io/delimited.py +222 -0
- quantized/io/excel.py +135 -0
- quantized/io/hdf5.py +192 -0
- quantized/io/import_filters.py +178 -0
- quantized/io/import_preview.py +262 -0
- quantized/io/jcamp.py +179 -0
- quantized/io/lakeshore.py +163 -0
- quantized/io/ncnr.py +278 -0
- quantized/io/netcdf.py +195 -0
- quantized/io/opus.py +231 -0
- quantized/io/origin.py +346 -0
- quantized/io/origin_com.py +194 -0
- quantized/io/origin_project/__init__.py +221 -0
- quantized/io/origin_project/annotation_marks.py +288 -0
- quantized/io/origin_project/container.py +262 -0
- quantized/io/origin_project/curve_style_color.py +359 -0
- quantized/io/origin_project/figure_geometry.py +108 -0
- quantized/io/origin_project/figure_layers.py +333 -0
- quantized/io/origin_project/figure_text.py +258 -0
- quantized/io/origin_project/figures.py +210 -0
- quantized/io/origin_project/figures_opju.py +440 -0
- quantized/io/origin_project/notes.py +302 -0
- quantized/io/origin_project/opj.py +459 -0
- quantized/io/origin_project/opj_curves.py +297 -0
- quantized/io/origin_project/opj_shapes.py +148 -0
- quantized/io/origin_project/opju.py +146 -0
- quantized/io/origin_project/opju_axis_real_form.py +418 -0
- quantized/io/origin_project/opju_axis_specimen_form.py +167 -0
- quantized/io/origin_project/opju_codec.py +370 -0
- quantized/io/origin_project/opju_curves.py +497 -0
- quantized/io/origin_project/opju_curves_allcols.py +258 -0
- quantized/io/origin_project/opju_figure_curves.py +302 -0
- quantized/io/origin_project/opju_figure_text.py +245 -0
- quantized/io/origin_project/opju_reports.py +129 -0
- quantized/io/origin_project/origin_richtext.py +145 -0
- quantized/io/origin_project/preview.py +132 -0
- quantized/io/origin_project/templates.py +314 -0
- quantized/io/origin_project/tree.py +379 -0
- quantized/io/origin_project/tree_opju.py +228 -0
- quantized/io/origin_project/windows.py +238 -0
- quantized/io/origin_project/windows_opju.py +393 -0
- quantized/io/origin_project/writer.py +156 -0
- quantized/io/origin_project/writer_blocks.py +282 -0
- quantized/io/qd.py +380 -0
- quantized/io/refl1d.py +132 -0
- quantized/io/registry.py +210 -0
- quantized/io/report_export.py +347 -0
- quantized/io/rigaku.py +100 -0
- quantized/io/sims.py +398 -0
- quantized/io/spc.py +311 -0
- quantized/io/xrd_csv.py +308 -0
- quantized/io/xrdml.py +394 -0
- quantized/jobs.py +173 -0
- quantized/plugins/__init__.py +50 -0
- quantized/plugins/contract.py +111 -0
- quantized/plugins/loader.py +394 -0
- quantized/plugins/steps.py +90 -0
- quantized/routes/__init__.py +7 -0
- quantized/routes/_bookcache.py +62 -0
- quantized/routes/_export_common.py +27 -0
- quantized/routes/_payload.py +58 -0
- quantized/routes/_uploadcache.py +59 -0
- quantized/routes/aggregate.py +46 -0
- quantized/routes/baseline.py +210 -0
- quantized/routes/books.py +117 -0
- quantized/routes/calc.py +56 -0
- quantized/routes/corrections.py +78 -0
- quantized/routes/crystallography.py +80 -0
- quantized/routes/diffusion.py +58 -0
- quantized/routes/electrical.py +101 -0
- quantized/routes/electrochemistry.py +83 -0
- quantized/routes/export.py +280 -0
- quantized/routes/export_facets.py +83 -0
- quantized/routes/export_figures.py +471 -0
- quantized/routes/export_page.py +125 -0
- quantized/routes/fitting.py +379 -0
- quantized/routes/fitting_bumps.py +97 -0
- quantized/routes/import_template.py +97 -0
- quantized/routes/import_wizard.py +150 -0
- quantized/routes/jobs_api.py +59 -0
- quantized/routes/magnetic.py +135 -0
- quantized/routes/magnetometry.py +133 -0
- quantized/routes/optics.py +98 -0
- quantized/routes/parsers.py +281 -0
- quantized/routes/peaks.py +184 -0
- quantized/routes/plot.py +103 -0
- quantized/routes/reductions.py +121 -0
- quantized/routes/reference.py +58 -0
- quantized/routes/reflectivity.py +91 -0
- quantized/routes/report_export.py +119 -0
- quantized/routes/rsm.py +136 -0
- quantized/routes/samples.py +32 -0
- quantized/routes/semiconductor.py +207 -0
- quantized/routes/sld.py +42 -0
- quantized/routes/spectral.py +54 -0
- quantized/routes/statplots.py +99 -0
- quantized/routes/stats.py +418 -0
- quantized/routes/stats_design.py +321 -0
- quantized/routes/substrates.py +46 -0
- quantized/routes/superconductor.py +139 -0
- quantized/routes/thermal.py +57 -0
- quantized/routes/thin_film.py +153 -0
- quantized/routes/vacuum.py +113 -0
- quantized/routes/xray.py +32 -0
- quantized/samples/demo_vsm.csv +42 -0
- quantized/server_launch.py +251 -0
- quantized/web/assets/JetBrainsMono-Bold-CUogYd9I.woff2 +0 -0
- quantized/web/assets/JetBrainsMono-Regular-CA-Os4ii.woff2 +0 -0
- quantized/web/assets/index-BHmmCL-x.js +27 -0
- quantized/web/assets/index-BiZzN7J6.css +1 -0
- quantized/web/index.html +13 -0
- quantized/web/loading.html +69 -0
- quantized_lab-0.8.0.dist-info/METADATA +122 -0
- quantized_lab-0.8.0.dist-info/RECORD +233 -0
- quantized_lab-0.8.0.dist-info/WHEEL +4 -0
- quantized_lab-0.8.0.dist-info/entry_points.txt +4 -0
- quantized_lab-0.8.0.dist-info/licenses/LICENSE +201 -0
- quantized_lab-0.8.0.dist-info/licenses/NOTICE +11 -0
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
"""Substrate property database + lattice-mismatch calculator.
|
|
2
|
+
|
|
3
|
+
Ports the MATLAB ``+calc/+substrates`` package (``listSubstrates`` /
|
|
4
|
+
``getSubstrate``) and the epitaxial-mismatch formula from
|
|
5
|
+
``+calc/+crystal/latticeMismatch.m``. Pure calc layer — no fastapi / pydantic
|
|
6
|
+
imports; data in → result dicts out.
|
|
7
|
+
|
|
8
|
+
The substrate table is a curated reference of common single-crystal oxide and
|
|
9
|
+
semiconductor substrates with their room-temperature lattice parameters
|
|
10
|
+
(Angstrom), coefficient of thermal expansion (CTE, 1e-6/K), relative
|
|
11
|
+
permittivity ``eps_r``, mass density (g/cm³) and lattice type. Values are
|
|
12
|
+
ported **verbatim** from the MATLAB ``getSubstrate`` table (the behavioural
|
|
13
|
+
reference) — do not "fix" them.
|
|
14
|
+
|
|
15
|
+
Lattice convention follows MATLAB ``getSubstrate``:
|
|
16
|
+
- ``cubic`` → a = b = c, α = β = γ = 90°
|
|
17
|
+
- ``hexagonal`` → a = b, c distinct, α = β = 90°, γ = 120°
|
|
18
|
+
- ``amorphous`` → no lattice (a/b/c and angles are ``None``)
|
|
19
|
+
|
|
20
|
+
Lattice mismatch (``latticeMismatch``):
|
|
21
|
+
|
|
22
|
+
.. math::
|
|
23
|
+
|
|
24
|
+
f = \frac{a_\text{film} - a_\text{sub}}{a_\text{sub}}
|
|
25
|
+
|
|
26
|
+
Positive ``f`` (film larger) → biaxial tension; negative → biaxial
|
|
27
|
+
compression; |f| ≤ 1e-6 → matched.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
from __future__ import annotations
|
|
31
|
+
|
|
32
|
+
from typing import Any
|
|
33
|
+
|
|
34
|
+
__all__ = [
|
|
35
|
+
"get_substrate",
|
|
36
|
+
"lattice_mismatch",
|
|
37
|
+
"list_substrates",
|
|
38
|
+
"substrate_table",
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
# Raw table ported verbatim from MATLAB getSubstrate.BuildTable():
|
|
42
|
+
# (name, formula, orientation, a, c, CTE[1e-6/K], eps_r, density[g/cm^3], type)
|
|
43
|
+
# c is None for cubic (set to a); a/c None for amorphous. DO NOT "fix" values.
|
|
44
|
+
_RAW: list[tuple[str, str, str, float | None, float | None, float, float, float, str]] = [
|
|
45
|
+
("Si(100)", "Si", "(100)", 5.431, None, 2.6, 11.7, 2.329, "cubic"),
|
|
46
|
+
("Si(111)", "Si", "(111)", 5.431, None, 2.6, 11.7, 2.329, "cubic"),
|
|
47
|
+
("SiO2/Si", "SiO2", "amorphous", None, None, 0.5, 3.9, 2.20, "amorphous"),
|
|
48
|
+
("Al2O3(0001)", "Al2O3", "(0001)", 4.758, 12.991, 5.0, 9.0, 3.987, "hexagonal"),
|
|
49
|
+
("Al2O3(11-20)", "Al2O3", "(11-20)", 4.758, 12.991, 5.0, 9.0, 3.987, "hexagonal"),
|
|
50
|
+
("MgO(100)", "MgO", "(100)", 4.212, None, 10.5, 9.8, 3.585, "cubic"),
|
|
51
|
+
("SrTiO3(100)", "SrTiO3", "(100)", 3.905, None, 11.0, 300.0, 5.117, "cubic"),
|
|
52
|
+
("GaAs(100)", "GaAs", "(100)", 5.653, None, 5.73, 12.9, 5.317, "cubic"),
|
|
53
|
+
("LaAlO3(100)", "LaAlO3", "(100)", 3.789, None, 10.0, 24.0, 6.52, "cubic"),
|
|
54
|
+
("LSAT(100)", "LSAT", "(100)", 3.868, None, 10.0, 22.0, 6.74, "cubic"),
|
|
55
|
+
("Ge(100)", "Ge", "(100)", 5.658, None, 5.9, 16.0, 5.323, "cubic"),
|
|
56
|
+
("InP(100)", "InP", "(100)", 5.869, None, 4.6, 12.5, 4.81, "cubic"),
|
|
57
|
+
("YSZ(100)", "YSZ", "(100)", 5.125, None, 10.5, 27.0, 5.96, "cubic"),
|
|
58
|
+
("MgAl2O4(100)", "MgAl2O4", "(100)", 8.083, None, 7.45, 8.1, 3.578, "cubic"),
|
|
59
|
+
]
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _build_row(
|
|
63
|
+
name: str,
|
|
64
|
+
formula: str,
|
|
65
|
+
orientation: str,
|
|
66
|
+
a_val: float | None,
|
|
67
|
+
c_val: float | None,
|
|
68
|
+
cte: float,
|
|
69
|
+
eps_r: float,
|
|
70
|
+
density: float,
|
|
71
|
+
lattice_type: str,
|
|
72
|
+
) -> dict[str, Any]:
|
|
73
|
+
"""Expand one raw tuple into a full substrate dict (MATLAB BuildTable logic)."""
|
|
74
|
+
if lattice_type == "cubic":
|
|
75
|
+
a_out: float | None = a_val
|
|
76
|
+
b_out: float | None = a_val
|
|
77
|
+
c_out: float | None = a_val
|
|
78
|
+
alpha: float | None = 90.0
|
|
79
|
+
beta: float | None = 90.0
|
|
80
|
+
gamma: float | None = 90.0
|
|
81
|
+
elif lattice_type == "hexagonal":
|
|
82
|
+
a_out = a_val
|
|
83
|
+
b_out = a_val
|
|
84
|
+
c_out = c_val
|
|
85
|
+
alpha, beta, gamma = 90.0, 90.0, 120.0
|
|
86
|
+
elif lattice_type == "amorphous":
|
|
87
|
+
a_out = b_out = c_out = None
|
|
88
|
+
alpha = beta = gamma = None
|
|
89
|
+
else: # pragma: no cover - guarded by the static table
|
|
90
|
+
raise ValueError(f'Unrecognised lattice type "{lattice_type}" in substrate table.')
|
|
91
|
+
return {
|
|
92
|
+
"name": name,
|
|
93
|
+
"formula": formula,
|
|
94
|
+
"orientation": orientation,
|
|
95
|
+
"a": a_out,
|
|
96
|
+
"b": b_out,
|
|
97
|
+
"c": c_out,
|
|
98
|
+
"alpha": alpha,
|
|
99
|
+
"beta": beta,
|
|
100
|
+
"gamma": gamma,
|
|
101
|
+
"thermalExpansion": cte,
|
|
102
|
+
"dielectric": eps_r,
|
|
103
|
+
"density": density,
|
|
104
|
+
"latticeType": lattice_type,
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
_TABLE: list[dict[str, Any]] | None = None
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def substrate_table() -> list[dict[str, Any]]:
|
|
112
|
+
"""Return the full substrate table (list of property dicts), built once."""
|
|
113
|
+
global _TABLE
|
|
114
|
+
if _TABLE is None:
|
|
115
|
+
_TABLE = [_build_row(*row) for row in _RAW]
|
|
116
|
+
return _TABLE
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def list_substrates() -> list[str]:
|
|
120
|
+
"""Return the canonical list of substrate names (MATLAB ``listSubstrates``)."""
|
|
121
|
+
return [row["name"] for row in substrate_table()]
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def _closest_name(name: str) -> str:
|
|
125
|
+
"""Suggest the closest known name by shared-character overlap (MATLAB logic)."""
|
|
126
|
+
name_low = set(name.lower())
|
|
127
|
+
best = ""
|
|
128
|
+
best_score = -1
|
|
129
|
+
for row in substrate_table():
|
|
130
|
+
score = sum(1 for ch in row["name"].lower() if ch in name_low)
|
|
131
|
+
if score > best_score:
|
|
132
|
+
best_score = score
|
|
133
|
+
best = row["name"]
|
|
134
|
+
return best
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def get_substrate(name: str) -> dict[str, Any]:
|
|
138
|
+
"""Return the property dict for a named substrate (case-insensitive).
|
|
139
|
+
|
|
140
|
+
Mirrors MATLAB ``calc.substrates.getSubstrate``: exact case-insensitive
|
|
141
|
+
match; on miss, raises with the closest-by-character-overlap suggestion.
|
|
142
|
+
|
|
143
|
+
>>> get_substrate("SrTiO3(100)")["a"]
|
|
144
|
+
3.905
|
|
145
|
+
>>> get_substrate("si(100)")["latticeType"]
|
|
146
|
+
'cubic'
|
|
147
|
+
"""
|
|
148
|
+
for row in substrate_table():
|
|
149
|
+
if row["name"].lower() == name.lower():
|
|
150
|
+
return row
|
|
151
|
+
suggestion = _closest_name(name)
|
|
152
|
+
raise ValueError(f'Unknown substrate "{name}". Did you mean "{suggestion}"?')
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def lattice_mismatch(a_film: float, a_sub: float) -> dict[str, Any]:
|
|
156
|
+
"""Epitaxial lattice mismatch f = (a_film - a_sub)/a_sub.
|
|
157
|
+
|
|
158
|
+
Ports ``calc.crystal.latticeMismatch``. Positive ``f`` → film under biaxial
|
|
159
|
+
tension; negative → compression; |f| ≤ 1e-6 → matched.
|
|
160
|
+
|
|
161
|
+
Args:
|
|
162
|
+
a_film: in-plane film lattice parameter (Å), > 0.
|
|
163
|
+
a_sub: substrate lattice parameter (Å), > 0.
|
|
164
|
+
|
|
165
|
+
>>> r = lattice_mismatch(3.876, 3.905)
|
|
166
|
+
>>> round(r["mismatchPct"], 4)
|
|
167
|
+
-0.7426
|
|
168
|
+
>>> r["description"]
|
|
169
|
+
'compressive'
|
|
170
|
+
"""
|
|
171
|
+
if a_film <= 0 or a_sub <= 0:
|
|
172
|
+
raise ValueError("a_film and a_sub must be positive")
|
|
173
|
+
f = (a_film - a_sub) / a_sub
|
|
174
|
+
f_pct = f * 100.0
|
|
175
|
+
if f > 1e-6:
|
|
176
|
+
desc = "tensile"
|
|
177
|
+
elif f < -1e-6:
|
|
178
|
+
desc = "compressive"
|
|
179
|
+
else:
|
|
180
|
+
desc = "matched"
|
|
181
|
+
return {"mismatch": f, "mismatchPct": f_pct, "description": desc}
|
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
r"""Superconductivity calculators (DiraCulator ``buildSuperconductorTab`` +
|
|
2
|
+
``+calc/+superconductor``).
|
|
3
|
+
|
|
4
|
+
Pure calc layer — closed-form scalars in, result dicts out. No fastapi /
|
|
5
|
+
pydantic imports. Ports the scalar MATLAB ``calc.superconductor`` functions
|
|
6
|
+
verbatim (London depth, coherence length, GL parameter, critical fields,
|
|
7
|
+
depairing current) plus the material-preset table and the weak-coupling BCS
|
|
8
|
+
gap relation.
|
|
9
|
+
|
|
10
|
+
.. math::
|
|
11
|
+
|
|
12
|
+
\lambda(T) = \frac{\lambda_0}{\sqrt{1 - (T/T_c)^4}} \qquad
|
|
13
|
+
\xi(T) = \frac{\xi_0}{\sqrt{1 - (T/T_c)^2}} \\
|
|
14
|
+
\kappa = \lambda/\xi \qquad
|
|
15
|
+
H_c(T) = H_{c0}\,(1 - (T/T_c)^2) \\
|
|
16
|
+
H_{c1} = \frac{\Phi_0}{4\pi\lambda^2}\,(\ln\kappa + 0.5) \qquad
|
|
17
|
+
H_{c2} = \frac{\Phi_0}{2\pi\xi^2} \\
|
|
18
|
+
J_d(T) = \frac{H_c(T)}{3\sqrt{6}\,\pi\,\lambda(T)} \qquad
|
|
19
|
+
\Delta_0 = 1.764\,k_B T_c
|
|
20
|
+
|
|
21
|
+
Conventions follow the MATLAB toolbox (the behavioural reference): lengths in
|
|
22
|
+
nm, temperatures in K, fields in Oe (Gaussian CGS, ``Φ₀`` converted to G·cm²),
|
|
23
|
+
current density in A/cm², gap in meV. ``type`` is ``'I'`` for
|
|
24
|
+
``κ < 1/√2 ≈ 0.7071`` and ``'II'`` otherwise.
|
|
25
|
+
|
|
26
|
+
Reference values (from the MATLAB docstrings / closed form):
|
|
27
|
+
- ``london_depth(39, 4.2, 9.25) -> lambda ≈ 39.86`` nm (Nb)
|
|
28
|
+
- ``coherence_length(38, 4.2, 9.25) -> xi ≈ 42.65`` nm (Nb)
|
|
29
|
+
- ``gl_parameter(39, 38) -> kappa ≈ 1.026`` (Nb, type II)
|
|
30
|
+
- ``critical_fields(1980, 9.25, 4.2) Nb -> type II, Hc1 < Hc < Hc2``
|
|
31
|
+
- ``bcs_gap(9.25) -> delta0 ≈ 1.406 meV, ratio = 3.528`` (weak coupling)
|
|
32
|
+
|
|
33
|
+
The three array-based data-reduction routines in the MATLAB package
|
|
34
|
+
(``bcsGap`` curve fitting, ``extractTc`` from R(T), ``beanJc`` from M(H)) are
|
|
35
|
+
measurement-analysis functions, not calculator-tab features, and are deferred
|
|
36
|
+
to the transport / magnetometry analysis surface.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
from __future__ import annotations
|
|
40
|
+
|
|
41
|
+
import math
|
|
42
|
+
from typing import Any
|
|
43
|
+
|
|
44
|
+
from quantized.calc.constants import constants
|
|
45
|
+
|
|
46
|
+
__all__ = [
|
|
47
|
+
"bcs_gap",
|
|
48
|
+
"coherence_length",
|
|
49
|
+
"critical_fields",
|
|
50
|
+
"depairing_current",
|
|
51
|
+
"gl_parameter",
|
|
52
|
+
"london_depth",
|
|
53
|
+
"material_presets",
|
|
54
|
+
]
|
|
55
|
+
|
|
56
|
+
# κ boundary between type-I and type-II superconductors.
|
|
57
|
+
_KAPPA_BOUNDARY = 1.0 / math.sqrt(2.0) # ≈ 0.7071
|
|
58
|
+
|
|
59
|
+
# Weak-coupling BCS: 2*Delta0/(kB*Tc) = 3.528 -> Delta0 = 1.764*kB*Tc.
|
|
60
|
+
_BCS_RATIO = 3.528
|
|
61
|
+
_BCS_HALF = _BCS_RATIO / 2.0 # 1.764
|
|
62
|
+
|
|
63
|
+
# Material presets (Tinkham 2nd ed.; Orlando & Delin). lambda0/xi0 in nm,
|
|
64
|
+
# Hc0 in Oe, Delta0 in meV. Port verbatim from materialPresets.m.
|
|
65
|
+
_PRESETS: dict[str, dict[str, Any]] = {
|
|
66
|
+
"Nb": {"Tc": 9.25, "lambda0": 39.0, "xi0": 38.0, "Hc0": 1980.0, "Delta0": 1.55, "type": "II"},
|
|
67
|
+
"NbN": {"Tc": 16.0, "lambda0": 200.0, "xi0": 5.0, "Hc0": 80000.0, "Delta0": 2.6, "type": "II"},
|
|
68
|
+
"YBCO": {"Tc": 92.0, "lambda0": 150.0, "xi0": 1.5, "Hc0": 0.0, "Delta0": 20.0, "type": "II"},
|
|
69
|
+
"MgB2": {"Tc": 39.0, "lambda0": 140.0, "xi0": 5.0, "Hc0": 0.0, "Delta0": 7.1, "type": "II"},
|
|
70
|
+
"Al": {"Tc": 1.18, "lambda0": 16.0, "xi0": 1600.0, "Hc0": 105.0, "Delta0": 0.172, "type": "I"},
|
|
71
|
+
"Pb": {"Tc": 7.19, "lambda0": 37.0, "xi0": 83.0, "Hc0": 803.0, "Delta0": 1.33, "type": "I"},
|
|
72
|
+
"In": {"Tc": 3.41, "lambda0": 24.0, "xi0": 440.0, "Hc0": 282.0, "Delta0": 0.541, "type": "I"},
|
|
73
|
+
"Sn": {"Tc": 3.72, "lambda0": 34.0, "xi0": 230.0, "Hc0": 305.0, "Delta0": 0.592, "type": "I"},
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def material_presets(material: str | None = None) -> dict[str, Any]:
|
|
78
|
+
"""Superconductor material property table (``materialPresets.m``).
|
|
79
|
+
|
|
80
|
+
Returns the full table keyed by material name when ``material`` is omitted,
|
|
81
|
+
or a single material's properties (``Tc``, ``lambda0``, ``xi0``, ``Hc0``,
|
|
82
|
+
``Delta0``, ``type``) when given. Names are matched case-insensitively.
|
|
83
|
+
|
|
84
|
+
>>> material_presets("Nb")["Tc"]
|
|
85
|
+
9.25
|
|
86
|
+
>>> sorted(material_presets()["materials"]) # doctest: +ELLIPSIS
|
|
87
|
+
['Al', 'In', 'MgB2', 'Nb', 'NbN', 'Pb', 'Sn', 'YBCO']
|
|
88
|
+
"""
|
|
89
|
+
if material is None or material == "":
|
|
90
|
+
return {"materials": {k: dict(v) for k, v in _PRESETS.items()}}
|
|
91
|
+
key = _match_material(material)
|
|
92
|
+
return dict(_PRESETS[key])
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def _match_material(material: str) -> str:
|
|
96
|
+
for name in _PRESETS:
|
|
97
|
+
if name.lower() == material.lower():
|
|
98
|
+
return name
|
|
99
|
+
valid = ", ".join(_PRESETS)
|
|
100
|
+
raise ValueError(f"Unknown material '{material}'. Valid options: {valid}.")
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _resolve(
|
|
104
|
+
explicit: float | None, material: str | None, field: str, label: str
|
|
105
|
+
) -> float:
|
|
106
|
+
"""Explicit value if given, else the preset field, else an error."""
|
|
107
|
+
if explicit is not None:
|
|
108
|
+
return explicit
|
|
109
|
+
if material:
|
|
110
|
+
return float(_PRESETS[_match_material(material)][field])
|
|
111
|
+
raise ValueError(f"Provide '{label}' or a Material name.")
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def _check_below_tc(t: float, tc: float) -> None:
|
|
115
|
+
if tc <= 0:
|
|
116
|
+
raise ValueError("Tc must be positive")
|
|
117
|
+
if t < 0:
|
|
118
|
+
raise ValueError("T must be non-negative")
|
|
119
|
+
if t >= tc:
|
|
120
|
+
raise ValueError(f"T ({t:.4g} K) must be below Tc ({tc:.4g} K).")
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def london_depth(
|
|
124
|
+
lambda0: float | None = None,
|
|
125
|
+
t: float = 0.0,
|
|
126
|
+
tc: float | None = None,
|
|
127
|
+
*,
|
|
128
|
+
material: str | None = None,
|
|
129
|
+
) -> dict[str, float]:
|
|
130
|
+
"""London penetration depth at temperature T (``londonDepth.m``).
|
|
131
|
+
|
|
132
|
+
Two-fluid (Gorter-Casimir) approximation
|
|
133
|
+
``λ(T) = λ₀ / √(1 − (T/T_c)⁴)``.
|
|
134
|
+
|
|
135
|
+
Args:
|
|
136
|
+
lambda0: zero-temperature London depth λ₀ (nm); from preset if omitted.
|
|
137
|
+
t: measurement temperature (K), 0 ≤ t < Tc.
|
|
138
|
+
tc: critical temperature (K); from preset if omitted.
|
|
139
|
+
material: optional preset name supplying λ₀ and Tc.
|
|
140
|
+
|
|
141
|
+
>>> round(london_depth(39.0, 4.2, 9.25)["lambda"], 2)
|
|
142
|
+
39.86
|
|
143
|
+
"""
|
|
144
|
+
lam0 = _resolve(lambda0, material, "lambda0", "lambda0")
|
|
145
|
+
tc_v = _resolve(tc, material, "Tc", "Tc")
|
|
146
|
+
if lam0 <= 0:
|
|
147
|
+
raise ValueError("lambda0 must be positive")
|
|
148
|
+
_check_below_tc(t, tc_v)
|
|
149
|
+
lam = lam0 / math.sqrt(1.0 - (t / tc_v) ** 4)
|
|
150
|
+
return {"lambda": lam, "lambda0": lam0, "T": t, "Tc": tc_v}
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def coherence_length(
|
|
154
|
+
xi0: float | None = None,
|
|
155
|
+
t: float = 0.0,
|
|
156
|
+
tc: float | None = None,
|
|
157
|
+
*,
|
|
158
|
+
material: str | None = None,
|
|
159
|
+
) -> dict[str, float]:
|
|
160
|
+
"""BCS coherence length at temperature T (``coherenceLength.m``).
|
|
161
|
+
|
|
162
|
+
Gorkov / GL-regime temperature dependence
|
|
163
|
+
``ξ(T) = ξ₀ / √(1 − (T/T_c)²)``.
|
|
164
|
+
|
|
165
|
+
>>> round(coherence_length(38.0, 4.2, 9.25)["xi"], 2)
|
|
166
|
+
42.65
|
|
167
|
+
"""
|
|
168
|
+
xi0_v = _resolve(xi0, material, "xi0", "xi0")
|
|
169
|
+
tc_v = _resolve(tc, material, "Tc", "Tc")
|
|
170
|
+
if xi0_v <= 0:
|
|
171
|
+
raise ValueError("xi0 must be positive")
|
|
172
|
+
_check_below_tc(t, tc_v)
|
|
173
|
+
xi = xi0_v / math.sqrt(1.0 - (t / tc_v) ** 2)
|
|
174
|
+
return {"xi": xi, "xi0": xi0_v, "T": t, "Tc": tc_v}
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def _sc_type(kappa: float) -> str:
|
|
178
|
+
return "I" if kappa < _KAPPA_BOUNDARY else "II"
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def gl_parameter(
|
|
182
|
+
lambda_: float | None = None,
|
|
183
|
+
xi: float | None = None,
|
|
184
|
+
*,
|
|
185
|
+
material: str | None = None,
|
|
186
|
+
t: float | None = None,
|
|
187
|
+
) -> dict[str, Any]:
|
|
188
|
+
"""Ginzburg-Landau parameter κ = λ/ξ (``glParameter.m``).
|
|
189
|
+
|
|
190
|
+
Type-I when ``κ < 1/√2 ≈ 0.7071``, type-II otherwise. When ``material``
|
|
191
|
+
and ``t`` are given, λ and ξ are computed at T from the preset.
|
|
192
|
+
|
|
193
|
+
>>> r = gl_parameter(39.0, 38.0)
|
|
194
|
+
>>> round(r["kappa"], 4), r["type"]
|
|
195
|
+
(1.0263, 'II')
|
|
196
|
+
"""
|
|
197
|
+
lam = lambda_
|
|
198
|
+
xi_v = xi
|
|
199
|
+
if material:
|
|
200
|
+
if t is None:
|
|
201
|
+
raise ValueError("Provide T when using Material for glParameter.")
|
|
202
|
+
if lam is None:
|
|
203
|
+
lam = london_depth(t=t, material=material)["lambda"]
|
|
204
|
+
if xi_v is None:
|
|
205
|
+
xi_v = coherence_length(t=t, material=material)["xi"]
|
|
206
|
+
if lam is None or xi_v is None:
|
|
207
|
+
raise ValueError("Provide both lambda and xi, or a Material name with T.")
|
|
208
|
+
if lam <= 0 or xi_v <= 0:
|
|
209
|
+
raise ValueError("lambda and xi must be positive")
|
|
210
|
+
kappa = lam / xi_v
|
|
211
|
+
return {"kappa": kappa, "lambda": lam, "xi": xi_v, "type": _sc_type(kappa)}
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def critical_fields(
|
|
215
|
+
hc0: float | None = None,
|
|
216
|
+
tc: float | None = None,
|
|
217
|
+
t: float = 0.0,
|
|
218
|
+
*,
|
|
219
|
+
material: str | None = None,
|
|
220
|
+
lambda_: float | None = None,
|
|
221
|
+
xi: float | None = None,
|
|
222
|
+
kappa: float | None = None,
|
|
223
|
+
) -> dict[str, Any]:
|
|
224
|
+
"""Superconducting critical fields at temperature T (``criticalFields.m``).
|
|
225
|
+
|
|
226
|
+
Thermodynamic ``Hc(T) = Hc0·(1 − (T/T_c)²)`` for both types. For type-II
|
|
227
|
+
(preset type ``'II'``, ``Hc0`` zero/absent, or explicit λ+ξ / κ):
|
|
228
|
+
|
|
229
|
+
- ``Hc1 = (Φ₀/(4πλ²))·(ln κ + 0.5)`` (Tinkham Eq. 5.11, NaN for type-I),
|
|
230
|
+
- ``Hc2 = Φ₀/(2πξ²)``,
|
|
231
|
+
|
|
232
|
+
with Φ₀ in G·cm² and λ, ξ in cm. All fields in Oe.
|
|
233
|
+
|
|
234
|
+
Args:
|
|
235
|
+
hc0: thermodynamic critical field at T=0 (Oe); from preset if omitted.
|
|
236
|
+
tc: critical temperature (K); from preset if omitted.
|
|
237
|
+
t: measurement temperature (K), 0 ≤ t < Tc.
|
|
238
|
+
material: optional preset name; supplies Hc0, Tc, type, and λ/ξ at T.
|
|
239
|
+
lambda_, xi: λ(T), ξ(T) in nm (override / direct type-II input).
|
|
240
|
+
kappa: GL parameter override (else λ/ξ).
|
|
241
|
+
|
|
242
|
+
>>> r = critical_fields(material="Nb", t=4.2)
|
|
243
|
+
>>> r["type"], r["Hc1"] < r["Hc"] < r["Hc2"]
|
|
244
|
+
('II', True)
|
|
245
|
+
"""
|
|
246
|
+
if material:
|
|
247
|
+
preset = material_presets(material)
|
|
248
|
+
hc0_v = hc0 if hc0 is not None else float(preset["Hc0"])
|
|
249
|
+
tc_v = tc if tc is not None else float(preset["Tc"])
|
|
250
|
+
sc_type: str = str(preset["type"])
|
|
251
|
+
else:
|
|
252
|
+
if hc0 is None or tc is None:
|
|
253
|
+
raise ValueError("Provide Hc0 and Tc, or a Material name.")
|
|
254
|
+
hc0_v = hc0
|
|
255
|
+
tc_v = tc
|
|
256
|
+
sc_type = ""
|
|
257
|
+
_check_below_tc(t, tc_v)
|
|
258
|
+
|
|
259
|
+
t_red = t / tc_v
|
|
260
|
+
hc = hc0_v * (1.0 - t_red**2)
|
|
261
|
+
|
|
262
|
+
phi0_gcm2 = constants()["Phi0"] * 1e8 # Wb -> G*cm^2 (= 2.0678e-7)
|
|
263
|
+
hc1 = math.nan
|
|
264
|
+
hc2 = math.nan
|
|
265
|
+
|
|
266
|
+
has_type_ii = (lambda_ is not None and xi is not None) or kappa is not None
|
|
267
|
+
if sc_type == "II" or hc0_v == 0 or has_type_ii:
|
|
268
|
+
lam = lambda_
|
|
269
|
+
xi_v = xi
|
|
270
|
+
kap = kappa
|
|
271
|
+
if lam is None and material:
|
|
272
|
+
lam = london_depth(t=t, material=material)["lambda"]
|
|
273
|
+
if xi_v is None and material:
|
|
274
|
+
xi_v = coherence_length(t=t, material=material)["xi"]
|
|
275
|
+
if kap is None and lam is not None and xi_v is not None:
|
|
276
|
+
kap = lam / xi_v
|
|
277
|
+
|
|
278
|
+
if lam is not None and xi_v is not None and kap is not None:
|
|
279
|
+
lam_cm = lam * 1e-7
|
|
280
|
+
xi_cm = xi_v * 1e-7
|
|
281
|
+
if kap > _KAPPA_BOUNDARY:
|
|
282
|
+
hc1 = phi0_gcm2 * (math.log(kap) + 0.5) / (4 * math.pi * lam_cm**2)
|
|
283
|
+
hc2 = phi0_gcm2 / (2 * math.pi * xi_cm**2)
|
|
284
|
+
if not sc_type:
|
|
285
|
+
sc_type = "II"
|
|
286
|
+
|
|
287
|
+
if not sc_type:
|
|
288
|
+
sc_type = "I"
|
|
289
|
+
|
|
290
|
+
return {"Hc": hc, "Hc1": hc1, "Hc2": hc2, "type": sc_type, "T": t, "Tc": tc_v}
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
def depairing_current(
|
|
294
|
+
hc0: float | None = None,
|
|
295
|
+
lambda0: float | None = None,
|
|
296
|
+
tc: float | None = None,
|
|
297
|
+
t: float = 0.0,
|
|
298
|
+
*,
|
|
299
|
+
material: str | None = None,
|
|
300
|
+
) -> dict[str, float]:
|
|
301
|
+
"""Depairing (pair-breaking) current density (``depairingCurrent.m``).
|
|
302
|
+
|
|
303
|
+
``Jd(T) = Hc(T) / (3√6·π·λ(T))`` in Gaussian CGS, converted to A/cm² via
|
|
304
|
+
``1 Oe/cm = (10³/4π) A/cm²``. Hc(T) from :func:`critical_fields`, λ(T) from
|
|
305
|
+
:func:`london_depth`.
|
|
306
|
+
|
|
307
|
+
>>> r = depairing_current(1980.0, 39.0, 9.25, 4.2)
|
|
308
|
+
>>> r["JdMA"] > 0
|
|
309
|
+
True
|
|
310
|
+
"""
|
|
311
|
+
hc0_v = _resolve(hc0, material, "Hc0", "Hc0")
|
|
312
|
+
lam0 = _resolve(lambda0, material, "lambda0", "lambda0")
|
|
313
|
+
tc_v = _resolve(tc, material, "Tc", "Tc")
|
|
314
|
+
_check_below_tc(t, tc_v)
|
|
315
|
+
|
|
316
|
+
if material:
|
|
317
|
+
hc_t = critical_fields(material=material, t=t)["Hc"]
|
|
318
|
+
lam_t = london_depth(t=t, material=material)["lambda"]
|
|
319
|
+
else:
|
|
320
|
+
hc_t = critical_fields(hc0=hc0_v, tc=tc_v, t=t)["Hc"]
|
|
321
|
+
lam_t = london_depth(lambda0=lam0, t=t, tc=tc_v)["lambda"]
|
|
322
|
+
|
|
323
|
+
lam_cm = lam_t * 1e-7
|
|
324
|
+
jd_cgs = hc_t / (3 * math.sqrt(6) * math.pi * lam_cm)
|
|
325
|
+
jd_acm2 = jd_cgs * (1e3 / (4 * math.pi))
|
|
326
|
+
return {"Jd": jd_acm2, "JdMA": jd_acm2 * 1e-6, "T": t, "Tc": tc_v}
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
def bcs_gap(tc: float, t: float | None = None) -> dict[str, float]:
|
|
330
|
+
"""Weak-coupling BCS energy gap from Tc.
|
|
331
|
+
|
|
332
|
+
Zero-temperature gap ``Δ₀ = 1.764·k_B·T_c`` (the weak-coupling relation
|
|
333
|
+
``2Δ₀/(k_B T_c) = 3.528``). When ``t`` is given (0 < t < Tc) the
|
|
334
|
+
Mühlschlegel approximation ``Δ(T) = Δ₀·tanh(1.74·√(T_c/T − 1))`` is also
|
|
335
|
+
returned. Gap values in meV.
|
|
336
|
+
|
|
337
|
+
>>> r = bcs_gap(9.25)
|
|
338
|
+
>>> round(r["delta0"], 3), round(r["ratio"], 3)
|
|
339
|
+
(1.406, 3.528)
|
|
340
|
+
"""
|
|
341
|
+
if tc <= 0:
|
|
342
|
+
raise ValueError("Tc must be positive")
|
|
343
|
+
kb_mev = constants()["kB"] / constants()["e"] * 1e3 # meV/K
|
|
344
|
+
delta0 = _BCS_HALF * kb_mev * tc
|
|
345
|
+
delta_t = delta0
|
|
346
|
+
if t is not None:
|
|
347
|
+
if t < 0:
|
|
348
|
+
raise ValueError("T must be non-negative")
|
|
349
|
+
if t == 0 or t >= tc:
|
|
350
|
+
delta_t = 0.0 if t >= tc else delta0
|
|
351
|
+
else:
|
|
352
|
+
delta_t = delta0 * math.tanh(1.74 * math.sqrt(tc / t - 1.0))
|
|
353
|
+
return {
|
|
354
|
+
"delta0": delta0,
|
|
355
|
+
"ratio": _BCS_RATIO,
|
|
356
|
+
"deltaT": delta_t,
|
|
357
|
+
"Tc": tc,
|
|
358
|
+
"T": t if t is not None else 0.0,
|
|
359
|
+
}
|